From 4743036bbe03baf2dee30a993c47af08413d007b Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 27 Oct 2021 13:28:52 -0700 Subject: [PATCH] fix: guard against undefined contexts (#9826) We do not create contexts when we are unable to attribute them to a frame or they come from a stale oopif. Async hop in the binding method can also cause the context to be destroyed already. Reproduced with codegen. --- .../src/server/chromium/crPage.ts | 12 ++++++++---- .../src/server/firefox/ffPage.ts | 16 +++++++++++----- .../src/server/webkit/wkPage.ts | 19 +++++++++++-------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index 568b8d350c..07347534d1 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -773,7 +773,9 @@ class FrameSession { // @see https://github.com/GoogleChrome/puppeteer/issues/3865 return; } - const context = this._contextIdToContext.get(event.executionContextId)!; + const context = this._contextIdToContext.get(event.executionContextId); + if (!context) + return; const values = event.args.map(arg => context.createHandle(arg)); this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace)); } @@ -786,10 +788,12 @@ class FrameSession { } async _onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) { - const context = this._contextIdToContext.get(event.executionContextId)!; const pageOrError = await this._crPage.pageOrError(); - if (!(pageOrError instanceof Error)) - await this._page._onBindingCalled(event.payload, context); + if (!(pageOrError instanceof Error)) { + const context = this._contextIdToContext.get(event.executionContextId); + if (context) + await this._page._onBindingCalled(event.payload, context); + } } _onDialog(event: Protocol.Page.javascriptDialogOpeningPayload) { diff --git a/packages/playwright-core/src/server/firefox/ffPage.ts b/packages/playwright-core/src/server/firefox/ffPage.ts index 4a40e3b01d..f9cd879207 100644 --- a/packages/playwright-core/src/server/firefox/ffPage.ts +++ b/packages/playwright-core/src/server/firefox/ffPage.ts @@ -237,7 +237,9 @@ export class FFPage implements PageDelegate { _onConsole(payload: Protocol.Runtime.consolePayload) { const { type, args, executionContextId, location } = payload; - const context = this._contextIdToContext.get(executionContextId)!; + const context = this._contextIdToContext.get(executionContextId); + if (!context) + return; this._page._addConsoleMessage(type, args.map(arg => context.createHandle(arg)), location); } @@ -253,15 +255,19 @@ export class FFPage implements PageDelegate { } async _onBindingCalled(event: Protocol.Page.bindingCalledPayload) { - const context = this._contextIdToContext.get(event.executionContextId)!; const pageOrError = await this.pageOrError(); - if (!(pageOrError instanceof Error)) - await this._page._onBindingCalled(event.payload, context); + if (!(pageOrError instanceof Error)) { + const context = this._contextIdToContext.get(event.executionContextId); + if (context) + await this._page._onBindingCalled(event.payload, context); + } } async _onFileChooserOpened(payload: Protocol.Page.fileChooserOpenedPayload) { const { executionContextId, element } = payload; - const context = this._contextIdToContext.get(executionContextId)!; + const context = this._contextIdToContext.get(executionContextId); + if (!context) + return; const handle = context.createHandle(element).asElement()!; await this._page._onFileChooserOpened(handle); } diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 2339def513..38f9675226 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -498,9 +498,9 @@ export class WKPage implements PageDelegate { const { type, level, text, parameters, url, line: lineNumber, column: columnNumber, source } = event.message; if (level === 'debug' && parameters && parameters[0].value === BINDING_CALL_MESSAGE) { const parsedObjectId = JSON.parse(parameters[1].objectId!); - const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId)!; this.pageOrError().then(pageOrError => { - if (!(pageOrError instanceof Error)) + const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId); + if (!(pageOrError instanceof Error) && context) this._page._onBindingCalled(parameters[2].value, context); }); return; @@ -531,16 +531,19 @@ export class WKPage implements PageDelegate { else if (type === 'timing') derivedType = 'timeEnd'; - const handles = (parameters || []).map(p => { - let context: dom.FrameExecutionContext | null = null; + const handles: JSHandle[] = []; + for (const p of parameters || []) { + let context: dom.FrameExecutionContext | undefined; if (p.objectId) { const objectId = JSON.parse(p.objectId); - context = this._contextIdToContext.get(objectId.injectedScriptId)!; + context = this._contextIdToContext.get(objectId.injectedScriptId); } else { - context = this._contextIdToContext.get(this._mainFrameContextId!)!; + context = this._contextIdToContext.get(this._mainFrameContextId!); } - return context.createHandle(p); - }); + if (!context) + return; + handles.push(context.createHandle(p)); + } this._lastConsoleMessage = { derivedType, text,