diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..5bf8fc159 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0=" + +use devenv \ No newline at end of file diff --git a/.gitignore b/.gitignore index 172ac8106..b6d416ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,12 @@ browserstack.err agent/mitm-socket/dist connect /real-user-agents/local.log +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/agent/main/injected-scripts/PaintEvents.ts b/agent/main/injected-scripts/PaintEvents.ts index cd7a15249..1ae575cd1 100644 --- a/agent/main/injected-scripts/PaintEvents.ts +++ b/agent/main/injected-scripts/PaintEvents.ts @@ -6,9 +6,11 @@ declare global { } } -declare const runtimeFunction: string; -// callback binding -const eventsCallback = window[runtimeFunction] as unknown as (data: string) => void; +declare const callbackName: string; +declare const callback: (name: string, data: string) => void; +const eventsCallback = (data: string) => { + callback(callbackName, data); +}; class PaintEvents { onEventCallbackFn: (event: IDomPaintEvent, timestamp: number, url: string) => void; diff --git a/agent/main/lib/Agent.ts b/agent/main/lib/Agent.ts index 3218e5a7c..df4709895 100644 --- a/agent/main/lib/Agent.ts +++ b/agent/main/lib/Agent.ts @@ -10,7 +10,10 @@ import { IHooksProvider } from '@ulixee/unblocked-specification/agent/hooks/IHoo import IEmulationProfile, { IEmulationOptions, } from '@ulixee/unblocked-specification/plugin/IEmulationProfile'; -import { IUnblockedPluginClass, PluginConfigs } from '@ulixee/unblocked-specification/plugin/IUnblockedPlugin'; +import { + IUnblockedPluginClass, + PluginConfigs, +} from '@ulixee/unblocked-specification/plugin/IUnblockedPlugin'; import { nanoid } from 'nanoid'; import env from '../env'; import ICommandMarker from '../interfaces/ICommandMarker'; diff --git a/agent/main/lib/BrowserContext.ts b/agent/main/lib/BrowserContext.ts index 594a04a43..7543728a7 100644 --- a/agent/main/lib/BrowserContext.ts +++ b/agent/main/lib/BrowserContext.ts @@ -31,6 +31,7 @@ import FrameOutOfProcess from './FrameOutOfProcess'; import CookieParam = Protocol.Network.CookieParam; import TargetInfo = Protocol.Target.TargetInfo; import CreateBrowserContextRequest = Protocol.Target.CreateBrowserContextRequest; +import { WebsocketSession } from './WebsocketSession'; const { log } = Log(module); @@ -68,6 +69,7 @@ export default class BrowserContext } public isIncognito = true; + public readonly websocketSession: WebsocketSession; public readonly idTracker = { navigationId: 0, @@ -87,6 +89,7 @@ export default class BrowserContext private readonly events = new EventSubscriber(); + constructor(browser: Browser, isIncognito: boolean, options?: IBrowserContextCreateOptions) { super(); this.browser = browser; @@ -102,6 +105,7 @@ export default class BrowserContext this.devtoolsSessionLogger.subscribeToDevtoolsMessages(this.browser.devtoolsSession, { sessionType: 'browser', }); + this.websocketSession = new WebsocketSession(); } public async open(): Promise { @@ -125,6 +129,7 @@ export default class BrowserContext this.logger = this.logger.createChild(module, { browserContextId, }); + await this.websocketSession.initialize(); } async newPage(options?: IPageCreateOptions): Promise { @@ -344,6 +349,7 @@ export default class BrowserContext this.resources.cleanup(); this.events.close(); this.emit('close'); + this.websocketSession.close(); this.devtoolsSessionLogger.close(); this.removeAllListeners(); this.cleanup(); diff --git a/agent/main/lib/BrowserProcess.ts b/agent/main/lib/BrowserProcess.ts index c909605fc..a34561704 100755 --- a/agent/main/lib/BrowserProcess.ts +++ b/agent/main/lib/BrowserProcess.ts @@ -29,7 +29,7 @@ export default class BrowserProcess extends TypedEventEmitter<{ close: void }> { private processEnv?: NodeJS.ProcessEnv, ) { super(); - + bindFunctions(this); this.launchedProcess = this.launch(); this.bindProcessEvents(); diff --git a/agent/main/lib/Frame.ts b/agent/main/lib/Frame.ts index e2eb2b95b..7fb7bd8ba 100644 --- a/agent/main/lib/Frame.ts +++ b/agent/main/lib/Frame.ts @@ -135,6 +135,8 @@ export default class Frame extends TypedEventEmitter implements IF private closedWithError: Error; private isClosing = false; private defaultContextCreated: Resolvable; + private shouldRefreshContextIds = false; + private hasCreatedIsolate = false; private readonly checkIfAttached: () => boolean; private inPageCounter = 0; private events = new EventSubscriber(); @@ -173,6 +175,7 @@ export default class Frame extends TypedEventEmitter implements IF activeContextIds: Set, ): Promise { if (this.devtoolsSession === devtoolsSession) return; + this.shouldRefreshContextIds = true; this.devtoolsSession = devtoolsSession; this.activeContextIds = activeContextIds; @@ -227,6 +230,17 @@ export default class Frame extends TypedEventEmitter implements IF this.waitForActiveContextId(false), ]); + // const [isolatedContextId, defaultContextId] = await Promise.all([ + // this.#framesManager.getExecutionContextId({ frameId: this.id, type: 'isolated' }), + // this.#framesManager.getExecutionContextId({ frameId: this.id, type: 'default' }), + // ]); + + // const [isolatedContextId, defaultContextId] = + // await this.#framesManager.getExecutionContextIds([ + // { frameId: this.id, type: 'isolated' }, + // { frameId: this.id, type: 'default' }, + // ]); + if (this.closedWithError || !this.devtoolsSession.isConnected()) return; await Promise.all( @@ -278,6 +292,8 @@ export default class Frame extends TypedEventEmitter implements IF const startOrigin = this.securityOrigin; const isolateFromWebPageEnvironment = options?.isolateFromWebPageEnvironment ?? false; const contextId = await this.waitForActiveContextId(isolateFromWebPageEnvironment); + + // contextId = 67890; try { if (!contextId) { const notFound: any = new Error('Could not find a valid context for this request'); @@ -691,9 +707,15 @@ export default class Frame extends TypedEventEmitter implements IF const hasLoaderError = await this.navigationLoadersById[loaderId]?.navigationResolver; if (hasLoaderError instanceof Error) throw hasLoaderError; - if (!this.getActiveContextId(false)) { - await this.waitForDefaultContext(); - } + // if (!this.getActiveContextId(false)) { + // await this.waitForDefaultContext(); + // } + + await this.#framesManager.getExecutionContextId({ + frameId: this.id, + type: 'default', + refresh: this.shouldRefreshContextIds, + }); } public onLifecycleEvent(name: string, timestamp?: number, pageLoaderId?: string): void { @@ -786,18 +808,34 @@ export default class Frame extends TypedEventEmitter implements IF public async waitForActiveContextId(isolatedContext = true): Promise { if (!this.isAttached) throw new Error('Execution Context is not available in detached frame'); - const existing = this.getActiveContextId(isolatedContext); - if (existing) return existing; - - if (isolatedContext) { + if (isolatedContext && !this.hasCreatedIsolate) { + this.hasCreatedIsolate = true; const context = await this.createIsolatedWorld(); // give one task to set up await new Promise(setImmediate); return context; } - await this.waitForDefaultContext(); - return this.getActiveContextId(isolatedContext); + const contextId = await this.#framesManager.getExecutionContextId({ + frameId: this.id, + type: isolatedContext ? 'isolated' : 'default', + refresh: this.shouldRefreshContextIds, + }); + + this.shouldRefreshContextIds = false; + return contextId; + // const existing = this.getActiveContextId(isolatedContext); + // if (existing) return existing; + + // if (isolatedContext) { + // const context = await this.createIsolatedWorld(); + // // give one task to set up + // await new Promise(setImmediate); + // return context; + // } + + // await this.waitForDefaultContext(); + // return this.getActiveContextId(isolatedContext); } public canEvaluate(isolatedFromWebPageEnvironment: boolean): boolean { @@ -883,6 +921,11 @@ export default class Frame extends TypedEventEmitter implements IF if (this.getActiveContextId(false)) return; this.defaultContextCreated = new Resolvable(); + + await Promise.all([ + this.devtoolsSession.send('Runtime.enable'), + this.devtoolsSession.send('Runtime.disable'), + ]); // don't time out this event, we'll just wait for the page to shut down await this.defaultContextCreated.promise.catch(err => { if (err instanceof CanceledPromiseError) return; diff --git a/agent/main/lib/FramesManager.ts b/agent/main/lib/FramesManager.ts index 666b74306..a3340e3b3 100644 --- a/agent/main/lib/FramesManager.ts +++ b/agent/main/lib/FramesManager.ts @@ -9,6 +9,7 @@ import { CanceledPromiseError } from '@ulixee/commons/interfaces/IPendingWaitEve import IResourceMeta from '@ulixee/unblocked-specification/agent/net/IResourceMeta'; import { IPageEvents } from '@ulixee/unblocked-specification/agent/browser/IPage'; import { IDomPaintEvent } from '@ulixee/unblocked-specification/agent/browser/Location'; +import Resolvable from '@ulixee/commons/lib/Resolvable'; import DevtoolsSession from './DevtoolsSession'; import Frame from './Frame'; import NetworkManager from './NetworkManager'; @@ -48,6 +49,7 @@ export default class FramesManager extends TypedEventEmitter(); protected readonly logger: IBoundLog; @@ -70,6 +72,9 @@ export default class FramesManager extends TypedEventEmitter; + // Key = ${frameId}__${type} + private frameToContextId = new Map>(); + constructor(page: Page, devtoolsSession: DevtoolsSession) { super(); this.page = page; @@ -121,20 +126,25 @@ export default class FramesManager extends TypedEventEmitter any, isolateFromWebPageEnvironment?: boolean, devtoolsSession?: DevtoolsSession, - ): Promise { - const params: Protocol.Runtime.AddBindingRequest = { - name, - }; - if (isolateFromWebPageEnvironment) { - params.executionContextName = ISOLATED_WORLD; - } - devtoolsSession ??= this.devtoolsSession; - // add binding to every new context automatically - await devtoolsSession.send('Runtime.addBinding', params); - return this.events.on(devtoolsSession, 'Runtime.bindingCalled', async event => { + ): Promise { + return this.page.browserContext.websocketSession.on('message-received', async event => { if (event.name === name) { await this.isReady; - const frame = this.getFrameForExecutionContext(event.executionContextId); + const frame = this.framesById.get(event.id); if (frame) onCallback(event.payload, frame); } }); + // const params: Protocol.Runtime.AddBindingRequest = { + // name, + // }; + // if (isolateFromWebPageEnvironment) { + // params.executionContextName = ISOLATED_WORLD; + // } + // devtoolsSession ??= this.devtoolsSession; + // // add binding to every new context automatically + // await devtoolsSession.send('Runtime.addBinding', params); + // return this.events.on(devtoolsSession, 'Runtime.bindingCalled', async event => { + // if (event.name === name) { + // await this.isReady; + // const frame = this.getFrameForExecutionContext(event.executionContextId); + // if (frame) onCallback(event.payload, frame); + // } + // }); } public async addNewDocumentScript( @@ -237,6 +255,7 @@ export default class FramesManager extends TypedEventEmitter { devtoolsSession ??= this.devtoolsSession; + script = this.page.browserContext.websocketSession.injectWebsocketCallbackIntoScript(script); const installedScript = await devtoolsSession.send('Page.addScriptToEvaluateOnNewDocument', { source: script, worldName: installInIsolatedScope ? ISOLATED_WORLD : undefined, @@ -411,6 +430,64 @@ export default class FramesManager extends TypedEventEmitter { + await this.isReady; + const { context } = event; + const frameId = context.auxData.frameId as string; + const type = context.auxData.type as string; + + const isDefault = + context.name === '' && context.auxData.isDefault === true && type === 'default'; + const isIsolatedWorld = context.name === ISOLATED_WORLD && type === 'isolated'; + + // Not interested for now in others + if (!isDefault && !isIsolatedWorld) return; + + const key = `${frameId}__${isDefault ? 'default' : 'isolated'}`; + const storedId = this.frameToContextId.get(key); + if (storedId && !storedId.isResolved) { + storedId.resolve(context.id); + } else { + const resolvable = new Resolvable(); + resolvable.resolve(context.id); + this.frameToContextId.set(key, resolvable); + } + } + + // eslint-disable-next-line @typescript-eslint/member-ordering + public async getExecutionContextId(opts: { + frameId: string; + type: 'default' | 'isolated'; + refresh?: boolean; + }): Promise { + const refresh = async (): Promise => { + await Promise.all([ + this.devtoolsSession.send('Runtime.enable'), + this.devtoolsSession.send('Runtime.disable'), + ]).catch(() => undefined); + }; + + if (opts.refresh) { + this.frameToContextId.clear(); + void refresh(); + } + + const key = `${opts.frameId}__${opts.type}`; + const stored = this.frameToContextId.get(key); + if (stored) { + return stored.promise; + } + + const resolvable = new Resolvable(3e3); + this.frameToContextId.set(key, resolvable); + if (!opts.refresh) void refresh(); + const id = await resolvable.promise; + return id; + } + /////// FRAMES /////////////////////////////////////////////////////////////// private async onFrameNavigated( @@ -626,6 +703,12 @@ export default class FramesManager extends TypedEventEmitter[0]; + +export type WebsocketCallback = (name: string, payload: string) => void; + +const SCRIPT_PLACEHOLDER = ''; + +export class WebsocketSession extends TypedEventEmitter { + readonly isReady: Promise; + port: number; + secret = Math.random().toString(); + + // We store resolvable when we received websocket message before, receiving + // targetId, this way we can await this, and still trigger get proper ids. + clientIdToTargetId = new Map | string>(); + + private server: Server; + private wss: WebsocketServer; + + constructor() { + super(); + this.server = createServer(); + this.wss = new WebsocketServer({ noServer: true }); + } + + async initialize(): Promise { + const resolver = new Resolvable(3e3); + + this.server.on('error', resolver.reject); + this.server.listen(0, () => { + const address = this.server.address(); + if (typeof address === 'string') { + throw new Error('Unexpected server address format (string)'); + } + this.port = address.port; + resolver.resolve(); + }); + + this.server.on('upgrade', this.handleUpgrade.bind(this)); + this.wss.on('connection', this.handleConnection.bind(this)); + + return resolver.promise; + } + + close(): void { + this.wss.close(); + this.server.close(); + } + + registerWebsocketFrameId(url: string, frameId: string): void { + const parsed = new URL(url); + if (parsed.searchParams.get('secret') !== this.secret) return; + const clientId = parsed.searchParams.get('clientId'); + if (!clientId) return; + + const targetId = this.clientIdToTargetId.get(clientId); + if (targetId instanceof Resolvable) { + targetId.resolve(frameId); + } + this.clientIdToTargetId.set(clientId, frameId); + } + + injectWebsocketCallbackIntoScript(script: string): string { + // We could do this as a simple template script but this logic might get + // complex over time and we want typescript to be able to check proxyScript(); + const scriptBody = this.proxyScript + .toString() + // eslint-disable-next-line no-template-curly-in-string + .replaceAll('${this.port}', this.port.toString()) + // eslint-disable-next-line no-template-curly-in-string + .replaceAll('${this.secret}', this.secret) + // Use function otherwise replace will try todo some magic + .replace('SCRIPT_PLACEHOLDER', () => script); + + const wsScript = `(function ${scriptBody})();`; + return wsScript; + } + + private proxyScript(): void { + const clientId = Math.random(); + const url = `localhost:${this.port}?secret=${this.secret}&clientId=${clientId}`; + // This will signal to network manager we are trying to make websocket connection + // This is needed later to map clientId to frameId + // void fetch(`http://${url}`); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let callback: WebsocketCallback = (): void => {}; + try { + const socket = new WebSocket(`ws://${url}`); + socket.addEventListener('open', _event => { + callback = (name, payload) => { + socket.send(JSON.stringify({ name, payload })); + }; + }); + } catch {} + + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + SCRIPT_PLACEHOLDER; + } + + private handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): void { + const url = new URL(request.url, 'ws://localhost'); + // Close and dont send 403 so this acts as an invisible websocket server + if (url.searchParams.get('secret') !== this.secret) { + socket.destroy(); + } + + const clientId = url.searchParams.get('clientId'); + this.wss.handleUpgrade(request, socket as Socket, head, ws => { + this.wss.emit('connection', ws, request, clientId); + }); + } + + private handleConnection(ws: Websocket, request: IncomingMessage, clientId: string): void { + // TODO handle somehow or blow up + ws.on('error', console.error); + ws.on('message', this.handleMessage.bind(this, clientId)); + } + + private async handleMessage(clientId: string, data: Buffer): Promise { + const { name, payload } = JSON.parse(data.toString()); + let frameId = this.clientIdToTargetId.get(clientId); + if (!frameId) { + const resolvable = new Resolvable(); + this.clientIdToTargetId.set(clientId, resolvable); + frameId = await resolvable.promise; + } else if (frameId instanceof Resolvable) { + frameId = await frameId.promise; + } + + this.emit('message-received', { id: frameId, name, payload }); + } +} diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 000000000..a9d6887b2 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,139 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1725385033, + "owner": "cachix", + "repo": "devenv", + "rev": "b3d6c84d66618197b47a8baa8d00d1d9d283a93c", + "treeHash": "15100bcb1f1ab557ea9977fc90d515ff8f7d43b8", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1716977621, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "4267e705586473d3e5c8d50299e71503f16a6fb6", + "treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1725001927, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6e99f2a27d600612004fbd2c3282d614bfee6421", + "treeHash": "1e85443cc9f0ba302df2cf61cacb8014943e2d19", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_go_1_23": { + "locked": { + "lastModified": 1725099143, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5629520edecb69630a3f4d17d3d33fc96c13f6fe", + "treeHash": "0f720493d5a0538d0e60af5d2416efbe6ad4dfef", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5629520edecb69630a3f4d17d3d33fc96c13f6fe", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1724857454, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", + "treeHash": "ced5a8df7c554ce10bf26223c002f41b31aff034", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "nixpkgs_go_1_23": "nixpkgs_go_1_23", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 000000000..ad0e6c688 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,27 @@ +{ pkgs, lib, config, inputs, ... }: +let + pkgs_go_1_23 = import inputs.nixpkgs_go_1_23 { system = pkgs.stdenv.system; }; +in { + packages = [ + # TODO remove once we enable languages.go + pkgs_go_1_23.go_1_23 + # Needed for node gyp and pcap + pkgs.python311 + pkgs.python311Packages.libpcap + ]; + languages.javascript = rec { + package = pkgs.nodejs_20; + enable = true; + yarn = { + enable = true; + install.enable = true; + package = package.pkgs.yarn; + }; + }; + + # TODO enable this once 1.23 is merged upstream + # languages.go = { + # package = pkgs_go.go_1_23; + # enable = true; + # }; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 000000000..cb013f889 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,6 @@ +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + nixpkgs_go_1_23: + url: github:NixOS/nixpkgs?rev=5629520edecb69630a3f4d17d3d33fc96c13f6fe + diff --git a/plugins/default-browser-emulator/index.ts b/plugins/default-browser-emulator/index.ts index ea119b358..334f843e8 100644 --- a/plugins/default-browser-emulator/index.ts +++ b/plugins/default-browser-emulator/index.ts @@ -73,6 +73,7 @@ const allEnabled = Object.values(InjectedScript).reduce((acc, value) => { export const defaultConfig = { ...allEnabled, [InjectedScript.JSON_STRINGIFY]: false, + [InjectedScript.CONSOLE]: false, }; @UnblockedPluginClassDecorator diff --git a/plugins/default-browser-emulator/injected-scripts/Document.prototype.cookie.ts b/plugins/default-browser-emulator/injected-scripts/Document.prototype.cookie.ts index 34c665759..ab50ce57c 100644 --- a/plugins/default-browser-emulator/injected-scripts/Document.prototype.cookie.ts +++ b/plugins/default-browser-emulator/injected-scripts/Document.prototype.cookie.ts @@ -2,17 +2,12 @@ export type Args = { callbackName: string; }; const typedArgs = args as Args; -const triggerName = typedArgs.callbackName; - -if (!self[triggerName]) throw new Error('No cookie trigger'); -const cookieTrigger = (self[triggerName] as unknown as Function).bind(self); - -delete self[triggerName]; +const cookieCallback = callback.bind(null, typedArgs.callbackName); replaceSetter(Document.prototype, 'cookie', (target, thisArg, argArray) => { const cookie = argArray.at(0); if (cookie) { - cookieTrigger(JSON.stringify({ cookie, origin: self.location.origin })); + cookieCallback(JSON.stringify({ cookie, origin: self.location.origin })); } return ReflectCached.apply(target, thisArg, argArray!); }); diff --git a/plugins/default-browser-emulator/injected-scripts/_proxyUtils.ts b/plugins/default-browser-emulator/injected-scripts/_proxyUtils.ts index 55443a28f..1e6e92e89 100644 --- a/plugins/default-browser-emulator/injected-scripts/_proxyUtils.ts +++ b/plugins/default-browser-emulator/injected-scripts/_proxyUtils.ts @@ -2,6 +2,7 @@ declare let sourceUrl: string; declare let targetType: string | undefined; declare let args: any; +declare const callback: (name: string, data: string) => void; /* eslint-disable no-restricted-properties */ @@ -225,6 +226,27 @@ function runAndInjectProxyInStack(target: any, thisArg: any, argArray: any, prox return wrapper[name](); } +// const socket = new WebSocket('ws://localhost:9898'); +// // Connection opened +// socket.addEventListener('open', event => { +// socket.send('Hello Server!'); +// }); + +// try { +// const socket = new WebSocket('ws://localhost:9898'); +// socket.addEventListener('open', event => { +// socket.send('Hello Server!'); +// }); +// } catch (error) { +// console.log(error); +// } + +// try { +// const socket = new WebSocket('ws://localhost:9897'); +// } catch (error) { +// console.log(error); +// } + (function trackProxyInstances() { if (typeof self === 'undefined') return; const descriptor = ObjectCached.getOwnPropertyDescriptor(self, 'Proxy'); @@ -253,8 +275,6 @@ function runAndInjectProxyInStack(target: any, thisArg: any, argArray: any, prox toOriginalFn.set(Proxy, OriginalProxy); })(); - - /////// END TOSTRING ////////////////////////////////////////////////////////////////////////////////////////////////// // TODO remove this, but make sure we clean this up in: diff --git a/plugins/default-browser-emulator/lib/loadDomOverrides.ts b/plugins/default-browser-emulator/lib/loadDomOverrides.ts index 832fd63dc..0c5f5f66b 100644 --- a/plugins/default-browser-emulator/lib/loadDomOverrides.ts +++ b/plugins/default-browser-emulator/lib/loadDomOverrides.ts @@ -25,12 +25,13 @@ export default function loadDomOverrides( const domPolyfill = data.domPolyfill; + const consolePluginUsed = !!config[InjectedScript.CONSOLE]; domOverrides.addOverrideAndUseConfig( InjectedScript.ERROR, { removeInjectedLines: true, applyStackTraceLimit: true, - fixConsoleStack: true, + fixConsoleStack: consolePluginUsed, }, { registerWorkerOverride: true }, ); diff --git a/specification/agent/browser/IWebsocketSession.ts b/specification/agent/browser/IWebsocketSession.ts new file mode 100644 index 000000000..b437584b1 --- /dev/null +++ b/specification/agent/browser/IWebsocketSession.ts @@ -0,0 +1,3 @@ +export interface IWebsocketEvents { + 'message-received': { id: string; name: string, payload: string }; + } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 6595aa06d..620008bc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1762,7 +1762,7 @@ dependencies: "@babel/types" "^7.20.7" -"@types/better-sqlite3@^7.6.9": +"@types/better-sqlite3@^7.6.11": version "7.6.11" resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.11.tgz#95acf22fcf5577624eea202058e26ba239760b9f" integrity sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg== @@ -2229,10 +2229,10 @@ progress "^2.0.3" tar "^6.1.11" -"@ulixee/commons@2.0.0-alpha.29": - version "2.0.0-alpha.29" - resolved "https://registry.yarnpkg.com/@ulixee/commons/-/commons-2.0.0-alpha.29.tgz#522321a6bbb1d9b2a24b249e6703615b44eae5f4" - integrity sha512-8sPJlVhcP/YY6FE/iMsxP6sMTEdZOjmk575HAo0V76/Y04Kzq3XoKjAq6OggTrba2Xfr//aHn3Q3yubsZXlKcw== +"@ulixee/commons@2.0.0-alpha.28": + version "2.0.0-alpha.28" + resolved "https://registry.yarnpkg.com/@ulixee/commons/-/commons-2.0.0-alpha.28.tgz#52c3401fbbb25563ccb93d1e02980de83780f44d" + integrity sha512-/WXy+4yRr7GMCZYuYiqpiv4qEhfOrgDL8jZ85H4I+uyPIxgSTk8TWujHp5XkieE/4AbIwb8I4Q/lE+0TKKPGUQ== dependencies: "@jridgewell/trace-mapping" "^0.3.18" bech32 "^2.0.0" @@ -9305,11 +9305,6 @@ ws@^7.2.0: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^7.5.9: - version "7.5.10" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" - integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== - ws@^8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"