mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 21:53:35 +03:00
feat(evaluate): survive null JSON in WK and FF (#352)
This commit is contained in:
parent
682e2be15f
commit
0fab90be79
@ -9,7 +9,7 @@
|
||||
"main": "index.js",
|
||||
"playwright": {
|
||||
"chromium_revision": "724623",
|
||||
"firefox_revision": "1008",
|
||||
"firefox_revision": "1009",
|
||||
"webkit_revision": "1055"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -69,7 +69,6 @@ export class CRBrowser extends browser.Browser {
|
||||
}
|
||||
|
||||
_createBrowserContext(contextId: string | null, options: BrowserContextOptions): BrowserContext {
|
||||
let overrides: CROverrides | null = null;
|
||||
const context = new BrowserContext({
|
||||
pages: async (): Promise<Page[]> => {
|
||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
||||
@ -81,28 +80,7 @@ export class CRBrowser extends browser.Browser {
|
||||
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
||||
const target = this._targets.get(targetId);
|
||||
assert(await target._initializedPromise, 'Failed to create target for page');
|
||||
const page = await target.page();
|
||||
const crPage = target._crPage;
|
||||
const session = crPage._client;
|
||||
const promises: Promise<any>[] = [ overrides._applyOverrides(crPage) ];
|
||||
if (options.bypassCSP)
|
||||
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.ignoreHTTPSErrors)
|
||||
promises.push(session.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||
if (options.viewport)
|
||||
promises.push(crPage.setViewport(options.viewport));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(session.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||
if (options.userAgent)
|
||||
crPage._networkManager.setUserAgent(options.userAgent);
|
||||
if (options.mediaType || options.colorScheme) {
|
||||
const features = options.colorScheme ? [{ name: 'prefers-color-scheme', value: options.colorScheme }] : [];
|
||||
promises.push(session.send('Emulation.setEmulatedMedia', { media: options.mediaType || '', features }));
|
||||
}
|
||||
if (options.timezoneId)
|
||||
promises.push(emulateTimezone(session, options.timezoneId));
|
||||
await Promise.all(promises);
|
||||
return page;
|
||||
return target.page();
|
||||
},
|
||||
|
||||
close: async (): Promise<void> => {
|
||||
@ -162,8 +140,7 @@ export class CRBrowser extends browser.Browser {
|
||||
}
|
||||
|
||||
}, options);
|
||||
overrides = new CROverrides(context);
|
||||
(context as any).overrides = overrides;
|
||||
(context as any).overrides = new CROverrides(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
@ -317,13 +294,3 @@ export class CRBrowser extends browser.Browser {
|
||||
return !this._connection._closed;
|
||||
}
|
||||
}
|
||||
|
||||
async function emulateTimezone(session: CRSession, timezoneId: string) {
|
||||
try {
|
||||
await session.send('Emulation.setTimezoneOverride', { timezoneId: timezoneId });
|
||||
} catch (exception) {
|
||||
if (exception.message.includes('Invalid timezone'))
|
||||
throw new Error(`Invalid timezone ID: ${timezoneId}`);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import { BrowserContext } from '../browserContext';
|
||||
import * as types from '../types';
|
||||
import * as input from '../input';
|
||||
import { ConsoleMessage } from '../console';
|
||||
import { CROverrides } from './features/crOverrides';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
@ -86,13 +87,32 @@ export class CRPage implements PageDelegate {
|
||||
this._client.send('Page.getFrameTree'),
|
||||
]);
|
||||
this._handleFrameTree(frameTree);
|
||||
await Promise.all([
|
||||
const promises: Promise<any>[] = [
|
||||
this._client.send('Log.enable', {}),
|
||||
this._client.send('Page.setInterceptFileChooserDialog', {enabled: true}),
|
||||
this._client.send('Page.setLifecycleEventsEnabled', { enabled: true }),
|
||||
this._client.send('Runtime.enable', {}).then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
|
||||
this._networkManager.initialize(),
|
||||
]);
|
||||
((this._page.browserContext() as any).overrides as CROverrides)._applyOverrides(this),
|
||||
];
|
||||
const options = this._page.browserContext()._options;
|
||||
if (options.bypassCSP)
|
||||
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.ignoreHTTPSErrors)
|
||||
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||
if (options.viewport)
|
||||
promises.push(this.setViewport(options.viewport));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||
if (options.userAgent)
|
||||
this._networkManager.setUserAgent(options.userAgent);
|
||||
if (options.mediaType || options.colorScheme) {
|
||||
const features = options.colorScheme ? [{ name: 'prefers-color-scheme', value: options.colorScheme }] : [];
|
||||
promises.push(this._client.send('Emulation.setEmulatedMedia', { media: options.mediaType || '', features }));
|
||||
}
|
||||
if (options.timezoneId)
|
||||
promises.push(emulateTimezone(this._client, options.timezoneId));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
didClose() {
|
||||
@ -488,3 +508,13 @@ export class ChromiumPage extends Page {
|
||||
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
||||
return handle._remoteObject as Protocol.Runtime.RemoteObject;
|
||||
}
|
||||
|
||||
async function emulateTimezone(session: CRSession, timezoneId: string) {
|
||||
try {
|
||||
await session.send('Emulation.setTimezoneOverride', { timezoneId: timezoneId });
|
||||
} catch (exception) {
|
||||
if (exception.message.includes('Invalid timezone'))
|
||||
throw new Error(`Invalid timezone ID: ${timezoneId}`);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -155,21 +155,7 @@ export class FFBrowser extends browser.Browser {
|
||||
browserContextId: browserContextId || undefined
|
||||
});
|
||||
const target = this._targets.get(targetId);
|
||||
const page = await target.page();
|
||||
const session = target._ffPage._session;
|
||||
const promises: Promise<any>[] = [];
|
||||
if (options.viewport)
|
||||
promises.push(page._delegate.setViewport(options.viewport));
|
||||
if (options.bypassCSP)
|
||||
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(session.send('Page.setJavascriptEnabled', { enabled: false }));
|
||||
if (options.userAgent)
|
||||
promises.push(session.send('Page.setUserAgent', { userAgent: options.userAgent }));
|
||||
if (options.mediaType || options.colorScheme)
|
||||
promises.push(session.send('Page.setEmulatedMedia', { type: options.mediaType, colorScheme: options.colorScheme }));
|
||||
await Promise.all(promises);
|
||||
return page;
|
||||
return target.page();
|
||||
},
|
||||
|
||||
close: async (): Promise<void> => {
|
||||
|
@ -68,12 +68,24 @@ export class FFPage implements PageDelegate {
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
await Promise.all([
|
||||
const promises: Promise<any>[] = [
|
||||
this._session.send('Runtime.enable'),
|
||||
this._session.send('Network.enable'),
|
||||
this._session.send('Page.enable'),
|
||||
this._session.send('Page.setInterceptFileChooserDialog', { enabled: true })
|
||||
]);
|
||||
];
|
||||
const options = this._page.browserContext()._options;
|
||||
if (options.viewport)
|
||||
promises.push(this.setViewport(options.viewport));
|
||||
if (options.bypassCSP)
|
||||
promises.push(this._session.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(this._session.send('Page.setJavascriptEnabled', { enabled: false }));
|
||||
if (options.userAgent)
|
||||
promises.push(this._session.send('Page.setUserAgent', { userAgent: options.userAgent }));
|
||||
if (options.mediaType || options.colorScheme)
|
||||
promises.push(this._session.send('Page.setEmulatedMedia', { type: options.mediaType, colorScheme: options.colorScheme }));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
_onExecutionContextCreated({executionContextId, auxData}) {
|
||||
|
@ -30,6 +30,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||
_contextId: number;
|
||||
private _contextDestroyedCallback: () => void;
|
||||
private _executionContextDestroyedPromise: Promise<unknown>;
|
||||
_jsonObjectId: Protocol.Runtime.RemoteObjectId | undefined;
|
||||
|
||||
constructor(client: WKTargetSession, contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||
this._session = client;
|
||||
@ -218,7 +219,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||
}
|
||||
|
||||
private _returnObjectByValue(objectId: Protocol.Runtime.RemoteObjectId) {
|
||||
const serializeFunction = function() {
|
||||
const serializeFunction = function(JSON: { stringify: (o: any) => string }) {
|
||||
try {
|
||||
return JSON.stringify(this);
|
||||
} catch (e) {
|
||||
@ -231,6 +232,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||
// Serialize object using standard JSON implementation to correctly pass 'undefined'.
|
||||
functionDeclaration: serializeFunction + '\n' + suffix + '\n',
|
||||
objectId: objectId,
|
||||
arguments: [ { objectId: this._jsonObjectId } ],
|
||||
returnByValue: true
|
||||
}).catch(e => {
|
||||
if (isSwappedOutError(e))
|
||||
|
@ -36,6 +36,8 @@ import { PNG } from 'pngjs';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
const BINDING_CALL_MESSAGE = '__playwright_binding_call__';
|
||||
const JSON_CALL_MESSAGE = '__playwright_json_call__';
|
||||
const JSON_SAVE_SCRIPT = `console.debug('${JSON_CALL_MESSAGE}', JSON)`;
|
||||
|
||||
export class WKPage implements PageDelegate {
|
||||
readonly rawMouse: RawMouseImpl;
|
||||
@ -47,7 +49,7 @@ export class WKPage implements PageDelegate {
|
||||
private readonly _contextIdToContext: Map<number, dom.FrameExecutionContext>;
|
||||
private _isolatedWorlds: Set<string>;
|
||||
private _sessionListeners: RegisteredListener[] = [];
|
||||
private readonly _bootstrapScripts: string[] = [];
|
||||
private readonly _bootstrapScripts: string[] = [ JSON_SAVE_SCRIPT ];
|
||||
|
||||
constructor(browser: WKBrowser, browserContext: BrowserContext) {
|
||||
this._browser = browser;
|
||||
@ -70,7 +72,7 @@ export class WKPage implements PageDelegate {
|
||||
this._isolatedWorlds = new Set();
|
||||
// New bootstrap scripts may have been added during provisional load, push them
|
||||
// again to be on the safe side.
|
||||
if (this._setBootstrapScripts.length)
|
||||
if (this._bootstrapScripts.length)
|
||||
this._setBootstrapScripts(session).catch(e => debugError(e));
|
||||
}
|
||||
|
||||
@ -107,6 +109,8 @@ export class WKPage implements PageDelegate {
|
||||
promises.push(this._setExtraHTTPHeaders(session, this._page._state.extraHTTPHeaders));
|
||||
if (this._page._state.viewport)
|
||||
promises.push(WKPage._setViewport(session, this._page._state.viewport));
|
||||
if (contextOptions.javaScriptEnabled !== false)
|
||||
promises.push(session.send('Page.setBootstrapScript', { source: JSON_SAVE_SCRIPT }));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
@ -217,6 +221,12 @@ export class WKPage implements PageDelegate {
|
||||
this._page._onBindingCalled(parameters[2].value, context);
|
||||
return;
|
||||
}
|
||||
if (level === 'debug' && parameters && parameters[0].value === JSON_CALL_MESSAGE) {
|
||||
const parsedObjectId = JSON.parse(parameters[1].objectId);
|
||||
const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId);
|
||||
(context._delegate as WKExecutionContext)._jsonObjectId = parameters[1].objectId;
|
||||
return;
|
||||
}
|
||||
let derivedType: string = type;
|
||||
if (type === 'log')
|
||||
derivedType = level;
|
||||
|
@ -259,10 +259,10 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
})).catch(e => error = e);
|
||||
expect(error.message).toContain('Error in promise');
|
||||
});
|
||||
it.skip(FFOX || WEBKIT)('should work even when JSON is set to null', async({page, server}) => {
|
||||
it('should work even when JSON is set to null', async ({ page }) => {
|
||||
await page.evaluate(() => { window.JSON.stringify = null; window.JSON = null; });
|
||||
const result = await page.evaluate(() => ({abc: 123}));
|
||||
expect(result).toEqual({abc: 123});
|
||||
expect(result).toEqual({abc: 123});
|
||||
})
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user