Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/plugins/default-browser-emulator/lib/BrowserData.ts
1029 views
1
import * as Fs from 'fs';
2
import IUserAgentOption from '@secret-agent/interfaces/IUserAgentOption';
3
import IBrowserData, {
4
IDataBrowserConfig,
5
IDataClienthello,
6
IDataCodecs,
7
IDataDomPolyfill,
8
IDataHeaders,
9
IDataHttp2Settings,
10
IDataWindowChrome,
11
IDataWindowFraming,
12
IDataWindowNavigator,
13
} from '../interfaces/IBrowserData';
14
import DataLoader, { loadData } from './DataLoader';
15
import getLocalOperatingSystemMeta from './utils/getLocalOperatingSystemMeta';
16
import { convertMacOsVersionString, findClosestVersionMatch } from './VersionUtils';
17
18
const localOsMeta = getLocalOperatingSystemMeta();
19
20
export default class BrowserData implements IBrowserData {
21
private readonly dataLoader: DataLoader;
22
private readonly baseDataDir: string;
23
private readonly osDataDir: string;
24
private domPolyfillFilename: string;
25
26
constructor(dataLoader: DataLoader, userAgentOption: IUserAgentOption) {
27
const browserId = createBrowserId(userAgentOption);
28
const os = getOperatingSystemParts(userAgentOption);
29
this.dataLoader = dataLoader;
30
this.baseDataDir = `${dataLoader.dataDir}/as-${browserId}`;
31
this.osDataDir = `${this.baseDataDir}/as-${os.name}-${os.version}`;
32
if (!this.dataLoader.isSupportedEmulator(this.osDataDir)) {
33
const otherVersions = this.dataLoader.getBrowserOperatingSystemVersions(browserId, os.name);
34
if (!otherVersions.length) {
35
throw new Error(`${browserId} has no emulation data for ${os.name}`);
36
}
37
38
const closestVersionMatch = findClosestVersionMatch(os.version, otherVersions);
39
this.osDataDir = `${this.baseDataDir}/as-${os.name}-${closestVersionMatch}`;
40
}
41
}
42
43
public get pkg(): any {
44
return this.dataLoader.pkg;
45
}
46
47
public get headers(): IDataHeaders {
48
return loadData(`${this.baseDataDir}/headers.json`);
49
}
50
51
public get windowBaseFraming(): IDataWindowFraming {
52
return loadData(`${this.baseDataDir}/window-base-framing.json`);
53
}
54
55
public get browserConfig(): IDataBrowserConfig {
56
return loadData(`${this.baseDataDir}/config.json`);
57
}
58
59
public get clienthello(): IDataClienthello {
60
return loadData(`${this.osDataDir}/clienthello.json`);
61
}
62
63
public get codecs(): IDataCodecs {
64
return loadData(`${this.osDataDir}/codecs.json`);
65
}
66
67
public get http2Settings(): IDataHttp2Settings {
68
return loadData(`${this.osDataDir}/http2-session.json`);
69
}
70
71
public get windowChrome(): IDataWindowChrome {
72
try {
73
return loadData(`${this.osDataDir}/window-chrome.json`);
74
} catch (e) {
75
return undefined;
76
}
77
}
78
79
public get windowFraming(): IDataWindowFraming {
80
return loadData(`${this.osDataDir}/window-framing.json`);
81
}
82
83
public get windowNavigator(): IDataWindowNavigator {
84
return loadData(`${this.osDataDir}/window-navigator.json`);
85
}
86
87
public get domPolyfill(): IDataDomPolyfill {
88
try {
89
this.domPolyfillFilename ??= extractPolyfillFilename(this.osDataDir);
90
return loadData(`${this.osDataDir}/${this.domPolyfillFilename}`);
91
} catch (e) {
92
return undefined;
93
}
94
}
95
}
96
97
const polyfillFilesByDatadir: {
98
[dataDir: string]: { [osName: string]: { [osVersion: string]: string } };
99
} = {};
100
101
function extractPolyfillFilename(dataDir: string) {
102
let filenameMap = polyfillFilesByDatadir[dataDir];
103
if (!filenameMap) {
104
filenameMap = {};
105
polyfillFilesByDatadir[dataDir] = filenameMap;
106
for (const filename of Fs.readdirSync(dataDir)) {
107
const matches = filename.match(/^dom-polyfill-when-runtime-([a-z-]+)(-([0-9-]+))?.json$/);
108
if (!matches) continue;
109
110
const [osName, _, osVersion] = matches.slice(1); // eslint-disable-line @typescript-eslint/naming-convention,@typescript-eslint/no-unused-vars
111
filenameMap[osName] = filenameMap[osName] || {};
112
filenameMap[osName][osVersion || 'ALL'] = filename;
113
}
114
}
115
116
if (!filenameMap[localOsMeta.name]) {
117
throw new Error(`Your OS (${localOsMeta.name}) is not supported by this emulator.`);
118
}
119
120
const versionMatch = findClosestVersionMatch(
121
localOsMeta.version,
122
Object.keys(filenameMap[localOsMeta.name]),
123
);
124
125
if (!versionMatch) {
126
throw new Error(
127
`Emulator could not find a version match for ${localOsMeta.name} ${localOsMeta.version}`,
128
);
129
}
130
131
return filenameMap[localOsMeta.name][versionMatch];
132
}
133
134
function createBrowserId(userAgentOption: IUserAgentOption) {
135
const { browserName, browserVersion } = userAgentOption;
136
return [browserName, browserVersion.major, browserVersion.minor].filter(x => x).join('-');
137
}
138
139
function getOperatingSystemParts(userAgentOption: IUserAgentOption) {
140
const { operatingSystemName: name, operatingSystemVersion: version } = userAgentOption;
141
let { major, minor } = version;
142
143
if (name.startsWith('mac')) {
144
[major, minor] = convertMacOsVersionString([major, minor].filter(x => x).join('.')).split('.');
145
} else if (name.startsWith('win') && version.minor === '0') {
146
minor = null;
147
}
148
const finalVersion = [major, minor].filter(x => x).join('-');
149
return { name, version: finalVersion };
150
}
151
152