chore: introduce session.sendMayFail to ease error logging (#2480)

This commit is contained in:
Dmitry Gozman 2020-06-05 07:50:26 -07:00 committed by GitHub
parent fc2432a23a
commit c08da50bb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 111 deletions

View File

@ -29,7 +29,6 @@ import { readProtocolStream } from './crProtocolHelper';
import { Events } from './events';
import { Protocol } from './protocol';
import { CRExecutionContext } from './crExecutionContext';
import { logError } from '../logger';
import { CRDevTools } from '../debug/crDevTools';
export class CRBrowser extends BrowserBase {
@ -133,8 +132,8 @@ export class CRBrowser extends BrowserBase {
if (targetInfo.type === 'other' || !context) {
if (waitingForDebugger) {
// Ideally, detaching should resume any target, but there is a bug in the backend.
session.send('Runtime.runIfWaitingForDebugger').catch(logError(this)).then(() => {
this._session.send('Target.detachFromTarget', { sessionId }).catch(logError(this));
session._sendMayFail('Runtime.runIfWaitingForDebugger').then(() => {
this._session._sendMayFail('Target.detachFromTarget', { sessionId });
});
}
return;

View File

@ -19,7 +19,7 @@ import { assert } from '../helper';
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
import { Protocol } from './protocol';
import { EventEmitter } from 'events';
import { InnerLogger } from '../logger';
import { InnerLogger, errorLog } from '../logger';
import { rewriteErrorMessage } from '../debug/stackTrace';
export const ConnectionEvents = {
@ -36,7 +36,7 @@ export class CRConnection extends EventEmitter {
private readonly _sessions = new Map<string, CRSession>();
readonly rootSession: CRSession;
_closed = false;
private _logger: InnerLogger;
readonly _logger: InnerLogger;
constructor(transport: ConnectionTransport, logger: InnerLogger) {
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) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id)!;

View File

@ -20,7 +20,6 @@ import { assert, helper, RegisteredListener } from '../helper';
import { Protocol } from './protocol';
import * as types from '../types';
import * as debugSupport from '../debug/debugSupport';
import { logError, InnerLogger } from '../logger';
type JSRange = {
startOffset: number,
@ -50,9 +49,9 @@ export class CRCoverage {
private _jsCoverage: JSCoverage;
private _cssCoverage: CSSCoverage;
constructor(client: CRSession, logger: InnerLogger) {
this._jsCoverage = new JSCoverage(client, logger);
this._cssCoverage = new CSSCoverage(client, logger);
constructor(client: CRSession) {
this._jsCoverage = new JSCoverage(client);
this._cssCoverage = new CSSCoverage(client);
}
async startJSCoverage(options?: types.JSCoverageOptions) {
@ -80,11 +79,9 @@ class JSCoverage {
_eventListeners: RegisteredListener[];
_resetOnNavigation: boolean;
_reportAnonymousScripts = false;
private _logger: InnerLogger;
constructor(client: CRSession, logger: InnerLogger) {
constructor(client: CRSession) {
this._client = client;
this._logger = logger;
this._enabled = false;
this._scriptIds = new Set();
this._scriptSources = new Map();
@ -131,13 +128,10 @@ class JSCoverage {
// Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
if (!event.url && !this._reportAnonymousScripts)
return;
try {
const response = await this._client.send('Debugger.getScriptSource', {scriptId: event.scriptId});
// This might fail if the page has already navigated away.
const response = await this._client._sendMayFail('Debugger.getScriptSource', {scriptId: event.scriptId});
if (response)
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[]> {
@ -174,11 +168,9 @@ class CSSCoverage {
_stylesheetSources: Map<string, string>;
_eventListeners: RegisteredListener[];
_resetOnNavigation: boolean;
private _logger: InnerLogger;
constructor(client: CRSession, logger: InnerLogger) {
constructor(client: CRSession) {
this._client = client;
this._logger = logger;
this._enabled = false;
this._stylesheetURLs = new Map();
this._stylesheetSources = new Map();
@ -216,13 +208,11 @@ class CSSCoverage {
// Ignore anonymous scripts
if (!header.sourceURL)
return;
try {
const response = await this._client.send('CSS.getStyleSheetText', {styleSheetId: header.styleSheetId});
// This might fail if the page has already navigated away.
const response = await this._client._sendMayFail('CSS.getStyleSheetText', {styleSheetId: header.styleSheetId});
if (response) {
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
this._stylesheetSources.set(header.styleSheetId, response.text);
} catch (e) {
// This might happen if the page has already navigated away.
logError(this._logger)(e);
}
}

View File

@ -23,7 +23,6 @@ import * as network from '../network';
import * as frames from '../frames';
import { Credentials } from '../types';
import { CRPage } from './crPage';
import { logError } from '../logger';
export class CRNetworkManager {
private _client: CRSession;
@ -130,26 +129,26 @@ export class CRNetworkManager {
this._attemptedAuthentications.add(event.requestId);
}
const {username, password} = this._credentials || {username: undefined, password: undefined};
this._client.send('Fetch.continueWithAuth', {
this._client._sendMayFail('Fetch.continueWithAuth', {
requestId: event.requestId,
authChallengeResponse: { response, username, password },
}).catch(logError(this._page));
});
}
_onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
if (!this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
this._client.send('Fetch.continueRequest', {
this._client._sendMayFail('Fetch.continueRequest', {
requestId: event.requestId
}).catch(logError(this._page));
});
}
if (!event.networkId) {
// 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
// that we can safely fail.
this._client.send('Fetch.failRequest', {
this._client._sendMayFail('Fetch.failRequest', {
requestId: event.requestId,
errorReason: 'Aborted',
}).catch(logError(this._page));
});
return;
}
if (event.request.url.startsWith('data:'))
@ -189,7 +188,7 @@ export class CRNetworkManager {
if (!frame) {
if (requestPausedEvent)
this._client.send('Fetch.continueRequest', { requestId: requestPausedEvent.requestId }).catch(logError(this._page));
this._client._sendMayFail('Fetch.continueRequest', { requestId: requestPausedEvent.requestId });
return;
}
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 } = {}) {
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!,
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
method: overrides.method,
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))
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!,
responseCode: response.status || 200,
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
responseHeaders: headersArray(responseHeaders),
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') {
const errorReason = errorReasons[errorCode];
assert(errorReason, 'Unknown error code: ' + errorCode);
await this._client.send('Fetch.failRequest', {
// 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.failRequest', {
requestId: this._interceptionId!,
errorReason
}).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);
});
}
}

View File

@ -36,7 +36,6 @@ import { CRBrowserContext } from './crBrowser';
import * as types from '../types';
import { ConsoleMessage } from '../console';
import { NotConnectedError } from '../errors';
import { logError } from '../logger';
import * as debugSupport from '../debug/debugSupport';
import { rewriteErrorMessage } from '../debug/stackTrace';
@ -70,7 +69,7 @@ export class CRPage implements PageDelegate {
this.rawKeyboard = new RawKeyboardImpl(client);
this.rawMouse = new RawMouseImpl(client);
this._pdf = new CRPDF(client);
this._coverage = new CRCoverage(client, browserContext);
this._coverage = new CRCoverage(client);
this._browserContext = browserContext;
this._page = new Page(this, browserContext);
this._mainFrameSession = new FrameSession(this, client, targetId, null);
@ -121,7 +120,7 @@ export class CRPage implements PageDelegate {
async exposeBinding(binding: PageBinding) {
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> {
@ -384,13 +383,13 @@ class FrameSession {
const localFrames = this._isMainFrame() ? this._page.frames() : [ this._page._frameManager.frame(this._targetId)! ];
for (const frame of localFrames) {
// Note: frames might be removed before we send these.
this._client.send('Page.createIsolatedWorld', {
this._client._sendMayFail('Page.createIsolatedWorld', {
frameId: frame._id,
grantUniveralAccess: true,
worldName: UTILITY_WORLD_NAME,
}).catch(logError(this._page));
});
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() === ':';
if (isInitialEmptyPage) {
@ -560,8 +559,8 @@ class FrameSession {
if (event.targetInfo.type !== 'worker') {
// Ideally, detaching should resume any target, but there is a bug in the backend.
session.send('Runtime.runIfWaitingForDebugger').catch(logError(this._page)).then(() => {
this._client.send('Target.detachFromTarget', { sessionId: event.sessionId }).catch(logError(this._page));
session._sendMayFail('Runtime.runIfWaitingForDebugger').then(() => {
this._client._sendMayFail('Target.detachFromTarget', { sessionId: event.sessionId });
});
return;
}
@ -573,10 +572,10 @@ class FrameSession {
worker._createExecutionContext(new CRExecutionContext(session, event.context));
});
Promise.all([
session.send('Runtime.enable'),
session.send('Network.enable'),
session.send('Runtime.runIfWaitingForDebugger'),
]).catch(logError(this._page)); // This might fail if the target is closed before we initialize.
session._sendMayFail('Runtime.enable'),
session._sendMayFail('Network.enable'),
session._sendMayFail('Runtime.runIfWaitingForDebugger'),
]); // This might fail if the target is closed before we initialize.
session.on('Runtime.consoleAPICalled', event => {
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
@ -816,9 +815,9 @@ class FrameSession {
}
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
}).catch(logError(this._page));
});
if (!result)
return null;
const quad = result.model.border;
@ -843,9 +842,9 @@ class FrameSession {
}
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
}).catch(logError(this._page));
});
if (!result)
return null;
return result.quads.map(quad => [
@ -864,10 +863,10 @@ class FrameSession {
}
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,
executionContextId: (to._delegate as CRExecutionContext)._contextId,
}).catch(logError(this._page));
});
if (!result || result.object.subtype === 'null')
throw new Error('Unable to adopt element handle from a different document');
return to.createHandle(result.object).asElement()!;

View File

@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
import { assert } from '../helper';
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
import { Protocol } from './protocol';
import { InnerLogger } from '../logger';
import { InnerLogger, errorLog } from '../logger';
import { rewriteErrorMessage } from '../debug/stackTrace';
export const ConnectionEvents = {
@ -34,7 +34,7 @@ export class FFConnection extends EventEmitter {
private _lastId: number;
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
private _transport: ConnectionTransport;
private _logger: InnerLogger;
readonly _logger: InnerLogger;
readonly _sessions: Map<string, FFSession>;
_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) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id)!;

View File

@ -21,7 +21,6 @@ import { Page } from '../page';
import * as network from '../network';
import * as frames from '../frames';
import { Protocol } from './protocol';
import { logError } from '../logger';
export class FFNetworkManager {
private _session: FFSession;
@ -164,12 +163,12 @@ class InterceptableRequest implements network.RouteDelegate {
headers,
postData
} = overrides;
await this._session.send('Network.resumeInterceptedRequest', {
await this._session.sendMayFail('Network.resumeInterceptedRequest', {
requestId: this._id,
method,
headers: headers ? headersArray(headers) : undefined,
postData: postData ? Buffer.from(postData).toString('base64') : undefined
}).catch(logError(this.request._page));
});
}
async fulfill(response: network.FulfillResponse) {
@ -185,20 +184,20 @@ class InterceptableRequest implements network.RouteDelegate {
if (responseBody && !('content-length' in responseHeaders))
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
await this._session.send('Network.fulfillInterceptedRequest', {
await this._session.sendMayFail('Network.fulfillInterceptedRequest', {
requestId: this._id,
status: response.status || 200,
statusText: network.STATUS_TEXTS[String(response.status || 200)] || '',
headers: headersArray(responseHeaders),
base64body: responseBody ? responseBody.toString('base64') : undefined,
}).catch(logError(this.request._page));
});
}
async abort(errorCode: string) {
await this._session.send('Network.abortInterceptedRequest', {
await this._session.sendMayFail('Network.abortInterceptedRequest', {
requestId: this._id,
errorCode,
}).catch(logError(this.request._page));
});
}
}

View File

@ -32,7 +32,6 @@ import { FFNetworkManager, headersArray } from './ffNetworkManager';
import { Protocol } from './protocol';
import { selectors } from '../selectors';
import { NotConnectedError } from '../errors';
import { logError } from '../logger';
import { rewriteErrorMessage } from '../debug/stackTrace';
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
@ -193,7 +192,7 @@ export class FFPage implements PageDelegate {
params.type,
params.message,
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));
}
@ -429,10 +428,10 @@ export class FFPage implements PageDelegate {
}
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,
objectId: handle._objectId,
}).catch(logError(this._page));
});
if (!result)
return null;
return result.quads.map(quad => [ quad.p1, quad.p2, quad.p3, quad.p4 ]);

View File

@ -19,7 +19,6 @@ import * as mime from 'mime';
import * as util from 'util';
import * as frames from './frames';
import { assert, helper } from './helper';
import { Page } from './page';
import { URLSearchParams } from 'url';
export type NetworkCookie = {
@ -112,14 +111,12 @@ export class Request {
private _frame: frames.Frame;
private _waitForResponsePromise: Promise<Response | null>;
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
readonly _page: Page;
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
url: string, resourceType: string, method: string, postData: string | null, headers: Headers) {
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
this._routeDelegate = routeDelegate;
this._frame = frame;
this._page = frame._page;
this._redirectedFrom = redirectedFrom;
if (redirectedFrom)
redirectedFrom._redirectedTo = this;

View File

@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
import { assert } from '../helper';
import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport';
import { Protocol } from './protocol';
import { InnerLogger } from '../logger';
import { InnerLogger, errorLog } from '../logger';
import { rewriteErrorMessage } from '../debug/stackTrace';
// WKPlaywright uses this special id to issue Browser.close command which we
@ -36,9 +36,8 @@ export class WKConnection {
private readonly _onDisconnect: () => void;
private _lastId = 0;
private _closed = false;
readonly browserSession: WKSession;
private _logger: InnerLogger;
readonly _logger: InnerLogger;
constructor(transport: ConnectionTransport, logger: InnerLogger, onDisconnect: () => void) {
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() {
this._crashed = true;
}

View File

@ -20,7 +20,6 @@ import { assert, helper } from '../helper';
import * as network from '../network';
import { Protocol } from './protocol';
import { WKSession } from './wkConnection';
import { logError } from '../logger';
const errorReasons: { [reason: string]: string } = {
'aborted': 'Cancellation',
@ -59,11 +58,9 @@ export class WKInterceptableRequest implements network.RouteDelegate {
const reason = errorReasons[errorCode];
assert(reason, 'Unknown error code: ' + errorCode);
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
// or the page was closed. We should tolerate these errors.
logError(this.request._page);
});
// 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.interceptAsError', { requestId: this._requestId, reason });
}
async fulfill(response: network.FulfillResponse) {
@ -89,7 +86,9 @@ export class WKInterceptableRequest implements network.RouteDelegate {
if (responseBody && !('content-length' in responseHeaders))
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,
status: response.status || 200,
statusText: network.STATUS_TEXTS[String(response.status || 200)],
@ -97,24 +96,18 @@ export class WKInterceptableRequest implements network.RouteDelegate {
headers: responseHeaders,
base64Encoded,
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 }) {
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,
method: overrides.method,
headers: overrides.headers,
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);
});
}

View File

@ -287,7 +287,7 @@ export class WKPage implements PageDelegate {
pageOrError = e;
}
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() === '') {
try {
// 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);
if (targetInfo.isPaused) {
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> {
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> {
@ -680,10 +680,10 @@ export class WKPage implements PageDelegate {
}
async closePage(runBeforeUnload: boolean): Promise<void> {
this._pageProxySession.send('Target.close', {
this._pageProxySession.sendMayFail('Target.close', {
targetId: this._session.sessionId,
runBeforeUnload
}).catch(logError(this._page));
});
}
canScreenshotOutsideViewport(): boolean {
@ -767,9 +767,9 @@ export class WKPage implements PageDelegate {
}
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
}).catch(logError(this._page));
});
if (!result)
return null;
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>> {
const result = await this._session.send('DOM.resolveNode', {
const result = await this._session.sendMayFail('DOM.resolveNode', {
objectId: handle._objectId,
executionContextId: (to._delegate as WKExecutionContext)._contextId
}).catch(logError(this._page));
});
if (!result || result.object.subtype === 'null')
throw new Error('Unable to adopt element handle from a different document');
return to.createHandle(result.object) as dom.ElementHandle<T>;