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 {
|
||||
const storage = await page.mainFrame().nonStallingEvaluateInExistingContext(`({
|
||||
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
||||
})`, false, 'utility');
|
||||
})`, 'utility');
|
||||
if (storage.localStorage.length)
|
||||
result.origins.push({ origin, localStorage: storage.localStorage } as channels.OriginStorage);
|
||||
originsToSave.delete(origin);
|
||||
|
|
@ -619,6 +619,10 @@ export abstract class BrowserContext extends SdkObject {
|
|||
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> {
|
||||
const harId = createGuid();
|
||||
this._harRecorders.set(harId, new HarRecorder(this, page, options));
|
||||
|
|
|
|||
|
|
@ -68,27 +68,27 @@ export class DragManager {
|
|||
let onDragIntercepted: (payload: Protocol.Input.dragInterceptedPayload) => void;
|
||||
const dragInterceptedPromise = new Promise<Protocol.Input.dragInterceptedPayload>(x => onDragIntercepted = x);
|
||||
|
||||
await Promise.all(this._crPage._page.frames().map(async frame => {
|
||||
await frame.nonStallingEvaluateInExistingContext((function() {
|
||||
let didStartDrag = Promise.resolve(false);
|
||||
let dragEvent: Event|null = null;
|
||||
const dragListener = (event: Event) => dragEvent = event;
|
||||
const mouseListener = () => {
|
||||
didStartDrag = new Promise<boolean>(callback => {
|
||||
window.addEventListener('dragstart', dragListener, { once: true, capture: true });
|
||||
setTimeout(() => callback(dragEvent ? !dragEvent.defaultPrevented : false), 0);
|
||||
});
|
||||
};
|
||||
window.addEventListener('mousemove', mouseListener, { once: true, capture: true });
|
||||
window.__cleanupDrag = async () => {
|
||||
const val = await didStartDrag;
|
||||
window.removeEventListener('mousemove', mouseListener, { capture: true });
|
||||
window.removeEventListener('dragstart', dragListener, { capture: true });
|
||||
delete window.__cleanupDrag;
|
||||
return val;
|
||||
};
|
||||
}).toString(), true, 'utility').catch(() => {});
|
||||
}));
|
||||
function setupDragListeners() {
|
||||
let didStartDrag = Promise.resolve(false);
|
||||
let dragEvent: Event|null = null;
|
||||
const dragListener = (event: Event) => dragEvent = event;
|
||||
const mouseListener = () => {
|
||||
didStartDrag = new Promise<boolean>(callback => {
|
||||
window.addEventListener('dragstart', dragListener, { once: true, capture: true });
|
||||
setTimeout(() => callback(dragEvent ? !dragEvent.defaultPrevented : false), 0);
|
||||
});
|
||||
};
|
||||
window.addEventListener('mousemove', mouseListener, { once: true, capture: true });
|
||||
window.__cleanupDrag = async () => {
|
||||
const val = await didStartDrag;
|
||||
window.removeEventListener('mousemove', mouseListener, { capture: true });
|
||||
window.removeEventListener('dragstart', dragListener, { capture: true });
|
||||
delete window.__cleanupDrag;
|
||||
return val;
|
||||
};
|
||||
}
|
||||
|
||||
await this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, 'utility');
|
||||
|
||||
client.on('Input.dragIntercepted', onDragIntercepted!);
|
||||
try {
|
||||
|
|
@ -102,7 +102,7 @@ export class DragManager {
|
|||
await moveCallback();
|
||||
|
||||
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);
|
||||
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
|
||||
client.off('Input.dragIntercepted', onDragIntercepted!);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import * as clockSource from '../generated/clockSource';
|
||||
import { isJavaScriptErrorInEvaluate } from './javascript';
|
||||
|
||||
export class Clock {
|
||||
private _browserContext: BrowserContext;
|
||||
|
|
@ -87,25 +86,12 @@ export class Clock {
|
|||
${clockSource.source}
|
||||
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
||||
})();`;
|
||||
await this._addAndEvaluate(script);
|
||||
}
|
||||
|
||||
private async _addAndEvaluate(script: string) {
|
||||
await this._browserContext.addInitScript(script);
|
||||
return await this._evaluateInFrames(script);
|
||||
await this._evaluateInFrames(script);
|
||||
}
|
||||
|
||||
private async _evaluateInFrames(script: string) {
|
||||
const frames = this._browserContext.pages().map(page => page.frames()).flat();
|
||||
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];
|
||||
await this._browserContext.safeNonStallingEvaluateInAllFrames(script, 'main', { throwOnJSErrors: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
const context = this._contextData.get(world)?.context;
|
||||
if (!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();
|
||||
}
|
||||
|
||||
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() {
|
||||
await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {})));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,21 +261,17 @@ export class Screenshotter {
|
|||
if (disableAnimations)
|
||||
progress.log(' disabled all CSS animations');
|
||||
const syncAnimations = this._page._delegate.shouldToggleStyleSheetToSyncAnimations();
|
||||
await Promise.all(this._page.frames().map(async frame => {
|
||||
await frame.nonStallingEvaluateInExistingContext('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, false, 'utility').catch(() => {});
|
||||
}));
|
||||
await this._page.safeNonStallingEvaluateInAllFrames('(' + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, 'utility');
|
||||
if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
|
||||
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.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
|
||||
}
|
||||
|
||||
async _restorePageAfterScreenshot() {
|
||||
await Promise.all(this._page.frames().map(async frame => {
|
||||
frame.nonStallingEvaluateInExistingContext('window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()', false, 'utility').catch(() => {});
|
||||
}));
|
||||
await this._page.safeNonStallingEvaluateInAllFrames('window.__pwCleanupScreenshot && window.__pwCleanupScreenshot()', 'utility');
|
||||
}
|
||||
|
||||
async _maskElements(progress: Progress, options: ScreenshotOptions): Promise<() => Promise<void>> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue