mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: cleanup runtimeGuid and other builtins bits (#36035)
This commit is contained in:
parent
28e925c001
commit
fd7d249ed4
@ -18,13 +18,6 @@ import { serializeAsCallArgument } from '@isomorphic/utilityScriptSerializers';
|
|||||||
|
|
||||||
import type { SerializedValue } from '@isomorphic/utilityScriptSerializers';
|
import type { SerializedValue } from '@isomorphic/utilityScriptSerializers';
|
||||||
|
|
||||||
// This runtime guid is replaced by the actual guid at runtime in all generated sources.
|
|
||||||
const kRuntimeGuid = '$runtime_guid$';
|
|
||||||
|
|
||||||
// The name of the global playwright binding, referenced in Node.js.
|
|
||||||
const kPlaywrightBinding = `__playwright__binding__${kRuntimeGuid}`;
|
|
||||||
const kPlaywrightBindingController = `__playwright__binding__controller__${kRuntimeGuid}`;
|
|
||||||
|
|
||||||
export type BindingPayload = {
|
export type BindingPayload = {
|
||||||
name: string;
|
name: string;
|
||||||
seq: number;
|
seq: number;
|
||||||
@ -37,14 +30,16 @@ type BindingData = {
|
|||||||
handles: Map<number, any>;
|
handles: Map<number, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BindingsController {
|
export class BindingsController {
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
private _global: typeof globalThis;
|
private _global: typeof globalThis;
|
||||||
|
private _globalBindingName: string;
|
||||||
private _bindings = new Map<string, BindingData>();
|
private _bindings = new Map<string, BindingData>();
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
constructor(global: typeof globalThis) {
|
constructor(global: typeof globalThis, globalBindingName: string) {
|
||||||
this._global = global;
|
this._global = global;
|
||||||
|
this._globalBindingName = globalBindingName;
|
||||||
}
|
}
|
||||||
|
|
||||||
addBinding(bindingName: string, needsHandle: boolean) {
|
addBinding(bindingName: string, needsHandle: boolean) {
|
||||||
@ -72,7 +67,7 @@ class BindingsController {
|
|||||||
}
|
}
|
||||||
payload = { name: bindingName, seq, serializedArgs };
|
payload = { name: bindingName, seq, serializedArgs };
|
||||||
}
|
}
|
||||||
(this._global as any)[kPlaywrightBinding](JSON.stringify(payload));
|
(this._global as any)[this._globalBindingName](JSON.stringify(payload));
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -93,10 +88,3 @@ class BindingsController {
|
|||||||
callbacks.delete(arg.seq);
|
callbacks.delete(arg.seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ensureBindingsController() {
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
const global = globalThis;
|
|
||||||
if (!(global as any)[kPlaywrightBindingController])
|
|
||||||
(global as any)[kPlaywrightBindingController] = new BindingsController(global);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -66,7 +66,7 @@ interface WebKitLegacyDeviceMotionEvent extends DeviceMotionEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type InjectedScriptOptions = {
|
export type InjectedScriptOptions = {
|
||||||
isUnderTest?: boolean;
|
isUnderTest: boolean;
|
||||||
sdkLanguage: Language;
|
sdkLanguage: Language;
|
||||||
// For strict error and codegen
|
// For strict error and codegen
|
||||||
testIdAttributeName: string;
|
testIdAttributeName: string;
|
||||||
@ -124,11 +124,10 @@ export class InjectedScript {
|
|||||||
constructor(window: Window & typeof globalThis, options: InjectedScriptOptions) {
|
constructor(window: Window & typeof globalThis, options: InjectedScriptOptions) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.document = window.document;
|
this.document = window.document;
|
||||||
|
this.isUnderTest = options.isUnderTest;
|
||||||
// Make sure builtins are created from "window". This is important for InjectedScript instantiated
|
// Make sure builtins are created from "window". This is important for InjectedScript instantiated
|
||||||
// inside a trace viewer snapshot, where "window" differs from "globalThis".
|
// inside a trace viewer snapshot, where "window" differs from "globalThis".
|
||||||
const utilityScript = new UtilityScript(window);
|
this.utils.builtins = new UtilityScript(window, options.isUnderTest).builtins;
|
||||||
this.isUnderTest = options.isUnderTest ?? utilityScript.isUnderTest;
|
|
||||||
this.utils.builtins = utilityScript.builtins;
|
|
||||||
this._sdkLanguage = options.sdkLanguage;
|
this._sdkLanguage = options.sdkLanguage;
|
||||||
this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;
|
this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;
|
||||||
this._evaluator = new SelectorEvaluatorImpl();
|
this._evaluator = new SelectorEvaluatorImpl();
|
||||||
|
|||||||
@ -16,11 +16,6 @@
|
|||||||
|
|
||||||
import { parseEvaluationResultValue, serializeAsCallArgument } from '@isomorphic/utilityScriptSerializers';
|
import { parseEvaluationResultValue, serializeAsCallArgument } from '@isomorphic/utilityScriptSerializers';
|
||||||
|
|
||||||
// --- This section should match javascript.ts and generated_injected_builtins.js ---
|
|
||||||
|
|
||||||
// This flag is replaced by true/false at runtime in all generated sources.
|
|
||||||
const kUtilityScriptIsUnderTest = false;
|
|
||||||
|
|
||||||
// Keep in sync with eslint.config.mjs
|
// Keep in sync with eslint.config.mjs
|
||||||
export type Builtins = {
|
export type Builtins = {
|
||||||
setTimeout: Window['setTimeout'],
|
setTimeout: Window['setTimeout'],
|
||||||
@ -38,8 +33,6 @@ export type Builtins = {
|
|||||||
Date: typeof window['Date'],
|
Date: typeof window['Date'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- End of the matching section ---
|
|
||||||
|
|
||||||
export class UtilityScript {
|
export class UtilityScript {
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
readonly global: typeof globalThis;
|
readonly global: typeof globalThis;
|
||||||
@ -48,9 +41,9 @@ export class UtilityScript {
|
|||||||
readonly isUnderTest: boolean;
|
readonly isUnderTest: boolean;
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
constructor(global: typeof globalThis) {
|
constructor(global: typeof globalThis, isUnderTest: boolean) {
|
||||||
this.global = global;
|
this.global = global;
|
||||||
this.isUnderTest = kUtilityScriptIsUnderTest;
|
this.isUnderTest = isUnderTest;
|
||||||
if ((global as any).__pwClock) {
|
if ((global as any).__pwClock) {
|
||||||
this.builtins = (global as any).__pwClock.builtins;
|
this.builtins = (global as any).__pwClock.builtins;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import * as network from '../network';
|
|||||||
import { BidiConnection } from './bidiConnection';
|
import { BidiConnection } from './bidiConnection';
|
||||||
import { bidiBytesValueToString } from './bidiNetworkManager';
|
import { bidiBytesValueToString } from './bidiNetworkManager';
|
||||||
import { BidiPage, kPlaywrightBindingChannel } from './bidiPage';
|
import { BidiPage, kPlaywrightBindingChannel } from './bidiPage';
|
||||||
import { kPlaywrightBinding } from '../javascript';
|
import { PageBinding } from '../page';
|
||||||
import * as bidi from './third_party/bidiProtocol';
|
import * as bidi from './third_party/bidiProtocol';
|
||||||
|
|
||||||
import type { RegisteredListener } from '../utils/eventsHelper';
|
import type { RegisteredListener } from '../utils/eventsHelper';
|
||||||
@ -391,7 +391,7 @@ export class BidiBrowserContext extends BrowserContext {
|
|||||||
ownership: bidi.Script.ResultOwnership.Root,
|
ownership: bidi.Script.ResultOwnership.Root,
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
const functionDeclaration = `function addMainBinding(callback) { globalThis['${kPlaywrightBinding}'] = callback; }`;
|
const functionDeclaration = `function addMainBinding(callback) { globalThis['${PageBinding.kBindingName}'] = callback; }`;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
promises.push(this._browser._browserSession.send('script.addPreloadScript', {
|
promises.push(this._browser._browserSession.send('script.addPreloadScript', {
|
||||||
functionDeclaration,
|
functionDeclaration,
|
||||||
|
|||||||
@ -34,9 +34,7 @@ import { Recorder } from './recorder';
|
|||||||
import { RecorderApp } from './recorder/recorderApp';
|
import { RecorderApp } from './recorder/recorderApp';
|
||||||
import { Selectors } from './selectors';
|
import { Selectors } from './selectors';
|
||||||
import { Tracing } from './trace/recorder/tracing';
|
import { Tracing } from './trace/recorder/tracing';
|
||||||
import * as js from './javascript';
|
|
||||||
import * as rawStorageSource from '../generated/storageScriptSource';
|
import * as rawStorageSource from '../generated/storageScriptSource';
|
||||||
import * as rawBindingsControllerSource from '../generated/bindingsControllerSource';
|
|
||||||
|
|
||||||
import type { Artifact } from './artifact';
|
import type { Artifact } from './artifact';
|
||||||
import type { Browser, BrowserOptions } from './browser';
|
import type { Browser, BrowserOptions } from './browser';
|
||||||
@ -327,13 +325,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
this._playwrightBindingExposed = true;
|
this._playwrightBindingExposed = true;
|
||||||
await this.doExposePlaywrightBinding();
|
await this.doExposePlaywrightBinding();
|
||||||
|
|
||||||
this.bindingsInitScript = new InitScript(`
|
this.bindingsInitScript = PageBinding.createInitScript();
|
||||||
(() => {
|
|
||||||
const module = {};
|
|
||||||
${js.prepareGeneratedScript(rawBindingsControllerSource.source)}
|
|
||||||
(module.exports.ensureBindingsController())();
|
|
||||||
})();
|
|
||||||
`, true /* internal */);
|
|
||||||
this.initScripts.push(this.bindingsInitScript);
|
this.initScripts.push(this.bindingsInitScript);
|
||||||
await this.doAddInitScript(this.bindingsInitScript);
|
await this.doAddInitScript(this.bindingsInitScript);
|
||||||
await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, 'main');
|
await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, 'main');
|
||||||
@ -530,7 +522,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
|
|
||||||
const collectScript = `(() => {
|
const collectScript = `(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${js.prepareGeneratedScript(rawStorageSource.source)}
|
${rawStorageSource.source}
|
||||||
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
||||||
return script.collect(${indexedDB});
|
return script.collect(${indexedDB});
|
||||||
})()`;
|
})()`;
|
||||||
@ -628,7 +620,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
await frame.goto(metadata, originState.origin, { timeout: 0 });
|
await frame.goto(metadata, originState.origin, { timeout: 0 });
|
||||||
const restoreScript = `(() => {
|
const restoreScript = `(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${js.prepareGeneratedScript(rawStorageSource.source)}
|
${rawStorageSource.source}
|
||||||
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
||||||
return script.restore(${JSON.stringify(originState)});
|
return script.restore(${JSON.stringify(originState)});
|
||||||
})()`;
|
})()`;
|
||||||
|
|||||||
@ -26,8 +26,7 @@ import * as dom from '../dom';
|
|||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { helper } from '../helper';
|
import { helper } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { kPlaywrightBinding } from '../javascript';
|
import { Page, PageBinding, Worker } from '../page';
|
||||||
import { Page, Worker } from '../page';
|
|
||||||
import { registry } from '../registry';
|
import { registry } from '../registry';
|
||||||
import { getAccessibilityTree } from './crAccessibility';
|
import { getAccessibilityTree } from './crAccessibility';
|
||||||
import { CRBrowserContext } from './crBrowser';
|
import { CRBrowserContext } from './crBrowser';
|
||||||
@ -1071,7 +1070,7 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async exposePlaywrightBinding() {
|
async exposePlaywrightBinding() {
|
||||||
await this._client.send('Runtime.addBinding', { name: kPlaywrightBinding });
|
await this._client.send('Runtime.addBinding', { name: PageBinding.kBindingName });
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
async _getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as rawClockSource from '../generated/clockSource';
|
import * as rawClockSource from '../generated/clockSource';
|
||||||
import { prepareGeneratedScript } from './javascript';
|
|
||||||
|
|
||||||
import type { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ export class Clock {
|
|||||||
this._scriptInstalled = true;
|
this._scriptInstalled = true;
|
||||||
const script = `(() => {
|
const script = `(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${prepareGeneratedScript(rawClockSource.source)}
|
${rawClockSource.source}
|
||||||
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
||||||
})();`;
|
})();`;
|
||||||
await this._browserContext.addInitScript(script);
|
await this._browserContext.addInitScript(script);
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { Page } from '../page';
|
|||||||
import { Dispatcher } from './dispatcher';
|
import { Dispatcher } from './dispatcher';
|
||||||
import { PageDispatcher } from './pageDispatcher';
|
import { PageDispatcher } from './pageDispatcher';
|
||||||
import * as rawWebSocketMockSource from '../../generated/webSocketMockSource';
|
import * as rawWebSocketMockSource from '../../generated/webSocketMockSource';
|
||||||
import { prepareGeneratedScript } from '../javascript';
|
|
||||||
import { createGuid } from '../utils/crypto';
|
import { createGuid } from '../utils/crypto';
|
||||||
import { urlMatches } from '../../utils/isomorphic/urlMatch';
|
import { urlMatches } from '../../utils/isomorphic/urlMatch';
|
||||||
import { eventsHelper } from '../utils/eventsHelper';
|
import { eventsHelper } from '../utils/eventsHelper';
|
||||||
@ -97,7 +96,7 @@ export class WebSocketRouteDispatcher extends Dispatcher<{ guid: string }, chann
|
|||||||
await target.addInitScript(`
|
await target.addInitScript(`
|
||||||
(() => {
|
(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${prepareGeneratedScript(rawWebSocketMockSource.source)}
|
${rawWebSocketMockSource.source}
|
||||||
(module.exports.inject())(globalThis);
|
(module.exports.inject())(globalThis);
|
||||||
})();
|
})();
|
||||||
`, kInitScriptName);
|
`, kInitScriptName);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import fs from 'fs';
|
|||||||
|
|
||||||
import * as js from './javascript';
|
import * as js from './javascript';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
import { asLocator } from '../utils';
|
import { asLocator, isUnderTest } from '../utils';
|
||||||
import { prepareFilesForUpload } from './fileUploadUtils';
|
import { prepareFilesForUpload } from './fileUploadUtils';
|
||||||
import { isSessionClosedError } from './protocolError';
|
import { isSessionClosedError } from './protocolError';
|
||||||
import * as rawInjectedScriptSource from '../generated/injectedScriptSource';
|
import * as rawInjectedScriptSource from '../generated/injectedScriptSource';
|
||||||
@ -89,6 +89,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
customEngines.push({ name, source });
|
customEngines.push({ name, source });
|
||||||
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
||||||
const options: InjectedScriptOptions = {
|
const options: InjectedScriptOptions = {
|
||||||
|
isUnderTest: isUnderTest(),
|
||||||
sdkLanguage,
|
sdkLanguage,
|
||||||
testIdAttributeName: selectorsRegistry.testIdAttributeName(),
|
testIdAttributeName: selectorsRegistry.testIdAttributeName(),
|
||||||
stableRafCount: this.frame._page.delegate.rafCountForStablePosition(),
|
stableRafCount: this.frame._page.delegate.rafCountForStablePosition(),
|
||||||
@ -99,7 +100,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
const source = `
|
const source = `
|
||||||
(() => {
|
(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${js.prepareGeneratedScript(rawInjectedScriptSource.source)}
|
${rawInjectedScriptSource.source}
|
||||||
return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options)});
|
return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options)});
|
||||||
})();
|
})();
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -19,10 +19,10 @@ import { assert } from '../../utils';
|
|||||||
import { Browser } from '../browser';
|
import { Browser } from '../browser';
|
||||||
import { BrowserContext, verifyGeolocation } from '../browserContext';
|
import { BrowserContext, verifyGeolocation } from '../browserContext';
|
||||||
import { TargetClosedError } from '../errors';
|
import { TargetClosedError } from '../errors';
|
||||||
import { kPlaywrightBinding } from '../javascript';
|
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { ConnectionEvents, FFConnection } from './ffConnection';
|
import { ConnectionEvents, FFConnection } from './ffConnection';
|
||||||
import { FFPage } from './ffPage';
|
import { FFPage } from './ffPage';
|
||||||
|
import { PageBinding } from '../page';
|
||||||
|
|
||||||
import type { BrowserOptions } from '../browser';
|
import type { BrowserOptions } from '../browser';
|
||||||
import type { SdkObject } from '../instrumentation';
|
import type { SdkObject } from '../instrumentation';
|
||||||
@ -393,7 +393,7 @@ export class FFBrowserContext extends BrowserContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override async doExposePlaywrightBinding() {
|
override async doExposePlaywrightBinding() {
|
||||||
this._browser.session.send('Browser.addBinding', { browserContextId: this._browserContextId, name: kPlaywrightBinding, script: '' });
|
this._browser.session.send('Browser.addBinding', { browserContextId: this._browserContextId, name: PageBinding.kBindingName, script: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosePersistent() {}
|
onClosePersistent() {}
|
||||||
|
|||||||
@ -16,40 +16,13 @@
|
|||||||
|
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
import * as rawUtilityScriptSource from '../generated/utilityScriptSource';
|
import * as rawUtilityScriptSource from '../generated/utilityScriptSource';
|
||||||
import { createGuid, isUnderTest } from '../utils';
|
import { isUnderTest } from '../utils';
|
||||||
import { serializeAsCallArgument } from '../utils/isomorphic/utilityScriptSerializers';
|
import { serializeAsCallArgument } from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
import { LongStandingScope } from '../utils/isomorphic/manualPromise';
|
import { LongStandingScope } from '../utils/isomorphic/manualPromise';
|
||||||
|
|
||||||
import type * as dom from './dom';
|
import type * as dom from './dom';
|
||||||
import type { UtilityScript } from '@injected/utilityScript';
|
import type { UtilityScript } from '@injected/utilityScript';
|
||||||
|
|
||||||
// --- This section should match utilityScript.ts and generated_injected_builtins.js ---
|
|
||||||
|
|
||||||
// Use in the web-facing names to avoid leaking Playwright to the pages.
|
|
||||||
export const runtimeGuid = createGuid();
|
|
||||||
|
|
||||||
// Preprocesses any generated script to include the runtime guid.
|
|
||||||
export function prepareGeneratedScript(source: string) {
|
|
||||||
return source.replaceAll('$runtime_guid$', runtimeGuid).replace('kUtilityScriptIsUnderTest = false', `kUtilityScriptIsUnderTest = ${isUnderTest()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const kUtilityScriptSource = prepareGeneratedScript(rawUtilityScriptSource.source);
|
|
||||||
|
|
||||||
// Include this code in any evaluated source to get access to the UtilityScript instance.
|
|
||||||
export function accessUtilityScript() {
|
|
||||||
return `globalThis['__playwright_utility_script__${runtimeGuid}']`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The name of the global playwright binding, accessed by UtilityScript.
|
|
||||||
export const kPlaywrightBinding = '__playwright__binding__' + runtimeGuid;
|
|
||||||
|
|
||||||
// Include this code in any evaluated source to get access to the BindingsController instance.
|
|
||||||
export function accessBindingsController() {
|
|
||||||
return `globalThis['__playwright__binding__controller__${runtimeGuid}']`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- End of the matching section ---
|
|
||||||
|
|
||||||
interface TaggedAsJSHandle<T> {
|
interface TaggedAsJSHandle<T> {
|
||||||
__jshandle: T;
|
__jshandle: T;
|
||||||
}
|
}
|
||||||
@ -130,8 +103,8 @@ export class ExecutionContext extends SdkObject {
|
|||||||
const source = `
|
const source = `
|
||||||
(() => {
|
(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${kUtilityScriptSource}
|
${rawUtilityScriptSource.source}
|
||||||
return new (module.exports.UtilityScript())(globalThis);
|
return new (module.exports.UtilityScript())(globalThis, ${isUnderTest()});
|
||||||
})();`;
|
})();`;
|
||||||
this._utilityScriptPromise = this._raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source))
|
this._utilityScriptPromise = this._raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source))
|
||||||
.then(handle => {
|
.then(handle => {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
|||||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||||
import { parseEvaluationResultValue } from '../utils/isomorphic/utilityScriptSerializers';
|
import { parseEvaluationResultValue } from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
import { compressCallLog } from './callLog';
|
import { compressCallLog } from './callLog';
|
||||||
|
import * as rawBindingsControllerSource from '../generated/bindingsControllerSource';
|
||||||
|
|
||||||
import type { Artifact } from './artifact';
|
import type { Artifact } from './artifact';
|
||||||
import type * as dom from './dom';
|
import type * as dom from './dom';
|
||||||
@ -849,6 +850,20 @@ export class Worker extends SdkObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PageBinding {
|
export class PageBinding {
|
||||||
|
private static kController = '__playwright__binding__controller__';
|
||||||
|
static kBindingName = '__playwright__binding__';
|
||||||
|
|
||||||
|
static createInitScript() {
|
||||||
|
return new InitScript(`
|
||||||
|
(() => {
|
||||||
|
const module = {};
|
||||||
|
${rawBindingsControllerSource.source}
|
||||||
|
const property = '${PageBinding.kController}';
|
||||||
|
if (!globalThis[property])
|
||||||
|
globalThis[property] = new (module.exports.BindingsController())(globalThis, '${PageBinding.kBindingName}');
|
||||||
|
})();
|
||||||
|
`, true /* internal */);
|
||||||
|
}
|
||||||
|
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly playwrightFunction: frames.FunctionWithSource;
|
readonly playwrightFunction: frames.FunctionWithSource;
|
||||||
@ -859,7 +874,7 @@ export class PageBinding {
|
|||||||
constructor(name: string, playwrightFunction: frames.FunctionWithSource, needsHandle: boolean) {
|
constructor(name: string, playwrightFunction: frames.FunctionWithSource, needsHandle: boolean) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.playwrightFunction = playwrightFunction;
|
this.playwrightFunction = playwrightFunction;
|
||||||
this.initScript = new InitScript(`${js.accessBindingsController()}.addBinding(${JSON.stringify(name)}, ${needsHandle})`, true /* internal */);
|
this.initScript = new InitScript(`globalThis['${PageBinding.kController}'].addBinding(${JSON.stringify(name)}, ${needsHandle})`, true /* internal */);
|
||||||
this.needsHandle = needsHandle;
|
this.needsHandle = needsHandle;
|
||||||
this.internal = name.startsWith('__pw');
|
this.internal = name.startsWith('__pw');
|
||||||
}
|
}
|
||||||
@ -873,7 +888,7 @@ export class PageBinding {
|
|||||||
throw new Error(`Function "${name}" is not exposed`);
|
throw new Error(`Function "${name}" is not exposed`);
|
||||||
let result: any;
|
let result: any;
|
||||||
if (binding.needsHandle) {
|
if (binding.needsHandle) {
|
||||||
const handle = await context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.takeBindingHandle(arg)`, { isFunction: true }, { name, seq }).catch(e => null);
|
const handle = await context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].takeBindingHandle(arg)`, { isFunction: true }, { name, seq }).catch(e => null);
|
||||||
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, handle);
|
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, handle);
|
||||||
} else {
|
} else {
|
||||||
if (!Array.isArray(serializedArgs))
|
if (!Array.isArray(serializedArgs))
|
||||||
@ -881,9 +896,9 @@ export class PageBinding {
|
|||||||
const args = serializedArgs!.map(a => parseEvaluationResultValue(a));
|
const args = serializedArgs!.map(a => parseEvaluationResultValue(a));
|
||||||
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, ...args);
|
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, ...args);
|
||||||
}
|
}
|
||||||
context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import { Frame } from '../frames';
|
|||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { ThrottledFile } from './throttledFile';
|
import { ThrottledFile } from './throttledFile';
|
||||||
import { generateCode } from '../codegen/language';
|
import { generateCode } from '../codegen/language';
|
||||||
import { prepareGeneratedScript } from '../javascript';
|
|
||||||
|
|
||||||
import type { RegisteredListener } from '../../utils';
|
import type { RegisteredListener } from '../../utils';
|
||||||
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from '../codegen/types';
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from '../codegen/types';
|
||||||
@ -147,7 +146,7 @@ export class ContextRecorder extends EventEmitter {
|
|||||||
await this._context.exposeBinding('__pw_recorderRecordAction', false,
|
await this._context.exposeBinding('__pw_recorderRecordAction', false,
|
||||||
(source: BindingSource, action: actions.Action) => this._recordAction(source.frame, action));
|
(source: BindingSource, action: actions.Action) => this._recordAction(source.frame, action));
|
||||||
|
|
||||||
await this._context.extendInjectedScript(prepareGeneratedScript(rawRecorderSource.source));
|
await this._context.extendInjectedScript(rawRecorderSource.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
setEnabled(enabled: boolean) {
|
setEnabled(enabled: boolean) {
|
||||||
|
|||||||
@ -30,8 +30,7 @@ import * as dom from '../dom';
|
|||||||
import { TargetClosedError } from '../errors';
|
import { TargetClosedError } from '../errors';
|
||||||
import { helper } from '../helper';
|
import { helper } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { kPlaywrightBinding } from '../javascript';
|
import { Page, PageBinding } from '../page';
|
||||||
import { Page } from '../page';
|
|
||||||
import { getAccessibilityTree } from './wkAccessibility';
|
import { getAccessibilityTree } from './wkAccessibility';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
import { createHandle, WKExecutionContext } from './wkExecutionContext';
|
import { createHandle, WKExecutionContext } from './wkExecutionContext';
|
||||||
@ -182,7 +181,7 @@ export class WKPage implements PageDelegate {
|
|||||||
this._workers.initializeSession(session)
|
this._workers.initializeSession(session)
|
||||||
];
|
];
|
||||||
if (this._page.browserContext.needsPlaywrightBinding())
|
if (this._page.browserContext.needsPlaywrightBinding())
|
||||||
promises.push(session.send('Runtime.addBinding', { name: kPlaywrightBinding }));
|
promises.push(session.send('Runtime.addBinding', { name: PageBinding.kBindingName }));
|
||||||
if (this._page.needsRequestInterception()) {
|
if (this._page.needsRequestInterception()) {
|
||||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
||||||
promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true }));
|
promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true }));
|
||||||
@ -774,7 +773,7 @@ export class WKPage implements PageDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async exposePlaywrightBinding() {
|
async exposePlaywrightBinding() {
|
||||||
await this._updateState('Runtime.addBinding', { name: kPlaywrightBinding });
|
await this._updateState('Runtime.addBinding', { name: PageBinding.kBindingName });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _calculateBootstrapScript(): string {
|
private _calculateBootstrapScript(): string {
|
||||||
|
|||||||
@ -144,9 +144,6 @@ const inlineCSSPlugin = {
|
|||||||
let content = await fs.promises.readFile(outFileJs, 'utf-8');
|
let content = await fs.promises.readFile(outFileJs, 'utf-8');
|
||||||
if (hasExports)
|
if (hasExports)
|
||||||
content = await replaceEsbuildHeader(content, outFileJs);
|
content = await replaceEsbuildHeader(content, outFileJs);
|
||||||
if (injected.endsWith('utilityScript.ts') && !content.includes('kUtilityScriptIsUnderTest = false')) {
|
|
||||||
throw new Error(`Utility script must include "kUtilityScriptIsUnderTest = false"\n\n${content}`);
|
|
||||||
}
|
|
||||||
const newContent = `export const source = ${JSON.stringify(content)};`;
|
const newContent = `export const source = ${JSON.stringify(content)};`;
|
||||||
await fs.promises.writeFile(path.join(generatedFolder, baseName.replace('.ts', 'Source.ts')), newContent);
|
await fs.promises.writeFile(path.join(generatedFolder, baseName.replace('.ts', 'Source.ts')), newContent);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user