Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/lib/CoreTab.ts
1028 views
1
import ISessionMeta from '@secret-agent/interfaces/ISessionMeta';
2
import { IJsPath } from 'awaited-dom/base/AwaitedPath';
3
import IWaitForResourceOptions from '@secret-agent/interfaces/IWaitForResourceOptions';
4
import IResourceMeta from '@secret-agent/interfaces/IResourceMeta';
5
import IUserProfile from '@secret-agent/interfaces/IUserProfile';
6
import IConfigureSessionOptions from '@secret-agent/interfaces/IConfigureSessionOptions';
7
import IWaitForOptions from '@secret-agent/interfaces/IWaitForOptions';
8
import IScreenshotOptions from '@secret-agent/interfaces/IScreenshotOptions';
9
import IFrameMeta from '@secret-agent/interfaces/IFrameMeta';
10
import IFileChooserPrompt from '@secret-agent/interfaces/IFileChooserPrompt';
11
import CoreCommandQueue from './CoreCommandQueue';
12
import CoreEventHeap from './CoreEventHeap';
13
import IWaitForResourceFilter from '../interfaces/IWaitForResourceFilter';
14
import { createResource } from './Resource';
15
import IJsPathEventTarget from '../interfaces/IJsPathEventTarget';
16
import ConnectionToCore from '../connections/ConnectionToCore';
17
import CoreFrameEnvironment from './CoreFrameEnvironment';
18
import { createDialog } from './Dialog';
19
import CoreSession from './CoreSession';
20
21
export default class CoreTab implements IJsPathEventTarget {
22
public tabId: number;
23
public sessionId: string;
24
public commandQueue: CoreCommandQueue;
25
public eventHeap: CoreEventHeap;
26
public get mainFrameEnvironment(): CoreFrameEnvironment {
27
return this.frameEnvironmentsById.get(this.mainFrameId);
28
}
29
30
public frameEnvironmentsById = new Map<number, CoreFrameEnvironment>();
31
protected readonly meta: ISessionMeta & { sessionName: string };
32
private readonly connection: ConnectionToCore;
33
private readonly mainFrameId: number;
34
private readonly coreSession: CoreSession;
35
36
constructor(
37
meta: ISessionMeta & { sessionName: string },
38
connection: ConnectionToCore,
39
coreSession: CoreSession,
40
) {
41
const { tabId, sessionId, frameId, sessionName } = meta;
42
this.tabId = tabId;
43
this.sessionId = sessionId;
44
this.mainFrameId = frameId;
45
this.meta = {
46
sessionId,
47
tabId,
48
sessionName,
49
};
50
this.connection = connection;
51
this.commandQueue = new CoreCommandQueue(meta, connection, coreSession);
52
this.coreSession = coreSession;
53
this.eventHeap = new CoreEventHeap(this.meta, connection);
54
this.frameEnvironmentsById.set(
55
frameId,
56
new CoreFrameEnvironment(meta, null, this.commandQueue),
57
);
58
59
const resolvedThis = Promise.resolve(this);
60
this.eventHeap.registerEventInterceptors({
61
resource: createResource.bind(null, resolvedThis),
62
dialog: createDialog.bind(null, resolvedThis),
63
});
64
}
65
66
public async getCoreFrameEnvironments(): Promise<CoreFrameEnvironment[]> {
67
const frameMetas = await this.commandQueue.run<IFrameMeta[]>('Tab.getFrameEnvironments');
68
for (const frameMeta of frameMetas) {
69
this.getCoreFrameForMeta(frameMeta);
70
}
71
return [...this.frameEnvironmentsById.values()];
72
}
73
74
public getCoreFrameForMeta(frameMeta: IFrameMeta): CoreFrameEnvironment {
75
if (!this.frameEnvironmentsById.has(frameMeta.id)) {
76
const meta = { ...this.meta };
77
meta.frameId = frameMeta.id;
78
this.frameEnvironmentsById.set(
79
frameMeta.id,
80
new CoreFrameEnvironment(meta, frameMeta.parentFrameId, this.commandQueue),
81
);
82
}
83
return this.frameEnvironmentsById.get(frameMeta.id);
84
}
85
86
public async getResourceProperty<T = any>(id: number, propertyPath: string): Promise<T> {
87
return await this.commandQueue.runOutOfBand('Tab.getResourceProperty', id, propertyPath);
88
}
89
90
public async configure(options: IConfigureSessionOptions): Promise<void> {
91
await this.commandQueue.run('Tab.configure', options);
92
}
93
94
public async goto(href: string, timeoutMs?: number): Promise<IResourceMeta> {
95
return await this.commandQueue.run('Tab.goto', href, timeoutMs);
96
}
97
98
public async goBack(timeoutMs?: number): Promise<string> {
99
return await this.commandQueue.run('Tab.goBack', timeoutMs);
100
}
101
102
public async goForward(timeoutMs?: number): Promise<string> {
103
return await this.commandQueue.run('Tab.goForward', timeoutMs);
104
}
105
106
public async reload(timeoutMs?: number): Promise<IResourceMeta> {
107
return await this.commandQueue.run('Tab.reload', timeoutMs);
108
}
109
110
public async exportUserProfile(): Promise<IUserProfile> {
111
return await this.commandQueue.run('Session.exportUserProfile');
112
}
113
114
public async takeScreenshot(options: IScreenshotOptions): Promise<Buffer> {
115
return await this.commandQueue.run('Tab.takeScreenshot', options);
116
}
117
118
public async waitForFileChooser(options: IWaitForOptions): Promise<IFileChooserPrompt> {
119
return await this.commandQueue.run('Tab.waitForFileChooser', options);
120
}
121
122
public async waitForResource(
123
filter: Pick<IWaitForResourceFilter, 'url' | 'type'>,
124
opts: IWaitForResourceOptions,
125
): Promise<IResourceMeta[]> {
126
return await this.commandQueue.run('Tab.waitForResource', filter, opts);
127
}
128
129
public async waitForMillis(millis: number): Promise<void> {
130
await this.commandQueue.run('Tab.waitForMillis', millis);
131
}
132
133
public async waitForNewTab(opts: IWaitForOptions): Promise<CoreTab> {
134
const sessionMeta = await this.commandQueue.run<ISessionMeta>('Session.waitForNewTab', opts);
135
const session = this.connection.getSession(sessionMeta.sessionId);
136
session.addTab(sessionMeta);
137
return new CoreTab(
138
{ ...this.meta, tabId: sessionMeta.tabId },
139
this.connection,
140
this.coreSession,
141
);
142
}
143
144
public async focusTab(): Promise<void> {
145
await this.commandQueue.run('Tab.focus');
146
}
147
148
public async dismissDialog(accept: boolean, promptText?: string): Promise<void> {
149
// NOTE: since this can only be called from inside event handlers, it should not go into the queue or it can deadlock
150
await this.commandQueue.runOutOfBand('Tab.dismissDialog', accept, promptText);
151
}
152
153
public async addEventListener(
154
jsPath: IJsPath | null,
155
eventType: string,
156
listenerFn: (...args: any[]) => void,
157
options?,
158
): Promise<void> {
159
await this.eventHeap.addListener(jsPath, eventType, listenerFn, options);
160
}
161
162
public async removeEventListener(
163
jsPath: IJsPath | null,
164
eventType: string,
165
listenerFn: (...args: any[]) => void,
166
): Promise<void> {
167
await this.eventHeap.removeListener(jsPath, eventType, listenerFn);
168
}
169
170
public async flush(): Promise<void> {
171
for (const frame of this.frameEnvironmentsById.values()) {
172
await frame.commandQueue.flush();
173
}
174
await this.commandQueue.flush();
175
}
176
177
public async close(): Promise<void> {
178
await this.flush();
179
await this.commandQueue.run('Tab.close');
180
const session = this.connection.getSession(this.sessionId);
181
session?.removeTab(this);
182
}
183
}
184
185