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.
@@ -1,25 +1,23 @@
1
1
  "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.default = exports.Chromedriver = void 0;
8
- require("source-map-support/register");
9
- var _events = _interopRequireDefault(require("events"));
10
- var _baseDriver = require("@appium/base-driver");
11
- var _child_process = _interopRequireDefault(require("child_process"));
12
- var _support = require("@appium/support");
13
- var _asyncbox = require("asyncbox");
14
- var _teen_process = require("teen_process");
15
- var _bluebird = _interopRequireDefault(require("bluebird"));
16
- var _utils = require("./utils");
17
- var _semver = _interopRequireDefault(require("semver"));
18
- var _lodash = _interopRequireDefault(require("lodash"));
19
- var _path = _interopRequireDefault(require("path"));
20
- var _compareVersions = require("compare-versions");
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 _events.default.EventEmitter {
33
- constructor(args = {}) {
34
- super();
35
- const {
36
- host = DEFAULT_HOST,
37
- port = DEFAULT_PORT,
38
- useSystemExecutable = false,
39
- executable,
40
- executableDir = (0, _utils.getChromedriverDir)(),
41
- bundleId,
42
- mappingPath,
43
- cmdArgs,
44
- adb,
45
- verbose,
46
- logPath,
47
- disableBuildCheck,
48
- details,
49
- isAutodownloadEnabled = false
50
- } = args;
51
- this._log = _support.logger.getLogger((0, _utils.generateLogPrefix)(this));
52
- this.proxyHost = host;
53
- this.proxyPort = port;
54
- this.adb = adb;
55
- this.cmdArgs = cmdArgs;
56
- this.proc = null;
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
- let version = match[1];
158
- let minChromeVersion = mapping[version];
159
- const coercedVersion = _semver.default.coerce(version);
160
- if (coercedVersion) {
161
- if (coercedVersion.major < NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
162
- version = `${coercedVersion.major}.${coercedVersion.minor}`;
163
- minChromeVersion = mapping[version];
164
- }
165
- if (!minChromeVersion && coercedVersion.major >= NEW_CD_VERSION_FORMAT_MAJOR_VERSION) {
166
- minChromeVersion = `${coercedVersion.major}`;
167
- }
168
- }
169
- return {
170
- executable,
171
- version,
172
- minChromeVersion
173
- };
174
- })).filter(cd => !!cd).sort((a, b) => (0, _compareVersions.compareVersions)(b.version, a.version));
175
- if (_lodash.default.isEmpty(cds)) {
176
- this.log.info(`No Chromedrivers were found in '${this.executableDir}'`);
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
- } catch (e) {
336
- this.log.warn(`Cannot synchronize local chromedrivers with the remote storage at ${_utils.CD_CDN}: ` + e.message);
337
- this.log.debug(e.stack);
338
- }
339
- }
340
- const autodownloadSuggestion = 'You could also try to enable automated chromedrivers download as ' + 'a possible workaround.';
341
- throw new Error(`No Chromedriver found that can automate Chrome '${chromeVersion}'.` + (this.storageClient ? '' : ` ${autodownloadSuggestion}`));
342
- }
343
- const binPath = matchingDrivers[0].executable;
344
- this.log.debug(`Found ${_support.util.pluralize('executable', matchingDrivers.length, true)} ` + `capable of automating Chrome '${chromeVersion}'.\nChoosing the most recent, '${binPath}'.`);
345
- this.log.debug('If a specific version is required, specify it with the `chromedriverExecutable`' + 'desired capability.');
346
- return binPath;
347
- } while (true);
348
- }
349
- async initChromedriverPath() {
350
- if (this.executableVerified) return;
351
- if (!this.chromedriver) {
352
- this.chromedriver = this.useSystemExecutable ? await (0, _utils.getChromedriverBinaryPath)() : await this.getCompatibleChromedriver();
353
- }
354
- if (!(await _support.fs.exists(this.chromedriver))) {
355
- throw new Error(`Trying to use a chromedriver binary at the path ` + `${this.chromedriver}, but it doesn't exist!`);
356
- }
357
- this.executableVerified = true;
358
- this.log.info(`Set chromedriver binary as: ${this.chromedriver}`);
359
- }
360
- syncProtocol(cdVersion = null) {
361
- const coercedVersion = _semver.default.coerce(cdVersion);
362
- if (!coercedVersion || coercedVersion.major < MIN_CD_VERSION_WITH_W3C_SUPPORT) {
363
- this.log.debug(`Chromedriver v. ${cdVersion} does not fully support ${_baseDriver.PROTOCOLS.W3C} protocol. ` + `Defaulting to ${_baseDriver.PROTOCOLS.MJSONWP}`);
364
- return;
365
- }
366
- const chromeOptions = (0, _protocolHelpers.getCapValue)(this.capabilities, 'chromeOptions', {});
367
- if (chromeOptions.w3c === false) {
368
- this.log.info(`Chromedriver v. ${cdVersion} supports ${_baseDriver.PROTOCOLS.W3C} protocol, ` + `but ${_baseDriver.PROTOCOLS.MJSONWP} one has been explicitly requested`);
369
- return;
370
- }
371
- this.desiredProtocol = _baseDriver.PROTOCOLS.W3C;
372
- this.capabilities = (0, _protocolHelpers.toW3cCapNames)(this.capabilities);
373
- }
374
- async start(caps, emitStartingState = true) {
375
- this.capabilities = _lodash.default.cloneDeep(caps);
376
- this.capabilities.loggingPrefs = _lodash.default.cloneDeep((0, _protocolHelpers.getCapValue)(caps, 'loggingPrefs', {}));
377
- if (_lodash.default.isEmpty(this.capabilities.loggingPrefs.browser)) {
378
- this.capabilities.loggingPrefs.browser = 'ALL';
379
- }
380
- if (emitStartingState) {
381
- this.changeState(Chromedriver.STATE_STARTING);
382
- }
383
- const args = [`--port=${this.proxyPort}`];
384
- if (this.adb && this.adb.adbPort) {
385
- args.push(`--adb-port=${this.adb.adbPort}`);
386
- }
387
- if (_lodash.default.isArray(this.cmdArgs)) {
388
- args.push(...this.cmdArgs);
389
- }
390
- if (this.logPath) {
391
- args.push(`--log-path=${this.logPath}`);
392
- }
393
- if (this.disableBuildCheck) {
394
- args.push('--disable-build-check');
395
- }
396
- args.push('--verbose');
397
- const startDetector = stdout => stdout.startsWith('Starting ');
398
- let processIsAlive = false;
399
- let webviewVersion;
400
- try {
401
- await this.initChromedriverPath();
402
- await this.killAll();
403
- this.proc = new _teen_process.SubProcess(this.chromedriver, args);
404
- processIsAlive = true;
405
- this.proc.on('output', (stdout, stderr) => {
406
- const out = stdout + stderr;
407
- let match = /"Browser": "(.*)"/.exec(out);
408
- if (match) {
409
- webviewVersion = match[1];
410
- this.log.debug(`Webview version: '${webviewVersion}'`);
411
- }
412
- match = /Starting ChromeDriver ([.\d]+)/.exec(out);
413
- if (match) {
414
- this.log.debug(`Chromedriver version: '${match[1]}'`);
415
- this.syncProtocol(match[1]);
416
- }
417
- if (this.verbose) {
418
- for (let line of (stdout || '').trim().split('\n')) {
419
- if (!line.trim().length) continue;
420
- this.log.debug(`[STDOUT] ${line}`);
421
- }
422
- for (let line of (stderr || '').trim().split('\n')) {
423
- if (!line.trim().length) continue;
424
- this.log.error(`[STDERR] ${line}`);
425
- }
426
- }
427
- });
428
- this.proc.on('exit', (code, signal) => {
429
- processIsAlive = false;
430
- if (this.state !== Chromedriver.STATE_STOPPED && this.state !== Chromedriver.STATE_STOPPING && this.state !== Chromedriver.STATE_RESTARTING) {
431
- const msg = `Chromedriver exited unexpectedly with code ${code}, signal ${signal}`;
432
- this.log.error(msg);
433
- this.changeState(Chromedriver.STATE_STOPPED);
434
- }
435
- });
436
- this.log.info(`Spawning chromedriver with: ${this.chromedriver} ${args.join(' ')}`);
437
- await this.proc.start(startDetector);
438
- await this.waitForOnline();
439
- await this.startSession();
440
- } catch (e) {
441
- this.log.debug(e);
442
- this.emit(Chromedriver.EVENT_ERROR, e);
443
- if (processIsAlive) {
444
- await this.proc.stop();
445
- }
446
- let message = '';
447
- if (e.message.includes('Chrome version must be')) {
448
- var _exec;
449
- message += 'Unable to automate Chrome version because it is not supported by this version of Chromedriver.\n';
450
- if (webviewVersion) {
451
- message += `Chrome version on the device: ${webviewVersion}\n`;
452
- }
453
- const versionsSupportedByDriver = ((_exec = /Chrome version must be (.+)/.exec(e.message)) === null || _exec === void 0 ? void 0 : _exec[1]) || '';
454
- if (versionsSupportedByDriver) {
455
- message += `Chromedriver supports Chrome version(s): ${versionsSupportedByDriver}\n`;
456
- }
457
- message += 'Check the driver tutorial for troubleshooting.\n';
458
- }
459
- message += e.message;
460
- this.log.errorAndThrow(message);
461
- }
462
- }
463
- sessionId() {
464
- return this.state === Chromedriver.STATE_ONLINE ? this.jwproxy.sessionId : null;
465
- }
466
- async restart() {
467
- this.log.info('Restarting chromedriver');
468
- if (this.state !== Chromedriver.STATE_ONLINE) {
469
- throw new Error("Can't restart when we're not online");
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
- this.changeState(Chromedriver.STATE_RESTARTING);
472
- await this.stop(false);
473
- await this.start(this.capabilities, false);
474
- }
475
- async waitForOnline() {
476
- let chromedriverStopped = false;
477
- await (0, _asyncbox.retryInterval)(20, 200, async () => {
478
- if (this.state === Chromedriver.STATE_STOPPED) {
479
- chromedriverStopped = true;
480
- return;
481
- }
482
- await this.getStatus();
483
- });
484
- if (chromedriverStopped) {
485
- throw new Error('ChromeDriver crashed during startup.');
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
- async getStatus() {
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
- const runSafeStep = async f => {
509
- try {
510
- return await f();
511
- } catch (e) {
512
- this.log.warn(e.message);
513
- this.log.debug(e.stack);
514
- }
515
- };
516
- await runSafeStep(() => this.jwproxy.command('', 'DELETE'));
517
- await runSafeStep(() => this.proc.stop('SIGTERM', 20000));
518
- this.log.prefix = (0, _utils.generateLogPrefix)(this);
519
- if (emitStates) {
520
- this.changeState(Chromedriver.STATE_STOPPED);
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
- changeState(state) {
524
- this.state = state;
525
- this.log.debug(`Changed state to '${state}'`);
526
- this.emit(Chromedriver.EVENT_CHANGED, {
527
- state
528
- });
529
- }
530
- async sendCommand(url, method, body) {
531
- return await this.jwproxy.command(url, method, body);
532
- }
533
- async proxyReq(req, res) {
534
- return await this.jwproxy.proxyReqRes(req, res);
535
- }
536
- async killAll() {
537
- let cmd = _support.system.isWindows() ? `wmic process where "commandline like '%chromedriver.exe%--port=${this.proxyPort}%'" delete` : `pkill -15 -f "${this.chromedriver}.*--port=${this.proxyPort}"`;
538
- this.log.debug(`Killing any old chromedrivers, running: ${cmd}`);
539
- try {
540
- await _bluebird.default.promisify(_child_process.default.exec)(cmd);
541
- this.log.debug('Successfully cleaned up old chromedrivers');
542
- } catch (err) {
543
- this.log.warn('No old chromedrivers seem to exist');
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
- if (this.adb) {
546
- const udidIndex = this.adb.executable.defaultArgs.findIndex(item => item === '-s');
547
- const udid = udidIndex > -1 ? this.adb.executable.defaultArgs[udidIndex + 1] : null;
548
- if (udid) {
549
- this.log.debug(`Cleaning this device's adb forwarded port socket connections: ${udid}`);
550
- } else {
551
- this.log.debug(`Cleaning any old adb forwarded port socket connections`);
552
- }
553
- try {
554
- for (let conn of await this.adb.getForwardList()) {
555
- if (!(conn.includes('webview_devtools') && (!udid || conn.includes(udid)))) {
556
- continue;
557
- }
558
- let params = conn.split(/\s+/);
559
- if (params.length > 1) {
560
- await this.adb.removePortForward(params[1].replace(/[\D]*/, ''));
561
- }
562
- }
563
- } catch (err) {
564
- this.log.warn(`Unable to clean forwarded ports. Error: '${err.message}'. Continuing.`);
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
- async hasWorkingWebview() {
569
- try {
570
- await this.jwproxy.command('/url', 'GET');
571
- return true;
572
- } catch (e) {
573
- return false;
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
- var _default = Chromedriver;
586
- exports.default = _default;
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