diff --git a/src/cli/cli.ts b/src/cli/cli.ts index aa4b642f8e..2df4cba122 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -22,7 +22,6 @@ import * as path from 'path'; import * as program from 'commander'; import * as os from 'os'; import * as fs from 'fs'; -import * as consoleApiSource from '../generated/consoleApiSource'; import { OutputMultiplexer, TerminalOutput, FileOutput } from './codegen/outputs'; import { CodeGenerator, CodeGeneratorOutput } from './codegen/codeGenerator'; import { JavaScriptLanguageGenerator, LanguageGenerator } from './codegen/languages'; @@ -318,7 +317,7 @@ async function openPage(context: BrowserContext, url: string | undefined): Promi async function open(options: Options, url: string | undefined) { const { context } = await launchContext(options, false); - (context as any)._extendInjectedScript(consoleApiSource.source); + (context as any)._exposeConsoleApi(); await openPage(context, url); if (process.env.PWCLI_EXIT_FOR_TEST) await Promise.all(context.pages().map(p => p.close())); @@ -347,7 +346,7 @@ async function codegen(options: Options, url: string | undefined, target: string const generator = new CodeGenerator(browserName, launchOptions, contextOptions, output, languageGenerator, options.device, options.saveStorage); new RecorderController(context, generator); - (context as any)._extendInjectedScript(consoleApiSource.source); + (context as any)._exposeConsoleApi(); await openPage(context, url); if (process.env.PWCLI_EXIT_FOR_TEST) await Promise.all(context.pages().map(p => p.close())); diff --git a/src/cli/codegen/recorderController.ts b/src/cli/codegen/recorderController.ts index c4a62d3a8b..d51685fa7b 100644 --- a/src/cli/codegen/recorderController.ts +++ b/src/cli/codegen/recorderController.ts @@ -18,7 +18,6 @@ import type { Page, BrowserContext, Frame, Download, Dialog } from '../../..'; import * as actions from './recorderActions'; import { CodeGenerator, ActionInContext } from './codeGenerator'; import { toClickOptions, toModifiers } from './utils'; -import * as recorderSource from '../../generated/recorderSource'; type BindingSource = { frame: Frame, page: Page }; @@ -30,7 +29,7 @@ export class RecorderController { private _timers = new Set(); constructor(context: BrowserContext, generator: CodeGenerator) { - (context as any)._extendInjectedScript(recorderSource.source); + (context as any)._enableRecorder(); this._generator = generator; diff --git a/src/cli/driver.ts b/src/cli/driver.ts index c56d514897..608e8a15a2 100644 --- a/src/cli/driver.ts +++ b/src/cli/driver.ts @@ -18,7 +18,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { installDebugController } from '../debug/debugController'; +import { installInspectorController } from '../server/inspector/inspectorController'; import { DispatcherConnection } from '../dispatchers/dispatcher'; import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher'; import { installBrowsersWithProgressBar } from '../install/installer'; @@ -38,7 +38,7 @@ export function printProtocol() { } export function runServer() { - installDebugController(); + installInspectorController(); installTracer(); installHarTracer(); diff --git a/src/client/browserContext.ts b/src/client/browserContext.ts index b03404697a..1c397a088e 100644 --- a/src/client/browserContext.ts +++ b/src/client/browserContext.ts @@ -29,7 +29,6 @@ import { Waiter } from './waiter'; import { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState } from './types'; import { isUnderTest, headersObjectToArray, mkdirIfNeeded } from '../utils/utils'; import { isSafeCloseError } from '../utils/errors'; -import { serializeArgument } from './jsHandle'; import * as api from '../../types/types'; import * as structs from '../../types/structs'; @@ -255,8 +254,12 @@ export class BrowserContext extends ChannelOwner(source: string, arg?: Arg) { - await this._channel.extendInjectedScript({ source, arg: serializeArgument(arg) }); + async _exposeConsoleApi() { + await this._channel.exposeConsoleApi(); + } + + async _enableRecorder() { + await this._channel.enableRecorder(); } } diff --git a/src/dispatchers/browserContextDispatcher.ts b/src/dispatchers/browserContextDispatcher.ts index c583a07c41..ce4efdb2b2 100644 --- a/src/dispatchers/browserContextDispatcher.ts +++ b/src/dispatchers/browserContextDispatcher.ts @@ -21,7 +21,6 @@ import * as channels from '../protocol/channels'; import { RouteDispatcher, RequestDispatcher } from './networkDispatchers'; import { CRBrowserContext } from '../server/chromium/crBrowser'; import { CDPSessionDispatcher } from './cdpSessionDispatcher'; -import { parseArgument } from './jsHandleDispatcher'; export class BrowserContextDispatcher extends Dispatcher implements channels.BrowserContextChannel { private _context: BrowserContext; @@ -126,8 +125,12 @@ export class BrowserContextDispatcher extends Dispatcher { - await this._context.extendInjectedScript(params.source, parseArgument(params.arg)); + async exposeConsoleApi(): Promise { + await this._context.exposeConsoleApi(); + } + + async enableRecorder(): Promise { + await this._context.enableRecorder(); } async crNewCDPSession(params: channels.BrowserContextCrNewCDPSessionParams): Promise { diff --git a/src/inprocess.ts b/src/inprocess.ts index cbdde71109..e9a89d6858 100644 --- a/src/inprocess.ts +++ b/src/inprocess.ts @@ -20,7 +20,7 @@ import type { Playwright as PlaywrightAPI } from './client/playwright'; import { PlaywrightDispatcher } from './dispatchers/playwrightDispatcher'; import { Connection } from './client/connection'; import { BrowserServerLauncherImpl } from './browserServerImpl'; -import { installDebugController } from './debug/debugController'; +import { installInspectorController } from './server/inspector/inspectorController'; import { installTracer } from './trace/tracer'; import { installHarTracer } from './trace/harTracer'; import * as path from 'path'; @@ -28,7 +28,7 @@ import * as path from 'path'; function setupInProcess(): PlaywrightAPI { const playwright = new PlaywrightImpl(path.join(__dirname, '..'), require('../browsers.json')['browsers']); - installDebugController(); + installInspectorController(); installTracer(); installHarTracer(); diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index 3cec93093e..38b40d950e 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -552,7 +552,8 @@ export interface BrowserContextChannel extends Channel { setNetworkInterceptionEnabled(params: BrowserContextSetNetworkInterceptionEnabledParams, metadata?: Metadata): Promise; setOffline(params: BrowserContextSetOfflineParams, metadata?: Metadata): Promise; storageState(params?: BrowserContextStorageStateParams, metadata?: Metadata): Promise; - extendInjectedScript(params: BrowserContextExtendInjectedScriptParams, metadata?: Metadata): Promise; + exposeConsoleApi(params?: BrowserContextExposeConsoleApiParams, metadata?: Metadata): Promise; + enableRecorder(params?: BrowserContextEnableRecorderParams, metadata?: Metadata): Promise; crNewCDPSession(params: BrowserContextCrNewCDPSessionParams, metadata?: Metadata): Promise; } export type BrowserContextBindingCallEvent = { @@ -694,14 +695,12 @@ export type BrowserContextStorageStateResult = { cookies: NetworkCookie[], origins: OriginStorage[], }; -export type BrowserContextExtendInjectedScriptParams = { - source: string, - arg: SerializedArgument, -}; -export type BrowserContextExtendInjectedScriptOptions = { - -}; -export type BrowserContextExtendInjectedScriptResult = void; +export type BrowserContextExposeConsoleApiParams = {}; +export type BrowserContextExposeConsoleApiOptions = {}; +export type BrowserContextExposeConsoleApiResult = void; +export type BrowserContextEnableRecorderParams = {}; +export type BrowserContextEnableRecorderOptions = {}; +export type BrowserContextEnableRecorderResult = void; export type BrowserContextCrNewCDPSessionParams = { page: PageChannel, }; diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 262fb3d81a..3e63927e68 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -599,11 +599,11 @@ BrowserContext: type: array items: OriginStorage - extendInjectedScript: + exposeConsoleApi: + experimental: True + + enableRecorder: experimental: True - parameters: - source: string - arg: SerializedArgument crNewCDPSession: parameters: diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index 678ef70a6c..918d4aae4e 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -335,10 +335,8 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { offline: tBoolean, }); scheme.BrowserContextStorageStateParams = tOptional(tObject({})); - scheme.BrowserContextExtendInjectedScriptParams = tObject({ - source: tString, - arg: tType('SerializedArgument'), - }); + scheme.BrowserContextExposeConsoleApiParams = tOptional(tObject({})); + scheme.BrowserContextEnableRecorderParams = tOptional(tObject({})); scheme.BrowserContextCrNewCDPSessionParams = tObject({ page: tChannel('Page'), }); diff --git a/src/remote/playwrightServer.ts b/src/remote/playwrightServer.ts index 66820a9740..3a12d06914 100644 --- a/src/remote/playwrightServer.ts +++ b/src/remote/playwrightServer.ts @@ -17,7 +17,7 @@ import * as debug from 'debug'; import * as http from 'http'; import * as WebSocket from 'ws'; -import { installDebugController } from '../debug/debugController'; +import { installInspectorController } from '../server/inspector/inspectorController'; import { DispatcherConnection } from '../dispatchers/dispatcher'; import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher'; import { Playwright } from '../server/playwright'; @@ -27,7 +27,7 @@ import { installHarTracer } from '../trace/harTracer'; const debugLog = debug('pw:server'); -installDebugController(); +installInspectorController(); installTracer(); installHarTracer(); diff --git a/src/server/browserContext.ts b/src/server/browserContext.ts index 2f13802f71..6dee12a2b3 100644 --- a/src/server/browserContext.ts +++ b/src/server/browserContext.ts @@ -19,6 +19,8 @@ import { EventEmitter } from 'events'; import { TimeoutSettings } from '../utils/timeoutSettings'; import { mkdirIfNeeded } from '../utils/utils'; import { Browser, BrowserOptions } from './browser'; +import * as consoleApiSource from '../generated/consoleApiSource'; +import * as recorderSource from '../generated/recorderSource'; import * as dom from './dom'; import { Download } from './download'; import * as frames from './frames'; @@ -379,8 +381,16 @@ export abstract class BrowserContext extends EventEmitter { } } - async extendInjectedScript(source: string, arg?: any) { - const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(e => {}); + async exposeConsoleApi() { + await this._extendInjectedScript(consoleApiSource.source); + } + + async enableRecorder() { + await this._extendInjectedScript(recorderSource.source); + } + + private async _extendInjectedScript(source: string) { + const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source).catch(e => {}); const installInPage = (page: Page) => { page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame); return Promise.all(page.frames().map(installInFrame)); diff --git a/src/debug/injected/consoleApi.ts b/src/server/inspector/injected/consoleApi.ts similarity index 97% rename from src/debug/injected/consoleApi.ts rename to src/server/inspector/injected/consoleApi.ts index 1c0c03b2a4..026d843a0e 100644 --- a/src/debug/injected/consoleApi.ts +++ b/src/server/inspector/injected/consoleApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type InjectedScript from '../../server/injected/injectedScript'; +import type InjectedScript from '../../injected/injectedScript'; import { generateSelector } from './selectorGenerator'; export class ConsoleAPI { diff --git a/src/debug/injected/consoleApi.webpack.config.js b/src/server/inspector/injected/consoleApi.webpack.config.js similarity index 88% rename from src/debug/injected/consoleApi.webpack.config.js rename to src/server/inspector/injected/consoleApi.webpack.config.js index 9595fa0111..a491ff6422 100644 --- a/src/debug/injected/consoleApi.webpack.config.js +++ b/src/server/inspector/injected/consoleApi.webpack.config.js @@ -15,7 +15,7 @@ */ const path = require('path'); -const InlineSource = require('../../server/injected/webpack-inline-source-plugin'); +const InlineSource = require('../../injected/webpack-inline-source-plugin'); /** @type {import('webpack').Configuration} */ module.exports = { @@ -45,6 +45,6 @@ module.exports = { path: path.resolve(__dirname, '../../../lib/server/injected/packed') }, plugins: [ - new InlineSource(path.join(__dirname, '..', '..', 'generated', 'consoleApiSource.ts')), + new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'consoleApiSource.ts')), ] }; diff --git a/src/cli/injected/html.ts b/src/server/inspector/injected/html.ts similarity index 100% rename from src/cli/injected/html.ts rename to src/server/inspector/injected/html.ts diff --git a/src/cli/injected/recorder.ts b/src/server/inspector/injected/recorder.ts similarity index 98% rename from src/cli/injected/recorder.ts rename to src/server/inspector/injected/recorder.ts index 5ec1bb7288..5a52d4a3bb 100644 --- a/src/cli/injected/recorder.ts +++ b/src/server/inspector/injected/recorder.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import type * as actions from '../codegen/recorderActions'; -import type InjectedScript from '../../server/injected/injectedScript'; -import { generateSelector } from '../../debug/injected/selectorGenerator'; +import type * as actions from '../../../cli/codegen/recorderActions'; +import type InjectedScript from '../../injected/injectedScript'; +import { generateSelector } from './selectorGenerator'; import { html } from './html'; declare global { diff --git a/src/cli/injected/recorder.webpack.config.js b/src/server/inspector/injected/recorder.webpack.config.js similarity index 88% rename from src/cli/injected/recorder.webpack.config.js rename to src/server/inspector/injected/recorder.webpack.config.js index 13f840931f..8552c31210 100644 --- a/src/cli/injected/recorder.webpack.config.js +++ b/src/server/inspector/injected/recorder.webpack.config.js @@ -15,7 +15,7 @@ */ const path = require('path'); -const InlineSource = require('../../server/injected/webpack-inline-source-plugin'); +const InlineSource = require('../../injected/webpack-inline-source-plugin'); /** @type {import('webpack').Configuration} */ module.exports = { @@ -39,12 +39,12 @@ module.exports = { }, output: { libraryTarget: 'var', - libraryExport: 'default', library: 'pwExport', + libraryExport: 'default', filename: 'recorderSource.js', path: path.resolve(__dirname, '../../../lib/server/injected/packed') }, plugins: [ - new InlineSource(path.join(__dirname, '..', '..', 'generated', 'recorderSource.ts')), + new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'recorderSource.ts')), ] }; diff --git a/src/debug/injected/selectorGenerator.ts b/src/server/inspector/injected/selectorGenerator.ts similarity index 99% rename from src/debug/injected/selectorGenerator.ts rename to src/server/inspector/injected/selectorGenerator.ts index a1065a9b31..189a09f3ae 100644 --- a/src/debug/injected/selectorGenerator.ts +++ b/src/server/inspector/injected/selectorGenerator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type InjectedScript from '../../server/injected/injectedScript'; +import type InjectedScript from '../../injected/injectedScript'; export function generateSelector(injectedScript: InjectedScript, targetElement: Element): { selector: string, elements: Element[] } { const path: SelectorToken[] = []; diff --git a/src/debug/debugController.ts b/src/server/inspector/inspectorController.ts similarity index 72% rename from src/debug/debugController.ts rename to src/server/inspector/inspectorController.ts index ff89c94571..8b03d319c0 100644 --- a/src/debug/debugController.ts +++ b/src/server/inspector/inspectorController.ts @@ -14,18 +14,17 @@ * limitations under the License. */ -import { BrowserContext, ContextListener, contextListeners } from '../server/browserContext'; -import { isDebugMode } from '../utils/utils'; -import * as consoleApiSource from '../generated/consoleApiSource'; +import { BrowserContext, ContextListener, contextListeners } from '../browserContext'; +import { isDebugMode } from '../../utils/utils'; -export function installDebugController() { - contextListeners.add(new DebugController()); +export function installInspectorController() { + contextListeners.add(new InspectorController()); } -class DebugController implements ContextListener { +class InspectorController implements ContextListener { async onContextCreated(context: BrowserContext): Promise { if (isDebugMode()) - context.extendInjectedScript(consoleApiSource.source); + context.exposeConsoleApi(); } async onContextWillDestroy(context: BrowserContext): Promise {} async onContextDidDestroy(context: BrowserContext): Promise {} diff --git a/test/browsercontext-expose-function.spec.ts b/test/browsercontext-expose-function.spec.ts index a26d271735..96a74a5299 100644 --- a/test/browsercontext-expose-function.spec.ts +++ b/test/browsercontext-expose-function.spec.ts @@ -82,26 +82,3 @@ it('exposeBindingHandle should work', async ({context}) => { expect(await target.evaluate(x => x.foo)).toBe(42); expect(result).toEqual(17); }); - -it('extendInjectedScript should work', async ({ context, server }) => { - await (context as any)._extendInjectedScript(`var pwExport = (() => { - class Foo { - constructor() { - window._counter = (window._counter || 0) + 1; - } - } - return Foo; - })()`); - - const page = await context.newPage(); - await page.waitForFunction(() => (window as any)._counter === 1); - - await page.goto(server.EMPTY_PAGE); - await page.waitForFunction(() => (window as any)._counter === 1); - - await Promise.all([ - page.waitForNavigation(), - page.evaluate(() => history.pushState({}, '', '/url.html')) - ]); - expect(await page.evaluate(() => (window as any)._counter)).toBe(1); -}); diff --git a/test/selector-generator.spec.ts b/test/selector-generator.spec.ts index 485c85cbb9..9407e820f2 100644 --- a/test/selector-generator.spec.ts +++ b/test/selector-generator.spec.ts @@ -16,11 +16,10 @@ import { folio } from './fixtures'; import type { Page, Frame } from '..'; -import { source } from '../src/generated/consoleApiSource'; const fixtures = folio.extend(); fixtures.context.override(async ({ context }, run) => { - await (context as any)._extendInjectedScript(source); + await (context as any)._exposeConsoleApi(); await run(context); }); const { describe, it, expect } = fixtures.build(); diff --git a/utils/build/build.js b/utils/build/build.js index 693bf98966..1de9904f06 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -68,8 +68,8 @@ function runBuild() { const webPackFiles = [ 'src/server/injected/injectedScript.webpack.config.js', 'src/server/injected/utilityScript.webpack.config.js', - 'src/debug/injected/consoleApi.webpack.config.js', - 'src/cli/injected/recorder.webpack.config.js', + 'src/server/inspector/injected/consoleApi.webpack.config.js', + 'src/server/inspector/injected/recorder.webpack.config.js', 'src/cli/traceViewer/web/web.webpack.config.js', ]; for (const file of webPackFiles) { diff --git a/utils/check_deps.js b/utils/check_deps.js index 781f7cdbcd..5071e07fe8 100644 --- a/utils/check_deps.js +++ b/utils/check_deps.js @@ -131,6 +131,7 @@ DEPS['src/server/'] = [ DEPS['src/server/common/'] = []; // Strict dependencies for injected code. DEPS['src/server/injected/'] = ['src/server/common/']; +DEPS['src/server/inspector/injected/'] = ['src/server/common/', 'src/cli/codegen/', 'src/server/injected/']; // Electron and Clank use chromium internally. DEPS['src/server/android/'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/protocol/']; @@ -142,11 +143,8 @@ DEPS['src/cli/driver.ts'] = DEPS['src/inprocess.ts'] = DEPS['src/browserServerIm // Tracing is a client/server plugin, nothing should depend on it. DEPS['src/trace/'] = ['src/utils/', 'src/client/**', 'src/server/**']; -// Debug is a server plugin, nothing should depend on it. -DEPS['src/debug/'] = ['src/utils/', 'src/generated/', 'src/server/**', 'src/debug/**']; - // The service is a cross-cutting feature, and so it depends on a bunch of things. -DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/electron/', 'src/trace/']; +DEPS['src/remote/'] = ['src/client/', 'src/debug/', 'src/dispatchers/', 'src/server/', 'src/server/inspector/', 'src/server/electron/', 'src/trace/']; DEPS['src/service.ts'] = ['src/remote/']; // CLI should only use client-side features.