diff --git a/package.json b/package.json index 222ed7fcff..ae0b6836a7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "playwright": { "chromium_revision": "719491", "firefox_revision": "1004", - "webkit_revision": "1022" + "webkit_revision": "1023" }, "scripts": { "unit": "node test/test.js", diff --git a/src/chromium/FrameManager.ts b/src/chromium/FrameManager.ts index 3b7eaf8ffe..9c0b796d2d 100644 --- a/src/chromium/FrameManager.ts +++ b/src/chromium/FrameManager.ts @@ -31,7 +31,6 @@ import { Protocol } from './protocol'; import { Events as CommonEvents } from '../events'; import { toConsoleMessageLocation, exceptionToError, releaseObject } from './protocolHelper'; import * as dialog from '../dialog'; -import * as console from '../console'; import { PageDelegate } from '../page'; import { RawMouseImpl, RawKeyboardImpl } from './Input'; import { CRScreenshotDelegate } from './Screenshotter'; @@ -45,6 +44,7 @@ import { Browser } from './Browser'; import { BrowserContext } from './BrowserContext'; import * as types from '../types'; import * as input from '../input'; +import { ConsoleMessage } from '../console'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; @@ -331,7 +331,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source: `//# sourceURL=${EVALUATION_SCRIPT_URL}`, worldName: name, - }), + }); await Promise.all(this.frames().map(frame => this._client.send('Page.createIsolatedWorld', { frameId: this._frameData(frame).id, grantUniveralAccess: true, @@ -457,7 +457,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, if (args) args.map(arg => releaseObject(this._client, arg)); if (source !== 'worker') - this._page.emit(CommonEvents.Page.Console, new console.ConsoleMessage(level, text, [], {url, lineNumber})); + this._page.emit(CommonEvents.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber})); } async _onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload) { diff --git a/src/webkit/ExecutionContext.ts b/src/webkit/ExecutionContext.ts index 9a15401dd1..72361eea9a 100644 --- a/src/webkit/ExecutionContext.ts +++ b/src/webkit/ExecutionContext.ts @@ -116,7 +116,6 @@ export class ExecutionContextDelegate implements js.ExecutionContextDelegate { try { callFunctionOnPromise = this._session.send('Runtime.callFunctionOn', { functionDeclaration: functionText + '\n' + suffix + '\n', - // TODO(yurys): support executionContextId in WebKit objectId: thisObjectId, arguments: serializableArgs.map((arg: any) => this._convertArgument(arg)), returnByValue: false, diff --git a/src/webkit/FrameManager.ts b/src/webkit/FrameManager.ts index f9ffd6d3c4..b1765ea678 100644 --- a/src/webkit/FrameManager.ts +++ b/src/webkit/FrameManager.ts @@ -25,7 +25,7 @@ import * as network from '../network'; import { TargetSession, TargetSessionEvents } from './Connection'; import { Events } from './events'; import { Events as CommonEvents } from '../events'; -import { ExecutionContextDelegate } from './ExecutionContext'; +import { ExecutionContextDelegate, EVALUATION_SCRIPT_URL } from './ExecutionContext'; import { NetworkManager, NetworkManagerEvents } from './NetworkManager'; import { Page, PageDelegate } from '../page'; import { Protocol } from './protocol'; @@ -37,6 +37,8 @@ import { WKScreenshotDelegate } from './Screenshotter'; import * as input from '../input'; import * as types from '../types'; +const UTILITY_WORLD_NAME = '__playwright_utility_world__'; + export const FrameManagerEvents = { FrameNavigatedWithinDocument: Symbol('FrameNavigatedWithinDocument'), TargetSwappedOnNavigation: Symbol('TargetSwappedOnNavigation'), @@ -98,7 +100,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, ]); this._handleFrameTree(frameTree); await Promise.all([ - this._session.send('Runtime.enable'), + this._session.send('Runtime.enable').then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)), this._session.send('Console.enable'), this._session.send('Dialog.enable'), this._session.send('Page.setInterceptFileChooserDialog', { enabled: true }), @@ -293,8 +295,6 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, _onExecutionContextCreated(contextPayload : Protocol.Runtime.ExecutionContextDescription) { if (this._contextIdToContext.has(contextPayload.id)) return; - if (!contextPayload.isPageContext) - return; const frameId = contextPayload.frameId; // If the frame was attached manually there is no navigation event. // FIXME: support frameAttached event in WebKit protocol. @@ -304,8 +304,10 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, const context = new js.ExecutionContext(new ExecutionContextDelegate(this._session, contextPayload)); if (frame) { context._domWorld = new dom.DOMWorld(context, new DOMWorldDelegate(this, frame)); - frame._contextCreated('main', context); - frame._contextCreated('utility', context); + if (contextPayload.isPageContext) + frame._contextCreated('main', context); + else if (contextPayload.name === UTILITY_WORLD_NAME) + frame._contextCreated('utility', context); } this._contextIdToContext.set(contextPayload.id, context); } @@ -413,7 +415,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, } async _onFileChooserOpened(event: {frameId: Protocol.Network.FrameId, element: Protocol.Runtime.RemoteObject}) { - const context = await this.frame(event.frameId)._utilityContext(); + const context = await this.frame(event.frameId)._mainContext(); const handle = context._createHandle(event.element).asElement()!; this._page._onFileChooserOpened(handle); } @@ -422,6 +424,16 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, return this._networkManager.setExtraHTTPHeaders(extraHTTPHeaders); } + async _ensureIsolatedWorld(name: string) { + if (this._isolatedWorlds.has(name)) + return; + this._isolatedWorlds.add(name); + await this._session.send('Page.createIsolatedWorld', { + name, + source: `//# sourceURL=${EVALUATION_SCRIPT_URL}` + }); + } + async setUserAgent(userAgent: string): Promise { await this._session.send('Page.overrideUserAgent', { value: userAgent }); } diff --git a/src/webkit/JSHandle.ts b/src/webkit/JSHandle.ts index 004b94c0ed..250bab91e8 100644 --- a/src/webkit/JSHandle.ts +++ b/src/webkit/JSHandle.ts @@ -15,12 +15,13 @@ * limitations under the License. */ -import { debugError, assert } from '../helper'; -import * as input from '../input'; import * as dom from '../dom'; import * as frames from '../frames'; +import { debugError } from '../helper'; +import * as input from '../input'; import * as types from '../types'; import { TargetSession } from './Connection'; +import { ExecutionContextDelegate } from './ExecutionContext'; import { FrameManager } from './FrameManager'; import { Protocol } from './protocol'; @@ -99,8 +100,11 @@ export class DOMWorldDelegate implements dom.DOMWorldDelegate { } async adoptElementHandle(handle: dom.ElementHandle, to: dom.DOMWorld): Promise> { - assert(false, 'Multiple isolated worlds are not implemented'); - return handle; + const result = await this._client.send('DOM.resolveNode', { + objectId: toRemoteObject(handle).objectId, + executionContextId: (to.context._delegate as ExecutionContextDelegate)._contextId + }); + return to.context._createHandle(result.object) as dom.ElementHandle; } } diff --git a/test/click.spec.js b/test/click.spec.js index 3f9ee71ee8..a389918150 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -36,7 +36,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME await page.click('circle'); expect(await page.evaluate(() => window.__CLICKED)).toBe(42); }); - it.skip(FFOX || WEBKIT)('should click the button if window.Node is removed', async({page, server}) => { + it.skip(FFOX)('should click the button if window.Node is removed', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => delete window.Node); await page.click('button');