mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: do not leak pw internals (#35721)
This commit is contained in:
parent
400f466957
commit
3371fb9bea
@ -44,6 +44,7 @@ import type { LayoutSelectorName } from './layoutSelectorUtils';
|
|||||||
import type { SelectorEngine, SelectorRoot } from './selectorEngine';
|
import type { SelectorEngine, SelectorRoot } from './selectorEngine';
|
||||||
import type { GenerateSelectorOptions } from './selectorGenerator';
|
import type { GenerateSelectorOptions } from './selectorGenerator';
|
||||||
import type { ElementText, TextMatcher } from './selectorUtils';
|
import type { ElementText, TextMatcher } from './selectorUtils';
|
||||||
|
import type { Builtins } from '@isomorphic/builtins';
|
||||||
|
|
||||||
|
|
||||||
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
||||||
@ -64,6 +65,18 @@ interface WebKitLegacyDeviceMotionEvent extends DeviceMotionEvent {
|
|||||||
readonly initDeviceMotionEvent: (type: string, bubbles: boolean, cancelable: boolean, acceleration: DeviceMotionEventAcceleration, accelerationIncludingGravity: DeviceMotionEventAcceleration, rotationRate: DeviceMotionEventRotationRate, interval: number) => void;
|
readonly initDeviceMotionEvent: (type: string, bubbles: boolean, cancelable: boolean, acceleration: DeviceMotionEventAcceleration, accelerationIncludingGravity: DeviceMotionEventAcceleration, rotationRate: DeviceMotionEventRotationRate, interval: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InjectedScriptOptions = {
|
||||||
|
isUnderTest: boolean;
|
||||||
|
sdkLanguage: Language;
|
||||||
|
// For strict error and codegen
|
||||||
|
testIdAttributeName: string;
|
||||||
|
stableRafCount: number;
|
||||||
|
browserName: string;
|
||||||
|
inputFileRoleTextbox: boolean;
|
||||||
|
customEngines: { name: string, source: string }[];
|
||||||
|
runtimeGuid: string;
|
||||||
|
};
|
||||||
|
|
||||||
export class InjectedScript {
|
export class InjectedScript {
|
||||||
private _engines: Map<string, SelectorEngine>;
|
private _engines: Map<string, SelectorEngine>;
|
||||||
readonly _evaluator: SelectorEvaluatorImpl;
|
readonly _evaluator: SelectorEvaluatorImpl;
|
||||||
@ -96,7 +109,7 @@ export class InjectedScript {
|
|||||||
isInsideScope,
|
isInsideScope,
|
||||||
normalizeWhiteSpace,
|
normalizeWhiteSpace,
|
||||||
parseAriaSnapshot,
|
parseAriaSnapshot,
|
||||||
builtins: builtins(),
|
builtins: null as unknown as Builtins,
|
||||||
};
|
};
|
||||||
|
|
||||||
private _autoClosingTags: Set<string>;
|
private _autoClosingTags: Set<string>;
|
||||||
@ -108,15 +121,15 @@ export class InjectedScript {
|
|||||||
private _allHitTargetInterceptorEvents: Set<string>;
|
private _allHitTargetInterceptorEvents: Set<string>;
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
constructor(window: Window & typeof globalThis, isUnderTest: boolean, sdkLanguage: Language, testIdAttributeNameForStrictErrorAndConsoleCodegen: string, stableRafCount: number, browserName: string, inputFileRoleTextbox: boolean, customEngines: { name: string, engine: SelectorEngine }[]) {
|
constructor(window: Window & typeof globalThis, options: InjectedScriptOptions) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.document = window.document;
|
this.document = window.document;
|
||||||
this.isUnderTest = isUnderTest;
|
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".
|
||||||
this.utils.builtins = builtins(window);
|
this.utils.builtins = builtins(options.runtimeGuid, window);
|
||||||
this._sdkLanguage = sdkLanguage;
|
this._sdkLanguage = options.sdkLanguage;
|
||||||
this._testIdAttributeNameForStrictErrorAndConsoleCodegen = testIdAttributeNameForStrictErrorAndConsoleCodegen;
|
this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;
|
||||||
this._evaluator = new SelectorEvaluatorImpl();
|
this._evaluator = new SelectorEvaluatorImpl();
|
||||||
this.consoleApi = new ConsoleAPI(this);
|
this.consoleApi = new ConsoleAPI(this);
|
||||||
|
|
||||||
@ -216,17 +229,17 @@ export class InjectedScript {
|
|||||||
this._engines.set('internal:role', createRoleEngine(true));
|
this._engines.set('internal:role', createRoleEngine(true));
|
||||||
this._engines.set('aria-ref', this._createAriaIdEngine());
|
this._engines.set('aria-ref', this._createAriaIdEngine());
|
||||||
|
|
||||||
for (const { name, engine } of customEngines)
|
for (const { name, source } of options.customEngines)
|
||||||
this._engines.set(name, engine);
|
this._engines.set(name, this.eval(source));
|
||||||
|
|
||||||
this._stableRafCount = stableRafCount;
|
this._stableRafCount = options.stableRafCount;
|
||||||
this._browserName = browserName;
|
this._browserName = options.browserName;
|
||||||
setGlobalOptions({ browserNameForWorkarounds: browserName, inputFileRoleTextbox });
|
setGlobalOptions({ browserNameForWorkarounds: options.browserName, inputFileRoleTextbox: options.inputFileRoleTextbox });
|
||||||
|
|
||||||
this._setupGlobalListenersRemovalDetection();
|
this._setupGlobalListenersRemovalDetection();
|
||||||
this._setupHitTargetInterceptors();
|
this._setupHitTargetInterceptors();
|
||||||
|
|
||||||
if (isUnderTest)
|
if (options.isUnderTest)
|
||||||
(this.window as any).__injectedScript = this;
|
(this.window as any).__injectedScript = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,13 +17,19 @@
|
|||||||
import { builtins } from '@isomorphic/builtins';
|
import { builtins } from '@isomorphic/builtins';
|
||||||
import { source } from '@isomorphic/utilityScriptSerializers';
|
import { source } from '@isomorphic/utilityScriptSerializers';
|
||||||
|
|
||||||
|
import type { Builtins } from '@isomorphic/builtins';
|
||||||
|
|
||||||
export class UtilityScript {
|
export class UtilityScript {
|
||||||
constructor(isUnderTest: boolean) {
|
|
||||||
|
private _builtins: Builtins;
|
||||||
|
|
||||||
|
constructor(runtimeGuid: string, isUnderTest: boolean) {
|
||||||
if (isUnderTest) {
|
if (isUnderTest) {
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
(globalThis as any).builtins = builtins();
|
(globalThis as any).builtins = builtins(runtimeGuid);
|
||||||
}
|
}
|
||||||
const result = source(builtins());
|
this._builtins = builtins(runtimeGuid);
|
||||||
|
const result = source(this._builtins);
|
||||||
this.serializeAsCallArgument = result.serializeAsCallArgument;
|
this.serializeAsCallArgument = result.serializeAsCallArgument;
|
||||||
this.parseEvaluationResultValue = result.parseEvaluationResultValue;
|
this.parseEvaluationResultValue = result.parseEvaluationResultValue;
|
||||||
}
|
}
|
||||||
@ -38,7 +44,7 @@ export class UtilityScript {
|
|||||||
for (let i = 0; i < args.length; i++)
|
for (let i = 0; i < args.length; i++)
|
||||||
parameters[i] = this.parseEvaluationResultValue(args[i], handles);
|
parameters[i] = this.parseEvaluationResultValue(args[i], handles);
|
||||||
|
|
||||||
let result = builtins().eval(expression);
|
let result = this._builtins.eval(expression);
|
||||||
if (isFunction === true) {
|
if (isFunction === true) {
|
||||||
result = result(...parameters);
|
result = result(...parameters);
|
||||||
} else if (isFunction === false) {
|
} else if (isFunction === false) {
|
||||||
|
@ -19,7 +19,7 @@ import { eventsHelper } from '../utils/eventsHelper';
|
|||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import * as dialog from '../dialog';
|
import * as dialog from '../dialog';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import { Page } from '../page';
|
import { Page, PageBinding } from '../page';
|
||||||
import { BidiExecutionContext, createHandle } from './bidiExecutionContext';
|
import { BidiExecutionContext, createHandle } from './bidiExecutionContext';
|
||||||
import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './bidiInput';
|
import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './bidiInput';
|
||||||
import { BidiNetworkManager } from './bidiNetworkManager';
|
import { BidiNetworkManager } from './bidiNetworkManager';
|
||||||
@ -573,7 +573,7 @@ export class BidiPage implements PageDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addMainBinding(callback: (arg: any) => void) {
|
export function addMainBinding(callback: (arg: any) => void) {
|
||||||
(globalThis as any)['__playwright__binding__'] = callback;
|
(globalThis as any)[PageBinding.kPlaywrightBinding] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toBidiExecutionContext(executionContext: dom.FrameExecutionContext): BidiExecutionContext {
|
function toBidiExecutionContext(executionContext: dom.FrameExecutionContext): BidiExecutionContext {
|
||||||
|
@ -28,7 +28,7 @@ import { mkdirIfNeeded } from './utils/fileUtils';
|
|||||||
import { HarRecorder } from './har/harRecorder';
|
import { HarRecorder } from './har/harRecorder';
|
||||||
import { helper } from './helper';
|
import { helper } from './helper';
|
||||||
import { SdkObject, serverSideCallMetadata } from './instrumentation';
|
import { SdkObject, serverSideCallMetadata } from './instrumentation';
|
||||||
import { builtins } from '../utils/isomorphic/builtins';
|
import { builtinsSource } from '../utils/isomorphic/builtins';
|
||||||
import * as utilityScriptSerializers from '../utils/isomorphic/utilityScriptSerializers';
|
import * as utilityScriptSerializers from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
import { InitScript } from './page';
|
import { InitScript } from './page';
|
||||||
@ -37,6 +37,7 @@ import { Recorder } from './recorder';
|
|||||||
import { RecorderApp } from './recorder/recorderApp';
|
import { RecorderApp } from './recorder/recorderApp';
|
||||||
import * as storageScript from './storageScript';
|
import * as storageScript from './storageScript';
|
||||||
import { Tracing } from './trace/recorder/tracing';
|
import { Tracing } from './trace/recorder/tracing';
|
||||||
|
import * as js from './javascript';
|
||||||
|
|
||||||
import type { Artifact } from './artifact';
|
import type { Artifact } from './artifact';
|
||||||
import type { Browser, BrowserOptions } from './browser';
|
import type { Browser, BrowserOptions } from './browser';
|
||||||
@ -518,7 +519,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
};
|
};
|
||||||
const originsToSave = new Set(this._origins);
|
const originsToSave = new Set(this._origins);
|
||||||
|
|
||||||
const collectScript = `(${storageScript.collect})(${utilityScriptSerializers.source}, (${builtins})(), ${this._browser.options.name === 'firefox'}, ${indexedDB})`;
|
const collectScript = `(${storageScript.collect})(${utilityScriptSerializers.source}, ${builtinsSource(js.runtimeGuid)}, ${this._browser.options.name === 'firefox'}, ${indexedDB})`;
|
||||||
|
|
||||||
// First try collecting storage stage from existing pages.
|
// First try collecting storage stage from existing pages.
|
||||||
for (const page of this.pages()) {
|
for (const page of this.pages()) {
|
||||||
@ -611,7 +612,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
for (const originState of state.origins) {
|
for (const originState of state.origins) {
|
||||||
const frame = page.mainFrame();
|
const frame = page.mainFrame();
|
||||||
await frame.goto(metadata, originState.origin);
|
await frame.goto(metadata, originState.origin);
|
||||||
await frame.evaluateExpression(`(${storageScript.restore})(${utilityScriptSerializers.source}, (${builtins})(), ${JSON.stringify(originState)})`, { world: 'utility' });
|
await frame.evaluateExpression(`(${storageScript.restore})(${utilityScriptSerializers.source}, ${builtinsSource(js.runtimeGuid)}, ${JSON.stringify(originState)})`, { world: 'utility' });
|
||||||
}
|
}
|
||||||
await page.close(internalMetadata);
|
await page.close(internalMetadata);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import { isSessionClosedError } from './protocolError';
|
|||||||
import * as injectedScriptSource from '../generated/injectedScriptSource';
|
import * as injectedScriptSource from '../generated/injectedScriptSource';
|
||||||
|
|
||||||
import type * as frames from './frames';
|
import type * as frames from './frames';
|
||||||
import type { ElementState, HitTargetInterceptionResult, InjectedScript } from '@injected/injectedScript';
|
import type { ElementState, HitTargetInterceptionResult, InjectedScript, InjectedScriptOptions } from '@injected/injectedScript';
|
||||||
import type { CallMetadata } from './instrumentation';
|
import type { CallMetadata } from './instrumentation';
|
||||||
import type { Page } from './page';
|
import type { Page } from './page';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
@ -85,25 +85,26 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
|
|
||||||
injectedScript(): Promise<js.JSHandle<InjectedScript>> {
|
injectedScript(): Promise<js.JSHandle<InjectedScript>> {
|
||||||
if (!this._injectedScriptPromise) {
|
if (!this._injectedScriptPromise) {
|
||||||
const custom: string[] = [];
|
const customEngines: InjectedScriptOptions['customEngines'] = [];
|
||||||
const selectorsRegistry = this.frame._page.context().selectors();
|
const selectorsRegistry = this.frame._page.context().selectors();
|
||||||
for (const [name, { source }] of selectorsRegistry._engines)
|
for (const [name, { source }] of selectorsRegistry._engines)
|
||||||
custom.push(`{ name: '${name}', engine: (${source}) }`);
|
customEngines.push({ name, source });
|
||||||
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
||||||
|
const options: InjectedScriptOptions = {
|
||||||
|
isUnderTest: isUnderTest(),
|
||||||
|
sdkLanguage,
|
||||||
|
testIdAttributeName: selectorsRegistry.testIdAttributeName(),
|
||||||
|
stableRafCount: this.frame._page._delegate.rafCountForStablePosition(),
|
||||||
|
browserName: this.frame._page._browserContext._browser.options.name,
|
||||||
|
inputFileRoleTextbox: process.env.PLAYWRIGHT_INPUT_FILE_TEXTBOX ? true : false,
|
||||||
|
customEngines,
|
||||||
|
runtimeGuid: js.runtimeGuid,
|
||||||
|
};
|
||||||
const source = `
|
const source = `
|
||||||
(() => {
|
(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${injectedScriptSource.source}
|
${injectedScriptSource.source}
|
||||||
return new (module.exports.InjectedScript())(
|
return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options)});
|
||||||
globalThis,
|
|
||||||
${isUnderTest()},
|
|
||||||
"${sdkLanguage}",
|
|
||||||
${JSON.stringify(selectorsRegistry.testIdAttributeName())},
|
|
||||||
${this.frame._page._delegate.rafCountForStablePosition()},
|
|
||||||
"${this.frame._page._browserContext._browser.options.name}",
|
|
||||||
${process.env.PLAYWRIGHT_INPUT_FILE_TEXTBOX ? 'true' : 'false'},
|
|
||||||
[${custom.join(',\n')}]
|
|
||||||
);
|
|
||||||
})();
|
})();
|
||||||
`;
|
`;
|
||||||
this._injectedScriptPromise = this.rawEvaluateHandle(source)
|
this._injectedScriptPromise = this.rawEvaluateHandle(source)
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
import * as utilityScriptSource from '../generated/utilityScriptSource';
|
import * as utilityScriptSource from '../generated/utilityScriptSource';
|
||||||
import { isUnderTest } from '../utils';
|
import { createGuid, isUnderTest } from '../utils';
|
||||||
import { builtins } from '../utils/isomorphic/builtins';
|
import { builtins } from '../utils/isomorphic/builtins';
|
||||||
import { source } from '../utils/isomorphic/utilityScriptSerializers';
|
import { source } from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
import { LongStandingScope } from '../utils/isomorphic/manualPromise';
|
import { LongStandingScope } from '../utils/isomorphic/manualPromise';
|
||||||
@ -24,6 +24,9 @@ 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';
|
||||||
|
|
||||||
|
// Use in the web-facing names to avoid leaking Playwright to the pages.
|
||||||
|
export const runtimeGuid = createGuid();
|
||||||
|
|
||||||
interface TaggedAsJSHandle<T> {
|
interface TaggedAsJSHandle<T> {
|
||||||
__jshandle: T;
|
__jshandle: T;
|
||||||
}
|
}
|
||||||
@ -46,7 +49,7 @@ export type Func1<Arg, R> = string | ((arg: Unboxed<Arg>) => R | Promise<R>);
|
|||||||
export type FuncOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
|
export type FuncOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
|
||||||
export type SmartHandle<T> = T extends Node ? dom.ElementHandle<T> : JSHandle<T>;
|
export type SmartHandle<T> = T extends Node ? dom.ElementHandle<T> : JSHandle<T>;
|
||||||
|
|
||||||
const utilityScriptSerializers = source(builtins());
|
const utilityScriptSerializers = source(builtins(runtimeGuid));
|
||||||
export const parseEvaluationResultValue = utilityScriptSerializers.parseEvaluationResultValue;
|
export const parseEvaluationResultValue = utilityScriptSerializers.parseEvaluationResultValue;
|
||||||
export const serializeAsCallArgument = utilityScriptSerializers.serializeAsCallArgument;
|
export const serializeAsCallArgument = utilityScriptSerializers.serializeAsCallArgument;
|
||||||
|
|
||||||
@ -109,7 +112,7 @@ export class ExecutionContext extends SdkObject {
|
|||||||
(() => {
|
(() => {
|
||||||
const module = {};
|
const module = {};
|
||||||
${utilityScriptSource.source}
|
${utilityScriptSource.source}
|
||||||
return new (module.exports.UtilityScript())(${isUnderTest()});
|
return new (module.exports.UtilityScript())(${JSON.stringify(runtimeGuid)}, ${isUnderTest()});
|
||||||
})();`;
|
})();`;
|
||||||
this._utilityScriptPromise = this._raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source))
|
this._utilityScriptPromise = this._raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source))
|
||||||
.then(handle => {
|
.then(handle => {
|
||||||
|
@ -24,7 +24,7 @@ import * as frames from './frames';
|
|||||||
import { helper } from './helper';
|
import { helper } from './helper';
|
||||||
import * as input from './input';
|
import * as input from './input';
|
||||||
import { SdkObject } from './instrumentation';
|
import { SdkObject } from './instrumentation';
|
||||||
import { builtins } from '../utils/isomorphic/builtins';
|
import { builtinsSource } from '../utils/isomorphic/builtins';
|
||||||
import { createPageBindingScript, deliverBindingResult, takeBindingHandle } from './pageBinding';
|
import { createPageBindingScript, deliverBindingResult, takeBindingHandle } from './pageBinding';
|
||||||
import * as js from './javascript';
|
import * as js from './javascript';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
@ -858,7 +858,7 @@ export class Worker extends SdkObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PageBinding {
|
export class PageBinding {
|
||||||
static kPlaywrightBinding = '__playwright__binding__';
|
static kPlaywrightBinding = '__playwright__binding__' + js.runtimeGuid;
|
||||||
|
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly playwrightFunction: frames.FunctionWithSource;
|
readonly playwrightFunction: frames.FunctionWithSource;
|
||||||
@ -906,11 +906,15 @@ export class InitScript {
|
|||||||
constructor(source: string, internal?: boolean, name?: string) {
|
constructor(source: string, internal?: boolean, name?: string) {
|
||||||
const guid = createGuid();
|
const guid = createGuid();
|
||||||
this.source = `(() => {
|
this.source = `(() => {
|
||||||
globalThis.__pwInitScripts = globalThis.__pwInitScripts || {};
|
const name = '__pw_init_scripts__${js.runtimeGuid}';
|
||||||
const hasInitScript = globalThis.__pwInitScripts[${JSON.stringify(guid)}];
|
if (!globalThis[name])
|
||||||
|
Object.defineProperty(globalThis, name, { value: {}, configurable: false, enumerable: false, writable: false });
|
||||||
|
|
||||||
|
const globalInitScripts = globalThis[name];
|
||||||
|
const hasInitScript = globalInitScripts[${JSON.stringify(guid)}];
|
||||||
if (hasInitScript)
|
if (hasInitScript)
|
||||||
return;
|
return;
|
||||||
globalThis.__pwInitScripts[${JSON.stringify(guid)}] = true;
|
globalThis[name][${JSON.stringify(guid)}] = true;
|
||||||
${source}
|
${source}
|
||||||
})();`;
|
})();`;
|
||||||
this.internal = !!internal;
|
this.internal = !!internal;
|
||||||
@ -918,7 +922,7 @@ export class InitScript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const kBuiltinsScript = new InitScript(`(${builtins})()`, true /* internal */);
|
export const kBuiltinsScript = new InitScript(builtinsSource(js.runtimeGuid), true /* internal */);
|
||||||
|
|
||||||
class FrameThrottler {
|
class FrameThrottler {
|
||||||
private _acks: (() => void)[] = [];
|
private _acks: (() => void)[] = [];
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { builtins } from '../utils/isomorphic/builtins';
|
import { builtinsSource } from '../utils/isomorphic/builtins';
|
||||||
import { source } from '../utils/isomorphic/utilityScriptSerializers';
|
import { source } from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
|
import * as js from './javascript';
|
||||||
|
|
||||||
import type { Builtins } from '../utils/isomorphic/builtins';
|
import type { Builtins } from '../utils/isomorphic/builtins';
|
||||||
import type { SerializedValue } from '../utils/isomorphic/utilityScriptSerializers';
|
import type { SerializedValue } from '../utils/isomorphic/utilityScriptSerializers';
|
||||||
@ -88,5 +89,5 @@ export function deliverBindingResult(arg: { name: string, seq: number, result?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPageBindingScript(playwrightBinding: string, name: string, needsHandle: boolean) {
|
export function createPageBindingScript(playwrightBinding: string, name: string, needsHandle: boolean) {
|
||||||
return `(${addPageBinding.toString()})(${JSON.stringify(playwrightBinding)}, ${JSON.stringify(name)}, ${needsHandle}, (${source}), (${builtins})())`;
|
return `(${addPageBinding.toString()})(${JSON.stringify(playwrightBinding)}, ${JSON.stringify(name)}, ${needsHandle}, (${source}), ${builtinsSource(js.runtimeGuid)})`;
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,14 @@ export type Builtins = {
|
|||||||
// anything else happens in the page. This way, original builtins are saved on the global object
|
// anything else happens in the page. This way, original builtins are saved on the global object
|
||||||
// before page can temper with them. Later on, any call to builtins() will retrieve the stored
|
// before page can temper with them. Later on, any call to builtins() will retrieve the stored
|
||||||
// builtins instead of initializing them again.
|
// builtins instead of initializing them again.
|
||||||
export function builtins(global?: typeof globalThis): Builtins {
|
export function builtins(runtimeGuid: string, global?: typeof globalThis): Builtins {
|
||||||
global = global ?? globalThis;
|
global = global ?? globalThis;
|
||||||
if (!(global as any)['__playwright_builtins__']) {
|
const name = `__playwright_builtins__${runtimeGuid}`;
|
||||||
const builtins: Builtins = {
|
let builtins: Builtins = (global as any)[name];
|
||||||
|
if (builtins)
|
||||||
|
return builtins;
|
||||||
|
|
||||||
|
builtins = {
|
||||||
setTimeout: global.setTimeout?.bind(global),
|
setTimeout: global.setTimeout?.bind(global),
|
||||||
clearTimeout: global.clearTimeout?.bind(global),
|
clearTimeout: global.clearTimeout?.bind(global),
|
||||||
setInterval: global.setInterval?.bind(global),
|
setInterval: global.setInterval?.bind(global),
|
||||||
@ -60,12 +64,16 @@ export function builtins(global?: typeof globalThis): Builtins {
|
|||||||
Map: global.Map,
|
Map: global.Map,
|
||||||
Set: global.Set,
|
Set: global.Set,
|
||||||
};
|
};
|
||||||
Object.defineProperty(global, '__playwright_builtins__', { value: builtins, configurable: false, enumerable: false, writable: false });
|
if (runtimeGuid)
|
||||||
}
|
Object.defineProperty(global, name, { value: builtins, configurable: false, enumerable: false, writable: false });
|
||||||
return (global as any)['__playwright_builtins__'];
|
return builtins;
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = builtins();
|
export function builtinsSource(runtimeGuid: string): string {
|
||||||
|
return `(${builtins})(${JSON.stringify(runtimeGuid)})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = builtins('');
|
||||||
export const setTimeout = instance.setTimeout;
|
export const setTimeout = instance.setTimeout;
|
||||||
export const clearTimeout = instance.clearTimeout;
|
export const clearTimeout = instance.clearTimeout;
|
||||||
export const setInterval = instance.setInterval;
|
export const setInterval = instance.setInterval;
|
||||||
|
@ -77,7 +77,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
<ToolbarButton icon='link-external' title='Open snapshot in a new tab' disabled={!snapshotUrls?.popoutUrl} onClick={() => {
|
<ToolbarButton icon='link-external' title='Open snapshot in a new tab' disabled={!snapshotUrls?.popoutUrl} onClick={() => {
|
||||||
const win = window.open(snapshotUrls?.popoutUrl || '', '_blank');
|
const win = window.open(snapshotUrls?.popoutUrl || '', '_blank');
|
||||||
win?.addEventListener('DOMContentLoaded', () => {
|
win?.addEventListener('DOMContentLoaded', () => {
|
||||||
const injectedScript = new InjectedScript(win as any, false, sdkLanguage, testIdAttributeName, 1, 'chromium', false, []);
|
const injectedScript = new InjectedScript(win as any, { isUnderTest: false, sdkLanguage, testIdAttributeName, stableRafCount: 1, browserName: 'chromium', inputFileRoleTextbox: false, customEngines: [], runtimeGuid: '' });
|
||||||
injectedScript.consoleApi.install();
|
injectedScript.consoleApi.install();
|
||||||
});
|
});
|
||||||
}} />
|
}} />
|
||||||
@ -280,7 +280,7 @@ function createRecorders(recorders: { recorder: Recorder, frameSelector: string
|
|||||||
return;
|
return;
|
||||||
const win = frameWindow as any;
|
const win = frameWindow as any;
|
||||||
if (!win._recorder) {
|
if (!win._recorder) {
|
||||||
const injectedScript = new InjectedScript(frameWindow as any, isUnderTest, sdkLanguage, testIdAttributeName, 1, 'chromium', false, []);
|
const injectedScript = new InjectedScript(frameWindow as any, { isUnderTest, sdkLanguage, testIdAttributeName, stableRafCount: 1, browserName: 'chromium', inputFileRoleTextbox: false, customEngines: [], runtimeGuid: '' });
|
||||||
const recorder = new Recorder(injectedScript);
|
const recorder = new Recorder(injectedScript);
|
||||||
win._injectedScript = injectedScript;
|
win._injectedScript = injectedScript;
|
||||||
win._recorder = { recorder, frameSelector: parentFrameSelector };
|
win._recorder = { recorder, frameSelector: parentFrameSelector };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user