Skip to content

Commit 65258a3

Browse files
committed
feat(plugins): add frames to execute js
1 parent 77d8e2e commit 65258a3

File tree

14 files changed

+172
-38
lines changed

14 files changed

+172
-38
lines changed

client/lib/CoreFrameEnvironment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@ export default class CoreFrameEnvironment {
1717
public frameId: number;
1818
public sessionId: string;
1919
public commandQueue: CoreCommandQueue;
20+
public parentFrameId: number;
2021

21-
constructor(meta: ISessionMeta & { sessionName: string }, commandQueue: CoreCommandQueue) {
22+
constructor(
23+
meta: ISessionMeta & { sessionName: string },
24+
parentFrameId: number,
25+
commandQueue: CoreCommandQueue,
26+
) {
2227
const { tabId, sessionId, frameId, sessionName } = meta;
2328
this.tabId = tabId;
2429
this.sessionId = sessionId;
2530
this.frameId = frameId;
31+
this.parentFrameId = parentFrameId;
2632
const queueMeta = {
2733
sessionId,
2834
tabId,

client/lib/CoreTab.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ export default class CoreTab implements IJsPathEventTarget {
5151
this.commandQueue = new CoreCommandQueue(meta, connection, coreSession);
5252
this.coreSession = coreSession;
5353
this.eventHeap = new CoreEventHeap(this.meta, connection);
54-
this.frameEnvironmentsById.set(frameId, new CoreFrameEnvironment(meta, this.commandQueue));
54+
this.frameEnvironmentsById.set(
55+
frameId,
56+
new CoreFrameEnvironment(meta, null, this.commandQueue),
57+
);
5558

5659
const resolvedThis = Promise.resolve(this);
5760
this.eventHeap.registerEventInterceptors({
@@ -74,7 +77,7 @@ export default class CoreTab implements IJsPathEventTarget {
7477
meta.frameId = frameMeta.id;
7578
this.frameEnvironmentsById.set(
7679
frameMeta.id,
77-
new CoreFrameEnvironment(meta, this.commandQueue),
80+
new CoreFrameEnvironment(meta, frameMeta.parentFrameId, this.commandQueue),
7881
);
7982
}
8083
return this.frameEnvironmentsById.get(frameMeta.id);

client/lib/FrameEnvironment.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ import { getComputedVisibilityFnName } from '@secret-agent/interfaces/jsPathFnNa
3030
import IAwaitedOptions from '../interfaces/IAwaitedOptions';
3131
import RequestGenerator, { getRequestIdOrUrl } from './Request';
3232
import CookieStorage, { createCookieStorage } from './CookieStorage';
33-
import Agent from './Agent';
33+
import Agent, { IState as IAgentState } from './Agent';
3434
import { delegate as AwaitedHandler, getAwaitedPathAsMethodArg } from './SetupAwaitedHandler';
3535
import CoreFrameEnvironment from './CoreFrameEnvironment';
3636
import Tab from './Tab';
3737
import { IMousePosition } from '../interfaces/IInteractions';
3838

3939
const { getState, setState } = StateMachine<FrameEnvironment, IState>();
40+
const agentState = StateMachine<Agent, IAgentState>();
4041
const awaitedPathState = StateMachine<
4142
any,
4243
{ awaitedPath: AwaitedPath; awaitedOptions: IAwaitedOptions }
@@ -67,6 +68,18 @@ export default class FrameEnvironment {
6768
tab,
6869
coreFrame,
6970
});
71+
async function sendToFrameEnvironment(pluginId: string, ...args: any[]): Promise<any> {
72+
return (await coreFrame).commandQueue.run(
73+
'FrameEnvironment.runPluginCommand',
74+
pluginId,
75+
args,
76+
);
77+
}
78+
79+
for (const clientPlugin of agentState.getState(secretAgent).clientPlugins) {
80+
if (clientPlugin.onFrameEnvironment)
81+
clientPlugin.onFrameEnvironment(secretAgent, this, sendToFrameEnvironment);
82+
}
7083
}
7184

7285
public get isMainFrame(): Promise<boolean> {
@@ -77,6 +90,21 @@ export default class FrameEnvironment {
7790
return getCoreFrameEnvironment(this).then(x => x.frameId);
7891
}
7992

93+
public get children(): Promise<FrameEnvironment[]> {
94+
return getState(this).tab.frameEnvironments.then(async frames => {
95+
const frameId = await this.frameId;
96+
97+
const childFrames: FrameEnvironment[] = [];
98+
for (const frame of frames) {
99+
const parentFrameId = await frame.parentFrameId;
100+
if (parentFrameId === frameId) {
101+
childFrames.push(frame);
102+
}
103+
}
104+
return childFrames;
105+
});
106+
}
107+
80108
public get url(): Promise<string> {
81109
return getCoreFrameEnvironment(this).then(x => x.getUrl());
82110
}
@@ -88,9 +116,7 @@ export default class FrameEnvironment {
88116
}
89117

90118
public get parentFrameId(): Promise<number | null> {
91-
return getCoreFrameEnvironment(this)
92-
.then(x => x.getFrameMeta())
93-
.then(x => x.parentFrameId);
119+
return getCoreFrameEnvironment(this).then(x => x.parentFrameId);
94120
}
95121

96122
public get cookieStorage(): CookieStorage {

core/lib/CorePlugins.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,13 @@ export default class CorePlugins implements ICorePlugins {
221221
): Promise<any> {
222222
const plugin = this.instanceById[toPluginId];
223223
if (plugin && plugin.onClientCommand) {
224-
return await plugin.onClientCommand(commandMeta, ...args);
224+
return await plugin.onClientCommand(
225+
{
226+
puppetPage: commandMeta.puppetPage,
227+
puppetFrame: commandMeta.puppetFrame,
228+
},
229+
...args,
230+
);
225231
}
226232
this.logger.warn(`Plugin (${toPluginId}) could not be found for command`);
227233
}

core/lib/FrameEnvironment.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export default class FrameEnvironment {
137137
this.getLocationHref,
138138
this.interact,
139139
this.removeCookie,
140+
this.runPluginCommand,
140141
this.setCookie,
141142
this.setFileInputFiles,
142143
this.waitForElement,
@@ -351,6 +352,14 @@ b) Use the UserProfile feature to set cookies for 1 or more domains before they'
351352
}
352353
}
353354

355+
public async runPluginCommand(toPluginId: string, args: any[]): Promise<any> {
356+
const commandMeta = {
357+
puppetPage: this.tab.puppetPage,
358+
puppetFrame: this.puppetFrame,
359+
};
360+
return await this.session.plugins.onPluginCommand(toPluginId, commandMeta, args);
361+
}
362+
354363
public waitForElement(jsPath: IJsPath, options?: IWaitForElementOptions): Promise<boolean> {
355364
return this.waitForDom(jsPath, options);
356365
}

core/lib/Tab.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ export default class Tab extends TypedEventEmitter<ITabEventParams> {
565565
public async runPluginCommand(toPluginId, args: any[]): Promise<any> {
566566
const commandMeta = {
567567
puppetPage: this.puppetPage,
568+
puppetFrame: this.mainFrameEnvironment?.puppetFrame,
568569
};
569570
return await this.session.plugins.onPluginCommand(toPluginId, commandMeta, args);
570571
}

interfaces/IClientPlugin.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { PluginTypes } from './IPluginTypes';
2-
import type { Agent, Tab } from '../client';
2+
import type { Agent, Tab, FrameEnvironment } from '../client';
33

44
export default interface IClientPlugin {
55
id: string;
66

77
onAgent?(agent: Agent, sendToCore: ISendToCoreFn): void;
88
onTab?(agent: Agent, tab: Tab, sendToCore: ISendToCoreFn): void;
9+
onFrameEnvironment?(
10+
agent: Agent,
11+
frameEnvironment: FrameEnvironment,
12+
sendToCore: ISendToCoreFn,
13+
): void;
914
}
1015

1116
export interface IClientPluginClass {

interfaces/ICorePlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import IDeviceProfile from './IDeviceProfile';
1616
import IHttp2ConnectSettings from './IHttp2ConnectSettings';
1717
import IHttpSocketAgent from './IHttpSocketAgent';
1818
import ISessionCreateOptions from './ISessionCreateOptions';
19+
import { IPuppetFrame } from './IPuppetFrame';
1920

2021
export default interface ICorePlugin
2122
extends ICorePluginMethods,
@@ -36,6 +37,7 @@ export interface ICorePluginMethods {
3637

3738
export interface IOnClientCommandMeta {
3839
puppetPage: IPuppetPage;
40+
puppetFrame: IPuppetFrame;
3941
}
4042

4143
// eslint-disable-next-line @typescript-eslint/no-unused-vars

plugins/execute-js/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import ClientPlugin from './lib/ClientPlugin';
55
import CorePlugin from './lib/CorePlugin';
66

77
type ExecuteJsPluginAdditions = {
8-
executeJs(fn: string | ((...args: any[]) => any), ...args: any[]);
8+
executeJs<T extends any[]>(fn: string | ((...args: T) => any), ...args: T);
99
};
1010

1111
declare module '@secret-agent/client/lib/extendables' {
1212
interface Agent extends ExecuteJsPluginAdditions {}
1313
interface Tab extends ExecuteJsPluginAdditions {}
14+
interface FrameEnvironment extends ExecuteJsPluginAdditions {}
1415
}
1516

1617
export { ClientPlugin, CorePlugin };
Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ISendToCoreFn } from '@secret-agent/interfaces/IClientPlugin';
22
import ClientPlugin from '@secret-agent/plugin-utils/lib/ClientPlugin';
3-
import type { Agent, Tab } from 'secret-agent';
3+
import type { Agent, Tab, FrameEnvironment } from 'secret-agent';
4+
import { IExecuteJsArgs } from './IExecuteJsArgs';
45

56
const { name: pluginId } = require('../package.json');
67

@@ -9,29 +10,39 @@ export default class ExecuteJsClientPlugin extends ClientPlugin {
910
public static coreDependencyIds = [pluginId];
1011

1112
public onAgent(agent: Agent, sendToCore: ISendToCoreFn) {
12-
agent.executeJs = (fn, ...args) => {
13-
return this.executeJs(fn, sendToCore, args);
14-
};
13+
agent.executeJs = this.executeJs.bind(this, sendToCore);
1514
}
1615

1716
public onTab(agent: Agent, tab: Tab, sendToCore: ISendToCoreFn) {
18-
tab.executeJs = (fn, ...args) => {
19-
return this.executeJs(fn, sendToCore, args);
20-
};
17+
tab.executeJs = this.executeJs.bind(this, sendToCore);
18+
}
19+
20+
public onFrameEnvironment(
21+
agent: Agent,
22+
frameEnvironment: FrameEnvironment,
23+
sendToCore: ISendToCoreFn,
24+
) {
25+
frameEnvironment.executeJs = this.executeJs.bind(this, sendToCore);
2126
}
2227

2328
// PRIVATE
2429

25-
private executeJs(fn, sendToCore, args): Promise<any> {
26-
let fnName;
27-
let fnSerialized;
28-
if (typeof fn === 'string') {
29-
fnName = '';
30-
fnSerialized = fn;
31-
} else {
30+
private executeJs<T extends any[]>(
31+
sendToCore: ISendToCoreFn,
32+
fn: string | ((...args: T) => any),
33+
...args: T
34+
): Promise<any> {
35+
let fnName = '';
36+
let fnSerialized = fn as string;
37+
if (typeof fn !== 'string') {
3238
fnName = fn.name;
3339
fnSerialized = `(${fn.toString()})(${JSON.stringify(args).slice(1, -1)});`;
3440
}
35-
return sendToCore(pluginId, fnName, fnSerialized, args);
41+
return sendToCore(pluginId, <IExecuteJsArgs>{
42+
fnName,
43+
fnSerialized,
44+
args,
45+
isolateFromWebPageEnvironment: false,
46+
});
3647
}
3748
}

0 commit comments

Comments
 (0)