appium-chromedriver 5.2.17 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +18 -18
- package/build/index.js.map +1 -0
- package/build/lib/chromedriver.d.ts +108 -0
- package/build/lib/chromedriver.d.ts.map +1 -0
- package/build/lib/chromedriver.js +685 -559
- package/build/lib/chromedriver.js.map +1 -1
- package/build/lib/install.d.ts +3 -0
- package/build/lib/install.d.ts.map +1 -0
- package/build/lib/install.js +39 -32
- package/build/lib/install.js.map +1 -1
- package/build/lib/protocol-helpers.d.ts +15 -0
- package/build/lib/protocol-helpers.d.ts.map +1 -0
- package/build/lib/protocol-helpers.js +38 -21
- package/build/lib/protocol-helpers.js.map +1 -1
- package/build/lib/storage-client.d.ts +119 -0
- package/build/lib/storage-client.d.ts.map +1 -0
- package/build/lib/storage-client.js +393 -274
- package/build/lib/storage-client.js.map +1 -1
- package/build/lib/types.d.ts +101 -0
- package/build/lib/types.d.ts.map +1 -0
- package/build/lib/types.js +3 -0
- package/build/lib/types.js.map +1 -0
- package/build/lib/utils.d.ts +52 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +117 -78
- package/build/lib/utils.js.map +1 -1
- package/index.js +9 -0
- package/lib/chromedriver.js +300 -162
- package/lib/install.js +8 -1
- package/lib/protocol-helpers.js +17 -1
- package/lib/storage-client.js +165 -129
- package/lib/types.ts +101 -0
- package/lib/utils.js +86 -42
- package/package.json +34 -30
- package/tsconfig.json +11 -0
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require("
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var _storageClient = _interopRequireDefault(require("./storage-client"));
|
|
22
|
-
var _protocolHelpers = require("./protocol-helpers");
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Chromedriver = void 0;
|
|
7
|
+
const events_1 = __importDefault(require("events"));
|
|
8
|
+
const base_driver_1 = require("@appium/base-driver");
|
|
9
|
+
const child_process_1 = __importDefault(require("child_process"));
|
|
10
|
+
const support_1 = require("@appium/support");
|
|
11
|
+
const asyncbox_1 = require("asyncbox");
|
|
12
|
+
const teen_process_1 = require("teen_process");
|
|
13
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const semver_1 = __importDefault(require("semver"));
|
|
16
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const compare_versions_1 = require("compare-versions");
|
|
19
|
+
const storage_client_1 = __importDefault(require("./storage-client"));
|
|
20
|
+
const protocol_helpers_1 = require("./protocol-helpers");
|
|
23
21
|
const NEW_CD_VERSION_FORMAT_MAJOR_VERSION = 73;
|
|
24
22
|
const DEFAULT_HOST = '127.0.0.1';
|
|
25
23
|
const MIN_CD_VERSION_WITH_W3C_SUPPORT = 75;
|
|
@@ -29,550 +27,676 @@ const WEBVIEW_SHELL_BUNDLE_ID = 'org.chromium.webview_shell';
|
|
|
29
27
|
const WEBVIEW_BUNDLE_IDS = ['com.google.android.webview', 'com.android.webview'];
|
|
30
28
|
const VERSION_PATTERN = /([\d.]+)/;
|
|
31
29
|
const CD_VERSION_TIMEOUT = 5000;
|
|
32
|
-
class Chromedriver extends
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.useSystemExecutable = useSystemExecutable;
|
|
58
|
-
this.chromedriver = executable;
|
|
59
|
-
this.executableDir = executableDir;
|
|
60
|
-
this.mappingPath = mappingPath;
|
|
61
|
-
this.bundleId = bundleId;
|
|
62
|
-
this.executableVerified = false;
|
|
63
|
-
this.state = Chromedriver.STATE_STOPPED;
|
|
64
|
-
this.jwproxy = new _baseDriver.JWProxy({
|
|
65
|
-
server: this.proxyHost,
|
|
66
|
-
port: this.proxyPort,
|
|
67
|
-
log: this._log
|
|
68
|
-
});
|
|
69
|
-
this.verbose = verbose;
|
|
70
|
-
this.logPath = logPath;
|
|
71
|
-
this.disableBuildCheck = !!disableBuildCheck;
|
|
72
|
-
this.storageClient = isAutodownloadEnabled ? new _storageClient.default({
|
|
73
|
-
chromedriverDir: this.executableDir
|
|
74
|
-
}) : null;
|
|
75
|
-
this.details = details;
|
|
76
|
-
this.capabilities = {};
|
|
77
|
-
this.desiredProtocol = _baseDriver.PROTOCOLS.MJSONWP;
|
|
78
|
-
}
|
|
79
|
-
get log() {
|
|
80
|
-
return this._log;
|
|
81
|
-
}
|
|
82
|
-
async getDriversMapping() {
|
|
83
|
-
let mapping = _lodash.default.cloneDeep(_utils.CHROMEDRIVER_CHROME_MAPPING);
|
|
84
|
-
if (this.mappingPath) {
|
|
85
|
-
this.log.debug(`Attempting to use Chromedriver->Chrome mapping from '${this.mappingPath}'`);
|
|
86
|
-
if (!(await _support.fs.exists(this.mappingPath))) {
|
|
87
|
-
this.log.warn(`No file found at '${this.mappingPath}'`);
|
|
88
|
-
this.log.info('Defaulting to the static Chromedriver->Chrome mapping');
|
|
89
|
-
} else {
|
|
90
|
-
try {
|
|
91
|
-
mapping = JSON.parse(await _support.fs.readFile(this.mappingPath, 'utf8'));
|
|
92
|
-
} catch (err) {
|
|
93
|
-
this.log.warn(`Error parsing mapping from '${this.mappingPath}': ${err.message}`);
|
|
94
|
-
this.log.info('Defaulting to the static Chromedriver->Chrome mapping');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
} else {
|
|
98
|
-
this.log.debug('Using the static Chromedriver->Chrome mapping');
|
|
99
|
-
}
|
|
100
|
-
for (const [cdVersion, chromeVersion] of _lodash.default.toPairs(mapping)) {
|
|
101
|
-
const coercedVersion = _semver.default.coerce(chromeVersion);
|
|
102
|
-
if (coercedVersion) {
|
|
103
|
-
mapping[cdVersion] = coercedVersion.version;
|
|
104
|
-
} else {
|
|
105
|
-
this.log.info(`'${chromeVersion}' is not a valid version number. Skipping it`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return mapping;
|
|
109
|
-
}
|
|
110
|
-
async getChromedrivers(mapping) {
|
|
111
|
-
const executables = await _support.fs.glob('*', {
|
|
112
|
-
cwd: this.executableDir,
|
|
113
|
-
strict: false,
|
|
114
|
-
nodir: true,
|
|
115
|
-
absolute: true
|
|
116
|
-
});
|
|
117
|
-
this.log.debug(`Found ${_support.util.pluralize('executable', executables.length, true)} ` + `in '${this.executableDir}'`);
|
|
118
|
-
const cds = (await (0, _asyncbox.asyncmap)(executables, async executable => {
|
|
119
|
-
const logError = ({
|
|
120
|
-
message,
|
|
121
|
-
stdout = null,
|
|
122
|
-
stderr = null
|
|
123
|
-
}) => {
|
|
124
|
-
let errMsg = `Cannot retrieve version number from '${_path.default.basename(executable)}' Chromedriver binary. ` + `Make sure it returns a valid version string in response to '--version' command line argument. ${message}`;
|
|
125
|
-
if (stdout) {
|
|
126
|
-
errMsg += `\nStdout: ${stdout}`;
|
|
127
|
-
}
|
|
128
|
-
if (stderr) {
|
|
129
|
-
errMsg += `\nStderr: ${stderr}`;
|
|
130
|
-
}
|
|
131
|
-
this.log.warn(errMsg);
|
|
132
|
-
return null;
|
|
133
|
-
};
|
|
134
|
-
let stdout;
|
|
135
|
-
let stderr;
|
|
136
|
-
try {
|
|
137
|
-
({
|
|
138
|
-
stdout,
|
|
139
|
-
stderr
|
|
140
|
-
} = await (0, _teen_process.exec)(executable, ['--version'], {
|
|
141
|
-
timeout: CD_VERSION_TIMEOUT
|
|
142
|
-
}));
|
|
143
|
-
} catch (err) {
|
|
144
|
-
if (!(err.message || '').includes('timed out') && !(err.stdout || '').includes('Starting ChromeDriver')) {
|
|
145
|
-
return logError(err);
|
|
146
|
-
}
|
|
147
|
-
stdout = err.stdout;
|
|
148
|
-
}
|
|
149
|
-
const match = /ChromeDriver\s+\(?v?([\d.]+)\)?/i.exec(stdout);
|
|
150
|
-
if (!match) {
|
|
151
|
-
return logError({
|
|
152
|
-
message: 'Cannot parse the version string',
|
|
153
|
-
stdout,
|
|
154
|
-
stderr
|
|
30
|
+
class Chromedriver extends events_1.default.EventEmitter {
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {import('./types').ChromedriverOpts} args
|
|
34
|
+
*/
|
|
35
|
+
constructor(args = {}) {
|
|
36
|
+
super();
|
|
37
|
+
const { host = DEFAULT_HOST, port = DEFAULT_PORT, useSystemExecutable = false, executable, executableDir = (0, utils_1.getChromedriverDir)(), bundleId, mappingPath, cmdArgs, adb, verbose, logPath, disableBuildCheck, details, isAutodownloadEnabled = false, } = args;
|
|
38
|
+
this._log = support_1.logger.getLogger((0, utils_1.generateLogPrefix)(this));
|
|
39
|
+
this.proxyHost = host;
|
|
40
|
+
this.proxyPort = port;
|
|
41
|
+
this.adb = adb;
|
|
42
|
+
this.cmdArgs = cmdArgs;
|
|
43
|
+
this.proc = null;
|
|
44
|
+
this.useSystemExecutable = useSystemExecutable;
|
|
45
|
+
this.chromedriver = executable;
|
|
46
|
+
this.executableDir = executableDir;
|
|
47
|
+
this.mappingPath = mappingPath;
|
|
48
|
+
this.bundleId = bundleId;
|
|
49
|
+
this.executableVerified = false;
|
|
50
|
+
this.state = Chromedriver.STATE_STOPPED;
|
|
51
|
+
this.jwproxy = new base_driver_1.JWProxy({
|
|
52
|
+
server: this.proxyHost,
|
|
53
|
+
port: this.proxyPort,
|
|
54
|
+
log: this._log,
|
|
155
55
|
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return cds;
|
|
178
|
-
}
|
|
179
|
-
this.log.debug(`The following Chromedriver executables were found:`);
|
|
180
|
-
for (const cd of cds) {
|
|
181
|
-
this.log.debug(` '${cd.executable}' (version '${cd.version}', minimum Chrome version '${cd.minChromeVersion ? cd.minChromeVersion : 'Unknown'}')`);
|
|
182
|
-
}
|
|
183
|
-
return cds;
|
|
184
|
-
}
|
|
185
|
-
async getChromeVersion() {
|
|
186
|
-
var _this$details, _this$details3, _this$details3$info;
|
|
187
|
-
if ((_this$details = this.details) !== null && _this$details !== void 0 && _this$details.info) {
|
|
188
|
-
var _this$details2, _this$details2$info;
|
|
189
|
-
this.log.debug(`Browser version in the supplied details: ${(_this$details2 = this.details) === null || _this$details2 === void 0 ? void 0 : (_this$details2$info = _this$details2.info) === null || _this$details2$info === void 0 ? void 0 : _this$details2$info.Browser}`);
|
|
190
|
-
}
|
|
191
|
-
const versionMatch = VERSION_PATTERN.exec((_this$details3 = this.details) === null || _this$details3 === void 0 ? void 0 : (_this$details3$info = _this$details3.info) === null || _this$details3$info === void 0 ? void 0 : _this$details3$info.Browser);
|
|
192
|
-
if (versionMatch) {
|
|
193
|
-
const coercedVersion = _semver.default.coerce(versionMatch[1]);
|
|
194
|
-
if (coercedVersion) {
|
|
195
|
-
return coercedVersion;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
let chromeVersion;
|
|
199
|
-
if (this.bundleId === WEBVIEW_SHELL_BUNDLE_ID) {
|
|
200
|
-
for (const bundleId of WEBVIEW_BUNDLE_IDS) {
|
|
201
|
-
chromeVersion = await (0, _utils.getChromeVersion)(this.adb, bundleId);
|
|
202
|
-
if (chromeVersion) {
|
|
203
|
-
this.bundleId = bundleId;
|
|
204
|
-
return _semver.default.coerce(chromeVersion);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
if (this.adb) {
|
|
210
|
-
const apiLevel = await this.adb.getApiLevel();
|
|
211
|
-
if (apiLevel >= 24 && apiLevel <= 28 && [WEBVIEW_SHELL_BUNDLE_ID, ...WEBVIEW_BUNDLE_IDS].includes(this.bundleId)) {
|
|
212
|
-
this.bundleId = CHROME_BUNDLE_ID;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
if (!this.bundleId) {
|
|
216
|
-
this.bundleId = CHROME_BUNDLE_ID;
|
|
217
|
-
for (const bundleId of WEBVIEW_BUNDLE_IDS) {
|
|
218
|
-
chromeVersion = await (0, _utils.getChromeVersion)(this.adb, bundleId);
|
|
219
|
-
if (chromeVersion) {
|
|
220
|
-
this.bundleId = bundleId;
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (!chromeVersion) {
|
|
226
|
-
chromeVersion = await (0, _utils.getChromeVersion)(this.adb, this.bundleId);
|
|
227
|
-
}
|
|
228
|
-
return chromeVersion ? _semver.default.coerce(chromeVersion) : null;
|
|
229
|
-
}
|
|
230
|
-
async updateDriversMapping(newMapping) {
|
|
231
|
-
let shouldUpdateStaticMapping = true;
|
|
232
|
-
if (await _support.fs.exists(this.mappingPath)) {
|
|
233
|
-
try {
|
|
234
|
-
await _support.fs.writeFile(this.mappingPath, JSON.stringify(newMapping, null, 2), 'utf8');
|
|
235
|
-
shouldUpdateStaticMapping = false;
|
|
236
|
-
} catch (e) {
|
|
237
|
-
this.log.warn(`Cannot store the updated chromedrivers mapping into '${this.mappingPath}'. ` + `This may reduce the performance of further executions. Original error: ${e.message}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (shouldUpdateStaticMapping) {
|
|
241
|
-
Object.assign(_utils.CHROMEDRIVER_CHROME_MAPPING, newMapping);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async getCompatibleChromedriver() {
|
|
245
|
-
if (!this.adb) {
|
|
246
|
-
return await (0, _utils.getChromedriverBinaryPath)();
|
|
247
|
-
}
|
|
248
|
-
const mapping = await this.getDriversMapping();
|
|
249
|
-
if (!_lodash.default.isEmpty(mapping)) {
|
|
250
|
-
this.log.debug(`The most recent known Chrome version: ${_lodash.default.values(mapping)[0]}`);
|
|
251
|
-
}
|
|
252
|
-
let didStorageSync = false;
|
|
253
|
-
const syncChromedrivers = async chromeVersion => {
|
|
254
|
-
didStorageSync = true;
|
|
255
|
-
const retrievedMapping = await this.storageClient.retrieveMapping();
|
|
256
|
-
this.log.debug('Got chromedrivers mapping from the storage: ' + JSON.stringify(retrievedMapping, null, 2));
|
|
257
|
-
const driverKeys = await this.storageClient.syncDrivers({
|
|
258
|
-
minBrowserVersion: chromeVersion.major
|
|
259
|
-
});
|
|
260
|
-
if (_lodash.default.isEmpty(driverKeys)) {
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
const synchronizedDriversMapping = driverKeys.reduce((acc, x) => {
|
|
264
|
-
const {
|
|
265
|
-
version,
|
|
266
|
-
minBrowserVersion
|
|
267
|
-
} = retrievedMapping[x];
|
|
268
|
-
acc[version] = minBrowserVersion;
|
|
269
|
-
return acc;
|
|
270
|
-
}, {});
|
|
271
|
-
Object.assign(mapping, synchronizedDriversMapping);
|
|
272
|
-
await this.updateDriversMapping(mapping);
|
|
273
|
-
return true;
|
|
274
|
-
};
|
|
275
|
-
do {
|
|
276
|
-
const cds = await this.getChromedrivers(mapping);
|
|
277
|
-
const missingVersions = {};
|
|
278
|
-
for (const {
|
|
279
|
-
version,
|
|
280
|
-
minChromeVersion
|
|
281
|
-
} of cds) {
|
|
282
|
-
if (!minChromeVersion || mapping[version]) {
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
const coercedVer = _semver.default.coerce(version);
|
|
286
|
-
if (!coercedVer || coercedVer.major < NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
missingVersions[version] = minChromeVersion;
|
|
290
|
-
}
|
|
291
|
-
if (!_lodash.default.isEmpty(missingVersions)) {
|
|
292
|
-
this.log.info(`Found ${_support.util.pluralize('Chromedriver', _lodash.default.size(missingVersions), true)}, ` + `which ${_lodash.default.size(missingVersions) === 1 ? 'is' : 'are'} missing in the list of known versions: ` + JSON.stringify(missingVersions));
|
|
293
|
-
await this.updateDriversMapping(Object.assign(mapping, missingVersions));
|
|
294
|
-
}
|
|
295
|
-
if (this.disableBuildCheck) {
|
|
296
|
-
if (_lodash.default.isEmpty(cds)) {
|
|
297
|
-
this.log.errorAndThrow(`There must be at least one Chromedriver executable available for use if ` + `'chromedriverDisableBuildCheck' capability is set to 'true'`);
|
|
298
|
-
}
|
|
299
|
-
const {
|
|
300
|
-
version,
|
|
301
|
-
executable
|
|
302
|
-
} = cds[0];
|
|
303
|
-
this.log.warn(`Chrome build check disabled. Using most recent Chromedriver version (${version}, at '${executable}')`);
|
|
304
|
-
this.log.warn(`If this is wrong, set 'chromedriverDisableBuildCheck' capability to 'false'`);
|
|
305
|
-
return executable;
|
|
306
|
-
}
|
|
307
|
-
const chromeVersion = await this.getChromeVersion();
|
|
308
|
-
if (!chromeVersion) {
|
|
309
|
-
if (_lodash.default.isEmpty(cds)) {
|
|
310
|
-
this.log.errorAndThrow(`There must be at least one Chromedriver executable available for use if ` + `the current Chrome version cannot be determined`);
|
|
311
|
-
}
|
|
312
|
-
const {
|
|
313
|
-
version,
|
|
314
|
-
executable
|
|
315
|
-
} = cds[0];
|
|
316
|
-
this.log.warn(`Unable to discover Chrome version. Using Chromedriver ${version} at '${executable}'`);
|
|
317
|
-
return executable;
|
|
318
|
-
}
|
|
319
|
-
this.log.debug(`Found Chrome bundle '${this.bundleId}' version '${chromeVersion}'`);
|
|
320
|
-
const matchingDrivers = cds.filter(({
|
|
321
|
-
minChromeVersion
|
|
322
|
-
}) => {
|
|
323
|
-
const minChromeVersionS = minChromeVersion && _semver.default.coerce(minChromeVersion);
|
|
324
|
-
if (!minChromeVersionS) {
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
return chromeVersion.major > NEW_CD_VERSION_FORMAT_MAJOR_VERSION ? minChromeVersionS.major === chromeVersion.major : _semver.default.gte(chromeVersion, minChromeVersionS);
|
|
328
|
-
});
|
|
329
|
-
if (_lodash.default.isEmpty(matchingDrivers)) {
|
|
330
|
-
if (this.storageClient && !didStorageSync) {
|
|
331
|
-
try {
|
|
332
|
-
if (await syncChromedrivers(chromeVersion)) {
|
|
333
|
-
continue;
|
|
56
|
+
this.verbose = verbose;
|
|
57
|
+
this.logPath = logPath;
|
|
58
|
+
this.disableBuildCheck = !!disableBuildCheck;
|
|
59
|
+
this.storageClient = isAutodownloadEnabled
|
|
60
|
+
? new storage_client_1.default({ chromedriverDir: this.executableDir })
|
|
61
|
+
: null;
|
|
62
|
+
this.details = details;
|
|
63
|
+
/** @type {any} */
|
|
64
|
+
this.capabilities = {};
|
|
65
|
+
this.desiredProtocol = base_driver_1.PROTOCOLS.MJSONWP;
|
|
66
|
+
}
|
|
67
|
+
get log() {
|
|
68
|
+
return this._log;
|
|
69
|
+
}
|
|
70
|
+
async getDriversMapping() {
|
|
71
|
+
let mapping = lodash_1.default.cloneDeep(utils_1.CHROMEDRIVER_CHROME_MAPPING);
|
|
72
|
+
if (this.mappingPath) {
|
|
73
|
+
this.log.debug(`Attempting to use Chromedriver->Chrome mapping from '${this.mappingPath}'`);
|
|
74
|
+
if (!(await support_1.fs.exists(this.mappingPath))) {
|
|
75
|
+
this.log.warn(`No file found at '${this.mappingPath}'`);
|
|
76
|
+
this.log.info('Defaulting to the static Chromedriver->Chrome mapping');
|
|
334
77
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
78
|
+
else {
|
|
79
|
+
try {
|
|
80
|
+
mapping = JSON.parse(await support_1.fs.readFile(this.mappingPath, 'utf8'));
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
const err = /** @type {Error} */ (e);
|
|
84
|
+
this.log.warn(`Error parsing mapping from '${this.mappingPath}': ${err.message}`);
|
|
85
|
+
this.log.info('Defaulting to the static Chromedriver->Chrome mapping');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.log.debug('Using the static Chromedriver->Chrome mapping');
|
|
91
|
+
}
|
|
92
|
+
// make sure that the values for minimum chrome version are semver compliant
|
|
93
|
+
for (const [cdVersion, chromeVersion] of lodash_1.default.toPairs(mapping)) {
|
|
94
|
+
const coercedVersion = semver_1.default.coerce(chromeVersion);
|
|
95
|
+
if (coercedVersion) {
|
|
96
|
+
mapping[cdVersion] = coercedVersion.version;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.log.info(`'${chromeVersion}' is not a valid version number. Skipping it`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return mapping;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @param {ChromedriverVersionMapping} mapping
|
|
106
|
+
*/
|
|
107
|
+
async getChromedrivers(mapping) {
|
|
108
|
+
// go through the versions available
|
|
109
|
+
const executables = await support_1.fs.glob('*', {
|
|
110
|
+
cwd: this.executableDir,
|
|
111
|
+
strict: false,
|
|
112
|
+
nodir: true,
|
|
113
|
+
absolute: true,
|
|
114
|
+
});
|
|
115
|
+
this.log.debug(`Found ${support_1.util.pluralize('executable', executables.length, true)} ` +
|
|
116
|
+
`in '${this.executableDir}'`);
|
|
117
|
+
const cds = (await (0, asyncbox_1.asyncmap)(executables, async (executable) => {
|
|
118
|
+
/**
|
|
119
|
+
* @param {{message: string, stdout?: string, stderr?: string}} opts
|
|
120
|
+
*/
|
|
121
|
+
const logError = ({ message, stdout, stderr }) => {
|
|
122
|
+
let errMsg = `Cannot retrieve version number from '${path_1.default.basename(executable)}' Chromedriver binary. ` +
|
|
123
|
+
`Make sure it returns a valid version string in response to '--version' command line argument. ${message}`;
|
|
124
|
+
if (stdout) {
|
|
125
|
+
errMsg += `\nStdout: ${stdout}`;
|
|
126
|
+
}
|
|
127
|
+
if (stderr) {
|
|
128
|
+
errMsg += `\nStderr: ${stderr}`;
|
|
129
|
+
}
|
|
130
|
+
this.log.warn(errMsg);
|
|
131
|
+
return null;
|
|
132
|
+
};
|
|
133
|
+
let stdout;
|
|
134
|
+
let stderr;
|
|
135
|
+
try {
|
|
136
|
+
({ stdout, stderr } = await (0, teen_process_1.exec)(executable, ['--version'], {
|
|
137
|
+
timeout: CD_VERSION_TIMEOUT,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
const err = /** @type {import('teen_process').ExecError} */ (e);
|
|
142
|
+
if (!(err.message || '').includes('timed out') &&
|
|
143
|
+
!(err.stdout || '').includes('Starting ChromeDriver')) {
|
|
144
|
+
return logError(err);
|
|
145
|
+
}
|
|
146
|
+
// if this has timed out, it has actually started Chromedriver,
|
|
147
|
+
// in which case there will also be the version string in the output
|
|
148
|
+
stdout = err.stdout;
|
|
149
|
+
}
|
|
150
|
+
const match = /ChromeDriver\s+\(?v?([\d.]+)\)?/i.exec(stdout); // https://regex101.com/r/zpj5wA/1
|
|
151
|
+
if (!match) {
|
|
152
|
+
return logError({ message: 'Cannot parse the version string', stdout, stderr });
|
|
153
|
+
}
|
|
154
|
+
let version = match[1];
|
|
155
|
+
let minChromeVersion = mapping[version];
|
|
156
|
+
const coercedVersion = semver_1.default.coerce(version);
|
|
157
|
+
if (coercedVersion) {
|
|
158
|
+
// before 2019-03-06 versions were of the form major.minor
|
|
159
|
+
if (coercedVersion.major < NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
|
|
160
|
+
version = /** @type {keyof typeof mapping} */ (`${coercedVersion.major}.${coercedVersion.minor}`);
|
|
161
|
+
minChromeVersion = mapping[version];
|
|
162
|
+
}
|
|
163
|
+
if (!minChromeVersion && coercedVersion.major >= NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
|
|
164
|
+
// Assume the major Chrome version is the same as the corresponding driver major version
|
|
165
|
+
minChromeVersion = `${coercedVersion.major}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
executable,
|
|
170
|
+
version,
|
|
171
|
+
minChromeVersion,
|
|
172
|
+
};
|
|
173
|
+
}))
|
|
174
|
+
.filter((cd) => !!cd)
|
|
175
|
+
.sort((a, b) => (0, compare_versions_1.compareVersions)(b.version, a.version));
|
|
176
|
+
if (lodash_1.default.isEmpty(cds)) {
|
|
177
|
+
this.log.info(`No Chromedrivers were found in '${this.executableDir}'`);
|
|
178
|
+
return cds;
|
|
179
|
+
}
|
|
180
|
+
this.log.debug(`The following Chromedriver executables were found:`);
|
|
181
|
+
for (const cd of cds) {
|
|
182
|
+
this.log.debug(` '${cd.executable}' (version '${cd.version}', minimum Chrome version '${cd.minChromeVersion ? cd.minChromeVersion : 'Unknown'}')`);
|
|
183
|
+
}
|
|
184
|
+
return cds;
|
|
185
|
+
}
|
|
186
|
+
async getChromeVersion() {
|
|
187
|
+
// Try to retrieve the version from `details` property if it is set
|
|
188
|
+
// The `info` item must contain the output of /json/version CDP command
|
|
189
|
+
// where `Browser` field looks like `Chrome/72.0.3601.0``
|
|
190
|
+
if (this.details?.info) {
|
|
191
|
+
this.log.debug(`Browser version in the supplied details: ${this.details?.info?.Browser}`);
|
|
192
|
+
}
|
|
193
|
+
const versionMatch = VERSION_PATTERN.exec(this.details?.info?.Browser ?? '');
|
|
194
|
+
if (versionMatch) {
|
|
195
|
+
const coercedVersion = semver_1.default.coerce(versionMatch[1]);
|
|
196
|
+
if (coercedVersion) {
|
|
197
|
+
return coercedVersion;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let chromeVersion;
|
|
201
|
+
// in case of WebView Browser Tester, simply try to find the underlying webview
|
|
202
|
+
if (this.bundleId === WEBVIEW_SHELL_BUNDLE_ID) {
|
|
203
|
+
if (this.adb) {
|
|
204
|
+
for (const bundleId of WEBVIEW_BUNDLE_IDS) {
|
|
205
|
+
chromeVersion = await (0, utils_1.getChromeVersion)(this.adb, bundleId);
|
|
206
|
+
if (chromeVersion) {
|
|
207
|
+
this.bundleId = bundleId;
|
|
208
|
+
return semver_1.default.coerce(chromeVersion);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
// on Android 7-9 webviews are backed by the main Chrome, not the system webview
|
|
215
|
+
if (this.adb) {
|
|
216
|
+
const apiLevel = await this.adb.getApiLevel();
|
|
217
|
+
if (apiLevel >= 24 &&
|
|
218
|
+
apiLevel <= 28 &&
|
|
219
|
+
[WEBVIEW_SHELL_BUNDLE_ID, ...WEBVIEW_BUNDLE_IDS].includes(this.bundleId ?? '')) {
|
|
220
|
+
this.bundleId = CHROME_BUNDLE_ID;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// try out webviews when no bundle id is sent in
|
|
224
|
+
if (!this.bundleId) {
|
|
225
|
+
// default to the generic Chrome bundle
|
|
226
|
+
this.bundleId = CHROME_BUNDLE_ID;
|
|
227
|
+
// we have a webview of some sort, so try to find the bundle version
|
|
228
|
+
for (const bundleId of WEBVIEW_BUNDLE_IDS) {
|
|
229
|
+
if (this.adb) {
|
|
230
|
+
chromeVersion = await (0, utils_1.getChromeVersion)(this.adb, bundleId);
|
|
231
|
+
if (chromeVersion) {
|
|
232
|
+
this.bundleId = bundleId;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// if we do not have a chrome version, it must not be a webview
|
|
239
|
+
if (!chromeVersion && this.adb) {
|
|
240
|
+
chromeVersion = await (0, utils_1.getChromeVersion)(this.adb, this.bundleId);
|
|
241
|
+
}
|
|
242
|
+
// make sure it is semver, so later checks won't fail
|
|
243
|
+
return chromeVersion ? semver_1.default.coerce(chromeVersion) : null;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
*
|
|
247
|
+
* @param {ChromedriverVersionMapping} newMapping
|
|
248
|
+
* @returns {Promise<void>}
|
|
249
|
+
*/
|
|
250
|
+
async updateDriversMapping(newMapping) {
|
|
251
|
+
let shouldUpdateStaticMapping = true;
|
|
252
|
+
if (!this.mappingPath) {
|
|
253
|
+
this.log.warn('No mapping path provided');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (await support_1.fs.exists(this.mappingPath)) {
|
|
257
|
+
try {
|
|
258
|
+
await support_1.fs.writeFile(this.mappingPath, JSON.stringify(newMapping, null, 2), 'utf8');
|
|
259
|
+
shouldUpdateStaticMapping = false;
|
|
260
|
+
}
|
|
261
|
+
catch (e) {
|
|
262
|
+
const err = /** @type {Error} */ (e);
|
|
263
|
+
this.log.warn(`Cannot store the updated chromedrivers mapping into '${this.mappingPath}'. ` +
|
|
264
|
+
`This may reduce the performance of further executions. Original error: ${err.message}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (shouldUpdateStaticMapping) {
|
|
268
|
+
Object.assign(utils_1.CHROMEDRIVER_CHROME_MAPPING, newMapping);
|
|
269
|
+
}
|
|
470
270
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
271
|
+
/**
|
|
272
|
+
* @returns {Promise<string>}
|
|
273
|
+
*/
|
|
274
|
+
async getCompatibleChromedriver() {
|
|
275
|
+
if (!this.adb) {
|
|
276
|
+
return await (0, utils_1.getChromedriverBinaryPath)();
|
|
277
|
+
}
|
|
278
|
+
const mapping = await this.getDriversMapping();
|
|
279
|
+
if (!lodash_1.default.isEmpty(mapping)) {
|
|
280
|
+
this.log.debug(`The most recent known Chrome version: ${lodash_1.default.values(mapping)[0]}`);
|
|
281
|
+
}
|
|
282
|
+
let didStorageSync = false;
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @param {import('semver').SemVer} chromeVersion
|
|
286
|
+
*/
|
|
287
|
+
const syncChromedrivers = async (chromeVersion) => {
|
|
288
|
+
didStorageSync = true;
|
|
289
|
+
if (!this.storageClient) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
const retrievedMapping = await this.storageClient.retrieveMapping();
|
|
293
|
+
this.log.debug('Got chromedrivers mapping from the storage: ' + JSON.stringify(retrievedMapping, null, 2));
|
|
294
|
+
const driverKeys = await this.storageClient.syncDrivers({
|
|
295
|
+
minBrowserVersion: chromeVersion.major,
|
|
296
|
+
});
|
|
297
|
+
if (lodash_1.default.isEmpty(driverKeys)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
const synchronizedDriversMapping = driverKeys.reduce((acc, x) => {
|
|
301
|
+
const { version, minBrowserVersion } = retrievedMapping[x];
|
|
302
|
+
acc[version] = minBrowserVersion;
|
|
303
|
+
return acc;
|
|
304
|
+
}, /** @type {ChromedriverVersionMapping} */ ({}));
|
|
305
|
+
Object.assign(mapping, synchronizedDriversMapping);
|
|
306
|
+
await this.updateDriversMapping(mapping);
|
|
307
|
+
return true;
|
|
308
|
+
};
|
|
309
|
+
do {
|
|
310
|
+
const cds = await this.getChromedrivers(mapping);
|
|
311
|
+
/** @type {ChromedriverVersionMapping} */
|
|
312
|
+
const missingVersions = {};
|
|
313
|
+
for (const { version, minChromeVersion } of cds) {
|
|
314
|
+
if (!minChromeVersion || mapping[version]) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const coercedVer = semver_1.default.coerce(version);
|
|
318
|
+
if (!coercedVer || coercedVer.major < NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
missingVersions[version] = minChromeVersion;
|
|
322
|
+
}
|
|
323
|
+
if (!lodash_1.default.isEmpty(missingVersions)) {
|
|
324
|
+
this.log.info(`Found ${support_1.util.pluralize('Chromedriver', lodash_1.default.size(missingVersions), true)}, ` +
|
|
325
|
+
`which ${lodash_1.default.size(missingVersions) === 1 ? 'is' : 'are'} missing in the list of known versions: ` +
|
|
326
|
+
JSON.stringify(missingVersions));
|
|
327
|
+
await this.updateDriversMapping(Object.assign(mapping, missingVersions));
|
|
328
|
+
}
|
|
329
|
+
if (this.disableBuildCheck) {
|
|
330
|
+
if (lodash_1.default.isEmpty(cds)) {
|
|
331
|
+
this.log.errorAndThrow(`There must be at least one Chromedriver executable available for use if ` +
|
|
332
|
+
`'chromedriverDisableBuildCheck' capability is set to 'true'`);
|
|
333
|
+
}
|
|
334
|
+
const { version, executable } = cds[0];
|
|
335
|
+
this.log.warn(`Chrome build check disabled. Using most recent Chromedriver version (${version}, at '${executable}')`);
|
|
336
|
+
this.log.warn(`If this is wrong, set 'chromedriverDisableBuildCheck' capability to 'false'`);
|
|
337
|
+
return executable;
|
|
338
|
+
}
|
|
339
|
+
const chromeVersion = await this.getChromeVersion();
|
|
340
|
+
if (!chromeVersion) {
|
|
341
|
+
// unable to get the chrome version
|
|
342
|
+
if (lodash_1.default.isEmpty(cds)) {
|
|
343
|
+
this.log.errorAndThrow(`There must be at least one Chromedriver executable available for use if ` +
|
|
344
|
+
`the current Chrome version cannot be determined`);
|
|
345
|
+
}
|
|
346
|
+
const { version, executable } = cds[0];
|
|
347
|
+
this.log.warn(`Unable to discover Chrome version. Using Chromedriver ${version} at '${executable}'`);
|
|
348
|
+
return executable;
|
|
349
|
+
}
|
|
350
|
+
this.log.debug(`Found Chrome bundle '${this.bundleId}' version '${chromeVersion}'`);
|
|
351
|
+
const matchingDrivers = cds.filter(({ minChromeVersion }) => {
|
|
352
|
+
const minChromeVersionS = minChromeVersion && semver_1.default.coerce(minChromeVersion);
|
|
353
|
+
if (!minChromeVersionS) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
return chromeVersion.major > NEW_CD_VERSION_FORMAT_MAJOR_VERSION
|
|
357
|
+
? minChromeVersionS.major === chromeVersion.major
|
|
358
|
+
: semver_1.default.gte(chromeVersion, minChromeVersionS);
|
|
359
|
+
});
|
|
360
|
+
if (lodash_1.default.isEmpty(matchingDrivers)) {
|
|
361
|
+
if (this.storageClient && !didStorageSync) {
|
|
362
|
+
try {
|
|
363
|
+
if (await syncChromedrivers(chromeVersion)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
const err = /** @type {Error} */ (e);
|
|
369
|
+
this.log.warn(`Cannot synchronize local chromedrivers with the remote storage at ${utils_1.CD_CDN}: ` +
|
|
370
|
+
err.message);
|
|
371
|
+
this.log.debug(err.stack);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const autodownloadSuggestion = 'You could also try to enable automated chromedrivers download as ' +
|
|
375
|
+
'a possible workaround.';
|
|
376
|
+
throw new Error(`No Chromedriver found that can automate Chrome '${chromeVersion}'.` +
|
|
377
|
+
(this.storageClient ? '' : ` ${autodownloadSuggestion}`));
|
|
378
|
+
}
|
|
379
|
+
const binPath = matchingDrivers[0].executable;
|
|
380
|
+
this.log.debug(`Found ${support_1.util.pluralize('executable', matchingDrivers.length, true)} ` +
|
|
381
|
+
`capable of automating Chrome '${chromeVersion}'.\nChoosing the most recent, '${binPath}'.`);
|
|
382
|
+
this.log.debug('If a specific version is required, specify it with the `chromedriverExecutable`' +
|
|
383
|
+
'desired capability.');
|
|
384
|
+
return binPath;
|
|
385
|
+
// eslint-disable-next-line no-constant-condition
|
|
386
|
+
} while (true);
|
|
387
|
+
}
|
|
388
|
+
async initChromedriverPath() {
|
|
389
|
+
if (this.executableVerified && this.chromedriver) {
|
|
390
|
+
return /** @type {string} */ (this.chromedriver);
|
|
391
|
+
}
|
|
392
|
+
let chromedriver = this.chromedriver;
|
|
393
|
+
// the executable might be set (if passed in)
|
|
394
|
+
// or we might want to use the basic one installed with this driver
|
|
395
|
+
// or we want to figure out the best one
|
|
396
|
+
if (!chromedriver) {
|
|
397
|
+
chromedriver = this.chromedriver = this.useSystemExecutable
|
|
398
|
+
? await (0, utils_1.getChromedriverBinaryPath)()
|
|
399
|
+
: await this.getCompatibleChromedriver();
|
|
400
|
+
}
|
|
401
|
+
if (!(await support_1.fs.exists(chromedriver))) {
|
|
402
|
+
throw new Error(`Trying to use a chromedriver binary at the path ` +
|
|
403
|
+
`${this.chromedriver}, but it doesn't exist!`);
|
|
404
|
+
}
|
|
405
|
+
this.executableVerified = true;
|
|
406
|
+
this.log.info(`Set chromedriver binary as: ${this.chromedriver}`);
|
|
407
|
+
return /** @type {string} */ (this.chromedriver);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
*
|
|
411
|
+
* @param {string} [cdVersion]
|
|
412
|
+
*/
|
|
413
|
+
syncProtocol(cdVersion) {
|
|
414
|
+
const coercedVersion = semver_1.default.coerce(cdVersion);
|
|
415
|
+
if (!coercedVersion || coercedVersion.major < MIN_CD_VERSION_WITH_W3C_SUPPORT) {
|
|
416
|
+
this.log.debug(`Chromedriver v. ${cdVersion} does not fully support ${base_driver_1.PROTOCOLS.W3C} protocol. ` +
|
|
417
|
+
`Defaulting to ${base_driver_1.PROTOCOLS.MJSONWP}`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const chromeOptions = (0, protocol_helpers_1.getCapValue)(this.capabilities, 'chromeOptions', {});
|
|
421
|
+
if (chromeOptions.w3c === false) {
|
|
422
|
+
this.log.info(`Chromedriver v. ${cdVersion} supports ${base_driver_1.PROTOCOLS.W3C} protocol, ` +
|
|
423
|
+
`but ${base_driver_1.PROTOCOLS.MJSONWP} one has been explicitly requested`);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.desiredProtocol = base_driver_1.PROTOCOLS.W3C;
|
|
427
|
+
// given caps might not be properly prefixed
|
|
428
|
+
// so we try to fix them in order to properly init
|
|
429
|
+
// the new W3C session
|
|
430
|
+
this.capabilities = (0, protocol_helpers_1.toW3cCapNames)(this.capabilities);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
*
|
|
434
|
+
* @param {object} caps
|
|
435
|
+
* @param {boolean} emitStartingState
|
|
436
|
+
*/
|
|
437
|
+
async start(caps, emitStartingState = true) {
|
|
438
|
+
this.capabilities = lodash_1.default.cloneDeep(caps);
|
|
439
|
+
// set the logging preferences to ALL the console logs
|
|
440
|
+
this.capabilities.loggingPrefs = lodash_1.default.cloneDeep((0, protocol_helpers_1.getCapValue)(caps, 'loggingPrefs', {}));
|
|
441
|
+
if (lodash_1.default.isEmpty(this.capabilities.loggingPrefs.browser)) {
|
|
442
|
+
this.capabilities.loggingPrefs.browser = 'ALL';
|
|
443
|
+
}
|
|
444
|
+
if (emitStartingState) {
|
|
445
|
+
this.changeState(Chromedriver.STATE_STARTING);
|
|
446
|
+
}
|
|
447
|
+
const args = [`--port=${this.proxyPort}`];
|
|
448
|
+
if (this.adb && this.adb.adbPort) {
|
|
449
|
+
args.push(`--adb-port=${this.adb.adbPort}`);
|
|
450
|
+
}
|
|
451
|
+
if (lodash_1.default.isArray(this.cmdArgs)) {
|
|
452
|
+
args.push(...this.cmdArgs);
|
|
453
|
+
}
|
|
454
|
+
if (this.logPath) {
|
|
455
|
+
args.push(`--log-path=${this.logPath}`);
|
|
456
|
+
}
|
|
457
|
+
if (this.disableBuildCheck) {
|
|
458
|
+
args.push('--disable-build-check');
|
|
459
|
+
}
|
|
460
|
+
args.push('--verbose');
|
|
461
|
+
// what are the process stdout/stderr conditions wherein we know that
|
|
462
|
+
// the process has started to our satisfaction?
|
|
463
|
+
const startDetector = /** @param {string} stdout */ (stdout) => stdout.startsWith('Starting ');
|
|
464
|
+
let processIsAlive = false;
|
|
465
|
+
let webviewVersion;
|
|
466
|
+
try {
|
|
467
|
+
const chromedriverPath = await this.initChromedriverPath();
|
|
468
|
+
await this.killAll();
|
|
469
|
+
// set up our subprocess object
|
|
470
|
+
this.proc = new teen_process_1.SubProcess(chromedriverPath, args);
|
|
471
|
+
processIsAlive = true;
|
|
472
|
+
// handle log output
|
|
473
|
+
this.proc.on('output', (stdout, stderr) => {
|
|
474
|
+
// if the cd output is not printed, find the chrome version and print
|
|
475
|
+
// will get a response like
|
|
476
|
+
// DevTools response: {
|
|
477
|
+
// "Android-Package": "io.appium.sampleapp",
|
|
478
|
+
// "Browser": "Chrome/55.0.2883.91",
|
|
479
|
+
// "Protocol-Version": "1.2",
|
|
480
|
+
// "User-Agent": "...",
|
|
481
|
+
// "WebKit-Version": "537.36"
|
|
482
|
+
// }
|
|
483
|
+
const out = stdout + stderr;
|
|
484
|
+
let match = /"Browser": "(.*)"/.exec(out);
|
|
485
|
+
if (match) {
|
|
486
|
+
webviewVersion = match[1];
|
|
487
|
+
this.log.debug(`Webview version: '${webviewVersion}'`);
|
|
488
|
+
}
|
|
489
|
+
// also print chromedriver version to logs
|
|
490
|
+
// will output something like
|
|
491
|
+
// Starting ChromeDriver 2.33.506106 (8a06c39c4582fbfbab6966dbb1c38a9173bfb1a2) on port 9515
|
|
492
|
+
match = /Starting ChromeDriver ([.\d]+)/.exec(out);
|
|
493
|
+
if (match) {
|
|
494
|
+
this.log.debug(`Chromedriver version: '${match[1]}'`);
|
|
495
|
+
this.syncProtocol(match[1]);
|
|
496
|
+
}
|
|
497
|
+
// give the output if it is requested
|
|
498
|
+
if (this.verbose) {
|
|
499
|
+
for (let line of (stdout || '').trim().split('\n')) {
|
|
500
|
+
if (!line.trim().length)
|
|
501
|
+
continue; // eslint-disable-line curly
|
|
502
|
+
this.log.debug(`[STDOUT] ${line}`);
|
|
503
|
+
}
|
|
504
|
+
for (let line of (stderr || '').trim().split('\n')) {
|
|
505
|
+
if (!line.trim().length)
|
|
506
|
+
continue; // eslint-disable-line curly
|
|
507
|
+
this.log.error(`[STDERR] ${line}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
// handle out-of-bound exit by simply emitting a stopped state
|
|
512
|
+
this.proc.on('exit', (code, signal) => {
|
|
513
|
+
processIsAlive = false;
|
|
514
|
+
if (this.state !== Chromedriver.STATE_STOPPED &&
|
|
515
|
+
this.state !== Chromedriver.STATE_STOPPING &&
|
|
516
|
+
this.state !== Chromedriver.STATE_RESTARTING) {
|
|
517
|
+
const msg = `Chromedriver exited unexpectedly with code ${code}, signal ${signal}`;
|
|
518
|
+
this.log.error(msg);
|
|
519
|
+
this.changeState(Chromedriver.STATE_STOPPED);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
this.log.info(`Spawning chromedriver with: ${this.chromedriver} ${args.join(' ')}`);
|
|
523
|
+
// start subproc and wait for startDetector
|
|
524
|
+
await this.proc.start(startDetector);
|
|
525
|
+
await this.waitForOnline();
|
|
526
|
+
await this.startSession();
|
|
527
|
+
}
|
|
528
|
+
catch (e) {
|
|
529
|
+
const err = /** @type {Error} */ (e);
|
|
530
|
+
this.log.debug(err);
|
|
531
|
+
this.emit(Chromedriver.EVENT_ERROR, err);
|
|
532
|
+
// just because we had an error doesn't mean the chromedriver process
|
|
533
|
+
// finished; we should clean up if necessary
|
|
534
|
+
if (processIsAlive) {
|
|
535
|
+
await this.proc?.stop();
|
|
536
|
+
}
|
|
537
|
+
let message = '';
|
|
538
|
+
// often the user's Chrome version is not supported by the version of Chromedriver
|
|
539
|
+
if (err.message.includes('Chrome version must be')) {
|
|
540
|
+
message +=
|
|
541
|
+
'Unable to automate Chrome version because it is not supported by this version of Chromedriver.\n';
|
|
542
|
+
if (webviewVersion) {
|
|
543
|
+
message += `Chrome version on the device: ${webviewVersion}\n`;
|
|
544
|
+
}
|
|
545
|
+
const versionsSupportedByDriver = /Chrome version must be (.+)/.exec(err.message)?.[1] || '';
|
|
546
|
+
if (versionsSupportedByDriver) {
|
|
547
|
+
message += `Chromedriver supports Chrome version(s): ${versionsSupportedByDriver}\n`;
|
|
548
|
+
}
|
|
549
|
+
message += 'Check the driver tutorial for troubleshooting.\n';
|
|
550
|
+
}
|
|
551
|
+
message += err.message;
|
|
552
|
+
this.log.errorAndThrow(message);
|
|
553
|
+
}
|
|
486
554
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
return await this.jwproxy.command('/status', 'GET');
|
|
490
|
-
}
|
|
491
|
-
async startSession() {
|
|
492
|
-
const sessionCaps = this.desiredProtocol === _baseDriver.PROTOCOLS.W3C ? {
|
|
493
|
-
capabilities: {
|
|
494
|
-
alwaysMatch: this.capabilities
|
|
495
|
-
}
|
|
496
|
-
} : {
|
|
497
|
-
desiredCapabilities: this.capabilities
|
|
498
|
-
};
|
|
499
|
-
this.log.info(`Starting ${this.desiredProtocol} Chromedriver session with capabilities: ` + JSON.stringify(sessionCaps, null, 2));
|
|
500
|
-
await this.jwproxy.command('/session', 'POST', sessionCaps);
|
|
501
|
-
this.log.prefix = (0, _utils.generateLogPrefix)(this, this.jwproxy.sessionId);
|
|
502
|
-
this.changeState(Chromedriver.STATE_ONLINE);
|
|
503
|
-
}
|
|
504
|
-
async stop(emitStates = true) {
|
|
505
|
-
if (emitStates) {
|
|
506
|
-
this.changeState(Chromedriver.STATE_STOPPING);
|
|
555
|
+
sessionId() {
|
|
556
|
+
return this.state === Chromedriver.STATE_ONLINE ? this.jwproxy.sessionId : null;
|
|
507
557
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
558
|
+
async restart() {
|
|
559
|
+
this.log.info('Restarting chromedriver');
|
|
560
|
+
if (this.state !== Chromedriver.STATE_ONLINE) {
|
|
561
|
+
throw new Error("Can't restart when we're not online");
|
|
562
|
+
}
|
|
563
|
+
this.changeState(Chromedriver.STATE_RESTARTING);
|
|
564
|
+
await this.stop(false);
|
|
565
|
+
await this.start(this.capabilities, false);
|
|
566
|
+
}
|
|
567
|
+
async waitForOnline() {
|
|
568
|
+
// we need to make sure that CD hasn't crashed
|
|
569
|
+
let chromedriverStopped = false;
|
|
570
|
+
await (0, asyncbox_1.retryInterval)(20, 200, async () => {
|
|
571
|
+
if (this.state === Chromedriver.STATE_STOPPED) {
|
|
572
|
+
// we are either stopped or stopping, so something went wrong
|
|
573
|
+
chromedriverStopped = true;
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
await this.getStatus();
|
|
577
|
+
});
|
|
578
|
+
if (chromedriverStopped) {
|
|
579
|
+
throw new Error('ChromeDriver crashed during startup.');
|
|
580
|
+
}
|
|
521
581
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
582
|
+
async getStatus() {
|
|
583
|
+
return await this.jwproxy.command('/status', 'GET');
|
|
584
|
+
}
|
|
585
|
+
async startSession() {
|
|
586
|
+
const sessionCaps = this.desiredProtocol === base_driver_1.PROTOCOLS.W3C
|
|
587
|
+
? { capabilities: { alwaysMatch: this.capabilities } }
|
|
588
|
+
: { desiredCapabilities: this.capabilities };
|
|
589
|
+
this.log.info(`Starting ${this.desiredProtocol} Chromedriver session with capabilities: ` +
|
|
590
|
+
JSON.stringify(sessionCaps, null, 2));
|
|
591
|
+
// jwproxy types have not been implemented yet
|
|
592
|
+
// @ts-expect-error
|
|
593
|
+
await this.jwproxy.command('/session', 'POST', sessionCaps);
|
|
594
|
+
this.log.prefix = (0, utils_1.generateLogPrefix)(this, this.jwproxy.sessionId);
|
|
595
|
+
this.changeState(Chromedriver.STATE_ONLINE);
|
|
596
|
+
}
|
|
597
|
+
async stop(emitStates = true) {
|
|
598
|
+
if (emitStates) {
|
|
599
|
+
this.changeState(Chromedriver.STATE_STOPPING);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
*
|
|
603
|
+
* @param {() => Promise<any>|any} f
|
|
604
|
+
*/
|
|
605
|
+
const runSafeStep = async (f) => {
|
|
606
|
+
try {
|
|
607
|
+
return await f();
|
|
608
|
+
}
|
|
609
|
+
catch (e) {
|
|
610
|
+
const err = /** @type {Error} */ (e);
|
|
611
|
+
this.log.warn(err.message);
|
|
612
|
+
this.log.debug(err.stack);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
await runSafeStep(() => this.jwproxy.command('', 'DELETE'));
|
|
616
|
+
await runSafeStep(() => this.proc?.stop('SIGTERM', 20000));
|
|
617
|
+
this.log.prefix = (0, utils_1.generateLogPrefix)(this);
|
|
618
|
+
if (emitStates) {
|
|
619
|
+
this.changeState(Chromedriver.STATE_STOPPED);
|
|
620
|
+
}
|
|
544
621
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
this.log.debug(`
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
622
|
+
/**
|
|
623
|
+
*
|
|
624
|
+
* @param {string} state
|
|
625
|
+
*/
|
|
626
|
+
changeState(state) {
|
|
627
|
+
this.state = state;
|
|
628
|
+
this.log.debug(`Changed state to '${state}'`);
|
|
629
|
+
this.emit(Chromedriver.EVENT_CHANGED, { state });
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
*
|
|
633
|
+
* @param {string} url
|
|
634
|
+
* @param {'POST'|'GET'|'DELETE'} method
|
|
635
|
+
* @param {any} body
|
|
636
|
+
* @returns
|
|
637
|
+
*/
|
|
638
|
+
async sendCommand(url, method, body) {
|
|
639
|
+
return await this.jwproxy.command(url, method, body);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
*
|
|
643
|
+
* @param {any} req
|
|
644
|
+
* @param {any} res
|
|
645
|
+
* @privateRemarks req / res probably from Express
|
|
646
|
+
*/
|
|
647
|
+
async proxyReq(req, res) {
|
|
648
|
+
return await this.jwproxy.proxyReqRes(req, res);
|
|
649
|
+
}
|
|
650
|
+
async killAll() {
|
|
651
|
+
let cmd = support_1.system.isWindows()
|
|
652
|
+
? `wmic process where "commandline like '%chromedriver.exe%--port=${this.proxyPort}%'" delete`
|
|
653
|
+
: `pkill -15 -f "${this.chromedriver}.*--port=${this.proxyPort}"`;
|
|
654
|
+
this.log.debug(`Killing any old chromedrivers, running: ${cmd}`);
|
|
655
|
+
try {
|
|
656
|
+
await bluebird_1.default.promisify(child_process_1.default.exec)(cmd);
|
|
657
|
+
this.log.debug('Successfully cleaned up old chromedrivers');
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
this.log.warn('No old chromedrivers seem to exist');
|
|
661
|
+
}
|
|
662
|
+
if (this.adb) {
|
|
663
|
+
const udidIndex = this.adb.executable.defaultArgs.findIndex((item) => item === '-s');
|
|
664
|
+
const udid = udidIndex > -1 ? this.adb.executable.defaultArgs[udidIndex + 1] : null;
|
|
665
|
+
if (udid) {
|
|
666
|
+
this.log.debug(`Cleaning this device's adb forwarded port socket connections: ${udid}`);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
this.log.debug(`Cleaning any old adb forwarded port socket connections`);
|
|
670
|
+
}
|
|
671
|
+
try {
|
|
672
|
+
for (let conn of await this.adb.getForwardList()) {
|
|
673
|
+
// chromedriver will ask ADB to forward a port like "deviceId tcp:port localabstract:webview_devtools_remote_port"
|
|
674
|
+
if (!(conn.includes('webview_devtools') && (!udid || conn.includes(udid)))) {
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
let params = conn.split(/\s+/);
|
|
678
|
+
if (params.length > 1) {
|
|
679
|
+
await this.adb.removePortForward(params[1].replace(/[\D]*/, ''));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch (e) {
|
|
684
|
+
const err = /** @type {Error} */ (e);
|
|
685
|
+
this.log.warn(`Unable to clean forwarded ports. Error: '${err.message}'. Continuing.`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
566
688
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
689
|
+
async hasWorkingWebview() {
|
|
690
|
+
// sometimes chromedriver stops automating webviews. this method runs a
|
|
691
|
+
// simple command to determine our state, and responds accordingly
|
|
692
|
+
try {
|
|
693
|
+
await this.jwproxy.command('/url', 'GET');
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
catch (e) {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
574
699
|
}
|
|
575
|
-
}
|
|
576
700
|
}
|
|
577
701
|
exports.Chromedriver = Chromedriver;
|
|
578
702
|
Chromedriver.EVENT_ERROR = 'chromedriver_error';
|
|
@@ -582,6 +706,8 @@ Chromedriver.STATE_STARTING = 'starting';
|
|
|
582
706
|
Chromedriver.STATE_ONLINE = 'online';
|
|
583
707
|
Chromedriver.STATE_STOPPING = 'stopping';
|
|
584
708
|
Chromedriver.STATE_RESTARTING = 'restarting';
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJORVdfQ0RfVkVSU0lPTl9GT1JNQVRfTUFKT1JfVkVSU0lPTiIsIkRFRkFVTFRfSE9TVCIsIk1JTl9DRF9WRVJTSU9OX1dJVEhfVzNDX1NVUFBPUlQiLCJERUZBVUxUX1BPUlQiLCJDSFJPTUVfQlVORExFX0lEIiwiV0VCVklFV19TSEVMTF9CVU5ETEVfSUQiLCJXRUJWSUVXX0JVTkRMRV9JRFMiLCJWRVJTSU9OX1BBVFRFUk4iLCJDRF9WRVJTSU9OX1RJTUVPVVQiLCJDaHJvbWVkcml2ZXIiLCJldmVudHMiLCJFdmVudEVtaXR0ZXIiLCJjb25zdHJ1Y3RvciIsImFyZ3MiLCJob3N0IiwicG9ydCIsInVzZVN5c3RlbUV4ZWN1dGFibGUiLCJleGVjdXRhYmxlIiwiZXhlY3V0YWJsZURpciIsImdldENocm9tZWRyaXZlckRpciIsImJ1bmRsZUlkIiwibWFwcGluZ1BhdGgiLCJjbWRBcmdzIiwiYWRiIiwidmVyYm9zZSIsImxvZ1BhdGgiLCJkaXNhYmxlQnVpbGRDaGVjayIsImRldGFpbHMiLCJpc0F1dG9kb3dubG9hZEVuYWJsZWQiLCJfbG9nIiwibG9nZ2VyIiwiZ2V0TG9nZ2VyIiwiZ2VuZXJhdGVMb2dQcmVmaXgiLCJwcm94eUhvc3QiLCJwcm94eVBvcnQiLCJwcm9jIiwiY2hyb21lZHJpdmVyIiwiZXhlY3V0YWJsZVZlcmlmaWVkIiwic3RhdGUiLCJTVEFURV9TVE9QUEVEIiwiandwcm94eSIsIkpXUHJveHkiLCJzZXJ2ZXIiLCJsb2ciLCJzdG9yYWdlQ2xpZW50IiwiQ2hyb21lZHJpdmVyU3RvcmFnZUNsaWVudCIsImNocm9tZWRyaXZlckRpciIsImNhcGFiaWxpdGllcyIsImRlc2lyZWRQcm90b2NvbCIsIlBST1RPQ09MUyIsIk1KU09OV1AiLCJnZXREcml2ZXJzTWFwcGluZyIsIm1hcHBpbmciLCJfIiwiY2xvbmVEZWVwIiwiQ0hST01FRFJJVkVSX0NIUk9NRV9NQVBQSU5HIiwiZGVidWciLCJmcyIsImV4aXN0cyIsIndhcm4iLCJpbmZvIiwiSlNPTiIsInBhcnNlIiwicmVhZEZpbGUiLCJlcnIiLCJtZXNzYWdlIiwiY2RWZXJzaW9uIiwiY2hyb21lVmVyc2lvbiIsInRvUGFpcnMiLCJjb2VyY2VkVmVyc2lvbiIsInNlbXZlciIsImNvZXJjZSIsInZlcnNpb24iLCJnZXRDaHJvbWVkcml2ZXJzIiwiZXhlY3V0YWJsZXMiLCJnbG9iIiwiY3dkIiwic3RyaWN0Iiwibm9kaXIiLCJhYnNvbHV0ZSIsInV0aWwiLCJwbHVyYWxpemUiLCJsZW5ndGgiLCJjZHMiLCJhc3luY21hcCIsImxvZ0Vycm9yIiwic3Rkb3V0Iiwic3RkZXJyIiwiZXJyTXNnIiwicGF0aCIsImJhc2VuYW1lIiwiZXhlYyIsInRpbWVvdXQiLCJpbmNsdWRlcyIsIm1hdGNoIiwibWluQ2hyb21lVmVyc2lvbiIsIm1ham9yIiwibWlub3IiLCJmaWx0ZXIiLCJjZCIsInNvcnQiLCJhIiwiYiIsImNvbXBhcmVWZXJzaW9ucyIsImlzRW1wdHkiLCJnZXRDaHJvbWVWZXJzaW9uIiwiQnJvd3NlciIsInZlcnNpb25NYXRjaCIsImFwaUxldmVsIiwiZ2V0QXBpTGV2ZWwiLCJ1cGRhdGVEcml2ZXJzTWFwcGluZyIsIm5ld01hcHBpbmciLCJzaG91bGRVcGRhdGVTdGF0aWNNYXBwaW5nIiwid3JpdGVGaWxlIiwic3RyaW5naWZ5IiwiZSIsIk9iamVjdCIsImFzc2lnbiIsImdldENvbXBhdGlibGVDaHJvbWVkcml2ZXIiLCJnZXRDaHJvbWVkcml2ZXJCaW5hcnlQYXRoIiwidmFsdWVzIiwiZGlkU3RvcmFnZVN5bmMiLCJzeW5jQ2hyb21lZHJpdmVycyIsInJldHJpZXZlZE1hcHBpbmciLCJyZXRyaWV2ZU1hcHBpbmciLCJkcml2ZXJLZXlzIiwic3luY0RyaXZlcnMiLCJtaW5Ccm93c2VyVmVyc2lvbiIsInN5bmNocm9uaXplZERyaXZlcnNNYXBwaW5nIiwicmVkdWNlIiwiYWNjIiwieCIsIm1pc3NpbmdWZXJzaW9ucyIsImNvZXJjZWRWZXIiLCJzaXplIiwiZXJyb3JBbmRUaHJvdyIsIm1hdGNoaW5nRHJpdmVycyIsIm1pbkNocm9tZVZlcnNpb25TIiwiZ3RlIiwiQ0RfQ0ROIiwic3RhY2siLCJhdXRvZG93bmxvYWRTdWdnZXN0aW9uIiwiRXJyb3IiLCJiaW5QYXRoIiwiaW5pdENocm9tZWRyaXZlclBhdGgiLCJzeW5jUHJvdG9jb2wiLCJXM0MiLCJjaHJvbWVPcHRpb25zIiwiZ2V0Q2FwVmFsdWUiLCJ3M2MiLCJ0b1czY0NhcE5hbWVzIiwic3RhcnQiLCJjYXBzIiwiZW1pdFN0YXJ0aW5nU3RhdGUiLCJsb2dnaW5nUHJlZnMiLCJicm93c2VyIiwiY2hhbmdlU3RhdGUiLCJTVEFURV9TVEFSVElORyIsImFkYlBvcnQiLCJwdXNoIiwiaXNBcnJheSIsInN0YXJ0RGV0ZWN0b3IiLCJzdGFydHNXaXRoIiwicHJvY2Vzc0lzQWxpdmUiLCJ3ZWJ2aWV3VmVyc2lvbiIsImtpbGxBbGwiLCJTdWJQcm9jZXNzIiwib24iLCJvdXQiLCJsaW5lIiwidHJpbSIsInNwbGl0IiwiZXJyb3IiLCJjb2RlIiwic2lnbmFsIiwiU1RBVEVfU1RPUFBJTkciLCJTVEFURV9SRVNUQVJUSU5HIiwibXNnIiwiam9pbiIsIndhaXRGb3JPbmxpbmUiLCJzdGFydFNlc3Npb24iLCJlbWl0IiwiRVZFTlRfRVJST1IiLCJzdG9wIiwidmVyc2lvbnNTdXBwb3J0ZWRCeURyaXZlciIsInNlc3Npb25JZCIsIlNUQVRFX09OTElORSIsInJlc3RhcnQiLCJjaHJvbWVkcml2ZXJTdG9wcGVkIiwicmV0cnlJbnRlcnZhbCIsImdldFN0YXR1cyIsImNvbW1hbmQiLCJzZXNzaW9uQ2FwcyIsImFsd2F5c01hdGNoIiwiZGVzaXJlZENhcGFiaWxpdGllcyIsInByZWZpeCIsImVtaXRTdGF0ZXMiLCJydW5TYWZlU3RlcCIsImYiLCJFVkVOVF9DSEFOR0VEIiwic2VuZENvbW1hbmQiLCJ1cmwiLCJtZXRob2QiLCJib2R5IiwicHJveHlSZXEiLCJyZXEiLCJyZXMiLCJwcm94eVJlcVJlcyIsImNtZCIsInN5c3RlbSIsImlzV2luZG93cyIsIkIiLCJwcm9taXNpZnkiLCJjcCIsInVkaWRJbmRleCIsImRlZmF1bHRBcmdzIiwiZmluZEluZGV4IiwiaXRlbSIsInVkaWQiLCJjb25uIiwiZ2V0Rm9yd2FyZExpc3QiLCJwYXJhbXMiLCJyZW1vdmVQb3J0Rm9yd2FyZCIsInJlcGxhY2UiLCJoYXNXb3JraW5nV2VidmlldyJdLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9jaHJvbWVkcml2ZXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gdHJhbnNwaWxlOm1haW5cblxuaW1wb3J0IGV2ZW50cyBmcm9tICdldmVudHMnO1xuaW1wb3J0IHsgSldQcm94eSwgUFJPVE9DT0xTIH0gZnJvbSAnQGFwcGl1bS9iYXNlLWRyaXZlcic7XG5pbXBvcnQgY3AgZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgeyBzeXN0ZW0sIGZzLCBsb2dnZXIsIHV0aWwgfSBmcm9tICdAYXBwaXVtL3N1cHBvcnQnO1xuaW1wb3J0IHsgcmV0cnlJbnRlcnZhbCwgYXN5bmNtYXAgfSBmcm9tICdhc3luY2JveCc7XG5pbXBvcnQgeyBTdWJQcm9jZXNzLCBleGVjIH0gZnJvbSAndGVlbl9wcm9jZXNzJztcbmltcG9ydCBCIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCB7XG4gIGdldENocm9tZVZlcnNpb24sIGdldENocm9tZWRyaXZlckRpciwgQ0hST01FRFJJVkVSX0NIUk9NRV9NQVBQSU5HLFxuICBnZXRDaHJvbWVkcml2ZXJCaW5hcnlQYXRoLCBDRF9DRE4sIGdlbmVyYXRlTG9nUHJlZml4XG59IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHNlbXZlciBmcm9tICdzZW12ZXInO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgY29tcGFyZVZlcnNpb25zIH0gZnJvbSAnY29tcGFyZS12ZXJzaW9ucyc7XG5pbXBvcnQgQ2hyb21lZHJpdmVyU3RvcmFnZUNsaWVudCBmcm9tICcuL3N0b3JhZ2UtY2xpZW50JztcbmltcG9ydCB7IHRvVzNjQ2FwTmFtZXMsIGdldENhcFZhbHVlIH0gZnJvbSAnLi9wcm90b2NvbC1oZWxwZXJzJztcblxuY29uc3QgTkVXX0NEX1ZFUlNJT05fRk9STUFUX01BSk9SX1ZFUlNJT04gPSA3MztcbmNvbnN0IERFRkFVTFRfSE9TVCA9ICcxMjcuMC4wLjEnO1xuY29uc3QgTUlOX0NEX1ZFUlNJT05fV0lUSF9XM0NfU1VQUE9SVCA9IDc1O1xuY29uc3QgREVGQVVMVF9QT1JUID0gOTUxNTtcbmNvbnN0IENIUk9NRV9CVU5ETEVfSUQgPSAnY29tLmFuZHJvaWQuY2hyb21lJztcbmNvbnN0IFdFQlZJRVdfU0hFTExfQlVORExFX0lEID0gJ29yZy5jaHJvbWl1bS53ZWJ2aWV3X3NoZWxsJztcbmNvbnN0IFdFQlZJRVdfQlVORExFX0lEUyA9IFtcbiAgJ2NvbS5nb29nbGUuYW5kcm9pZC53ZWJ2aWV3JyxcbiAgJ2NvbS5hbmRyb2lkLndlYnZpZXcnLFxuXTtcbmNvbnN0IFZFUlNJT05fUEFUVEVSTiA9IC8oW1xcZC5dKykvO1xuXG5jb25zdCBDRF9WRVJTSU9OX1RJTUVPVVQgPSA1MDAwO1xuXG5jbGFzcyBDaHJvbWVkcml2ZXIgZXh0ZW5kcyBldmVudHMuRXZlbnRFbWl0dGVyIHtcbiAgY29uc3RydWN0b3IgKGFyZ3MgPSB7fSkge1xuICAgIHN1cGVyKCk7XG5cbiAgICBjb25zdCB7XG4gICAgICBob3N0ID0gREVGQVVMVF9IT1NULFxuICAgICAgcG9ydCA9IERFRkFVTFRfUE9SVCxcbiAgICAgIHVzZVN5c3RlbUV4ZWN1dGFibGUgPSBmYWxzZSxcbiAgICAgIGV4ZWN1dGFibGUsXG4gICAgICBleGVjdXRhYmxlRGlyID0gZ2V0Q2hyb21lZHJpdmVyRGlyKCksXG4gICAgICBidW5kbGVJZCxcbiAgICAgIG1hcHBpbmdQYXRoLFxuICAgICAgY21kQXJncyxcbiAgICAgIGFkYixcbiAgICAgIHZlcmJvc2UsXG4gICAgICBsb2dQYXRoLFxuICAgICAgZGlzYWJsZUJ1aWxkQ2hlY2ssXG4gICAgICBkZXRhaWxzLFxuICAgICAgaXNBdXRvZG93bmxvYWRFbmFibGVkID0gZmFsc2UsXG4gICAgfSA9IGFyZ3M7XG4gICAgdGhpcy5fbG9nID0gbG9nZ2VyLmdldExvZ2dlcihnZW5lcmF0ZUxvZ1ByZWZpeCh0aGlzKSk7XG5cbiAgICB0aGlzLnByb3h5SG9zdCA9IGhvc3Q7XG4gICAgdGhpcy5wcm94eVBvcnQgPSBwb3J0O1xuICAgIHRoaXMuYWRiID0gYWRiO1xuICAgIHRoaXMuY21kQXJncyA9IGNtZEFyZ3M7XG4gICAgdGhpcy5wcm9jID0gbnVsbDtcbiAgICB0aGlzLnVzZVN5c3RlbUV4ZWN1dGFibGUgPSB1c2VTeXN0ZW1FeGVjdXRhYmxlO1xuICAgIHRoaXMuY2hyb21lZHJpdmVyID0gZXhlY3V0YWJsZTtcbiAgICB0aGlzLmV4ZWN1dGFibGVEaXIgPSBleGVjdXRhYmxlRGlyO1xuICAgIHRoaXMubWFwcGluZ1BhdGggPSBtYXBwaW5nUGF0aDtcbiAgICB0aGlzLmJ1bmRsZUlkID0gYnVuZGxlSWQ7XG4gICAgdGhpcy5leGVjdXRhYmxlVmVyaWZpZWQgPSBmYWxzZTtcbiAgICB0aGlzLnN0YXRlID0gQ2hyb21lZHJpdmVyLlNUQVRFX1NUT1BQRUQ7XG4gICAgdGhpcy5qd3Byb3h5ID0gbmV3IEpXUHJveHkoe1xuICAgICAgc2VydmVyOiB0aGlzLnByb3h5SG9zdCxcbiAgICAgIHBvcnQ6IHRoaXMucHJveHlQb3J0LFxuICAgICAgbG9nOiB0aGlzLl9sb2csXG4gICAgfSk7XG4gICAgdGhpcy52ZXJib3NlID0gdmVyYm9zZTtcbiAgICB0aGlzLmxvZ1BhdGggPSBsb2dQYXRoO1xuICAgIHRoaXMuZGlzYWJsZUJ1aWxkQ2hlY2sgPSAhIWRpc2FibGVCdWlsZENoZWNrO1xuICAgIHRoaXMuc3RvcmFnZUNsaWVudCA9IGlzQXV0b2Rvd25sb2FkRW5hYmxlZFxuICAgICAgPyBuZXcgQ2hyb21lZHJpdmVyU3RvcmFnZUNsaWVudCh7IGNocm9tZWRyaXZlckRpcjogdGhpcy5leGVjdXRhYmxlRGlyIH0pXG4gICAgICA6IG51bGw7XG4gICAgdGhpcy5kZXRhaWxzID0gZGV0YWlscztcbiAgICB0aGlzLmNhcGFiaWxpdGllcyA9IHt9O1xuICAgIHRoaXMuZGVzaXJlZFByb3RvY29sID0gUFJPVE9DT0xTLk1KU09OV1A7XG4gIH1cblxuICBnZXQgbG9nICgpIHtcbiAgICByZXR1cm4gdGhpcy5fbG9nO1xuICB9XG5cbiAgYXN5bmMgZ2V0RHJpdmVyc01hcHBpbmcgKCkge1xuICAgIGxldCBtYXBwaW5nID0gXy5jbG9uZURlZXAoQ0hST01FRFJJVkVSX0NIUk9NRV9NQVBQSU5HKTtcbiAgICBpZiAodGhpcy5tYXBwaW5nUGF0aCkge1xuICAgICAgdGhpcy5sb2cuZGVidWcoYEF0dGVtcHRpbmcgdG8gdXNlIENocm9tZWRyaXZlci0+Q2hyb21lIG1hcHBpbmcgZnJvbSAnJHt0aGlzLm1hcHBpbmdQYXRofSdgKTtcbiAgICAgIGlmICghYXdhaXQgZnMuZXhpc3RzKHRoaXMubWFwcGluZ1BhdGgpKSB7XG4gICAgICAgIHRoaXMubG9nLndhcm4oYE5vIGZpbGUgZm91bmQgYXQgJyR7dGhpcy5tYXBwaW5nUGF0aH0nYCk7XG4gICAgICAgIHRoaXMubG9nLmluZm8oJ0RlZmF1bHRpbmcgdG8gdGhlIHN0YXRpYyBDaHJvbWVkcml2ZXItPkNocm9tZSBtYXBwaW5nJyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIG1hcHBpbmcgPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKHRoaXMubWFwcGluZ1BhdGgsICd1dGY4JykpO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICB0aGlzLmxvZy53YXJuKGBFcnJvciBwYXJzaW5nIG1hcHBpbmcgZnJvbSAnJHt0aGlzLm1hcHBpbmdQYXRofSc6ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICAgICAgdGhpcy5sb2cuaW5mbygnRGVmYXVsdGluZyB0byB0aGUgc3RhdGljIENocm9tZWRyaXZlci0+Q2hyb21lIG1hcHBpbmcnKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZygnVXNpbmcgdGhlIHN0YXRpYyBDaHJvbWVkcml2ZXItPkNocm9tZSBtYXBwaW5nJyk7XG4gICAgfVxuXG4gICAgLy8gbWFrZSBzdXJlIHRoYXQgdGhlIHZhbHVlcyBmb3IgbWluaW11bSBjaHJvbWUgdmVyc2lvbiBhcmUgc2VtdmVyIGNvbXBsaWFudFxuICAgIGZvciAoY29uc3QgW2NkVmVyc2lvbiwgY2hyb21lVmVyc2lvbl0gb2YgXy50b1BhaXJzKG1hcHBpbmcpKSB7XG4gICAgICBjb25zdCBjb2VyY2VkVmVyc2lvbiA9IHNlbXZlci5jb2VyY2UoY2hyb21lVmVyc2lvbik7XG4gICAgICBpZiAoY29lcmNlZFZlcnNpb24pIHtcbiAgICAgICAgbWFwcGluZ1tjZFZlcnNpb25dID0gY29lcmNlZFZlcnNpb24udmVyc2lvbjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMubG9nLmluZm8oYCcke2Nocm9tZVZlcnNpb259JyBpcyBub3QgYSB2YWxpZCB2ZXJzaW9uIG51bWJlci4gU2tpcHBpbmcgaXRgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1hcHBpbmc7XG4gIH1cblxuICBhc3luYyBnZXRDaHJvbWVkcml2ZXJzIChtYXBwaW5nKSB7XG4gICAgLy8gZ28gdGhyb3VnaCB0aGUgdmVyc2lvbnMgYXZhaWxhYmxlXG4gICAgY29uc3QgZXhlY3V0YWJsZXMgPSBhd2FpdCBmcy5nbG9iKCcqJywge1xuICAgICAgY3dkOiB0aGlzLmV4ZWN1dGFibGVEaXIsXG4gICAgICBzdHJpY3Q6IGZhbHNlLFxuICAgICAgbm9kaXI6IHRydWUsXG4gICAgICBhYnNvbHV0ZTogdHJ1ZSxcbiAgICB9KTtcbiAgICB0aGlzLmxvZy5kZWJ1ZyhgRm91bmQgJHt1dGlsLnBsdXJhbGl6ZSgnZXhlY3V0YWJsZScsIGV4ZWN1dGFibGVzLmxlbmd0aCwgdHJ1ZSl9IGAgK1xuICAgICAgYGluICcke3RoaXMuZXhlY3V0YWJsZURpcn0nYCk7XG4gICAgY29uc3QgY2RzID0gKGF3YWl0IGFzeW5jbWFwKGV4ZWN1dGFibGVzLCBhc3luYyAoZXhlY3V0YWJsZSkgPT4ge1xuICAgICAgY29uc3QgbG9nRXJyb3IgPSAoe21lc3NhZ2UsIHN0ZG91dCA9IG51bGwsIHN0ZGVyciA9IG51bGx9KSA9PiB7XG4gICAgICAgIGxldCBlcnJNc2cgPSBgQ2Fubm90IHJldHJpZXZlIHZlcnNpb24gbnVtYmVyIGZyb20gJyR7cGF0aC5iYXNlbmFtZShleGVjdXRhYmxlKX0nIENocm9tZWRyaXZlciBiaW5hcnkuIGAgK1xuICAgICAgICAgIGBNYWtlIHN1cmUgaXQgcmV0dXJucyBhIHZhbGlkIHZlcnNpb24gc3RyaW5nIGluIHJlc3BvbnNlIHRvICctLXZlcnNpb24nIGNvbW1hbmQgbGluZSBhcmd1bWVudC4gJHttZXNzYWdlfWA7XG4gICAgICAgIGlmIChzdGRvdXQpIHtcbiAgICAgICAgICBlcnJNc2cgKz0gYFxcblN0ZG91dDogJHtzdGRvdXR9YDtcbiAgICAgICAgfVxuICAgICAgICBpZiAoc3RkZXJyKSB7XG4gICAgICAgICAgZXJyTXNnICs9IGBcXG5TdGRlcnI6ICR7c3RkZXJyfWA7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sb2cud2FybihlcnJNc2cpO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH07XG5cbiAgICAgIGxldCBzdGRvdXQ7XG4gICAgICBsZXQgc3RkZXJyO1xuICAgICAgdHJ5IHtcbiAgICAgICAgKHtzdGRvdXQsIHN0ZGVycn0gPSBhd2FpdCBleGVjKGV4ZWN1dGFibGUsIFsnLS12ZXJzaW9uJ10sIHtcbiAgICAgICAgICB0aW1lb3V0OiBDRF9WRVJTSU9OX1RJTUVPVVQsXG4gICAgICAgIH0pKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBpZiAoIShlcnIubWVzc2FnZSB8fCAnJykuaW5jbHVkZXMoJ3RpbWVkIG91dCcpICYmICEoZXJyLnN0ZG91dCB8fCAnJykuaW5jbHVkZXMoJ1N0YXJ0aW5nIENocm9tZURyaXZlcicpKSB7XG4gICAgICAgICAgcmV0dXJuIGxvZ0Vycm9yKGVycik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpZiB0aGlzIGhhcyB0aW1lZCBvdXQsIGl0IGhhcyBhY3R1YWxseSBzdGFydGVkIENocm9tZWRyaXZlcixcbiAgICAgICAgLy8gaW4gd2hpY2ggY2FzZSB0aGVyZSB3aWxsIGFsc28gYmUgdGhlIHZlcnNpb24gc3RyaW5nIGluIHRoZSBvdXRwdXRcbiAgICAgICAgc3Rkb3V0ID0gZXJyLnN0ZG91dDtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbWF0Y2ggPSAvQ2hyb21lRHJpdmVyXFxzK1xcKD92PyhbXFxkLl0rKVxcKT8vaS5leGVjKHN0ZG91dCk7IC8vIGh0dHBzOi8vcmVnZXgxMDEuY29tL3IvenBqNXdBLzFcbiAgICAgIGlmICghbWF0Y2gpIHtcbiAgICAgICAgcmV0dXJuIGxvZ0Vycm9yKHttZXNzYWdlOiAnQ2Fubm90IHBhcnNlIHRoZSB2ZXJzaW9uIHN0cmluZycsIHN0ZG91dCwgc3RkZXJyfSk7XG4gICAgICB9XG4gICAgICBsZXQgdmVyc2lvbiA9IG1hdGNoWzFdO1xuICAgICAgbGV0IG1pbkNocm9tZVZlcnNpb24gPSBtYXBwaW5nW3ZlcnNpb25dO1xuICAgICAgY29uc3QgY29lcmNlZFZlcnNpb24gPSBzZW12ZXIuY29lcmNlKHZlcnNpb24pO1xuICAgICAgaWYgKGNvZXJjZWRWZXJzaW9uKSB7XG4gICAgICAgIC8vIGJlZm9yZSAyMDE5LTAzLTA2IHZlcnNpb25zIHdlcmUgb2YgdGhlIGZvcm0gbWFqb3IubWlub3JcbiAgICAgICAgaWYgKGNvZXJjZWRWZXJzaW9uLm1ham9yIDwgTkVXX0NEX1ZFUlNJT05fRk9STUFUX01BSk9SX1ZFUlNJT04pIHtcbiAgICAgICAgICB2ZXJzaW9uID0gYCR7Y29lcmNlZFZlcnNpb24ubWFqb3J9LiR7Y29lcmNlZFZlcnNpb24ubWlub3J9YDtcbiAgICAgICAgICBtaW5DaHJvbWVWZXJzaW9uID0gbWFwcGluZ1t2ZXJzaW9uXTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIW1pbkNocm9tZVZlcnNpb24gJiYgY29lcmNlZFZlcnNpb24ubWFqb3IgPj0gTkVXX0NEX1ZFUlNJT05fRk9STUFUX01BSk9SX1ZFUlNJT04pIHtcbiAgICAgICAgICAvLyBBc3N1bWUgdGhlIG1ham9yIENocm9tZSB2ZXJzaW9uIGlzIHRoZSBzYW1lIGFzIHRoZSBjb3JyZXNwb25kaW5nIGRyaXZlciBtYWpvciB2ZXJzaW9uXG4gICAgICAgICAgbWluQ2hyb21lVmVyc2lvbiA9IGAke2NvZXJjZWRWZXJzaW9uLm1ham9yfWA7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGV4ZWN1dGFibGUsXG4gICAgICAgIHZlcnNpb24sXG4gICAgICAgIG1pbkNocm9tZVZlcnNpb24sXG4gICAgICB9O1xuICAgIH0pKVxuICAgICAgLmZpbHRlcigoY2QpID0+ICEhY2QpXG4gICAgICAuc29ydCgoYSwgYikgPT4gY29tcGFyZVZlcnNpb25zKGIudmVyc2lvbiwgYS52ZXJzaW9uKSk7XG4gICAgaWYgKF8uaXNFbXB0eShjZHMpKSB7XG4gICAgICB0aGlzLmxvZy5pbmZvKGBObyBDaHJvbWVkcml2ZXJzIHdlcmUgZm91bmQgaW4gJyR7dGhpcy5leGVjdXRhYmxlRGlyfSdgKTtcbiAgICAgIHJldHVybiBjZHM7XG4gICAgfVxuICAgIHRoaXMubG9nLmRlYnVnKGBUaGUgZm9sbG93aW5nIENocm9tZWRyaXZlciBleGVjdXRhYmxlcyB3ZXJlIGZvdW5kOmApO1xuICAgIGZvciAoY29uc3QgY2Qgb2YgY2RzKSB7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZyhgICAgICcke2NkLmV4ZWN1dGFibGV9JyAodmVyc2lvbiAnJHtjZC52ZXJzaW9ufScsIG1pbmltdW0gQ2hyb21lIHZlcnNpb24gJyR7Y2QubWluQ2hyb21lVmVyc2lvbiA/IGNkLm1pbkNocm9tZVZlcnNpb24gOiAnVW5rbm93bid9JylgKTtcbiAgICB9XG4gICAgcmV0dXJuIGNkcztcbiAgfVxuXG4gIGFzeW5jIGdldENocm9tZVZlcnNpb24gKCkge1xuICAgIC8vIFRyeSB0byByZXRyaWV2ZSB0aGUgdmVyc2lvbiBmcm9tIGBkZXRhaWxzYCBwcm9wZXJ0eSBpZiBpdCBpcyBzZXRcbiAgICAvLyBUaGUgYGluZm9gIGl0ZW0gbXVzdCBjb250YWluIHRoZSBvdXRwdXQgb2YgL2pzb24vdmVyc2lvbiBDRFAgY29tbWFuZFxuICAgIC8vIHdoZXJlIGBCcm93c2VyYCBmaWVsZCBsb29rcyBsaWtlIGBDaHJvbWUvNzIuMC4zNjAxLjBgYFxuICAgIGlmICh0aGlzLmRldGFpbHM/LmluZm8pIHtcbiAgICAgIHRoaXMubG9nLmRlYnVnKGBCcm93c2VyIHZlcnNpb24gaW4gdGhlIHN1cHBsaWVkIGRldGFpbHM6ICR7dGhpcy5kZXRhaWxzPy5pbmZvPy5Ccm93c2VyfWApO1xuICAgIH1cbiAgICBjb25zdCB2ZXJzaW9uTWF0Y2ggPSBWRVJTSU9OX1BBVFRFUk4uZXhlYyh0aGlzLmRldGFpbHM/LmluZm8/LkJyb3dzZXIpO1xuICAgIGlmICh2ZXJzaW9uTWF0Y2gpIHtcbiAgICAgIGNvbnN0IGNvZXJjZWRWZXJzaW9uID0gc2VtdmVyLmNvZXJjZSh2ZXJzaW9uTWF0Y2hbMV0pO1xuICAgICAgaWYgKGNvZXJjZWRWZXJzaW9uKSB7XG4gICAgICAgIHJldHVybiBjb2VyY2VkVmVyc2lvbjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsZXQgY2hyb21lVmVyc2lvbjtcblxuICAgIC8vIGluIGNhc2Ugb2YgV2ViVmlldyBCcm93c2VyIFRlc3Rlciwgc2ltcGx5IHRyeSB0byBmaW5kIHRoZSB1bmRlcmx5aW5nIHdlYnZpZXdcbiAgICBpZiAodGhpcy5idW5kbGVJZCA9PT0gV0VCVklFV19TSEVMTF9CVU5ETEVfSUQpIHtcbiAgICAgIGZvciAoY29uc3QgYnVuZGxlSWQgb2YgV0VCVklFV19CVU5ETEVfSURTKSB7XG4gICAgICAgIGNocm9tZVZlcnNpb24gPSBhd2FpdCBnZXRDaHJvbWVWZXJzaW9uKHRoaXMuYWRiLCBidW5kbGVJZCk7XG4gICAgICAgIGlmIChjaHJvbWVWZXJzaW9uKSB7XG4gICAgICAgICAgdGhpcy5idW5kbGVJZCA9IGJ1bmRsZUlkO1xuICAgICAgICAgIHJldHVybiBzZW12ZXIuY29lcmNlKGNocm9tZVZlcnNpb24pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBvbiBBbmRyb2lkIDctOSB3ZWJ2aWV3cyBhcmUgYmFja2VkIGJ5IHRoZSBtYWluIENocm9tZSwgbm90IHRoZSBzeXN0ZW0gd2Vidmlld1xuICAgIGlmICh0aGlzLmFkYikge1xuICAgICAgY29uc3QgYXBpTGV2ZWwgPSBhd2FpdCB0aGlzLmFkYi5nZXRBcGlMZXZlbCgpO1xuICAgICAgaWYgKGFwaUxldmVsID49IDI0ICYmIGFwaUxldmVsIDw9IDI4ICYmXG4gICAgICAgICAgW1dFQlZJRVdfU0hFTExfQlVORExFX0lELCAuLi5XRUJWSUVXX0JVTkRMRV9JRFNdLmluY2x1ZGVzKHRoaXMuYnVuZGxlSWQpKSB7XG4gICAgICAgIHRoaXMuYnVuZGxlSWQgPSBDSFJPTUVfQlVORExFX0lEO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRyeSBvdXQgd2Vidmlld3Mgd2hlbiBubyBidW5kbGUgaWQgaXMgc2VudCBpblxuICAgIGlmICghdGhpcy5idW5kbGVJZCkge1xuICAgICAgLy8gZGVmYXVsdCB0byB0aGUgZ2VuZXJpYyBDaHJvbWUgYnVuZGxlXG4gICAgICB0aGlzLmJ1bmRsZUlkID0gQ0hST01FX0JVTkRMRV9JRDtcblxuICAgICAgLy8gd2UgaGF2ZSBhIHdlYnZpZXcgb2Ygc29tZSBzb3J0LCBzbyB0cnkgdG8gZmluZCB0aGUgYnVuZGxlIHZlcnNpb25cbiAgICAgIGZvciAoY29uc3QgYnVuZGxlSWQgb2YgV0VCVklFV19CVU5ETEVfSURTKSB7XG4gICAgICAgIGNocm9tZVZlcnNpb24gPSBhd2FpdCBnZXRDaHJvbWVWZXJzaW9uKHRoaXMuYWRiLCBidW5kbGVJZCk7XG4gICAgICAgIGlmIChjaHJvbWVWZXJzaW9uKSB7XG4gICAgICAgICAgdGhpcy5idW5kbGVJZCA9IGJ1bmRsZUlkO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gaWYgd2UgZG8gbm90IGhhdmUgYSBjaHJvbWUgdmVyc2lvbiwgaXQgbXVzdCBub3QgYmUgYSB3ZWJ2aWV3XG4gICAgaWYgKCFjaHJvbWVWZXJzaW9uKSB7XG4gICAgICBjaHJvbWVWZXJzaW9uID0gYXdhaXQgZ2V0Q2hyb21lVmVyc2lvbih0aGlzLmFkYiwgdGhpcy5idW5kbGVJZCk7XG4gICAgfVxuXG4gICAgLy8gbWFrZSBzdXJlIGl0IGlzIHNlbXZlciwgc28gbGF0ZXIgY2hlY2tzIHdvbid0IGZhaWxcbiAgICByZXR1cm4gY2hyb21lVmVyc2lvbiA/IHNlbXZlci5jb2VyY2UoY2hyb21lVmVyc2lvbikgOiBudWxsO1xuICB9XG5cbiAgYXN5bmMgdXBkYXRlRHJpdmVyc01hcHBpbmcgKG5ld01hcHBpbmcpIHtcbiAgICBsZXQgc2hvdWxkVXBkYXRlU3RhdGljTWFwcGluZyA9IHRydWU7XG4gICAgaWYgKGF3YWl0IGZzLmV4aXN0cyh0aGlzLm1hcHBpbmdQYXRoKSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZnMud3JpdGVGaWxlKHRoaXMubWFwcGluZ1BhdGgsIEpTT04uc3RyaW5naWZ5KG5ld01hcHBpbmcsIG51bGwsIDIpLCAndXRmOCcpO1xuICAgICAgICBzaG91bGRVcGRhdGVTdGF0aWNNYXBwaW5nID0gZmFsc2U7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRoaXMubG9nLndhcm4oYENhbm5vdCBzdG9yZSB0aGUgdXBkYXRlZCBjaHJvbWVkcml2ZXJzIG1hcHBpbmcgaW50byAnJHt0aGlzLm1hcHBpbmdQYXRofScuIGAgK1xuICAgICAgICAgIGBUaGlzIG1heSByZWR1Y2UgdGhlIHBlcmZvcm1hbmNlIG9mIGZ1cnRoZXIgZXhlY3V0aW9ucy4gT3JpZ2luYWwgZXJyb3I6ICR7ZS5tZXNzYWdlfWApO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoc2hvdWxkVXBkYXRlU3RhdGljTWFwcGluZykge1xuICAgICAgT2JqZWN0LmFzc2lnbihDSFJPTUVEUklWRVJfQ0hST01FX01BUFBJTkcsIG5ld01hcHBpbmcpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGdldENvbXBhdGlibGVDaHJvbWVkcml2ZXIgKCkge1xuICAgIGlmICghdGhpcy5hZGIpIHtcbiAgICAgIHJldHVybiBhd2FpdCBnZXRDaHJvbWVkcml2ZXJCaW5hcnlQYXRoKCk7XG4gICAgfVxuXG4gICAgY29uc3QgbWFwcGluZyA9IGF3YWl0IHRoaXMuZ2V0RHJpdmVyc01hcHBpbmcoKTtcbiAgICBpZiAoIV8uaXNFbXB0eShtYXBwaW5nKSkge1xuICAgICAgdGhpcy5sb2cuZGVidWcoYFRoZSBtb3N0IHJlY2VudCBrbm93biBDaHJvbWUgdmVyc2lvbjogJHtfLnZhbHVlcyhtYXBwaW5nKVswXX1gKTtcbiAgICB9XG5cbiAgICBsZXQgZGlkU3RvcmFnZVN5bmMgPSBmYWxzZTtcbiAgICBjb25zdCBzeW5jQ2hyb21lZHJpdmVycyA9IGFzeW5jIChjaHJvbWVWZXJzaW9uKSA9PiB7XG4gICAgICBkaWRTdG9yYWdlU3luYyA9IHRydWU7XG4gICAgICBjb25zdCByZXRyaWV2ZWRNYXBwaW5nID0gYXdhaXQgdGhpcy5zdG9yYWdlQ2xpZW50LnJldHJpZXZlTWFwcGluZygpO1xuICAgICAgdGhpcy5sb2cuZGVidWcoJ0dvdCBjaHJvbWVkcml2ZXJzIG1hcHBpbmcgZnJvbSB0aGUgc3RvcmFnZTogJyArXG4gICAgICAgIEpTT04uc3RyaW5naWZ5KHJldHJpZXZlZE1hcHBpbmcsIG51bGwsIDIpKTtcbiAgICAgIGNvbnN0IGRyaXZlcktleXMgPSBhd2FpdCB0aGlzLnN0b3JhZ2VDbGllbnQuc3luY0RyaXZlcnMoe1xuICAgICAgICBtaW5Ccm93c2VyVmVyc2lvbjogY2hyb21lVmVyc2lvbi5tYWpvcixcbiAgICAgIH0pO1xuICAgICAgaWYgKF8uaXNFbXB0eShkcml2ZXJLZXlzKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBjb25zdCBzeW5jaHJvbml6ZWREcml2ZXJzTWFwcGluZyA9IGRyaXZlcktleXMucmVkdWNlKChhY2MsIHgpID0+IHtcbiAgICAgICAgY29uc3Qge3ZlcnNpb24sIG1pbkJyb3dzZXJWZXJzaW9ufSA9IHJldHJpZXZlZE1hcHBpbmdbeF07XG4gICAgICAgIGFjY1t2ZXJzaW9uXSA9IG1pbkJyb3dzZXJWZXJzaW9uO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwge30pO1xuICAgICAgT2JqZWN0LmFzc2lnbihtYXBwaW5nLCBzeW5jaHJvbml6ZWREcml2ZXJzTWFwcGluZyk7XG4gICAgICBhd2FpdCB0aGlzLnVwZGF0ZURyaXZlcnNNYXBwaW5nKG1hcHBpbmcpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfTtcblxuICAgIGRvIHtcbiAgICAgIGNvbnN0IGNkcyA9IGF3YWl0IHRoaXMuZ2V0Q2hyb21lZHJpdmVycyhtYXBwaW5nKTtcblxuICAgICAgY29uc3QgbWlzc2luZ1ZlcnNpb25zID0ge307XG4gICAgICBmb3IgKGNvbnN0IHt2ZXJzaW9uLCBtaW5DaHJvbWVWZXJzaW9ufSBvZiBjZHMpIHtcbiAgICAgICAgaWYgKCFtaW5DaHJvbWVWZXJzaW9uIHx8IG1hcHBpbmdbdmVyc2lvbl0pIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjb2VyY2VkVmVyID0gc2VtdmVyLmNvZXJjZSh2ZXJzaW9uKTtcbiAgICAgICAgaWYgKCFjb2VyY2VkVmVyIHx8IGNvZXJjZWRWZXIubWFqb3IgPCBORVdfQ0RfVkVSU0lPTl9GT1JNQVRfTUFKT1JfVkVSU0lPTikge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgbWlzc2luZ1ZlcnNpb25zW3ZlcnNpb25dID0gbWluQ2hyb21lVmVyc2lvbjtcbiAgICAgIH1cbiAgICAgIGlmICghXy5pc0VtcHR5KG1pc3NpbmdWZXJzaW9ucykpIHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbyhgRm91bmQgJHt1dGlsLnBsdXJhbGl6ZSgnQ2hyb21lZHJpdmVyJywgXy5zaXplKG1pc3NpbmdWZXJzaW9ucyksIHRydWUpfSwgYCArXG4gICAgICAgICAgYHdoaWNoICR7Xy5zaXplKG1pc3NpbmdWZXJzaW9ucykgPT09IDEgPyAnaXMnIDogJ2FyZSd9IG1pc3NpbmcgaW4gdGhlIGxpc3Qgb2Yga25vd24gdmVyc2lvbnM6IGAgK1xuICAgICAgICAgIEpTT04uc3RyaW5naWZ5KG1pc3NpbmdWZXJzaW9ucykpO1xuICAgICAgICBhd2FpdCB0aGlzLnVwZGF0ZURyaXZlcnNNYXBwaW5nKE9iamVjdC5hc3NpZ24obWFwcGluZywgbWlzc2luZ1ZlcnNpb25zKSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmRpc2FibGVCdWlsZENoZWNrKSB7XG4gICAgICAgIGlmIChfLmlzRW1wdHkoY2RzKSkge1xuICAgICAgICAgIHRoaXMubG9nLmVycm9yQW5kVGhyb3coYFRoZXJlIG11c3QgYmUgYXQgbGVhc3Qgb25lIENocm9tZWRyaXZlciBleGVjdXRhYmxlIGF2YWlsYWJsZSBmb3IgdXNlIGlmIGAgK1xuICAgICAgICAgICAgYCdjaHJvbWVkcml2ZXJEaXNhYmxlQnVpbGRDaGVjaycgY2FwYWJpbGl0eSBpcyBzZXQgdG8gJ3RydWUnYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qge3ZlcnNpb24sIGV4ZWN1dGFibGV9ID0gY2RzWzBdO1xuICAgICAgICB0aGlzLmxvZy53YXJuKGBDaHJvbWUgYnVpbGQgY2hlY2sgZGlzYWJsZWQuIFVzaW5nIG1vc3QgcmVjZW50IENocm9tZWRyaXZlciB2ZXJzaW9uICgke3ZlcnNpb259LCBhdCAnJHtleGVjdXRhYmxlfScpYCk7XG4gICAgICAgIHRoaXMubG9nLndhcm4oYElmIHRoaXMgaXMgd3JvbmcsIHNldCAnY2hyb21lZHJpdmVyRGlzYWJsZUJ1aWxkQ2hlY2snIGNhcGFiaWxpdHkgdG8gJ2ZhbHNlJ2ApO1xuICAgICAgICByZXR1cm4gZXhlY3V0YWJsZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY2hyb21lVmVyc2lvbiA9IGF3YWl0IHRoaXMuZ2V0Q2hyb21lVmVyc2lvbigpO1xuICAgICAgaWYgKCFjaHJvbWVWZXJzaW9uKSB7XG4gICAgICAgIC8vIHVuYWJsZSB0byBnZXQgdGhlIGNocm9tZSB2ZXJzaW9uXG4gICAgICAgIGlmIChfLmlzRW1wdHkoY2RzKSkge1xuICAgICAgICAgIHRoaXMubG9nLmVycm9yQW5kVGhyb3coYFRoZXJlIG11c3QgYmUgYXQgbGVhc3Qgb25lIENocm9tZWRyaXZlciBleGVjdXRhYmxlIGF2YWlsYWJsZSBmb3IgdXNlIGlmIGAgK1xuICAgICAgICAgICAgYHRoZSBjdXJyZW50IENocm9tZSB2ZXJzaW9uIGNhbm5vdCBiZSBkZXRlcm1pbmVkYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qge3ZlcnNpb24sIGV4ZWN1dGFibGV9ID0gY2RzWzBdO1xuICAgICAgICB0aGlzLmxvZy53YXJuKGBVbmFibGUgdG8gZGlzY292ZXIgQ2hyb21lIHZlcnNpb24uIFVzaW5nIENocm9tZWRyaXZlciAke3ZlcnNpb259IGF0ICcke2V4ZWN1dGFibGV9J2ApO1xuICAgICAgICByZXR1cm4gZXhlY3V0YWJsZTtcbiAgICAgIH1cbiAgICAgIHRoaXMubG9nLmRlYnVnKGBGb3VuZCBDaHJvbWUgYnVuZGxlICcke3RoaXMuYnVuZGxlSWR9JyB2ZXJzaW9uICcke2Nocm9tZVZlcnNpb259J2ApO1xuXG4gICAgICBjb25zdCBtYXRjaGluZ0RyaXZlcnMgPSBjZHMuZmlsdGVyKCh7bWluQ2hyb21lVmVyc2lvbn0pID0+IHtcbiAgICAgICAgY29uc3QgbWluQ2hyb21lVmVyc2lvblMgPSBtaW5DaHJvbWVWZXJzaW9uICYmIHNlbXZlci5jb2VyY2UobWluQ2hyb21lVmVyc2lvbik7XG4gICAgICAgIGlmICghbWluQ2hyb21lVmVyc2lvblMpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gY2hyb21lVmVyc2lvbi5tYWpvciA+IE5FV19DRF9WRVJTSU9OX0ZPUk1BVF9NQUpPUl9WRVJTSU9OXG4gICAgICAgICAgPyBtaW5DaHJvbWVWZXJzaW9uUy5tYWpvciA9PT0gY2hyb21lVmVyc2lvbi5tYWpvclxuICAgICAgICAgIDogc2VtdmVyLmd0ZShjaHJvbWVWZXJzaW9uLCBtaW5DaHJvbWVWZXJzaW9uUyk7XG4gICAgICB9KTtcbiAgICAgIGlmIChfLmlzRW1wdHkobWF0Y2hpbmdEcml2ZXJzKSkge1xuICAgICAgICBpZiAodGhpcy5zdG9yYWdlQ2xpZW50ICYmICFkaWRTdG9yYWdlU3luYykge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBpZiAoYXdhaXQgc3luY0Nocm9tZWRyaXZlcnMoY2hyb21lVmVyc2lvbikpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGhpcy5sb2cud2FybihgQ2Fubm90IHN5bmNocm9uaXplIGxvY2FsIGNocm9tZWRyaXZlcnMgd2l0aCB0aGUgcmVtb3RlIHN0b3JhZ2UgYXQgJHtDRF9DRE59OiBgICtcbiAgICAgICAgICAgICAgZS5tZXNzYWdlKTtcbiAgICAgICAgICAgIHRoaXMubG9nLmRlYnVnKGUuc3RhY2spO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBhdXRvZG93bmxvYWRTdWdnZXN0aW9uID1cbiAgICAgICAgICAnWW91IGNvdWxkIGFsc28gdHJ5IHRvIGVuYWJsZSBhdXRvbWF0ZWQgY2hyb21lZHJpdmVycyBkb3dubG9hZCBhcyAnICtcbiAgICAgICAgICAnYSBwb3NzaWJsZSB3b3JrYXJvdW5kLic7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gQ2hyb21lZHJpdmVyIGZvdW5kIHRoYXQgY2FuIGF1dG9tYXRlIENocm9tZSAnJHtjaHJvbWVWZXJzaW9ufScuYCArXG4gICAgICAgICAgKHRoaXMuc3RvcmFnZUNsaWVudCA/ICcnIDogYCAke2F1dG9kb3dubG9hZFN1Z2dlc3Rpb259YCkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBiaW5QYXRoID0gbWF0Y2hpbmdEcml2ZXJzWzBdLmV4ZWN1dGFibGU7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZyhgRm91bmQgJHt1dGlsLnBsdXJhbGl6ZSgnZXhlY3V0YWJsZScsIG1hdGNoaW5nRHJpdmVycy5sZW5ndGgsIHRydWUpfSBgICtcbiAgICAgICAgYGNhcGFibGUgb2YgYXV0b21hdGluZyBDaHJvbWUgJyR7Y2hyb21lVmVyc2lvbn0nLlxcbkNob29zaW5nIHRoZSBtb3N0IHJlY2VudCwgJyR7YmluUGF0aH0nLmApO1xuICAgICAgdGhpcy5sb2cuZGVidWcoJ0lmIGEgc3BlY2lmaWMgdmVyc2lvbiBpcyByZXF1aXJlZCwgc3BlY2lmeSBpdCB3aXRoIHRoZSBgY2hyb21lZHJpdmVyRXhlY3V0YWJsZWAnICtcbiAgICAgICAgJ2Rlc2lyZWQgY2FwYWJpbGl0eS4nKTtcbiAgICAgIHJldHVybiBiaW5QYXRoO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zdGFudC1jb25kaXRpb25cbiAgICB9IHdoaWxlICh0cnVlKTtcbiAgfVxuXG4gIGFzeW5jIGluaXRDaHJvbWVkcml2ZXJQYXRoICgpIHtcbiAgICBpZiAodGhpcy5leGVjdXRhYmxlVmVyaWZpZWQpIHJldHVybjsgLy9lc2xpbnQtZGlzYWJsZS1saW5lIGN1cmx5XG5cbiAgICAvLyB0aGUgZXhlY3V0YWJsZSBtaWdodCBiZSBzZXQgKGlmIHBhc3NlZCBpbilcbiAgICAvLyBvciB3ZSBtaWdodCB3YW50IHRvIHVzZSB0aGUgYmFzaWMgb25lIGluc3RhbGxlZCB3aXRoIHRoaXMgZHJpdmVyXG4gICAgLy8gb3Igd2Ugd2FudCB0byBmaWd1cmUgb3V0IHRoZSBiZXN0IG9uZVxuICAgIGlmICghdGhpcy5jaHJvbWVkcml2ZXIpIHtcbiAgICAgIHRoaXMuY2hyb21lZHJpdmVyID0gdGhpcy51c2VTeXN0ZW1FeGVjdXRhYmxlXG4gICAgICAgID8gYXdhaXQgZ2V0Q2hyb21lZHJpdmVyQmluYXJ5UGF0aCgpXG4gICAgICAgIDogYXdhaXQgdGhpcy5nZXRDb21wYXRpYmxlQ2hyb21lZHJpdmVyKCk7XG4gICAgfVxuXG4gICAgaWYgKCFhd2FpdCBmcy5leGlzdHModGhpcy5jaHJvbWVkcml2ZXIpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRyeWluZyB0byB1c2UgYSBjaHJvbWVkcml2ZXIgYmluYXJ5IGF0IHRoZSBwYXRoIGAgK1xuICAgICAgICAgICAgICAgICAgICAgIGAke3RoaXMuY2hyb21lZHJpdmVyfSwgYnV0IGl0IGRvZXNuJ3QgZXhpc3QhYCk7XG4gICAgfVxuICAgIHRoaXMuZXhlY3V0YWJsZVZlcmlmaWVkID0gdHJ1ZTtcbiAgICB0aGlzLmxvZy5pbmZvKGBTZXQgY2hyb21lZHJpdmVyIGJpbmFyeSBhczogJHt0aGlzLmNocm9tZWRyaXZlcn1gKTtcbiAgfVxuXG4gIHN5bmNQcm90b2NvbCAoY2RWZXJzaW9uID0gbnVsbCkge1xuICAgIGNvbnN0IGNvZXJjZWRWZXJzaW9uID0gc2VtdmVyLmNvZXJjZShjZFZlcnNpb24pO1xuICAgIGlmICghY29lcmNlZFZlcnNpb24gfHwgY29lcmNlZFZlcnNpb24ubWFqb3IgPCBNSU5fQ0RfVkVSU0lPTl9XSVRIX1czQ19TVVBQT1JUKSB7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZyhgQ2hyb21lZHJpdmVyIHYuICR7Y2RWZXJzaW9ufSBkb2VzIG5vdCBmdWxseSBzdXBwb3J0ICR7UFJPVE9DT0xTLlczQ30gcHJvdG9jb2wuIGAgK1xuICAgICAgICBgRGVmYXVsdGluZyB0byAke1BST1RPQ09MUy5NSlNPTldQfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjaHJvbWVPcHRpb25zID0gZ2V0Q2FwVmFsdWUodGhpcy5jYXBhYmlsaXRpZXMsICdjaHJvbWVPcHRpb25zJywge30pO1xuICAgIGlmIChjaHJvbWVPcHRpb25zLnczYyA9PT0gZmFsc2UpIHtcbiAgICAgIHRoaXMubG9nLmluZm8oYENocm9tZWRyaXZlciB2LiAke2NkVmVyc2lvbn0gc3VwcG9ydHMgJHtQUk9UT0NPTFMuVzNDfSBwcm90b2NvbCwgYCArXG4gICAgICAgIGBidXQgJHtQUk9UT0NPTFMuTUpTT05XUH0gb25lIGhhcyBiZWVuIGV4cGxpY2l0bHkgcmVxdWVzdGVkYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuZGVzaXJlZFByb3RvY29sID0gUFJPVE9DT0xTLlczQztcbiAgICAvLyBnaXZlbiBjYXBzIG1pZ2h0IG5vdCBiZSBwcm9wZXJseSBwcmVmaXhlZFxuICAgIC8vIHNvIHdlIHRyeSB0byBmaXggdGhlbSBpbiBvcmRlciB0byBwcm9wZXJseSBpbml0XG4gICAgLy8gdGhlIG5ldyBXM0Mgc2Vzc2lvblxuICAgIHRoaXMuY2FwYWJpbGl0aWVzID0gdG9XM2NDYXBOYW1lcyh0aGlzLmNhcGFiaWxpdGllcyk7XG4gIH1cblxuICBhc3luYyBzdGFydCAoY2FwcywgZW1pdFN0YXJ0aW5nU3RhdGUgPSB0cnVlKSB7XG4gICAgdGhpcy5jYXBhYmlsaXRpZXMgPSBfLmNsb25lRGVlcChjYXBzKTtcblxuICAgIC8vIHNldCB0aGUgbG9nZ2luZyBwcmVmZXJlbmNlcyB0byBBTEwgdGhlIGNvbnNvbGUgbG9nc1xuICAgIHRoaXMuY2FwYWJpbGl0aWVzLmxvZ2dpbmdQcmVmcyA9IF8uY2xvbmVEZWVwKGdldENhcFZhbHVlKGNhcHMsICdsb2dnaW5nUHJlZnMnLCB7fSkpO1xuICAgIGlmIChfLmlzRW1wdHkodGhpcy5jYXBhYmlsaXRpZXMubG9nZ2luZ1ByZWZzLmJyb3dzZXIpKSB7XG4gICAgICB0aGlzLmNhcGFiaWxpdGllcy5sb2dnaW5nUHJlZnMuYnJvd3NlciA9ICdBTEwnO1xuICAgIH1cblxuICAgIGlmIChlbWl0U3RhcnRpbmdTdGF0ZSkge1xuICAgICAgdGhpcy5jaGFuZ2VTdGF0ZShDaHJvbWVkcml2ZXIuU1RBVEVfU1RBUlRJTkcpO1xuICAgIH1cblxuICAgIGNvbnN0IGFyZ3MgPSBbYC0tcG9ydD0ke3RoaXMucHJveHlQb3J0fWBdO1xuICAgIGlmICh0aGlzLmFkYiAmJiB0aGlzLmFkYi5hZGJQb3J0KSB7XG4gICAgICBhcmdzLnB1c2goYC0tYWRiLXBvcnQ9JHt0aGlzLmFkYi5hZGJQb3J0fWApO1xuICAgIH1cbiAgICBpZiAoXy5pc0FycmF5KHRoaXMuY21kQXJncykpIHtcbiAgICAgIGFyZ3MucHVzaCguLi50aGlzLmNtZEFyZ3MpO1xuICAgIH1cbiAgICBpZiAodGhpcy5sb2dQYXRoKSB7XG4gICAgICBhcmdzLnB1c2goYC0tbG9nLXBhdGg9JHt0aGlzLmxvZ1BhdGh9YCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmRpc2FibGVCdWlsZENoZWNrKSB7XG4gICAgICBhcmdzLnB1c2goJy0tZGlzYWJsZS1idWlsZC1jaGVjaycpO1xuICAgIH1cbiAgICBhcmdzLnB1c2goJy0tdmVyYm9zZScpO1xuICAgIC8vIHdoYXQgYXJlIHRoZSBwcm9jZXNzIHN0ZG91dC9zdGRlcnIgY29uZGl0aW9ucyB3aGVyZWluIHdlIGtub3cgdGhhdFxuICAgIC8vIHRoZSBwcm9jZXNzIGhhcyBzdGFydGVkIHRvIG91ciBzYXRpc2ZhY3Rpb24/XG4gICAgY29uc3Qgc3RhcnREZXRlY3RvciA9IChzdGRvdXQpID0+IHN0ZG91dC5zdGFydHNXaXRoKCdTdGFydGluZyAnKTtcblxuICAgIGxldCBwcm9jZXNzSXNBbGl2ZSA9IGZhbHNlO1xuICAgIGxldCB3ZWJ2aWV3VmVyc2lvbjtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5pbml0Q2hyb21lZHJpdmVyUGF0aCgpO1xuICAgICAgYXdhaXQgdGhpcy5raWxsQWxsKCk7XG5cbiAgICAgIC8vIHNldCB1cCBvdXIgc3VicHJvY2VzcyBvYmplY3RcbiAgICAgIHRoaXMucHJvYyA9IG5ldyBTdWJQcm9jZXNzKHRoaXMuY2hyb21lZHJpdmVyLCBhcmdzKTtcbiAgICAgIHByb2Nlc3NJc0FsaXZlID0gdHJ1ZTtcblxuICAgICAgLy8gaGFuZGxlIGxvZyBvdXRwdXRcbiAgICAgIHRoaXMucHJvYy5vbignb3V0cHV0JywgKHN0ZG91dCwgc3RkZXJyKSA9PiB7XG4gICAgICAgIC8vIGlmIHRoZSBjZCBvdXRwdXQgaXMgbm90IHByaW50ZWQsIGZpbmQgdGhlIGNocm9tZSB2ZXJzaW9uIGFuZCBwcmludFxuICAgICAgICAvLyB3aWxsIGdldCBhIHJlc3BvbnNlIGxpa2VcbiAgICAgICAgLy8gICBEZXZUb29scyByZXNwb25zZToge1xuICAgICAgICAvLyAgICAgIFwiQW5kcm9pZC1QYWNrYWdlXCI6IFwiaW8uYXBwaXVtLnNhbXBsZWFwcFwiLFxuICAgICAgICAvLyAgICAgIFwiQnJvd3NlclwiOiBcIkNocm9tZS81NS4wLjI4ODMuOTFcIixcbiAgICAgICAgLy8gICAgICBcIlByb3RvY29sLVZlcnNpb25cIjogXCIxLjJcIixcbiAgICAgICAgLy8gICAgICBcIlVzZXItQWdlbnRcIjogXCIuLi5cIixcbiAgICAgICAgLy8gICAgICBcIldlYktpdC1WZXJzaW9uXCI6IFwiNTM3LjM2XCJcbiAgICAgICAgLy8gICB9XG4gICAgICAgIGNvbnN0IG91dCA9IHN0ZG91dCArIHN0ZGVycjtcbiAgICAgICAgbGV0IG1hdGNoID0gL1wiQnJvd3NlclwiOiBcIiguKilcIi8uZXhlYyhvdXQpO1xuICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICB3ZWJ2aWV3VmVyc2lvbiA9IG1hdGNoWzFdO1xuICAgICAgICAgIHRoaXMubG9nLmRlYnVnKGBXZWJ2aWV3IHZlcnNpb246ICcke3dlYnZpZXdWZXJzaW9ufSdgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGFsc28gcHJpbnQgY2hyb21lZHJpdmVyIHZlcnNpb24gdG8gbG9nc1xuICAgICAgICAvLyB3aWxsIG91dHB1dCBzb21ldGhpbmcgbGlrZVxuICAgICAgICAvLyAgU3RhcnRpbmcgQ2hyb21lRHJpdmVyIDIuMzMuNTA2MTA2ICg4YTA2YzM5YzQ1ODJmYmZiYWI2OTY2ZGJiMWMzOGE5MTczYmZiMWEyKSBvbiBwb3J0IDk1MTVcbiAgICAgICAgbWF0Y2ggPSAvU3RhcnRpbmcgQ2hyb21lRHJpdmVyIChbLlxcZF0rKS8uZXhlYyhvdXQpO1xuICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhgQ2hyb21lZHJpdmVyIHZlcnNpb246ICcke21hdGNoWzFdfSdgKTtcbiAgICAgICAgICB0aGlzLnN5bmNQcm90b2NvbChtYXRjaFsxXSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBnaXZlIHRoZSBvdXRwdXQgaWYgaXQgaXMgcmVxdWVzdGVkXG4gICAgICAgIGlmICh0aGlzLnZlcmJvc2UpIHtcbiAgICAgICAgICBmb3IgKGxldCBsaW5lIG9mIChzdGRvdXQgfHwgJycpLnRyaW0oKS5zcGxpdCgnXFxuJykpIHtcbiAgICAgICAgICAgIGlmICghbGluZS50cmltKCkubGVuZ3RoKSBjb250aW51ZTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBjdXJseVxuICAgICAgICAgICAgdGhpcy5sb2cuZGVidWcoYFtTVERPVVRdICR7bGluZX1gKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZm9yIChsZXQgbGluZSBvZiAoc3RkZXJyIHx8ICcnKS50cmltKCkuc3BsaXQoJ1xcbicpKSB7XG4gICAgICAgICAgICBpZiAoIWxpbmUudHJpbSgpLmxlbmd0aCkgY29udGludWU7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgY3VybHlcbiAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKGBbU1RERVJSXSAke2xpbmV9YCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgLy8gaGFuZGxlIG91dC1vZi1ib3VuZCBleGl0IGJ5IHNpbXBseSBlbWl0dGluZyBhIHN0b3BwZWQgc3RhdGVcbiAgICAgIHRoaXMucHJvYy5vbignZXhpdCcsIChjb2RlLCBzaWduYWwpID0+IHtcbiAgICAgICAgcHJvY2Vzc0lzQWxpdmUgPSBmYWxzZTtcbiAgICAgICAgaWYgKHRoaXMuc3RhdGUgIT09IENocm9tZWRyaXZlci5TVEFURV9TVE9QUEVEICYmXG4gICAgICAgICAgICB0aGlzLnN0YXRlICE9PSBDaHJvbWVkcml2ZXIuU1RBVEVfU1RPUFBJTkcgJiZcbiAgICAgICAgICAgIHRoaXMuc3RhdGUgIT09IENocm9tZWRyaXZlci5TVEFURV9SRVNUQVJUSU5HKSB7XG4gICAgICAgICAgY29uc3QgbXNnID0gYENocm9tZWRyaXZlciBleGl0ZWQgdW5leHBlY3RlZGx5IHdpdGggY29kZSAke2NvZGV9LCBzaWduYWwgJHtzaWduYWx9YDtcbiAgICAgICAgICB0aGlzLmxvZy5lcnJvcihtc2cpO1xuICAgICAgICAgIHRoaXMuY2hhbmdlU3RhdGUoQ2hyb21lZHJpdmVyLlNUQVRFX1NUT1BQRUQpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMubG9nLmluZm8oYFNwYXduaW5nIGNocm9tZWRyaXZlciB3aXRoOiAke3RoaXMuY2hyb21lZHJpdmVyfSAke2FyZ3Muam9pbignICcpfWApO1xuICAgICAgLy8gc3RhcnQgc3VicHJvYyBhbmQgd2FpdCBmb3Igc3RhcnREZXRlY3RvclxuICAgICAgYXdhaXQgdGhpcy5wcm9jLnN0YXJ0KHN0YXJ0RGV0ZWN0b3IpO1xuICAgICAgYXdhaXQgdGhpcy53YWl0Rm9yT25saW5lKCk7XG4gICAgICBhd2FpdCB0aGlzLnN0YXJ0U2Vzc2lvbigpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubG9nLmRlYnVnKGUpO1xuICAgICAgdGhpcy5lbWl0KENocm9tZWRyaXZlci5FVkVOVF9FUlJPUiwgZSk7XG4gICAgICAvLyBqdXN0IGJlY2F1c2Ugd2UgaGFkIGFuIGVycm9yIGRvZXNuJ3QgbWVhbiB0aGUgY2hyb21lZHJpdmVyIHByb2Nlc3NcbiAgICAgIC8vIGZpbmlzaGVkOyB3ZSBzaG91bGQgY2xlYW4gdXAgaWYgbmVjZXNzYXJ5XG4gICAgICBpZiAocHJvY2Vzc0lzQWxpdmUpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5wcm9jLnN0b3AoKTtcbiAgICAgIH1cblxuICAgICAgbGV0IG1lc3NhZ2UgPSAnJztcbiAgICAgIC8vIG9mdGVuIHRoZSB1c2VyJ3MgQ2hyb21lIHZlcnNpb24gaXMgbm90IHN1cHBvcnRlZCBieSB0aGUgdmVyc2lvbiBvZiBDaHJvbWVkcml2ZXJcbiAgICAgIGlmIChlLm1lc3NhZ2UuaW5jbHVkZXMoJ0Nocm9tZSB2ZXJzaW9uIG11c3QgYmUnKSkge1xuICAgICAgICBtZXNzYWdlICs9ICdVbmFibGUgdG8gYXV0b21hdGUgQ2hyb21lIHZlcnNpb24gYmVjYXVzZSBpdCBpcyBub3Qgc3VwcG9ydGVkIGJ5IHRoaXMgdmVyc2lvbiBvZiBDaHJvbWVkcml2ZXIuXFxuJztcbiAgICAgICAgaWYgKHdlYnZpZXdWZXJzaW9uKSB7XG4gICAgICAgICAgbWVzc2FnZSArPSBgQ2hyb21lIHZlcnNpb24gb24gdGhlIGRldmljZTogJHt3ZWJ2aWV3VmVyc2lvbn1cXG5gO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHZlcnNpb25zU3VwcG9ydGVkQnlEcml2ZXIgPSAvQ2hyb21lIHZlcnNpb24gbXVzdCBiZSAoLispLy5leGVjKGUubWVzc2FnZSk/LlsxXSB8fCAnJztcbiAgICAgICAgaWYgKHZlcnNpb25zU3VwcG9ydGVkQnlEcml2ZXIpIHtcbiAgICAgICAgICBtZXNzYWdlICs9IGBDaHJvbWVkcml2ZXIgc3VwcG9ydHMgQ2hyb21lIHZlcnNpb24ocyk6ICR7dmVyc2lvbnNTdXBwb3J0ZWRCeURyaXZlcn1cXG5gO1xuICAgICAgICB9XG4gICAgICAgIG1lc3NhZ2UgKz0gJ0NoZWNrIHRoZSBkcml2ZXIgdHV0b3JpYWwgZm9yIHRyb3VibGVzaG9vdGluZy5cXG4nO1xuICAgICAgfVxuXG4gICAgICBtZXNzYWdlICs9IGUubWVzc2FnZTtcbiAgICAgIHRoaXMubG9nLmVycm9yQW5kVGhyb3cobWVzc2FnZSk7XG4gICAgfVxuICB9XG5cbiAgc2Vzc2lvbklkICgpIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZSA9PT0gQ2hyb21lZHJpdmVyLlNUQVRFX09OTElORSA/IHRoaXMuandwcm94eS5zZXNzaW9uSWQgOiBudWxsO1xuICB9XG5cbiAgYXN5bmMgcmVzdGFydCAoKSB7XG4gICAgdGhpcy5sb2cuaW5mbygnUmVzdGFydGluZyBjaHJvbWVkcml2ZXInKTtcbiAgICBpZiAodGhpcy5zdGF0ZSAhPT0gQ2hyb21lZHJpdmVyLlNUQVRFX09OTElORSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ2FuJ3QgcmVzdGFydCB3aGVuIHdlJ3JlIG5vdCBvbmxpbmVcIik7XG4gICAgfVxuICAgIHRoaXMuY2hhbmdlU3RhdGUoQ2hyb21lZHJpdmVyLlNUQVRFX1JFU1RBUlRJTkcpO1xuICAgIGF3YWl0IHRoaXMuc3RvcChmYWxzZSk7XG4gICAgYXdhaXQgdGhpcy5zdGFydCh0aGlzLmNhcGFiaWxpdGllcywgZmFsc2UpO1xuICB9XG5cbiAgYXN5bmMgd2FpdEZvck9ubGluZSAoKSB7XG4gICAgLy8gd2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCBDRCBoYXNuJ3QgY3Jhc2hlZFxuICAgIGxldCBjaHJvbWVkcml2ZXJTdG9wcGVkID0gZmFsc2U7XG4gICAgYXdhaXQgcmV0cnlJbnRlcnZhbCgyMCwgMjAwLCBhc3luYyAoKSA9PiB7XG4gICAgICBpZiAodGhpcy5zdGF0ZSA9PT0gQ2hyb21lZHJpdmVyLlNUQVRFX1NUT1BQRUQpIHtcbiAgICAgICAgLy8gd2UgYXJlIGVpdGhlciBzdG9wcGVkIG9yIHN0b3BwaW5nLCBzbyBzb21ldGhpbmcgd2VudCB3cm9uZ1xuICAgICAgICBjaHJvbWVkcml2ZXJTdG9wcGVkID0gdHJ1ZTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgYXdhaXQgdGhpcy5nZXRTdGF0dXMoKTtcbiAgICB9KTtcbiAgICBpZiAoY2hyb21lZHJpdmVyU3RvcHBlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDaHJvbWVEcml2ZXIgY3Jhc2hlZCBkdXJpbmcgc3RhcnR1cC4nKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBnZXRTdGF0dXMgKCkge1xuICAgIHJldHVybiBhd2FpdCB0aGlzLmp3cHJveHkuY29tbWFuZCgnL3N0YXR1cycsICdHRVQnKTtcbiAgfVxuXG4gIGFzeW5jIHN0YXJ0U2Vzc2lvbiAoKSB7XG4gICAgY29uc3Qgc2Vzc2lvbkNhcHMgPSB0aGlzLmRlc2lyZWRQcm90b2NvbCA9PT0gUFJPVE9DT0xTLlczQ1xuICAgICAgPyB7Y2FwYWJpbGl0aWVzOiB7YWx3YXlzTWF0Y2g6IHRoaXMuY2FwYWJpbGl0aWVzfX1cbiAgICAgIDoge2Rlc2lyZWRDYXBhYmlsaXRpZXM6IHRoaXMuY2FwYWJpbGl0aWVzfTtcbiAgICB0aGlzLmxvZy5pbmZvKGBTdGFydGluZyAke3RoaXMuZGVzaXJlZFByb3RvY29sfSBDaHJvbWVkcml2ZXIgc2Vzc2lvbiB3aXRoIGNhcGFiaWxpdGllczogYCArXG4gICAgICBKU09OLnN0cmluZ2lmeShzZXNzaW9uQ2FwcywgbnVsbCwgMikpO1xuICAgIGF3YWl0IHRoaXMuandwcm94eS5jb21tYW5kKCcvc2Vzc2lvbicsICdQT1NUJywgc2Vzc2lvbkNhcHMpO1xuICAgIHRoaXMubG9nLnByZWZpeCA9IGdlbmVyYXRlTG9nUHJlZml4KHRoaXMsIHRoaXMuandwcm94eS5zZXNzaW9uSWQpO1xuICAgIHRoaXMuY2hhbmdlU3RhdGUoQ2hyb21lZHJpdmVyLlNUQVRFX09OTElORSk7XG4gIH1cblxuICBhc3luYyBzdG9wIChlbWl0U3RhdGVzID0gdHJ1ZSkge1xuICAgIGlmIChlbWl0U3RhdGVzKSB7XG4gICAgICB0aGlzLmNoYW5nZVN0YXRlKENocm9tZWRyaXZlci5TVEFURV9TVE9QUElORyk7XG4gICAgfVxuICAgIGNvbnN0IHJ1blNhZmVTdGVwID0gYXN5bmMgKGYpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRoaXMubG9nLndhcm4oZS5tZXNzYWdlKTtcbiAgICAgICAgdGhpcy5sb2cuZGVidWcoZS5zdGFjayk7XG4gICAgICB9XG4gICAgfTtcbiAgICBhd2FpdCBydW5TYWZlU3RlcCgoKSA9PiB0aGlzLmp3cHJveHkuY29tbWFuZCgnJywgJ0RFTEVURScpKTtcbiAgICBhd2FpdCBydW5TYWZlU3RlcCgoKSA9PiB0aGlzLnByb2Muc3RvcCgnU0lHVEVSTScsIDIwMDAwKSk7XG4gICAgdGhpcy5sb2cucHJlZml4ID0gZ2VuZXJhdGVMb2dQcmVmaXgodGhpcyk7XG4gICAgaWYgKGVtaXRTdGF0ZXMpIHtcbiAgICAgIHRoaXMuY2hhbmdlU3RhdGUoQ2hyb21lZHJpdmVyLlNUQVRFX1NUT1BQRUQpO1xuICAgIH1cbiAgfVxuXG4gIGNoYW5nZVN0YXRlIChzdGF0ZSkge1xuICAgIHRoaXMuc3RhdGUgPSBzdGF0ZTtcbiAgICB0aGlzLmxvZy5kZWJ1ZyhgQ2hhbmdlZCBzdGF0ZSB0byAnJHtzdGF0ZX0nYCk7XG4gICAgdGhpcy5lbWl0KENocm9tZWRyaXZlci5FVkVOVF9DSEFOR0VELCB7c3RhdGV9KTtcbiAgfVxuXG4gIGFzeW5jIHNlbmRDb21tYW5kICh1cmwsIG1ldGhvZCwgYm9keSkge1xuICAgIHJldHVybiBhd2FpdCB0aGlzLmp3cHJveHkuY29tbWFuZCh1cmwsIG1ldGhvZCwgYm9keSk7XG4gIH1cblxuICBhc3luYyBwcm94eVJlcSAocmVxLCByZXMpIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy5qd3Byb3h5LnByb3h5UmVxUmVzKHJlcSwgcmVzKTtcbiAgfVxuXG4gIGFzeW5jIGtpbGxBbGwgKCkge1xuICAgIGxldCBjbWQgPSBzeXN0ZW0uaXNXaW5kb3dzKClcbiAgICAgID8gYHdtaWMgcHJvY2VzcyB3aGVyZSBcImNvbW1hbmRsaW5lIGxpa2UgJyVjaHJvbWVkcml2ZXIuZXhlJS0tcG9ydD0ke3RoaXMucHJveHlQb3J0fSUnXCIgZGVsZXRlYFxuICAgICAgOiBgcGtpbGwgLTE1IC1mIFwiJHt0aGlzLmNocm9tZWRyaXZlcn0uKi0tcG9ydD0ke3RoaXMucHJveHlQb3J0fVwiYDtcbiAgICB0aGlzLmxvZy5kZWJ1ZyhgS2lsbGluZyBhbnkgb2xkIGNocm9tZWRyaXZlcnMsIHJ1bm5pbmc6ICR7Y21kfWApO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCAoQi5wcm9taXNpZnkoY3AuZXhlYykpKGNtZCk7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZygnU3VjY2Vzc2Z1bGx5IGNsZWFuZWQgdXAgb2xkIGNocm9tZWRyaXZlcnMnKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMubG9nLndhcm4oJ05vIG9sZCBjaHJvbWVkcml2ZXJzIHNlZW0gdG8gZXhpc3QnKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5hZGIpIHtcbiAgICAgIGNvbnN0IHVkaWRJbmRleCA9IHRoaXMuYWRiLmV4ZWN1dGFibGUuZGVmYXVsdEFyZ3MuZmluZEluZGV4KChpdGVtKSA9PiBpdGVtID09PSAnLXMnKTtcbiAgICAgIGNvbnN0IHVkaWQgPSB1ZGlkSW5kZXggPiAtMSA/IHRoaXMuYWRiLmV4ZWN1dGFibGUuZGVmYXVsdEFyZ3NbdWRpZEluZGV4ICsgMV0gOiBudWxsO1xuXG4gICAgICBpZiAodWRpZCkge1xuICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhgQ2xlYW5pbmcgdGhpcyBkZXZpY2UncyBhZGIgZm9yd2FyZGVkIHBvcnQgc29ja2V0IGNvbm5lY3Rpb25zOiAke3VkaWR9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhgQ2xlYW5pbmcgYW55IG9sZCBhZGIgZm9yd2FyZGVkIHBvcnQgc29ja2V0IGNvbm5lY3Rpb25zYCk7XG4gICAgICB9XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGZvciAobGV0IGNvbm4gb2YgYXdhaXQgdGhpcy5hZGIuZ2V0Rm9yd2FyZExpc3QoKSkge1xuICAgICAgICAgIC8vIGNocm9tZWRyaXZlciB3aWxsIGFzayBBREIgdG8gZm9yd2FyZCBhIHBvcnQgbGlrZSBcImRldmljZUlkIHRjcDpwb3J0IGxvY2FsYWJzdHJhY3Q6d2Vidmlld19kZXZ0b29sc19yZW1vdGVfcG9ydFwiXG4gICAgICAgICAgaWYgKCEoY29ubi5pbmNsdWRlcygnd2Vidmlld19kZXZ0b29scycpICYmICghdWRpZCB8fCBjb25uLmluY2x1ZGVzKHVkaWQpKSkpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxldCBwYXJhbXMgPSBjb25uLnNwbGl0KC9cXHMrLyk7XG4gICAgICAgICAgaWYgKHBhcmFtcy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmFkYi5yZW1vdmVQb3J0Rm9yd2FyZChwYXJhbXNbMV0ucmVwbGFjZSgvW1xcRF0qLywgJycpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICB0aGlzLmxvZy53YXJuKGBVbmFibGUgdG8gY2xlYW4gZm9yd2FyZGVkIHBvcnRzLiBFcnJvcjogJyR7ZXJyLm1lc3NhZ2V9Jy4gQ29udGludWluZy5gKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBhc3luYyBoYXNXb3JraW5nV2VidmlldyAoKSB7XG4gICAgLy8gc29tZXRpbWVzIGNocm9tZWRyaXZlciBzdG9wcyBhdXRvbWF0aW5nIHdlYnZpZXdzLiB0aGlzIG1ldGhvZCBydW5zIGFcbiAgICAvLyBzaW1wbGUgY29tbWFuZCB0byBkZXRlcm1pbmUgb3VyIHN0YXRlLCBhbmQgcmVzcG9uZHMgYWNjb3JkaW5nbHlcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5qd3Byb3h5LmNvbW1hbmQoJy91cmwnLCAnR0VUJyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG59XG5cbkNocm9tZWRyaXZlci5FVkVOVF9FUlJPUiA9ICdjaHJvbWVkcml2ZXJfZXJyb3InO1xuQ2hyb21lZHJpdmVyLkVWRU5UX0NIQU5HRUQgPSAnc3RhdGVDaGFuZ2VkJztcbkNocm9tZWRyaXZlci5TVEFURV9TVE9QUEVEID0gJ3N0b3BwZWQnO1xuQ2hyb21lZHJpdmVyLlNUQVRFX1NUQVJUSU5HID0gJ3N0YXJ0aW5nJztcbkNocm9tZWRyaXZlci5TVEFURV9PTkxJTkUgPSAnb25saW5lJztcbkNocm9tZWRyaXZlci5TVEFURV9TVE9QUElORyA9ICdzdG9wcGluZyc7XG5DaHJvbWVkcml2ZXIuU1RBVEVfUkVTVEFSVElORyA9ICdyZXN0YXJ0aW5nJztcblxuZXhwb3J0IHsgQ2hyb21lZHJpdmVyIH07XG5leHBvcnQgZGVmYXVsdCBDaHJvbWVkcml2ZXI7XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUlBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUVBLE1BQU1BLG1DQUFtQyxHQUFHLEVBQUU7QUFDOUMsTUFBTUMsWUFBWSxHQUFHLFdBQVc7QUFDaEMsTUFBTUMsK0JBQStCLEdBQUcsRUFBRTtBQUMxQyxNQUFNQyxZQUFZLEdBQUcsSUFBSTtBQUN6QixNQUFNQyxnQkFBZ0IsR0FBRyxvQkFBb0I7QUFDN0MsTUFBTUMsdUJBQXVCLEdBQUcsNEJBQTRCO0FBQzVELE1BQU1DLGtCQUFrQixHQUFHLENBQ3pCLDRCQUE0QixFQUM1QixxQkFBcUIsQ0FDdEI7QUFDRCxNQUFNQyxlQUFlLEdBQUcsVUFBVTtBQUVsQyxNQUFNQyxrQkFBa0IsR0FBRyxJQUFJO0FBRS9CLE1BQU1DLFlBQVksU0FBU0MsZUFBTSxDQUFDQyxZQUFZLENBQUM7RUFDN0NDLFdBQVcsQ0FBRUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxFQUFFO0lBQ3RCLEtBQUssRUFBRTtJQUVQLE1BQU07TUFDSkMsSUFBSSxHQUFHYixZQUFZO01BQ25CYyxJQUFJLEdBQUdaLFlBQVk7TUFDbkJhLG1CQUFtQixH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
|
|
709
|
+
exports.default = Chromedriver;
|
|
710
|
+
/**
|
|
711
|
+
* @typedef {import('./types').ChromedriverVersionMapping} ChromedriverVersionMapping
|
|
712
|
+
*/
|
|
713
|
+
//# sourceMappingURL=chromedriver.js.map
|