feat(firefox): support isolated worlds (#507)
This commit is contained in:
parent
6b0b7500bd
commit
21510a5b06
|
|
@ -9,7 +9,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"playwright": {
|
"playwright": {
|
||||||
"chromium_revision": "724623",
|
"chromium_revision": "724623",
|
||||||
"firefox_revision": "1012",
|
"firefox_revision": "1013",
|
||||||
"webkit_revision": "1092"
|
"webkit_revision": "1092"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import { CRBrowser } from './crBrowser';
|
||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { ConsoleMessage } from '../console';
|
import { ConsoleMessage } from '../console';
|
||||||
import * as accessibility from '../accessibility';
|
|
||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { assert, helper, RegisteredListener, debugError } from '../helper';
|
import { helper, RegisteredListener, debugError } from '../helper';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import { FFSession } from './ffConnection';
|
import { FFSession } from './ffConnection';
|
||||||
import { FFExecutionContext } from './ffExecutionContext';
|
import { FFExecutionContext } from './ffExecutionContext';
|
||||||
|
|
@ -30,10 +30,11 @@ import { BrowserContext } from '../browserContext';
|
||||||
import { getAccessibilityTree } from './ffAccessibility';
|
import { getAccessibilityTree } from './ffAccessibility';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import * as accessibility from '../accessibility';
|
|
||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
import { kScreenshotDuringNavigationError } from '../screenshotter';
|
import { kScreenshotDuringNavigationError } from '../screenshotter';
|
||||||
|
|
||||||
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
||||||
export class FFPage implements PageDelegate {
|
export class FFPage implements PageDelegate {
|
||||||
readonly rawMouse: RawMouseImpl;
|
readonly rawMouse: RawMouseImpl;
|
||||||
readonly rawKeyboard: RawKeyboardImpl;
|
readonly rawKeyboard: RawKeyboardImpl;
|
||||||
|
|
@ -70,7 +71,7 @@ export class FFPage implements PageDelegate {
|
||||||
|
|
||||||
async _initialize() {
|
async _initialize() {
|
||||||
const promises: Promise<any>[] = [
|
const promises: Promise<any>[] = [
|
||||||
this._session.send('Runtime.enable'),
|
this._session.send('Runtime.enable').then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
|
||||||
this._session.send('Network.enable'),
|
this._session.send('Network.enable'),
|
||||||
this._session.send('Page.enable'),
|
this._session.send('Page.enable'),
|
||||||
this._session.send('Page.setInterceptFileChooserDialog', { enabled: true })
|
this._session.send('Page.setInterceptFileChooserDialog', { enabled: true })
|
||||||
|
|
@ -87,6 +88,13 @@ export class FFPage implements PageDelegate {
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _ensureIsolatedWorld(name: string) {
|
||||||
|
await this._session.send('Page.addScriptToEvaluateOnNewDocument', {
|
||||||
|
script: '',
|
||||||
|
worldName: name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_onExecutionContextCreated(payload: Protocol.Runtime.executionContextCreatedPayload) {
|
_onExecutionContextCreated(payload: Protocol.Runtime.executionContextCreatedPayload) {
|
||||||
const {executionContextId, auxData} = payload;
|
const {executionContextId, auxData} = payload;
|
||||||
const frame = this._page._frameManager.frame(auxData ? auxData.frameId : null);
|
const frame = this._page._frameManager.frame(auxData ? auxData.frameId : null);
|
||||||
|
|
@ -94,8 +102,10 @@ export class FFPage implements PageDelegate {
|
||||||
return;
|
return;
|
||||||
const delegate = new FFExecutionContext(this._session, executionContextId);
|
const delegate = new FFExecutionContext(this._session, executionContextId);
|
||||||
const context = new dom.FrameExecutionContext(delegate, frame);
|
const context = new dom.FrameExecutionContext(delegate, frame);
|
||||||
frame._contextCreated('main', context);
|
if (auxData.name === UTILITY_WORLD_NAME)
|
||||||
frame._contextCreated('utility', context);
|
frame._contextCreated('utility', context);
|
||||||
|
else if (!auxData.name)
|
||||||
|
frame._contextCreated('main', context);
|
||||||
this._contextIdToContext.set(executionContextId, context);
|
this._contextIdToContext.set(executionContextId, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,8 +361,12 @@ export class FFPage 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>> {
|
||||||
assert(false, 'Multiple isolated worlds are not implemented');
|
const result = await this._session.send('Page.adoptNode', {
|
||||||
return handle;
|
frameId: handle._context.frame._id,
|
||||||
|
objectId: toRemoteObject(handle).objectId!,
|
||||||
|
executionContextId: (to._delegate as FFExecutionContext)._executionContextId
|
||||||
|
});
|
||||||
|
return to._createHandle(result.remoteObject) as dom.ElementHandle<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccessibilityTree(needle?: dom.ElementHandle) {
|
async getAccessibilityTree(needle?: dom.ElementHandle) {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
||||||
await page.click('circle');
|
await page.click('circle');
|
||||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
|
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should click the button if window.Node is removed', async({page, server}) => {
|
it('should click the button if window.Node is removed', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
await page.evaluate(() => delete window.Node);
|
await page.evaluate(() => delete window.Node);
|
||||||
await page.click('button');
|
await page.click('button');
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
|
||||||
await button.click();
|
await button.click();
|
||||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should work with Node removed', async({page, server}) => {
|
it('should work with Node removed', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
await page.evaluate(() => delete window['Node']);
|
await page.evaluate(() => delete window['Node']);
|
||||||
const button = await page.$('button');
|
const button = await page.$('button');
|
||||||
|
|
@ -239,7 +239,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
|
||||||
await button.hover();
|
await button.hover();
|
||||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should work when Node is removed', async({page, server}) => {
|
it('should work when Node is removed', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||||
await page.evaluate(() => delete window['Node']);
|
await page.evaluate(() => delete window['Node']);
|
||||||
const button = await page.$('#button-6');
|
const button = await page.$('#button-6');
|
||||||
|
|
@ -257,7 +257,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
|
||||||
expect(Math.round(ratio * 10)).toBe(10 - i);
|
expect(Math.round(ratio * 10)).toBe(10 - i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should work when Node is removed', async({page, server}) => {
|
it('should work when Node is removed', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
||||||
await page.evaluate(() => delete window['Node']);
|
await page.evaluate(() => delete window['Node']);
|
||||||
for (let i = 0; i < 11; ++i) {
|
for (let i = 0; i < 11; ++i) {
|
||||||
|
|
@ -290,7 +290,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
|
||||||
await handle.fill('some value');
|
await handle.fill('some value');
|
||||||
expect(await page.evaluate(() => result)).toBe('some value');
|
expect(await page.evaluate(() => result)).toBe('some value');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should fill input when Node is removed', async({page, server}) => {
|
it('should fill input when Node is removed', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||||
await page.evaluate(() => delete window['Node']);
|
await page.evaluate(() => delete window['Node']);
|
||||||
const handle = await page.$('input');
|
const handle = await page.$('input');
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT,
|
||||||
await page.hover('#button-91');
|
await page.hover('#button-91');
|
||||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should trigger hover state with removed window.Node', async({page, server}) => {
|
it('should trigger hover state with removed window.Node', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||||
await page.evaluate(() => delete window.Node);
|
await page.evaluate(() => delete window.Node);
|
||||||
await page.hover('#button-6');
|
await page.hover('#button-6');
|
||||||
|
|
|
||||||
|
|
@ -960,7 +960,7 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
|
||||||
expect(error.message).toContain('Indices must be numbers');
|
expect(error.message).toContain('Indices must be numbers');
|
||||||
});
|
});
|
||||||
// @see https://github.com/GoogleChrome/puppeteer/issues/3327
|
// @see https://github.com/GoogleChrome/puppeteer/issues/3327
|
||||||
it.skip(FFOX)('should work when re-defining top-level Event class', async({page, server}) => {
|
it('should work when re-defining top-level Event class', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/select.html');
|
await page.goto(server.PREFIX + '/input/select.html');
|
||||||
await page.evaluate(() => window.Event = null);
|
await page.evaluate(() => window.Event = null);
|
||||||
await page.select('select', 'blue');
|
await page.select('select', 'blue');
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
|
||||||
await frame.waitForSelector('div');
|
await frame.waitForSelector('div');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip(FFOX)('should work with removed MutationObserver', async({page, server}) => {
|
it('should work with removed MutationObserver', async({page, server}) => {
|
||||||
await page.evaluate(() => delete window.MutationObserver);
|
await page.evaluate(() => delete window.MutationObserver);
|
||||||
const [handle] = await Promise.all([
|
const [handle] = await Promise.all([
|
||||||
page.waitForSelector('.zombo'),
|
page.waitForSelector('.zombo'),
|
||||||
|
|
@ -318,7 +318,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
|
||||||
await waitForSelector;
|
await waitForSelector;
|
||||||
expect(boxFound).toBe(true);
|
expect(boxFound).toBe(true);
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should wait for visible', async({page, server}) => {
|
it('should wait for visible', async({page, server}) => {
|
||||||
let divFound = false;
|
let divFound = false;
|
||||||
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
|
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
|
||||||
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue