mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix: avoid unhandled promise rejection in WKSession.send (#770)
This commit is contained in:
parent
fd3a93084c
commit
985faebd12
@ -119,20 +119,19 @@ export class WKSession extends platform.EventEmitter {
|
|||||||
this.once = super.once;
|
this.once = super.once;
|
||||||
}
|
}
|
||||||
|
|
||||||
send<T extends keyof Protocol.CommandParameters>(
|
async send<T extends keyof Protocol.CommandParameters>(
|
||||||
method: T,
|
method: T,
|
||||||
params?: Protocol.CommandParameters[T]
|
params?: Protocol.CommandParameters[T]
|
||||||
): Promise<Protocol.CommandReturnValues[T]> {
|
): Promise<Protocol.CommandReturnValues[T]> {
|
||||||
if (this._disposed)
|
if (this._disposed)
|
||||||
return Promise.reject(new Error(`Protocol error (${method}): ${this.errorText}`));
|
throw new Error(`Protocol error (${method}): ${this.errorText}`);
|
||||||
const id = this.connection.nextMessageId();
|
const id = this.connection.nextMessageId();
|
||||||
const messageObj = { id, method, params };
|
const messageObj = { id, method, params };
|
||||||
platform.debug('pw:wrapped:' + this.sessionId)('SEND ► ' + JSON.stringify(messageObj, null, 2));
|
platform.debug('pw:wrapped:' + this.sessionId)('SEND ► ' + JSON.stringify(messageObj, null, 2));
|
||||||
const result = new Promise<Protocol.CommandReturnValues[T]>((resolve, reject) => {
|
this._rawSend(messageObj);
|
||||||
|
return new Promise<Protocol.CommandReturnValues[T]>((resolve, reject) => {
|
||||||
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
||||||
});
|
});
|
||||||
this._rawSend(messageObj);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isDisposed(): boolean {
|
isDisposed(): boolean {
|
||||||
|
@ -120,21 +120,17 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
let callFunctionOnPromise;
|
return this._session.send('Runtime.callFunctionOn', {
|
||||||
try {
|
functionDeclaration: functionText + '\n' + suffix + '\n',
|
||||||
callFunctionOnPromise = this._session.send('Runtime.callFunctionOn', {
|
objectId: thisObjectId,
|
||||||
functionDeclaration: functionText + '\n' + suffix + '\n',
|
arguments: serializableArgs.map((arg: any) => this._convertArgument(arg)),
|
||||||
objectId: thisObjectId,
|
returnByValue: false,
|
||||||
arguments: serializableArgs.map((arg: any) => this._convertArgument(arg)),
|
emulateUserGesture: true
|
||||||
returnByValue: false,
|
}).catch(err => {
|
||||||
emulateUserGesture: true
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof TypeError && err.message.startsWith('Converting circular structure to JSON'))
|
if (err instanceof TypeError && err.message.startsWith('Converting circular structure to JSON'))
|
||||||
err.message += ' Are you passing a nested JSHandle?';
|
err.message += ' Are you passing a nested JSHandle?';
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}).then(response => {
|
||||||
return callFunctionOnPromise.then(response => {
|
|
||||||
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
||||||
|
@ -95,10 +95,24 @@ export class WKPage implements PageDelegate {
|
|||||||
// This method is called for provisional targets as well. The session passed as the parameter
|
// This method is called for provisional targets as well. The session passed as the parameter
|
||||||
// may be different from the current session and may be destroyed without becoming current.
|
// may be different from the current session and may be destroyed without becoming current.
|
||||||
async _initializeSession(session: WKSession, resourceTreeHandler: (r: Protocol.Page.getResourceTreeReturnValue) => void) {
|
async _initializeSession(session: WKSession, resourceTreeHandler: (r: Protocol.Page.getResourceTreeReturnValue) => void) {
|
||||||
const promises : Promise<any>[] = [
|
await this._initializeSessionMayThrow(session, resourceTreeHandler).catch(e => {
|
||||||
|
if (session.isDisposed())
|
||||||
|
return;
|
||||||
|
// Swallow initialization errors due to newer target swap in,
|
||||||
|
// since we will reinitialize again.
|
||||||
|
if (this._session === session)
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _initializeSessionMayThrow(session: WKSession, resourceTreeHandler: (r: Protocol.Page.getResourceTreeReturnValue) => void) {
|
||||||
|
const [, frameTree] = await Promise.all([
|
||||||
// Page agent must be enabled before Runtime.
|
// Page agent must be enabled before Runtime.
|
||||||
session.send('Page.enable'),
|
session.send('Page.enable'),
|
||||||
session.send('Page.getResourceTree').then(resourceTreeHandler),
|
session.send('Page.getResourceTree'),
|
||||||
|
]);
|
||||||
|
resourceTreeHandler(frameTree);
|
||||||
|
const promises : Promise<any>[] = [
|
||||||
// Resource tree should be received before first execution context.
|
// Resource tree should be received before first execution context.
|
||||||
session.send('Runtime.enable'),
|
session.send('Runtime.enable'),
|
||||||
session.send('Page.createUserWorld', { name: UTILITY_WORLD_NAME }).catch(_ => {}), // Worlds are per-process
|
session.send('Page.createUserWorld', { name: UTILITY_WORLD_NAME }).catch(_ => {}), // Worlds are per-process
|
||||||
@ -129,19 +143,13 @@ export class WKPage implements PageDelegate {
|
|||||||
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._page._state.extraHTTPHeaders }));
|
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._page._state.extraHTTPHeaders }));
|
||||||
if (this._page._state.hasTouch)
|
if (this._page._state.hasTouch)
|
||||||
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));
|
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));
|
||||||
await Promise.all(promises).catch(e => {
|
await Promise.all(promises);
|
||||||
if (session.isDisposed())
|
|
||||||
return;
|
|
||||||
// Swallow initialization errors due to newer target swap in,
|
|
||||||
// since we will reinitialize again.
|
|
||||||
if (this._session === session)
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onProvisionalLoadStarted(provisionalSession: WKSession) {
|
initializeProvisionalPage(provisionalSession: WKSession) : Promise<void> {
|
||||||
assert(!this._provisionalPage);
|
assert(!this._provisionalPage);
|
||||||
this._provisionalPage = new WKProvisionalPage(provisionalSession, this);
|
this._provisionalPage = new WKProvisionalPage(provisionalSession, this);
|
||||||
|
return this._provisionalPage.initializationPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
onProvisionalLoadCommitted(session: WKSession) {
|
onProvisionalLoadCommitted(session: WKSession) {
|
||||||
|
@ -139,10 +139,13 @@ export class WKPageProxy {
|
|||||||
}
|
}
|
||||||
if (targetInfo.isProvisional) {
|
if (targetInfo.isProvisional) {
|
||||||
(session as any)[isPovisionalSymbol] = true;
|
(session as any)[isPovisionalSymbol] = true;
|
||||||
if (this._wkPage)
|
if (this._wkPage) {
|
||||||
this._wkPage.onProvisionalLoadStarted(session);
|
const provisionalPageInitialized = this._wkPage.initializeProvisionalPage(session);
|
||||||
if (targetInfo.isPaused)
|
if (targetInfo.isPaused)
|
||||||
|
provisionalPageInitialized.then(() => this._resumeTarget(targetInfo.targetId));
|
||||||
|
} else if (targetInfo.isPaused) {
|
||||||
this._resumeTarget(targetInfo.targetId);
|
this._resumeTarget(targetInfo.targetId);
|
||||||
|
}
|
||||||
} else if (this._pagePromise) {
|
} else if (this._pagePromise) {
|
||||||
assert(!this._pagePausedOnStart);
|
assert(!this._pagePausedOnStart);
|
||||||
// This is the first time page target is created, will resume
|
// This is the first time page target is created, will resume
|
||||||
|
@ -24,6 +24,7 @@ export class WKProvisionalPage {
|
|||||||
private readonly _wkPage: WKPage;
|
private readonly _wkPage: WKPage;
|
||||||
private _sessionListeners: RegisteredListener[] = [];
|
private _sessionListeners: RegisteredListener[] = [];
|
||||||
private _mainFrameId: string | null = null;
|
private _mainFrameId: string | null = null;
|
||||||
|
readonly initializationPromise: Promise<void>;
|
||||||
|
|
||||||
constructor(session: WKSession, page: WKPage) {
|
constructor(session: WKSession, page: WKPage) {
|
||||||
this._session = session;
|
this._session = session;
|
||||||
@ -47,7 +48,7 @@ export class WKProvisionalPage {
|
|||||||
helper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(e))),
|
helper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(e))),
|
||||||
];
|
];
|
||||||
|
|
||||||
this._wkPage._initializeSession(session, ({frameTree}) => this._handleFrameTree(frameTree));
|
this.initializationPromise = this._wkPage._initializeSession(session, ({frameTree}) => this._handleFrameTree(frameTree));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user