chore: introduce session.sendMayFail to ease error logging (#2480)
This commit is contained in:
parent
fc2432a23a
commit
c08da50bb3
|
|
@ -29,7 +29,6 @@ import { readProtocolStream } from './crProtocolHelper';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { CRExecutionContext } from './crExecutionContext';
|
import { CRExecutionContext } from './crExecutionContext';
|
||||||
import { logError } from '../logger';
|
|
||||||
import { CRDevTools } from '../debug/crDevTools';
|
import { CRDevTools } from '../debug/crDevTools';
|
||||||
|
|
||||||
export class CRBrowser extends BrowserBase {
|
export class CRBrowser extends BrowserBase {
|
||||||
|
|
@ -133,8 +132,8 @@ export class CRBrowser extends BrowserBase {
|
||||||
if (targetInfo.type === 'other' || !context) {
|
if (targetInfo.type === 'other' || !context) {
|
||||||
if (waitingForDebugger) {
|
if (waitingForDebugger) {
|
||||||
// Ideally, detaching should resume any target, but there is a bug in the backend.
|
// Ideally, detaching should resume any target, but there is a bug in the backend.
|
||||||
session.send('Runtime.runIfWaitingForDebugger').catch(logError(this)).then(() => {
|
session._sendMayFail('Runtime.runIfWaitingForDebugger').then(() => {
|
||||||
this._session.send('Target.detachFromTarget', { sessionId }).catch(logError(this));
|
this._session._sendMayFail('Target.detachFromTarget', { sessionId });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { assert } from '../helper';
|
||||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { InnerLogger } from '../logger';
|
import { InnerLogger, errorLog } from '../logger';
|
||||||
import { rewriteErrorMessage } from '../debug/stackTrace';
|
import { rewriteErrorMessage } from '../debug/stackTrace';
|
||||||
|
|
||||||
export const ConnectionEvents = {
|
export const ConnectionEvents = {
|
||||||
|
|
@ -36,7 +36,7 @@ export class CRConnection extends EventEmitter {
|
||||||
private readonly _sessions = new Map<string, CRSession>();
|
private readonly _sessions = new Map<string, CRSession>();
|
||||||
readonly rootSession: CRSession;
|
readonly rootSession: CRSession;
|
||||||
_closed = false;
|
_closed = false;
|
||||||
private _logger: InnerLogger;
|
readonly _logger: InnerLogger;
|
||||||
|
|
||||||
constructor(transport: ConnectionTransport, logger: InnerLogger) {
|
constructor(transport: ConnectionTransport, logger: InnerLogger) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -165,6 +165,13 @@ export class CRSession extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||||
|
return this.send(method, params).catch(error => {
|
||||||
|
if (this._connection)
|
||||||
|
this._connection._logger._log(errorLog, error, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_onMessage(object: ProtocolResponse) {
|
_onMessage(object: ProtocolResponse) {
|
||||||
if (object.id && this._callbacks.has(object.id)) {
|
if (object.id && this._callbacks.has(object.id)) {
|
||||||
const callback = this._callbacks.get(object.id)!;
|
const callback = this._callbacks.get(object.id)!;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import { assert, helper, RegisteredListener } from '../helper';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import * as debugSupport from '../debug/debugSupport';
|
import * as debugSupport from '../debug/debugSupport';
|
||||||
import { logError, InnerLogger } from '../logger';
|
|
||||||
|
|
||||||
type JSRange = {
|
type JSRange = {
|
||||||
startOffset: number,
|
startOffset: number,
|
||||||
|
|
@ -50,9 +49,9 @@ export class CRCoverage {
|
||||||
private _jsCoverage: JSCoverage;
|
private _jsCoverage: JSCoverage;
|
||||||
private _cssCoverage: CSSCoverage;
|
private _cssCoverage: CSSCoverage;
|
||||||
|
|
||||||
constructor(client: CRSession, logger: InnerLogger) {
|
constructor(client: CRSession) {
|
||||||
this._jsCoverage = new JSCoverage(client, logger);
|
this._jsCoverage = new JSCoverage(client);
|
||||||
this._cssCoverage = new CSSCoverage(client, logger);
|
this._cssCoverage = new CSSCoverage(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
async startJSCoverage(options?: types.JSCoverageOptions) {
|
async startJSCoverage(options?: types.JSCoverageOptions) {
|
||||||
|
|
@ -80,11 +79,9 @@ class JSCoverage {
|
||||||
_eventListeners: RegisteredListener[];
|
_eventListeners: RegisteredListener[];
|
||||||
_resetOnNavigation: boolean;
|
_resetOnNavigation: boolean;
|
||||||
_reportAnonymousScripts = false;
|
_reportAnonymousScripts = false;
|
||||||
private _logger: InnerLogger;
|
|
||||||
|
|
||||||
constructor(client: CRSession, logger: InnerLogger) {
|
constructor(client: CRSession) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._logger = logger;
|
|
||||||
this._enabled = false;
|
this._enabled = false;
|
||||||
this._scriptIds = new Set();
|
this._scriptIds = new Set();
|
||||||
this._scriptSources = new Map();
|
this._scriptSources = new Map();
|
||||||
|
|
@ -131,13 +128,10 @@ class JSCoverage {
|
||||||
// Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
|
// Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
|
||||||
if (!event.url && !this._reportAnonymousScripts)
|
if (!event.url && !this._reportAnonymousScripts)
|
||||||
return;
|
return;
|
||||||
try {
|
// This might fail if the page has already navigated away.
|
||||||
const response = await this._client.send('Debugger.getScriptSource', {scriptId: event.scriptId});
|
const response = await this._client._sendMayFail('Debugger.getScriptSource', {scriptId: event.scriptId});
|
||||||
|
if (response)
|
||||||
this._scriptSources.set(event.scriptId, response.scriptSource);
|
this._scriptSources.set(event.scriptId, response.scriptSource);
|
||||||
} catch (e) {
|
|
||||||
// This might happen if the page has already navigated away.
|
|
||||||
logError(this._logger)(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop(): Promise<JSCoverageEntry[]> {
|
async stop(): Promise<JSCoverageEntry[]> {
|
||||||
|
|
@ -174,11 +168,9 @@ class CSSCoverage {
|
||||||
_stylesheetSources: Map<string, string>;
|
_stylesheetSources: Map<string, string>;
|
||||||
_eventListeners: RegisteredListener[];
|
_eventListeners: RegisteredListener[];
|
||||||
_resetOnNavigation: boolean;
|
_resetOnNavigation: boolean;
|
||||||
private _logger: InnerLogger;
|
|
||||||
|
|
||||||
constructor(client: CRSession, logger: InnerLogger) {
|
constructor(client: CRSession) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._logger = logger;
|
|
||||||
this._enabled = false;
|
this._enabled = false;
|
||||||
this._stylesheetURLs = new Map();
|
this._stylesheetURLs = new Map();
|
||||||
this._stylesheetSources = new Map();
|
this._stylesheetSources = new Map();
|
||||||
|
|
@ -216,13 +208,11 @@ class CSSCoverage {
|
||||||
// Ignore anonymous scripts
|
// Ignore anonymous scripts
|
||||||
if (!header.sourceURL)
|
if (!header.sourceURL)
|
||||||
return;
|
return;
|
||||||
try {
|
// This might fail if the page has already navigated away.
|
||||||
const response = await this._client.send('CSS.getStyleSheetText', {styleSheetId: header.styleSheetId});
|
const response = await this._client._sendMayFail('CSS.getStyleSheetText', {styleSheetId: header.styleSheetId});
|
||||||
|
if (response) {
|
||||||
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
|
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
|
||||||
this._stylesheetSources.set(header.styleSheetId, response.text);
|
this._stylesheetSources.set(header.styleSheetId, response.text);
|
||||||
} catch (e) {
|
|
||||||
// This might happen if the page has already navigated away.
|
|
||||||
logError(this._logger)(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import * as network from '../network';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { Credentials } from '../types';
|
import { Credentials } from '../types';
|
||||||
import { CRPage } from './crPage';
|
import { CRPage } from './crPage';
|
||||||
import { logError } from '../logger';
|
|
||||||
|
|
||||||
export class CRNetworkManager {
|
export class CRNetworkManager {
|
||||||
private _client: CRSession;
|
private _client: CRSession;
|
||||||
|
|
@ -130,26 +129,26 @@ export class CRNetworkManager {
|
||||||
this._attemptedAuthentications.add(event.requestId);
|
this._attemptedAuthentications.add(event.requestId);
|
||||||
}
|
}
|
||||||
const {username, password} = this._credentials || {username: undefined, password: undefined};
|
const {username, password} = this._credentials || {username: undefined, password: undefined};
|
||||||
this._client.send('Fetch.continueWithAuth', {
|
this._client._sendMayFail('Fetch.continueWithAuth', {
|
||||||
requestId: event.requestId,
|
requestId: event.requestId,
|
||||||
authChallengeResponse: { response, username, password },
|
authChallengeResponse: { response, username, password },
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
|
_onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
|
||||||
if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
|
if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
|
||||||
this._client.send('Fetch.continueRequest', {
|
this._client._sendMayFail('Fetch.continueRequest', {
|
||||||
requestId: event.requestId
|
requestId: event.requestId
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
}
|
}
|
||||||
if (!event.networkId) {
|
if (!event.networkId) {
|
||||||
// Fetch without networkId means that request was not recongnized by inspector, and
|
// Fetch without networkId means that request was not recongnized by inspector, and
|
||||||
// it will never receive Network.requestWillBeSent. Most likely, this is an internal request
|
// it will never receive Network.requestWillBeSent. Most likely, this is an internal request
|
||||||
// that we can safely fail.
|
// that we can safely fail.
|
||||||
this._client.send('Fetch.failRequest', {
|
this._client._sendMayFail('Fetch.failRequest', {
|
||||||
requestId: event.requestId,
|
requestId: event.requestId,
|
||||||
errorReason: 'Aborted',
|
errorReason: 'Aborted',
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.request.url.startsWith('data:'))
|
if (event.request.url.startsWith('data:'))
|
||||||
|
|
@ -189,7 +188,7 @@ export class CRNetworkManager {
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
if (requestPausedEvent)
|
if (requestPausedEvent)
|
||||||
this._client.send('Fetch.continueRequest', { requestId: requestPausedEvent.requestId }).catch(logError(this._page));
|
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === 'Document';
|
const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === 'Document';
|
||||||
|
|
@ -325,15 +324,13 @@ class InterceptableRequest implements network.RouteDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) {
|
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) {
|
||||||
await this._client.send('Fetch.continueRequest', {
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
// or the page was closed. We should tolerate these errors.
|
||||||
|
await this._client._sendMayFail('Fetch.continueRequest', {
|
||||||
requestId: this._interceptionId!,
|
requestId: this._interceptionId!,
|
||||||
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
||||||
method: overrides.method,
|
method: overrides.method,
|
||||||
postData: overrides.postData
|
postData: overrides.postData
|
||||||
}).catch(error => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
|
||||||
// or the page was closed. We should tolerate these errors.
|
|
||||||
logError(this.request._page)(error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,29 +347,25 @@ class InterceptableRequest implements network.RouteDelegate {
|
||||||
if (responseBody && !('content-length' in responseHeaders))
|
if (responseBody && !('content-length' in responseHeaders))
|
||||||
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
||||||
|
|
||||||
await this._client.send('Fetch.fulfillRequest', {
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
// or the page was closed. We should tolerate these errors.
|
||||||
|
await this._client._sendMayFail('Fetch.fulfillRequest', {
|
||||||
requestId: this._interceptionId!,
|
requestId: this._interceptionId!,
|
||||||
responseCode: response.status || 200,
|
responseCode: response.status || 200,
|
||||||
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
|
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
|
||||||
responseHeaders: headersArray(responseHeaders),
|
responseHeaders: headersArray(responseHeaders),
|
||||||
body: responseBody ? responseBody.toString('base64') : undefined,
|
body: responseBody ? responseBody.toString('base64') : undefined,
|
||||||
}).catch(error => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
|
||||||
// or the page was closed. We should tolerate these errors.
|
|
||||||
logError(this.request._page)(error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async abort(errorCode: string = 'failed') {
|
async abort(errorCode: string = 'failed') {
|
||||||
const errorReason = errorReasons[errorCode];
|
const errorReason = errorReasons[errorCode];
|
||||||
assert(errorReason, 'Unknown error code: ' + errorCode);
|
assert(errorReason, 'Unknown error code: ' + errorCode);
|
||||||
await this._client.send('Fetch.failRequest', {
|
|
||||||
requestId: this._interceptionId!,
|
|
||||||
errorReason
|
|
||||||
}).catch(error => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
logError(this.request._page)(error);
|
await this._client._sendMayFail('Fetch.failRequest', {
|
||||||
|
requestId: this._interceptionId!,
|
||||||
|
errorReason
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ import { CRBrowserContext } from './crBrowser';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { ConsoleMessage } from '../console';
|
import { ConsoleMessage } from '../console';
|
||||||
import { NotConnectedError } from '../errors';
|
import { NotConnectedError } from '../errors';
|
||||||
import { logError } from '../logger';
|
|
||||||
import * as debugSupport from '../debug/debugSupport';
|
import * as debugSupport from '../debug/debugSupport';
|
||||||
import { rewriteErrorMessage } from '../debug/stackTrace';
|
import { rewriteErrorMessage } from '../debug/stackTrace';
|
||||||
|
|
||||||
|
|
@ -70,7 +69,7 @@ export class CRPage implements PageDelegate {
|
||||||
this.rawKeyboard = new RawKeyboardImpl(client);
|
this.rawKeyboard = new RawKeyboardImpl(client);
|
||||||
this.rawMouse = new RawMouseImpl(client);
|
this.rawMouse = new RawMouseImpl(client);
|
||||||
this._pdf = new CRPDF(client);
|
this._pdf = new CRPDF(client);
|
||||||
this._coverage = new CRCoverage(client, browserContext);
|
this._coverage = new CRCoverage(client);
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
this._page = new Page(this, browserContext);
|
this._page = new Page(this, browserContext);
|
||||||
this._mainFrameSession = new FrameSession(this, client, targetId, null);
|
this._mainFrameSession = new FrameSession(this, client, targetId, null);
|
||||||
|
|
@ -121,7 +120,7 @@ export class CRPage implements PageDelegate {
|
||||||
|
|
||||||
async exposeBinding(binding: PageBinding) {
|
async exposeBinding(binding: PageBinding) {
|
||||||
await this._forAllFrameSessions(frame => frame._initBinding(binding));
|
await this._forAllFrameSessions(frame => frame._initBinding(binding));
|
||||||
await Promise.all(this._page.frames().map(frame => frame.evaluate(binding.source).catch(logError(this._page))));
|
await Promise.all(this._page.frames().map(frame => frame.evaluate(binding.source).catch(e => {})));
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateExtraHTTPHeaders(): Promise<void> {
|
async updateExtraHTTPHeaders(): Promise<void> {
|
||||||
|
|
@ -384,13 +383,13 @@ class FrameSession {
|
||||||
const localFrames = this._isMainFrame() ? this._page.frames() : [ this._page._frameManager.frame(this._targetId)! ];
|
const localFrames = this._isMainFrame() ? this._page.frames() : [ this._page._frameManager.frame(this._targetId)! ];
|
||||||
for (const frame of localFrames) {
|
for (const frame of localFrames) {
|
||||||
// Note: frames might be removed before we send these.
|
// Note: frames might be removed before we send these.
|
||||||
this._client.send('Page.createIsolatedWorld', {
|
this._client._sendMayFail('Page.createIsolatedWorld', {
|
||||||
frameId: frame._id,
|
frameId: frame._id,
|
||||||
grantUniveralAccess: true,
|
grantUniveralAccess: true,
|
||||||
worldName: UTILITY_WORLD_NAME,
|
worldName: UTILITY_WORLD_NAME,
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
for (const binding of this._crPage._browserContext._pageBindings.values())
|
for (const binding of this._crPage._browserContext._pageBindings.values())
|
||||||
frame.evaluate(binding.source).catch(logError(this._page));
|
frame.evaluate(binding.source).catch(e => {});
|
||||||
}
|
}
|
||||||
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ':';
|
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ':';
|
||||||
if (isInitialEmptyPage) {
|
if (isInitialEmptyPage) {
|
||||||
|
|
@ -560,8 +559,8 @@ class FrameSession {
|
||||||
|
|
||||||
if (event.targetInfo.type !== 'worker') {
|
if (event.targetInfo.type !== 'worker') {
|
||||||
// Ideally, detaching should resume any target, but there is a bug in the backend.
|
// Ideally, detaching should resume any target, but there is a bug in the backend.
|
||||||
session.send('Runtime.runIfWaitingForDebugger').catch(logError(this._page)).then(() => {
|
session._sendMayFail('Runtime.runIfWaitingForDebugger').then(() => {
|
||||||
this._client.send('Target.detachFromTarget', { sessionId: event.sessionId }).catch(logError(this._page));
|
this._client._sendMayFail('Target.detachFromTarget', { sessionId: event.sessionId });
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -573,10 +572,10 @@ class FrameSession {
|
||||||
worker._createExecutionContext(new CRExecutionContext(session, event.context));
|
worker._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||||
});
|
});
|
||||||
Promise.all([
|
Promise.all([
|
||||||
session.send('Runtime.enable'),
|
session._sendMayFail('Runtime.enable'),
|
||||||
session.send('Network.enable'),
|
session._sendMayFail('Network.enable'),
|
||||||
session.send('Runtime.runIfWaitingForDebugger'),
|
session._sendMayFail('Runtime.runIfWaitingForDebugger'),
|
||||||
]).catch(logError(this._page)); // This might fail if the target is closed before we initialize.
|
]); // This might fail if the target is closed before we initialize.
|
||||||
session.on('Runtime.consoleAPICalled', event => {
|
session.on('Runtime.consoleAPICalled', event => {
|
||||||
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
|
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
|
||||||
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
|
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
|
||||||
|
|
@ -816,9 +815,9 @@ class FrameSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getBoundingBox(handle: dom.ElementHandle): Promise<types.Rect | null> {
|
async _getBoundingBox(handle: dom.ElementHandle): Promise<types.Rect | null> {
|
||||||
const result = await this._client.send('DOM.getBoxModel', {
|
const result = await this._client._sendMayFail('DOM.getBoxModel', {
|
||||||
objectId: handle._objectId
|
objectId: handle._objectId
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result)
|
if (!result)
|
||||||
return null;
|
return null;
|
||||||
const quad = result.model.border;
|
const quad = result.model.border;
|
||||||
|
|
@ -843,9 +842,9 @@ class FrameSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async _getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._client.send('DOM.getContentQuads', {
|
const result = await this._client._sendMayFail('DOM.getContentQuads', {
|
||||||
objectId: handle._objectId
|
objectId: handle._objectId
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result)
|
if (!result)
|
||||||
return null;
|
return null;
|
||||||
return result.quads.map(quad => [
|
return result.quads.map(quad => [
|
||||||
|
|
@ -864,10 +863,10 @@ class FrameSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId, to: dom.FrameExecutionContext): Promise<dom.ElementHandle> {
|
async _adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId, to: dom.FrameExecutionContext): Promise<dom.ElementHandle> {
|
||||||
const result = await this._client.send('DOM.resolveNode', {
|
const result = await this._client._sendMayFail('DOM.resolveNode', {
|
||||||
backendNodeId,
|
backendNodeId,
|
||||||
executionContextId: (to._delegate as CRExecutionContext)._contextId,
|
executionContextId: (to._delegate as CRExecutionContext)._contextId,
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result || result.object.subtype === 'null')
|
if (!result || result.object.subtype === 'null')
|
||||||
throw new Error('Unable to adopt element handle from a different document');
|
throw new Error('Unable to adopt element handle from a different document');
|
||||||
return to.createHandle(result.object).asElement()!;
|
return to.createHandle(result.object).asElement()!;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
|
||||||
import { assert } from '../helper';
|
import { assert } from '../helper';
|
||||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { InnerLogger } from '../logger';
|
import { InnerLogger, errorLog } from '../logger';
|
||||||
import { rewriteErrorMessage } from '../debug/stackTrace';
|
import { rewriteErrorMessage } from '../debug/stackTrace';
|
||||||
|
|
||||||
export const ConnectionEvents = {
|
export const ConnectionEvents = {
|
||||||
|
|
@ -34,7 +34,7 @@ export class FFConnection extends EventEmitter {
|
||||||
private _lastId: number;
|
private _lastId: number;
|
||||||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||||
private _transport: ConnectionTransport;
|
private _transport: ConnectionTransport;
|
||||||
private _logger: InnerLogger;
|
readonly _logger: InnerLogger;
|
||||||
readonly _sessions: Map<string, FFSession>;
|
readonly _sessions: Map<string, FFSession>;
|
||||||
_closed: boolean;
|
_closed: boolean;
|
||||||
|
|
||||||
|
|
@ -185,6 +185,12 @@ export class FFSession extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||||
|
return this.send(method, params).catch(error => {
|
||||||
|
this._connection._logger._log(errorLog, error, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
dispatchMessage(object: ProtocolResponse) {
|
dispatchMessage(object: ProtocolResponse) {
|
||||||
if (object.id && this._callbacks.has(object.id)) {
|
if (object.id && this._callbacks.has(object.id)) {
|
||||||
const callback = this._callbacks.get(object.id)!;
|
const callback = this._callbacks.get(object.id)!;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import { Page } from '../page';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { logError } from '../logger';
|
|
||||||
|
|
||||||
export class FFNetworkManager {
|
export class FFNetworkManager {
|
||||||
private _session: FFSession;
|
private _session: FFSession;
|
||||||
|
|
@ -164,12 +163,12 @@ class InterceptableRequest implements network.RouteDelegate {
|
||||||
headers,
|
headers,
|
||||||
postData
|
postData
|
||||||
} = overrides;
|
} = overrides;
|
||||||
await this._session.send('Network.resumeInterceptedRequest', {
|
await this._session.sendMayFail('Network.resumeInterceptedRequest', {
|
||||||
requestId: this._id,
|
requestId: this._id,
|
||||||
method,
|
method,
|
||||||
headers: headers ? headersArray(headers) : undefined,
|
headers: headers ? headersArray(headers) : undefined,
|
||||||
postData: postData ? Buffer.from(postData).toString('base64') : undefined
|
postData: postData ? Buffer.from(postData).toString('base64') : undefined
|
||||||
}).catch(logError(this.request._page));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: network.FulfillResponse) {
|
async fulfill(response: network.FulfillResponse) {
|
||||||
|
|
@ -185,20 +184,20 @@ class InterceptableRequest implements network.RouteDelegate {
|
||||||
if (responseBody && !('content-length' in responseHeaders))
|
if (responseBody && !('content-length' in responseHeaders))
|
||||||
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
||||||
|
|
||||||
await this._session.send('Network.fulfillInterceptedRequest', {
|
await this._session.sendMayFail('Network.fulfillInterceptedRequest', {
|
||||||
requestId: this._id,
|
requestId: this._id,
|
||||||
status: response.status || 200,
|
status: response.status || 200,
|
||||||
statusText: network.STATUS_TEXTS[String(response.status || 200)] || '',
|
statusText: network.STATUS_TEXTS[String(response.status || 200)] || '',
|
||||||
headers: headersArray(responseHeaders),
|
headers: headersArray(responseHeaders),
|
||||||
base64body: responseBody ? responseBody.toString('base64') : undefined,
|
base64body: responseBody ? responseBody.toString('base64') : undefined,
|
||||||
}).catch(logError(this.request._page));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async abort(errorCode: string) {
|
async abort(errorCode: string) {
|
||||||
await this._session.send('Network.abortInterceptedRequest', {
|
await this._session.sendMayFail('Network.abortInterceptedRequest', {
|
||||||
requestId: this._id,
|
requestId: this._id,
|
||||||
errorCode,
|
errorCode,
|
||||||
}).catch(logError(this.request._page));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import { FFNetworkManager, headersArray } from './ffNetworkManager';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { selectors } from '../selectors';
|
import { selectors } from '../selectors';
|
||||||
import { NotConnectedError } from '../errors';
|
import { NotConnectedError } from '../errors';
|
||||||
import { logError } from '../logger';
|
|
||||||
import { rewriteErrorMessage } from '../debug/stackTrace';
|
import { rewriteErrorMessage } from '../debug/stackTrace';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
@ -193,7 +192,7 @@ export class FFPage implements PageDelegate {
|
||||||
params.type,
|
params.type,
|
||||||
params.message,
|
params.message,
|
||||||
async (accept: boolean, promptText?: string) => {
|
async (accept: boolean, promptText?: string) => {
|
||||||
await this._session.send('Page.handleDialog', { dialogId: params.dialogId, accept, promptText }).catch(logError(this._page));
|
await this._session.sendMayFail('Page.handleDialog', { dialogId: params.dialogId, accept, promptText });
|
||||||
},
|
},
|
||||||
params.defaultValue));
|
params.defaultValue));
|
||||||
}
|
}
|
||||||
|
|
@ -429,10 +428,10 @@ export class FFPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._session.send('Page.getContentQuads', {
|
const result = await this._session.sendMayFail('Page.getContentQuads', {
|
||||||
frameId: handle._context.frame._id,
|
frameId: handle._context.frame._id,
|
||||||
objectId: handle._objectId,
|
objectId: handle._objectId,
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result)
|
if (!result)
|
||||||
return null;
|
return null;
|
||||||
return result.quads.map(quad => [ quad.p1, quad.p2, quad.p3, quad.p4 ]);
|
return result.quads.map(quad => [ quad.p1, quad.p2, quad.p3, quad.p4 ]);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import * as mime from 'mime';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as frames from './frames';
|
import * as frames from './frames';
|
||||||
import { assert, helper } from './helper';
|
import { assert, helper } from './helper';
|
||||||
import { Page } from './page';
|
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
export type NetworkCookie = {
|
export type NetworkCookie = {
|
||||||
|
|
@ -112,14 +111,12 @@ export class Request {
|
||||||
private _frame: frames.Frame;
|
private _frame: frames.Frame;
|
||||||
private _waitForResponsePromise: Promise<Response | null>;
|
private _waitForResponsePromise: Promise<Response | null>;
|
||||||
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
||||||
readonly _page: Page;
|
|
||||||
|
|
||||||
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
||||||
url: string, resourceType: string, method: string, postData: string | null, headers: Headers) {
|
url: string, resourceType: string, method: string, postData: string | null, headers: Headers) {
|
||||||
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
||||||
this._routeDelegate = routeDelegate;
|
this._routeDelegate = routeDelegate;
|
||||||
this._frame = frame;
|
this._frame = frame;
|
||||||
this._page = frame._page;
|
|
||||||
this._redirectedFrom = redirectedFrom;
|
this._redirectedFrom = redirectedFrom;
|
||||||
if (redirectedFrom)
|
if (redirectedFrom)
|
||||||
redirectedFrom._redirectedTo = this;
|
redirectedFrom._redirectedTo = this;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
|
||||||
import { assert } from '../helper';
|
import { assert } from '../helper';
|
||||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { InnerLogger } from '../logger';
|
import { InnerLogger, errorLog } from '../logger';
|
||||||
import { rewriteErrorMessage } from '../debug/stackTrace';
|
import { rewriteErrorMessage } from '../debug/stackTrace';
|
||||||
|
|
||||||
// WKPlaywright uses this special id to issue Browser.close command which we
|
// WKPlaywright uses this special id to issue Browser.close command which we
|
||||||
|
|
@ -36,9 +36,8 @@ export class WKConnection {
|
||||||
private readonly _onDisconnect: () => void;
|
private readonly _onDisconnect: () => void;
|
||||||
private _lastId = 0;
|
private _lastId = 0;
|
||||||
private _closed = false;
|
private _closed = false;
|
||||||
|
|
||||||
readonly browserSession: WKSession;
|
readonly browserSession: WKSession;
|
||||||
private _logger: InnerLogger;
|
readonly _logger: InnerLogger;
|
||||||
|
|
||||||
constructor(transport: ConnectionTransport, logger: InnerLogger, onDisconnect: () => void) {
|
constructor(transport: ConnectionTransport, logger: InnerLogger, onDisconnect: () => void) {
|
||||||
this._transport = transport;
|
this._transport = transport;
|
||||||
|
|
@ -138,6 +137,12 @@ export class WKSession extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMayFail<T extends keyof Protocol.CommandParameters>(method: T, params?: Protocol.CommandParameters[T]): Promise<Protocol.CommandReturnValues[T] | void> {
|
||||||
|
return this.send(method, params).catch(error => {
|
||||||
|
this.connection._logger._log(errorLog, error, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
markAsCrashed() {
|
markAsCrashed() {
|
||||||
this._crashed = true;
|
this._crashed = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import { assert, helper } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
import { logError } from '../logger';
|
|
||||||
|
|
||||||
const errorReasons: { [reason: string]: string } = {
|
const errorReasons: { [reason: string]: string } = {
|
||||||
'aborted': 'Cancellation',
|
'aborted': 'Cancellation',
|
||||||
|
|
@ -59,11 +58,9 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
||||||
const reason = errorReasons[errorCode];
|
const reason = errorReasons[errorCode];
|
||||||
assert(reason, 'Unknown error code: ' + errorCode);
|
assert(reason, 'Unknown error code: ' + errorCode);
|
||||||
await this._interceptedPromise;
|
await this._interceptedPromise;
|
||||||
await this._session.send('Network.interceptAsError', { requestId: this._requestId, reason }).catch(error => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
logError(this.request._page);
|
await this._session.sendMayFail('Network.interceptAsError', { requestId: this._requestId, reason });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: network.FulfillResponse) {
|
async fulfill(response: network.FulfillResponse) {
|
||||||
|
|
@ -89,7 +86,9 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
||||||
if (responseBody && !('content-length' in responseHeaders))
|
if (responseBody && !('content-length' in responseHeaders))
|
||||||
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
|
||||||
|
|
||||||
await this._session.send('Network.interceptWithResponse', {
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
// or the page was closed. We should tolerate these errors.
|
||||||
|
await this._session.sendMayFail('Network.interceptWithResponse', {
|
||||||
requestId: this._requestId,
|
requestId: this._requestId,
|
||||||
status: response.status || 200,
|
status: response.status || 200,
|
||||||
statusText: network.STATUS_TEXTS[String(response.status || 200)],
|
statusText: network.STATUS_TEXTS[String(response.status || 200)],
|
||||||
|
|
@ -97,24 +96,18 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
||||||
headers: responseHeaders,
|
headers: responseHeaders,
|
||||||
base64Encoded,
|
base64Encoded,
|
||||||
content: responseBody
|
content: responseBody
|
||||||
}).catch(error => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
|
||||||
// or the page was closed. We should tolerate these errors.
|
|
||||||
logError(this.request._page);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) {
|
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) {
|
||||||
await this._interceptedPromise;
|
await this._interceptedPromise;
|
||||||
await this._session.send('Network.interceptContinue', {
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
// or the page was closed. We should tolerate these errors.
|
||||||
|
await this._session.sendMayFail('Network.interceptContinue', {
|
||||||
requestId: this._requestId,
|
requestId: this._requestId,
|
||||||
method: overrides.method,
|
method: overrides.method,
|
||||||
headers: overrides.headers,
|
headers: overrides.headers,
|
||||||
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined
|
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined
|
||||||
}).catch((error: Error) => {
|
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
|
||||||
// or the page was closed. We should tolerate these errors.
|
|
||||||
logError(this.request._page);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ export class WKPage implements PageDelegate {
|
||||||
pageOrError = e;
|
pageOrError = e;
|
||||||
}
|
}
|
||||||
if (targetInfo.isPaused)
|
if (targetInfo.isPaused)
|
||||||
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(logError(this._page));
|
this._pageProxySession.sendMayFail('Target.resume', { targetId: targetInfo.targetId });
|
||||||
if ((pageOrError instanceof Page) && this._page.mainFrame().url() === '') {
|
if ((pageOrError instanceof Page) && this._page.mainFrame().url() === '') {
|
||||||
try {
|
try {
|
||||||
// Initial empty page has an empty url. We should wait until the first real url has been loaded,
|
// Initial empty page has an empty url. We should wait until the first real url has been loaded,
|
||||||
|
|
@ -309,7 +309,7 @@ export class WKPage implements PageDelegate {
|
||||||
this._provisionalPage = new WKProvisionalPage(session, this);
|
this._provisionalPage = new WKProvisionalPage(session, this);
|
||||||
if (targetInfo.isPaused) {
|
if (targetInfo.isPaused) {
|
||||||
this._provisionalPage.initializationPromise.then(() => {
|
this._provisionalPage.initializationPromise.then(() => {
|
||||||
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(logError(this._page));
|
this._pageProxySession.sendMayFail('Target.resume', { targetId: targetInfo.targetId });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -652,7 +652,7 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
private async _evaluateBindingScript(binding: PageBinding): Promise<void> {
|
private async _evaluateBindingScript(binding: PageBinding): Promise<void> {
|
||||||
const script = this._bindingToScript(binding);
|
const script = this._bindingToScript(binding);
|
||||||
await Promise.all(this._page.frames().map(frame => frame.evaluate(script).catch(logError(this._page))));
|
await Promise.all(this._page.frames().map(frame => frame.evaluate(script).catch(e => {})));
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateOnNewDocument(script: string): Promise<void> {
|
async evaluateOnNewDocument(script: string): Promise<void> {
|
||||||
|
|
@ -680,10 +680,10 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async closePage(runBeforeUnload: boolean): Promise<void> {
|
async closePage(runBeforeUnload: boolean): Promise<void> {
|
||||||
this._pageProxySession.send('Target.close', {
|
this._pageProxySession.sendMayFail('Target.close', {
|
||||||
targetId: this._session.sessionId,
|
targetId: this._session.sessionId,
|
||||||
runBeforeUnload
|
runBeforeUnload
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
canScreenshotOutsideViewport(): boolean {
|
canScreenshotOutsideViewport(): boolean {
|
||||||
|
|
@ -767,9 +767,9 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._session.send('DOM.getContentQuads', {
|
const result = await this._session.sendMayFail('DOM.getContentQuads', {
|
||||||
objectId: handle._objectId
|
objectId: handle._objectId
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result)
|
if (!result)
|
||||||
return null;
|
return null;
|
||||||
return result.quads.map(quad => [
|
return result.quads.map(quad => [
|
||||||
|
|
@ -790,10 +790,10 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>> {
|
async adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>> {
|
||||||
const result = await this._session.send('DOM.resolveNode', {
|
const result = await this._session.sendMayFail('DOM.resolveNode', {
|
||||||
objectId: handle._objectId,
|
objectId: handle._objectId,
|
||||||
executionContextId: (to._delegate as WKExecutionContext)._contextId
|
executionContextId: (to._delegate as WKExecutionContext)._contextId
|
||||||
}).catch(logError(this._page));
|
});
|
||||||
if (!result || result.object.subtype === 'null')
|
if (!result || result.object.subtype === 'null')
|
||||||
throw new Error('Unable to adopt element handle from a different document');
|
throw new Error('Unable to adopt element handle from a different document');
|
||||||
return to.createHandle(result.object) as dom.ElementHandle<T>;
|
return to.createHandle(result.object) as dom.ElementHandle<T>;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue