Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/lib/FrozenFrameEnvironment.ts
1028 views
1
import inspectInstanceProperties from 'awaited-dom/base/inspectInstanceProperties';
2
import * as Util from 'util';
3
import StateMachine from 'awaited-dom/base/StateMachine';
4
import AwaitedPath from 'awaited-dom/base/AwaitedPath';
5
import { IRequestInit } from 'awaited-dom/base/interfaces/official';
6
import SuperDocument from 'awaited-dom/impl/super-klasses/SuperDocument';
7
import Storage from 'awaited-dom/impl/official-klasses/Storage';
8
import CSSStyleDeclaration from 'awaited-dom/impl/official-klasses/CSSStyleDeclaration';
9
import {
10
createCSSStyleDeclaration,
11
createResponse,
12
createStorage,
13
createSuperDocument,
14
} from 'awaited-dom/impl/create';
15
import Request from 'awaited-dom/impl/official-klasses/Request';
16
import Response from 'awaited-dom/impl/official-klasses/Response';
17
import { IElementIsolate, INodeIsolate } from 'awaited-dom/base/interfaces/isolate';
18
import { INodeVisibility } from '@secret-agent/interfaces/INodeVisibility';
19
import {
20
getComputedStyleFnName,
21
getComputedVisibilityFnName,
22
} from '@secret-agent/interfaces/jsPathFnNames';
23
import IJsPathResult from '@secret-agent/interfaces/IJsPathResult';
24
import IAwaitedOptions from '../interfaces/IAwaitedOptions';
25
import RequestGenerator, { getRequestIdOrUrl } from './Request';
26
import CookieStorage, { createCookieStorage } from './CookieStorage';
27
import Agent from './Agent';
28
import CoreFrameEnvironment from './CoreFrameEnvironment';
29
import FrozenTab from './FrozenTab';
30
import * as AwaitedHandler from './SetupAwaitedHandler';
31
32
const { getState, setState } = StateMachine<FrozenFrameEnvironment, IState>();
33
const awaitedPathState = StateMachine<
34
any,
35
{ awaitedPath: AwaitedPath; awaitedOptions: IAwaitedOptions }
36
>();
37
38
export interface IState {
39
secretAgent: Agent;
40
tab: FrozenTab;
41
coreFrame: Promise<CoreFrameEnvironment>;
42
prefetchedJsPaths: Promise<Map<string, IJsPathResult>>;
43
}
44
45
const propertyKeys: (keyof FrozenFrameEnvironment)[] = [
46
'frameId',
47
'url',
48
'name',
49
'parentFrameId',
50
'cookieStorage',
51
'localStorage',
52
'sessionStorage',
53
'document',
54
'Request',
55
];
56
57
export default class FrozenFrameEnvironment {
58
constructor(
59
secretAgent: Agent,
60
tab: FrozenTab,
61
coreFrame: Promise<CoreFrameEnvironment>,
62
prefetchedJsPaths: Promise<IJsPathResult[]>,
63
) {
64
setState(this, {
65
secretAgent,
66
tab,
67
coreFrame,
68
prefetchedJsPaths: prefetchedJsPaths.then(x => {
69
const resultMap = new Map<string, IJsPathResult>();
70
for (let i = 0; i < x.length; i += 1) {
71
const result = x[i];
72
result.index = i;
73
resultMap.set(JSON.stringify(result.jsPath), result);
74
}
75
return resultMap;
76
}),
77
});
78
}
79
80
public get isMainFrame(): Promise<boolean> {
81
return this.parentFrameId.then(x => !x);
82
}
83
84
public get frameId(): Promise<number> {
85
return getCoreFrameEnvironment(this).then(x => x.frameId);
86
}
87
88
public get url(): Promise<string> {
89
return getCoreFrameEnvironment(this).then(x => x.getUrl());
90
}
91
92
public get name(): Promise<string> {
93
return getCoreFrameEnvironment(this)
94
.then(x => x.getFrameMeta())
95
.then(x => x.name);
96
}
97
98
public get parentFrameId(): Promise<number | null> {
99
return getCoreFrameEnvironment(this)
100
.then(x => x.getFrameMeta())
101
.then(x => x.parentFrameId);
102
}
103
104
public get cookieStorage(): CookieStorage {
105
return createCookieStorage(getCoreFrameEnvironment(this));
106
}
107
108
public get document(): SuperDocument {
109
const awaitedPath = new AwaitedPath(null, 'document');
110
const awaitedOptions = { ...getState(this) };
111
return createSuperDocument<IAwaitedOptions>(awaitedPath, awaitedOptions) as SuperDocument;
112
}
113
114
public get localStorage(): Storage {
115
const awaitedPath = new AwaitedPath(null, 'localStorage');
116
const awaitedOptions = { ...getState(this) };
117
return createStorage<IAwaitedOptions>(awaitedPath, awaitedOptions) as Storage;
118
}
119
120
public get sessionStorage(): Storage {
121
const awaitedPath = new AwaitedPath(null, 'sessionStorage');
122
const awaitedOptions = { ...getState(this) };
123
return createStorage<IAwaitedOptions>(awaitedPath, awaitedOptions) as Storage;
124
}
125
126
public get Request(): typeof Request {
127
return RequestGenerator(getCoreFrameEnvironment(this));
128
}
129
130
// METHODS
131
132
public async fetch(request: Request | string, init?: IRequestInit): Promise<Response> {
133
const requestInput = await getRequestIdOrUrl(request);
134
const coreFrame = await getCoreFrameEnvironment(this);
135
const nodePointer = await coreFrame.fetch(requestInput, init);
136
137
const awaitedPath = new AwaitedPath(null).withNodeId(null, nodePointer.id);
138
return createResponse(awaitedPath, { ...getState(this) });
139
}
140
141
public async getComputedStyle(
142
element: IElementIsolate,
143
pseudoElement?: string,
144
): Promise<CSSStyleDeclaration & { [style: string]: string }> {
145
const { awaitedPath, coreFrame, awaitedOptions } = await AwaitedHandler.getAwaitedState(
146
awaitedPathState,
147
element,
148
);
149
const newPath = awaitedPath.addMethod(element, getComputedStyleFnName, pseudoElement);
150
const result = await AwaitedHandler.execJsPath<Record<string, string>>(
151
coreFrame,
152
awaitedOptions,
153
newPath.toJSON(),
154
);
155
const declaration = createCSSStyleDeclaration<IAwaitedOptions>(newPath, awaitedOptions);
156
const attributes = AwaitedHandler.cleanResult(
157
awaitedPathState,
158
declaration,
159
result,
160
new Error().stack,
161
);
162
Object.assign(declaration, attributes);
163
return declaration;
164
}
165
166
public async getComputedVisibility(node: INodeIsolate): Promise<INodeVisibility> {
167
if (!node) return { isVisible: false, nodeExists: false };
168
return await AwaitedHandler.delegate.runMethod<INodeVisibility, INodeIsolate>(
169
awaitedPathState,
170
node,
171
getComputedVisibilityFnName,
172
[],
173
);
174
}
175
176
// @deprecated 2021-04-30: Replaced with getComputedVisibility
177
public async isElementVisible(element: IElementIsolate): Promise<boolean> {
178
return await this.getComputedVisibility(element as any).then(x => x.isVisible);
179
}
180
181
public async getJsValue<T>(path: string): Promise<T> {
182
const coreFrame = await getCoreFrameEnvironment(this);
183
return coreFrame.getJsValue<T>(path);
184
}
185
186
public toJSON(): any {
187
// return empty so we can avoid infinite "stringifying" in jest
188
return {
189
type: this.constructor.name,
190
};
191
}
192
193
public [Util.inspect.custom](): any {
194
return inspectInstanceProperties(this, propertyKeys as any);
195
}
196
}
197
198
export function getFrameState(object: any): IState {
199
return getState(object);
200
}
201
202
export function getCoreFrameEnvironment(
203
frameEnvironment: FrozenFrameEnvironment,
204
): Promise<CoreFrameEnvironment> {
205
return getState(frameEnvironment).coreFrame;
206
}
207
208