chore: introduce helpers for non-stalling eval on page/context (#31658)
This commit is contained in:
parent
1b85ec9dc2
commit
229000501e
|
|
@ -507,7 +507,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
try {
|
try {
|
||||||
const storage = await page.mainFrame().nonStallingEvaluateInExistingContext(`({
|
const storage = await page.mainFrame().nonStallingEvaluateInExistingContext(`({
|
||||||
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
||||||
})`, false, 'utility');
|
})`, 'utility');
|
||||||
if (storage.localStorage.length)
|
if (storage.localStorage.length)
|
||||||
result.origins.push({ origin, localStorage: storage.localStorage } as channels.OriginStorage);
|
result.origins.push({ origin, localStorage: storage.localStorage } as channels.OriginStorage);
|
||||||
originsToSave.delete(origin);
|
originsToSave.delete(origin);
|
||||||
|
|
@ -619,6 +619,10 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
return Promise.all(this.pages().map(installInPage));
|
return Promise.all(this.pages().map(installInPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async safeNonStallingEvaluateInAllFrames(expression: string, world: types.World, options: { throwOnJSErrors?: boolean } = {}) {
|
||||||
|
await Promise.all(this.pages().map(page => page.safeNonStallingEvaluateInAllFrames(expression, world, options)));
|
||||||
|
}
|
||||||
|
|
||||||
async _harStart(page: Page | null, options: channels.RecordHarOptions): Promise<string> {
|
async _harStart(page: Page | null, options: channels.RecordHarOptions): Promise<string> {
|
||||||
const harId = createGuid();
|
const harId = createGuid();
|
||||||
this._harRecorders.set(harId, new HarRecorder(this, page, options));
|
this._harRecorders.set(harId, new HarRecorder(this, page, options));
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,7 @@ export class DragManager {
|
||||||
let onDragIntercepted: (payload: Protocol.Input.dragInterceptedPayload) => void;
|
let onDragIntercepted: (payload: Protocol.Input.dragInterceptedPayload) => void;
|
||||||
const dragInterceptedPromise = new Promise<Protocol.Input.dragInterceptedPayload>(x => onDragIntercepted = x);
|
const dragInterceptedPromise = new Promise<Protocol.Input.dragInterceptedPayload>(x => onDragIntercepted = x);
|
||||||
|
|
||||||
await Promise.all(this._crPage._page.frames().map(async frame => {
|
function setupDragListeners() {
|
||||||
await frame.nonStallingEvaluateInExistingContext((function() {
|
|
||||||
let didStartDrag = Promise.resolve(false);
|
let didStartDrag = Promise.resolve(false);
|
||||||
let dragEvent: Event|null = null;
|
let dragEvent: Event|null = null;
|
||||||
const dragListener = (event: Event) => dragEvent = event;
|
const dragListener = (event: Event) => dragEvent = event;
|
||||||
|
|
@ -87,8 +86,9 @@ export class DragManager {
|
||||||
delete window.__cleanupDrag;
|
delete window.__cleanupDrag;
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
}).toString(), true, 'utility').catch(() => {});
|
}
|
||||||
}));
|
|
||||||
|
await this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, 'utility');
|
||||||
|
|
||||||
client.on('Input.dragIntercepted', onDragIntercepted!);
|
client.on('Input.dragIntercepted', onDragIntercepted!);
|
||||||
try {
|
try {
|
||||||
|
|
@ -102,7 +102,7 @@ export class DragManager {
|
||||||
await moveCallback();
|
await moveCallback();
|
||||||
|
|
||||||
const expectingDrag = (await Promise.all(this._crPage._page.frames().map(async frame => {
|
const expectingDrag = (await Promise.all(this._crPage._page.frames().map(async frame => {
|
||||||
return frame.nonStallingEvaluateInExistingContext('window.__cleanupDrag && window.__cleanupDrag()', false, 'utility').catch(() => false);
|
return frame.nonStallingEvaluateInExistingContext('window.__cleanupDrag && window.__cleanupDrag()', 'utility').catch(() => false);
|
||||||
}))).some(x => x);
|
}))).some(x => x);
|
||||||
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
|
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
|
||||||
client.off('Input.dragIntercepted', onDragIntercepted!);
|
client.off('Input.dragIntercepted', onDragIntercepted!);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
import type { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import * as clockSource from '../generated/clockSource';
|
import * as clockSource from '../generated/clockSource';
|
||||||
import { isJavaScriptErrorInEvaluate } from './javascript';
|
|
||||||
|
|
||||||
export class Clock {
|
export class Clock {
|
||||||
private _browserContext: BrowserContext;
|
private _browserContext: BrowserContext;
|
||||||
|
|
@ -87,25 +86,12 @@ export class Clock {
|
||||||
${clockSource.source}
|
${clockSource.source}
|
||||||
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
||||||
})();`;
|
})();`;
|
||||||
await this._addAndEvaluate(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _addAndEvaluate(script: string) {
|
|
||||||
await this._browserContext.addInitScript(script);
|
await this._browserContext.addInitScript(script);
|
||||||
return await this._evaluateInFrames(script);
|
await this._evaluateInFrames(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _evaluateInFrames(script: string) {
|
private async _evaluateInFrames(script: string) {
|
||||||
const frames = this._browserContext.pages().map(page => page.frames()).flat();
|
await this._browserContext.safeNonStallingEvaluateInAllFrames(script, 'main', { throwOnJSErrors: true });
|
||||||
const results = await Promise.all(frames.map(async frame => {
|
|
||||||
try {
|
|
||||||
await frame.nonStallingEvaluateInExistingContext(script, false, 'main');
|
|
||||||
} catch (e) {
|
|
||||||
if (isJavaScriptErrorInEvaluate(e))
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
return results[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -581,12 +581,12 @@ export class Frame extends SdkObject {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
nonStallingEvaluateInExistingContext(expression: string, isFunction: boolean | undefined, world: types.World): Promise<any> {
|
nonStallingEvaluateInExistingContext(expression: string, world: types.World): Promise<any> {
|
||||||
return this.raceAgainstEvaluationStallingEvents(() => {
|
return this.raceAgainstEvaluationStallingEvents(() => {
|
||||||
const context = this._contextData.get(world)?.context;
|
const context = this._contextData.get(world)?.context;
|
||||||
if (!context)
|
if (!context)
|
||||||
throw new Error('Frame does not yet have the execution context');
|
throw new Error('Frame does not yet have the execution context');
|
||||||
return context.evaluateExpression(expression, { isFunction });
|
return context.evaluateExpression(expression, { isFunction: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -749,6 +749,17 @@ export class Page extends SdkObject {
|
||||||
this._frameThrottler.recharge();
|
this._frameThrottler.recharge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async safeNonStallingEvaluateInAllFrames(expression: string, world: types.World, options: { throwOnJSErrors?: boolean } = {}) {
|
||||||
|
await Promise.all(this.frames().map(async frame => {
|
||||||
|
try {
|
||||||
|
await frame.nonStallingEvaluateInExistingContext(expression, world);
|
||||||
|
} catch (e) {
|
||||||
|
if (options.throwOnJSErrors && js.isJavaScriptErrorInEvaluate(e))
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async hideHighlight() {
|
async hideHighlight() {
|
||||||
await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {})));
|
await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {})));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,21 +261,17 @@ export class Screenshotter {
|
||||||
if (disableAnimations)
|
if (disableAnimations)
|
||||||
progress.log(' disabled all CSS animations');
|
progress.log(' disabled all CSS animations');
|
||||||
const syncAnimations = this._page._delegate.shouldToggleStyleSheetToSyncAnimations();
|
const syncAnimations = this._page._delegate.shouldToggleStyleSheetToSyncAnimations();
|
||||||
await Promise.all(this._page.frames().map(async frame => {
|
await this._page.safeNonStallingEvaluateInAllFrames('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, 'utility');
|
||||||
await frame.nonStallingEvaluateInExistingContext('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, false, 'utility').catch(() => {});
|
|
||||||
}));
|
|
||||||
if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
|
if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
|
||||||
progress.log('waiting for fonts to load...');
|
progress.log('waiting for fonts to load...');
|
||||||
await frame.nonStallingEvaluateInExistingContext('document.fonts.ready', false, 'utility').catch(() => {});
|
await frame.nonStallingEvaluateInExistingContext('document.fonts.ready', 'utility').catch(() => {});
|
||||||
progress.log('fonts loaded');
|
progress.log('fonts loaded');
|
||||||
}
|
}
|
||||||
progress.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
|
progress.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
async _restorePageAfterScreenshot() {
|
async _restorePageAfterScreenshot() {
|
||||||
await Promise.all(this._page.frames().map(async frame => {
|
await this._page.safeNonStallingEvaluateInAllFrames('window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()', 'utility');
|
||||||
frame.nonStallingEvaluateInExistingContext('window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()', false, 'utility').catch(() => {});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _maskElements(progress: Progress, options: ScreenshotOptions): Promise<() => Promise<void>> {
|
async _maskElements(progress: Progress, options: ScreenshotOptions): Promise<() => Promise<void>> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue