web-ext 7.1.1 → 7.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/README.md +2 -2
- package/lib/cmd/build.js +18 -36
- package/lib/cmd/build.js.map +1 -1
- package/lib/cmd/docs.js +1 -0
- package/lib/cmd/docs.js.map +1 -1
- package/lib/cmd/index.js +3 -5
- package/lib/cmd/index.js.map +1 -1
- package/lib/cmd/lint.js +28 -17
- package/lib/cmd/lint.js.map +1 -1
- package/lib/cmd/run.js +19 -23
- package/lib/cmd/run.js.map +1 -1
- package/lib/cmd/sign.js +80 -47
- package/lib/cmd/sign.js.map +1 -1
- package/lib/config.js +18 -29
- package/lib/config.js.map +1 -1
- package/lib/errors.js +8 -18
- package/lib/errors.js.map +1 -1
- package/lib/extension-runners/base.js +2 -0
- package/lib/extension-runners/base.js.map +1 -1
- package/lib/extension-runners/chromium.js +36 -62
- package/lib/extension-runners/chromium.js.map +1 -1
- package/lib/extension-runners/firefox-android.js +65 -102
- package/lib/extension-runners/firefox-android.js.map +1 -1
- package/lib/extension-runners/firefox-desktop.js +30 -42
- package/lib/extension-runners/firefox-desktop.js.map +1 -1
- package/lib/extension-runners/index.js +33 -46
- package/lib/extension-runners/index.js.map +1 -1
- package/lib/firefox/index.js +75 -47
- package/lib/firefox/index.js.map +1 -1
- package/lib/firefox/package-identifiers.js +2 -0
- package/lib/firefox/package-identifiers.js.map +1 -1
- package/lib/firefox/preferences.js +14 -15
- package/lib/firefox/preferences.js.map +1 -1
- package/lib/firefox/rdp-client.js +16 -64
- package/lib/firefox/rdp-client.js.map +1 -1
- package/lib/firefox/remote.js +15 -30
- package/lib/firefox/remote.js.map +1 -1
- package/lib/main.js +4 -2
- package/lib/main.js.map +1 -1
- package/lib/program.js +75 -77
- package/lib/program.js.map +1 -1
- package/lib/util/adb.js +33 -63
- package/lib/util/adb.js.map +1 -1
- package/lib/util/artifacts.js +3 -5
- package/lib/util/artifacts.js.map +1 -1
- package/lib/util/desktop-notifier.js +1 -0
- package/lib/util/desktop-notifier.js.map +1 -1
- package/lib/util/file-exists.js +1 -2
- package/lib/util/file-exists.js.map +1 -1
- package/lib/util/file-filter.js +17 -19
- package/lib/util/file-filter.js.map +1 -1
- package/lib/util/is-directory.js +2 -1
- package/lib/util/is-directory.js.map +1 -1
- package/lib/util/logger.js +7 -12
- package/lib/util/logger.js.map +1 -1
- package/lib/util/manifest.js +6 -13
- package/lib/util/manifest.js.map +1 -1
- package/lib/util/promisify.js +7 -3
- package/lib/util/promisify.js.map +1 -1
- package/lib/util/stdin.js +2 -0
- package/lib/util/stdin.js.map +1 -1
- package/lib/util/submit-addon.js +288 -0
- package/lib/util/submit-addon.js.map +1 -0
- package/lib/util/temp-dir.js +8 -18
- package/lib/util/temp-dir.js.map +1 -1
- package/lib/util/updates.js +1 -1
- package/lib/util/updates.js.map +1 -1
- package/lib/watcher.js +14 -9
- package/lib/watcher.js.map +1 -1
- package/package.json +19 -18
package/lib/errors.js
CHANGED
|
@@ -1,64 +1,59 @@
|
|
|
1
|
+
|
|
1
2
|
import ExtendableError from 'es6-error';
|
|
3
|
+
|
|
2
4
|
/*
|
|
3
5
|
* Base error for all custom web-ext errors.
|
|
4
6
|
*/
|
|
5
|
-
|
|
6
7
|
export class WebExtError extends ExtendableError {
|
|
7
8
|
constructor(message) {
|
|
8
9
|
super(message);
|
|
9
10
|
}
|
|
10
|
-
|
|
11
11
|
}
|
|
12
|
+
|
|
12
13
|
/*
|
|
13
14
|
* The class for errors that can be fixed by the developer.
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
16
|
export class UsageError extends WebExtError {
|
|
17
17
|
constructor(message) {
|
|
18
18
|
super(message);
|
|
19
19
|
}
|
|
20
|
-
|
|
21
20
|
}
|
|
21
|
+
|
|
22
22
|
/*
|
|
23
23
|
* The manifest for the extension is invalid (or missing).
|
|
24
24
|
*/
|
|
25
|
-
|
|
26
25
|
export class InvalidManifest extends UsageError {
|
|
27
26
|
constructor(message) {
|
|
28
27
|
super(message);
|
|
29
28
|
}
|
|
30
|
-
|
|
31
29
|
}
|
|
30
|
+
|
|
32
31
|
/*
|
|
33
32
|
* The remote Firefox does not support temporary add-on installation.
|
|
34
33
|
*/
|
|
35
|
-
|
|
36
34
|
export class RemoteTempInstallNotSupported extends WebExtError {
|
|
37
35
|
constructor(message) {
|
|
38
36
|
super(message);
|
|
39
37
|
}
|
|
40
|
-
|
|
41
38
|
}
|
|
39
|
+
|
|
42
40
|
/*
|
|
43
41
|
* The errors collected when reloading all extensions at once
|
|
44
42
|
* (initialized from a map of errors by extensionSourceDir string).
|
|
45
43
|
*/
|
|
46
|
-
|
|
47
44
|
export class MultiExtensionsReloadError extends WebExtError {
|
|
48
45
|
constructor(errorsMap) {
|
|
49
46
|
let errors = '';
|
|
50
|
-
|
|
51
47
|
for (const [sourceDir, error] of errorsMap) {
|
|
52
48
|
const msg = String(error);
|
|
53
49
|
errors += `\nError on extension loaded from ${sourceDir}: ${msg}\n`;
|
|
54
50
|
}
|
|
55
|
-
|
|
56
51
|
const message = `Reload errors: ${errors}`;
|
|
57
52
|
super(message);
|
|
58
53
|
this.errorsBySourceDir = errorsMap;
|
|
59
54
|
}
|
|
60
|
-
|
|
61
55
|
}
|
|
56
|
+
|
|
62
57
|
/*
|
|
63
58
|
* Sugar-y way to catch only instances of a certain error.
|
|
64
59
|
*
|
|
@@ -72,7 +67,6 @@ export class MultiExtensionsReloadError extends WebExtError {
|
|
|
72
67
|
* All other errors will be re-thrown.
|
|
73
68
|
*
|
|
74
69
|
*/
|
|
75
|
-
|
|
76
70
|
export function onlyInstancesOf(predicate, errorHandler) {
|
|
77
71
|
return error => {
|
|
78
72
|
if (error instanceof predicate) {
|
|
@@ -82,6 +76,7 @@ export function onlyInstancesOf(predicate, errorHandler) {
|
|
|
82
76
|
}
|
|
83
77
|
};
|
|
84
78
|
}
|
|
79
|
+
|
|
85
80
|
/*
|
|
86
81
|
* Sugar-y way to catch only errors having certain code(s).
|
|
87
82
|
*
|
|
@@ -102,11 +97,9 @@ export function onlyInstancesOf(predicate, errorHandler) {
|
|
|
102
97
|
* All other errors will be re-thrown.
|
|
103
98
|
*
|
|
104
99
|
*/
|
|
105
|
-
|
|
106
100
|
export function onlyErrorsWithCode(codeWanted, errorHandler) {
|
|
107
101
|
return error => {
|
|
108
102
|
let throwError = true;
|
|
109
|
-
|
|
110
103
|
if (Array.isArray(codeWanted)) {
|
|
111
104
|
if (codeWanted.indexOf(error.code) !== -1 || codeWanted.indexOf(error.errno) !== -1) {
|
|
112
105
|
throwError = false;
|
|
@@ -114,11 +107,9 @@ export function onlyErrorsWithCode(codeWanted, errorHandler) {
|
|
|
114
107
|
} else if (error.code === codeWanted || error.errno === codeWanted) {
|
|
115
108
|
throwError = false;
|
|
116
109
|
}
|
|
117
|
-
|
|
118
110
|
if (throwError) {
|
|
119
111
|
throw error;
|
|
120
112
|
}
|
|
121
|
-
|
|
122
113
|
return errorHandler(error);
|
|
123
114
|
};
|
|
124
115
|
}
|
|
@@ -128,7 +119,6 @@ export function isErrorWithCode(codeWanted, error) {
|
|
|
128
119
|
} else if (error.code === codeWanted) {
|
|
129
120
|
return true;
|
|
130
121
|
}
|
|
131
|
-
|
|
132
122
|
return false;
|
|
133
123
|
}
|
|
134
124
|
//# sourceMappingURL=errors.js.map
|
package/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":["ExtendableError","WebExtError","constructor","message","UsageError","InvalidManifest","RemoteTempInstallNotSupported","MultiExtensionsReloadError","errorsMap","errors","sourceDir","error","msg","String","errorsBySourceDir","onlyInstancesOf","predicate","errorHandler","onlyErrorsWithCode","codeWanted","throwError","Array","isArray","indexOf","code","errno","isErrorWithCode"],"sources":["../src/errors.js"],"sourcesContent":["/* @flow */\nimport ExtendableError from 'es6-error';\n\n\n/*\n * Base error for all custom web-ext errors.\n */\nexport class WebExtError extends ExtendableError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The class for errors that can be fixed by the developer.\n */\nexport class UsageError extends WebExtError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The manifest for the extension is invalid (or missing).\n */\nexport class InvalidManifest extends UsageError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The remote Firefox does not support temporary add-on installation.\n */\nexport class RemoteTempInstallNotSupported extends WebExtError {\n constructor(message: string) {\n super(message);\n }\n}\n\n/*\n * The errors collected when reloading all extensions at once\n * (initialized from a map of errors by extensionSourceDir string).\n */\nexport class MultiExtensionsReloadError extends WebExtError {\n constructor(errorsMap: Map<string, Error>) {\n let errors = '';\n for (const [sourceDir, error] of errorsMap) {\n const msg = String(error);\n errors += `\\nError on extension loaded from ${sourceDir}: ${msg}\\n`;\n }\n const message = `Reload errors: ${errors}`;\n\n super(message);\n this.errorsBySourceDir = errorsMap;\n }\n}\n\n/*\n * Sugar-y way to catch only instances of a certain error.\n *\n * Usage:\n *\n * Promise.reject(SyntaxError)\n * .catch(onlyInstancesOf(SyntaxError, (error) => {\n * // error is guaranteed to be an instance of SyntaxError\n * }))\n *\n * All other errors will be re-thrown.\n *\n */\nexport function onlyInstancesOf(\n predicate: Function, errorHandler: Function\n): Function {\n return (error) => {\n if (error instanceof predicate) {\n return errorHandler(error);\n } else {\n throw error;\n }\n };\n}\n\n\n/*\n * Sugar-y way to catch only errors having certain code(s).\n *\n * Usage:\n *\n * Promise.resolve()\n * .catch(onlyErrorsWithCode('ENOENT', (error) => {\n * // error.code is guaranteed to be ENOENT\n * }))\n *\n * or:\n *\n * Promise.resolve()\n * .catch(onlyErrorsWithCode(['ENOENT', 'ENOTDIR'], (error) => {\n * // ...\n * }))\n *\n * All other errors will be re-thrown.\n *\n */\nexport function onlyErrorsWithCode(\n codeWanted: (string | number) | Array<string | number>,\n errorHandler: Function\n): Function {\n return (error) => {\n let throwError = true;\n\n if (Array.isArray(codeWanted)) {\n if (codeWanted.indexOf(error.code) !== -1 ||\n codeWanted.indexOf(error.errno) !== -1) {\n throwError = false;\n }\n } else if (error.code === codeWanted || error.errno === codeWanted) {\n throwError = false;\n }\n\n if (throwError) {\n throw error;\n }\n\n return errorHandler(error);\n };\n}\n\nexport function isErrorWithCode(\n codeWanted: string | Array<string>,\n error: Object,\n): boolean {\n if (Array.isArray(codeWanted) && codeWanted.indexOf(error.code) !== -1) {\n return true;\n } else if (error.code === codeWanted) {\n return true;\n }\n\n return false;\n}\n"],"mappings":"AACA,OAAOA,
|
|
1
|
+
{"version":3,"file":"errors.js","names":["ExtendableError","WebExtError","constructor","message","UsageError","InvalidManifest","RemoteTempInstallNotSupported","MultiExtensionsReloadError","errorsMap","errors","sourceDir","error","msg","String","errorsBySourceDir","onlyInstancesOf","predicate","errorHandler","onlyErrorsWithCode","codeWanted","throwError","Array","isArray","indexOf","code","errno","isErrorWithCode"],"sources":["../src/errors.js"],"sourcesContent":["/* @flow */\nimport ExtendableError from 'es6-error';\n\n\n/*\n * Base error for all custom web-ext errors.\n */\nexport class WebExtError extends ExtendableError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The class for errors that can be fixed by the developer.\n */\nexport class UsageError extends WebExtError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The manifest for the extension is invalid (or missing).\n */\nexport class InvalidManifest extends UsageError {\n constructor(message: string) {\n super(message);\n }\n}\n\n\n/*\n * The remote Firefox does not support temporary add-on installation.\n */\nexport class RemoteTempInstallNotSupported extends WebExtError {\n constructor(message: string) {\n super(message);\n }\n}\n\n/*\n * The errors collected when reloading all extensions at once\n * (initialized from a map of errors by extensionSourceDir string).\n */\nexport class MultiExtensionsReloadError extends WebExtError {\n constructor(errorsMap: Map<string, Error>) {\n let errors = '';\n for (const [sourceDir, error] of errorsMap) {\n const msg = String(error);\n errors += `\\nError on extension loaded from ${sourceDir}: ${msg}\\n`;\n }\n const message = `Reload errors: ${errors}`;\n\n super(message);\n this.errorsBySourceDir = errorsMap;\n }\n}\n\n/*\n * Sugar-y way to catch only instances of a certain error.\n *\n * Usage:\n *\n * Promise.reject(SyntaxError)\n * .catch(onlyInstancesOf(SyntaxError, (error) => {\n * // error is guaranteed to be an instance of SyntaxError\n * }))\n *\n * All other errors will be re-thrown.\n *\n */\nexport function onlyInstancesOf(\n predicate: Function, errorHandler: Function\n): Function {\n return (error) => {\n if (error instanceof predicate) {\n return errorHandler(error);\n } else {\n throw error;\n }\n };\n}\n\n\n/*\n * Sugar-y way to catch only errors having certain code(s).\n *\n * Usage:\n *\n * Promise.resolve()\n * .catch(onlyErrorsWithCode('ENOENT', (error) => {\n * // error.code is guaranteed to be ENOENT\n * }))\n *\n * or:\n *\n * Promise.resolve()\n * .catch(onlyErrorsWithCode(['ENOENT', 'ENOTDIR'], (error) => {\n * // ...\n * }))\n *\n * All other errors will be re-thrown.\n *\n */\nexport function onlyErrorsWithCode(\n codeWanted: (string | number) | Array<string | number>,\n errorHandler: Function\n): Function {\n return (error) => {\n let throwError = true;\n\n if (Array.isArray(codeWanted)) {\n if (codeWanted.indexOf(error.code) !== -1 ||\n codeWanted.indexOf(error.errno) !== -1) {\n throwError = false;\n }\n } else if (error.code === codeWanted || error.errno === codeWanted) {\n throwError = false;\n }\n\n if (throwError) {\n throw error;\n }\n\n return errorHandler(error);\n };\n}\n\nexport function isErrorWithCode(\n codeWanted: string | Array<string>,\n error: Object,\n): boolean {\n if (Array.isArray(codeWanted) && codeWanted.indexOf(error.code) !== -1) {\n return true;\n } else if (error.code === codeWanted) {\n return true;\n }\n\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":["showDesktopNotification","defaultDesktopNotifications"],"sources":["../../src/extension-runners/base.js"],"sourcesContent":["/* @flow */\n\nimport {\n showDesktopNotification as defaultDesktopNotifications,\n} from '../util/desktop-notifier';\nimport type {ExtensionManifest} from '../util/manifest';\n\nexport type Extension = {|\n sourceDir: string,\n manifestData: ExtensionManifest,\n|};\n\nexport type ExtensionRunnerParams = {|\n // Common cli params.\n extensions: Array<Extension>,\n profilePath?: string,\n keepProfileChanges: boolean,\n startUrl: ?string | ?Array<string>,\n args?: Array<string>,\n\n // Common injected dependencies.\n desktopNotifications: typeof defaultDesktopNotifications,\n|};\n\nexport type ExtensionRunnerReloadResult = {|\n runnerName: string,\n reloadError?: Error,\n sourceDir?: string,\n|};\n\nexport interface IExtensionRunner {\n getName(): string,\n run(): Promise<void>,\n reloadAllExtensions(): Promise<Array<ExtensionRunnerReloadResult>>,\n reloadExtensionBySourceDir(\n extensionSourceDir: string\n ): Promise<Array<ExtensionRunnerReloadResult>>,\n registerCleanup(fn: Function): void,\n exit(): Promise<void>,\n}\n"],"mappings":"AAEA,SACEA,uBAAuB,IAAIC,
|
|
1
|
+
{"version":3,"file":"base.js","names":["showDesktopNotification","defaultDesktopNotifications"],"sources":["../../src/extension-runners/base.js"],"sourcesContent":["/* @flow */\n\nimport {\n showDesktopNotification as defaultDesktopNotifications,\n} from '../util/desktop-notifier';\nimport type {ExtensionManifest} from '../util/manifest';\n\nexport type Extension = {|\n sourceDir: string,\n manifestData: ExtensionManifest,\n|};\n\nexport type ExtensionRunnerParams = {|\n // Common cli params.\n extensions: Array<Extension>,\n profilePath?: string,\n keepProfileChanges: boolean,\n startUrl: ?string | ?Array<string>,\n args?: Array<string>,\n\n // Common injected dependencies.\n desktopNotifications: typeof defaultDesktopNotifications,\n|};\n\nexport type ExtensionRunnerReloadResult = {|\n runnerName: string,\n reloadError?: Error,\n sourceDir?: string,\n|};\n\nexport interface IExtensionRunner {\n getName(): string,\n run(): Promise<void>,\n reloadAllExtensions(): Promise<Array<ExtensionRunnerReloadResult>>,\n reloadExtensionBySourceDir(\n extensionSourceDir: string\n ): Promise<Array<ExtensionRunnerReloadResult>>,\n registerCleanup(fn: Function): void,\n exit(): Promise<void>,\n}\n"],"mappings":";;AAEA,SACEA,uBAAuB,IAAIC,2BAA2B,QACjD,0BAA0B"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* This module provide an ExtensionRunner subclass that manage an extension executed
|
|
3
5
|
* in a Chromium-based browser instance.
|
|
4
6
|
*/
|
|
7
|
+
|
|
5
8
|
import path from 'path';
|
|
6
9
|
import fs from 'fs-extra';
|
|
7
10
|
import asyncMkdirp from 'mkdirp';
|
|
@@ -14,10 +17,10 @@ import fileExists from '../util/file-exists.js';
|
|
|
14
17
|
const log = createLogger(import.meta.url);
|
|
15
18
|
const EXCLUDED_CHROME_FLAGS = ['--disable-extensions', '--mute-audio'];
|
|
16
19
|
export const DEFAULT_CHROME_FLAGS = ChromeLauncher.defaultFlags().filter(flag => !EXCLUDED_CHROME_FLAGS.includes(flag));
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Implements an IExtensionRunner which manages a Chromium instance.
|
|
19
23
|
*/
|
|
20
|
-
|
|
21
24
|
export class ChromiumExtensionRunner {
|
|
22
25
|
constructor(params) {
|
|
23
26
|
const {
|
|
@@ -26,36 +29,32 @@ export class ChromiumExtensionRunner {
|
|
|
26
29
|
this.params = params;
|
|
27
30
|
this.chromiumLaunch = chromiumLaunch;
|
|
28
31
|
this.cleanupCallbacks = new Set();
|
|
29
|
-
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Method exported from the IExtensionRunner interface.
|
|
30
35
|
|
|
31
36
|
/**
|
|
32
37
|
* Returns the runner name.
|
|
33
38
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
39
|
getName() {
|
|
37
40
|
return 'Chromium';
|
|
38
41
|
}
|
|
39
|
-
|
|
40
42
|
async run() {
|
|
41
43
|
// Run should never be called more than once.
|
|
42
44
|
this._promiseSetupDone = this.setupInstance();
|
|
43
45
|
await this._promiseSetupDone;
|
|
44
46
|
}
|
|
45
|
-
|
|
46
47
|
static async isUserDataDir(dirPath) {
|
|
47
48
|
const localStatePath = path.join(dirPath, 'Local State');
|
|
48
|
-
const defaultPath = path.join(dirPath, 'Default');
|
|
49
|
-
|
|
49
|
+
const defaultPath = path.join(dirPath, 'Default');
|
|
50
|
+
// Local State and Default are typical for the user-data-dir
|
|
50
51
|
return (await fileExists(localStatePath)) && (await isDirectory(defaultPath));
|
|
51
52
|
}
|
|
52
|
-
|
|
53
53
|
static async isProfileDir(dirPath) {
|
|
54
|
-
const securePreferencesPath = path.join(dirPath, 'Secure Preferences');
|
|
55
|
-
|
|
54
|
+
const securePreferencesPath = path.join(dirPath, 'Secure Preferences');
|
|
55
|
+
//Secure Preferences is typical for a profile dir inside a user data dir
|
|
56
56
|
return await fileExists(securePreferencesPath);
|
|
57
57
|
}
|
|
58
|
-
|
|
59
58
|
static async getProfilePaths(chromiumProfile) {
|
|
60
59
|
if (!chromiumProfile) {
|
|
61
60
|
return {
|
|
@@ -63,9 +62,7 @@ export class ChromiumExtensionRunner {
|
|
|
63
62
|
profileDirName: null
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
|
-
|
|
67
65
|
const isProfileDirAndNotUserData = (await ChromiumExtensionRunner.isProfileDir(chromiumProfile)) && !(await ChromiumExtensionRunner.isUserDataDir(chromiumProfile));
|
|
68
|
-
|
|
69
66
|
if (isProfileDirAndNotUserData) {
|
|
70
67
|
const {
|
|
71
68
|
dir: userDataDir,
|
|
@@ -76,40 +73,43 @@ export class ChromiumExtensionRunner {
|
|
|
76
73
|
profileDirName
|
|
77
74
|
};
|
|
78
75
|
}
|
|
79
|
-
|
|
80
76
|
return {
|
|
81
77
|
userDataDir: chromiumProfile,
|
|
82
78
|
profileDirName: null
|
|
83
79
|
};
|
|
84
80
|
}
|
|
81
|
+
|
|
85
82
|
/**
|
|
86
83
|
* Setup the Chromium Profile and run a Chromium instance.
|
|
87
84
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
85
|
async setupInstance() {
|
|
91
86
|
// Start a websocket server on a free localhost TCP port.
|
|
92
87
|
this.wss = await new Promise(resolve => {
|
|
93
|
-
const server = new WebSocketServer(
|
|
88
|
+
const server = new WebSocketServer(
|
|
89
|
+
// Use a ipv4 host so we don't need to escape ipv6 address
|
|
94
90
|
// https://github.com/mozilla/web-ext/issues/2331
|
|
95
91
|
{
|
|
96
92
|
port: 0,
|
|
97
93
|
host: '127.0.0.1',
|
|
98
94
|
clientTracking: true
|
|
99
|
-
},
|
|
95
|
+
},
|
|
96
|
+
// Wait the server to be listening (so that the extension
|
|
100
97
|
// runner can successfully retrieve server address and port).
|
|
101
98
|
() => resolve(server));
|
|
102
|
-
});
|
|
103
|
-
// is exiting, See https://github.com/websockets/ws/issues/1256).
|
|
99
|
+
});
|
|
104
100
|
|
|
101
|
+
// Prevent unhandled socket error (e.g. when chrome
|
|
102
|
+
// is exiting, See https://github.com/websockets/ws/issues/1256).
|
|
105
103
|
this.wss.on('connection', function (socket) {
|
|
106
104
|
socket.on('error', err => {
|
|
107
105
|
log.debug(`websocket connection error: ${err}`);
|
|
108
106
|
});
|
|
109
|
-
});
|
|
107
|
+
});
|
|
110
108
|
|
|
111
|
-
|
|
109
|
+
// Create the extension that will manage the addon reloads
|
|
110
|
+
this.reloadManagerExtension = await this.createReloadManagerExtension();
|
|
112
111
|
|
|
112
|
+
// Start chrome pointing it to a given profile dir
|
|
113
113
|
const extensions = [this.reloadManagerExtension].concat(this.params.extensions.map(({
|
|
114
114
|
sourceDir
|
|
115
115
|
}) => sourceDir)).join(',');
|
|
@@ -117,24 +117,20 @@ export class ChromiumExtensionRunner {
|
|
|
117
117
|
chromiumBinary
|
|
118
118
|
} = this.params;
|
|
119
119
|
log.debug('Starting Chromium instance...');
|
|
120
|
-
|
|
121
120
|
if (chromiumBinary) {
|
|
122
121
|
log.debug(`(chromiumBinary: ${chromiumBinary})`);
|
|
123
122
|
}
|
|
124
|
-
|
|
125
123
|
const chromeFlags = [...DEFAULT_CHROME_FLAGS];
|
|
126
124
|
chromeFlags.push(`--load-extension=${extensions}`);
|
|
127
|
-
|
|
128
125
|
if (this.params.args) {
|
|
129
126
|
chromeFlags.push(...this.params.args);
|
|
130
|
-
}
|
|
131
|
-
|
|
127
|
+
}
|
|
132
128
|
|
|
129
|
+
// eslint-disable-next-line prefer-const
|
|
133
130
|
let {
|
|
134
131
|
userDataDir,
|
|
135
132
|
profileDirName
|
|
136
133
|
} = await ChromiumExtensionRunner.getProfilePaths(this.params.chromiumProfile);
|
|
137
|
-
|
|
138
134
|
if (userDataDir && this.params.keepProfileChanges) {
|
|
139
135
|
if (profileDirName && !(await ChromiumExtensionRunner.isUserDataDir(userDataDir))) {
|
|
140
136
|
throw new Error('The profile you provided is not in a ' + 'user-data-dir. The changes cannot be kept. Please either ' + 'remove --keep-profile-changes or use a profile in a ' + 'user-data-dir directory');
|
|
@@ -146,29 +142,23 @@ export class ChromiumExtensionRunner {
|
|
|
146
142
|
const tmpDir = new TempDir();
|
|
147
143
|
await tmpDir.create();
|
|
148
144
|
const tmpDirPath = tmpDir.path();
|
|
149
|
-
|
|
150
145
|
if (userDataDir && profileDirName) {
|
|
151
146
|
// copy profile dir to this temp user data dir.
|
|
152
147
|
await fs.copy(path.join(userDataDir, profileDirName), path.join(tmpDirPath, profileDirName));
|
|
153
148
|
} else if (userDataDir) {
|
|
154
149
|
await fs.copy(userDataDir, tmpDirPath);
|
|
155
150
|
}
|
|
156
|
-
|
|
157
151
|
userDataDir = tmpDirPath;
|
|
158
152
|
}
|
|
159
|
-
|
|
160
153
|
if (profileDirName) {
|
|
161
154
|
chromeFlags.push(`--profile-directory=${profileDirName}`);
|
|
162
155
|
}
|
|
163
|
-
|
|
164
156
|
let startingUrl;
|
|
165
|
-
|
|
166
157
|
if (this.params.startUrl) {
|
|
167
158
|
const startingUrls = Array.isArray(this.params.startUrl) ? this.params.startUrl : [this.params.startUrl];
|
|
168
159
|
startingUrl = startingUrls.shift();
|
|
169
160
|
chromeFlags.push(...startingUrls);
|
|
170
161
|
}
|
|
171
|
-
|
|
172
162
|
this.chromiumInstance = await this.chromiumLaunch({
|
|
173
163
|
enableExtensions: true,
|
|
174
164
|
chromePath: chromiumBinary,
|
|
@@ -180,37 +170,30 @@ export class ChromiumExtensionRunner {
|
|
|
180
170
|
});
|
|
181
171
|
this.chromiumInstance.process.once('close', () => {
|
|
182
172
|
this.chromiumInstance = null;
|
|
183
|
-
|
|
184
173
|
if (!this.exiting) {
|
|
185
174
|
log.info('Exiting on Chromium instance disconnected.');
|
|
186
175
|
this.exit();
|
|
187
176
|
}
|
|
188
177
|
});
|
|
189
178
|
}
|
|
190
|
-
|
|
191
179
|
async wssBroadcast(data) {
|
|
192
180
|
return new Promise(resolve => {
|
|
193
181
|
const clients = this.wss ? new Set(this.wss.clients) : new Set();
|
|
194
|
-
|
|
195
182
|
function cleanWebExtReloadComplete() {
|
|
196
183
|
const client = this;
|
|
197
184
|
client.removeEventListener('message', webExtReloadComplete);
|
|
198
185
|
client.removeEventListener('close', cleanWebExtReloadComplete);
|
|
199
186
|
clients.delete(client);
|
|
200
187
|
}
|
|
201
|
-
|
|
202
188
|
const webExtReloadComplete = async message => {
|
|
203
189
|
const msg = JSON.parse(message.data);
|
|
204
|
-
|
|
205
190
|
if (msg.type === 'webExtReloadExtensionComplete') {
|
|
206
191
|
for (const client of clients) {
|
|
207
192
|
cleanWebExtReloadComplete.call(client);
|
|
208
193
|
}
|
|
209
|
-
|
|
210
194
|
resolve();
|
|
211
195
|
}
|
|
212
196
|
};
|
|
213
|
-
|
|
214
197
|
for (const client of clients) {
|
|
215
198
|
if (client.readyState === WebSocket.OPEN) {
|
|
216
199
|
client.addEventListener('message', webExtReloadComplete);
|
|
@@ -220,13 +203,11 @@ export class ChromiumExtensionRunner {
|
|
|
220
203
|
clients.delete(client);
|
|
221
204
|
}
|
|
222
205
|
}
|
|
223
|
-
|
|
224
206
|
if (clients.size === 0) {
|
|
225
207
|
resolve();
|
|
226
208
|
}
|
|
227
209
|
});
|
|
228
210
|
}
|
|
229
|
-
|
|
230
211
|
async createReloadManagerExtension() {
|
|
231
212
|
const tmpDir = new TempDir();
|
|
232
213
|
await tmpDir.create();
|
|
@@ -242,8 +223,9 @@ export class ChromiumExtensionRunner {
|
|
|
242
223
|
background: {
|
|
243
224
|
scripts: ['bg.js']
|
|
244
225
|
}
|
|
245
|
-
}));
|
|
226
|
+
}));
|
|
246
227
|
|
|
228
|
+
// $FlowIgnore: this method is only called right after creating the server and so wss should be defined.
|
|
247
229
|
const wssInfo = this.wss.address();
|
|
248
230
|
const bgPage = `(function bgPage() {
|
|
249
231
|
async function getAllDevExtensions() {
|
|
@@ -282,12 +264,11 @@ export class ChromiumExtensionRunner {
|
|
|
282
264
|
await fs.writeFile(path.join(extPath, 'bg.js'), bgPage);
|
|
283
265
|
return extPath;
|
|
284
266
|
}
|
|
267
|
+
|
|
285
268
|
/**
|
|
286
269
|
* Reloads all the extensions, collect any reload error and resolves to
|
|
287
270
|
* an array composed by a single ExtensionRunnerReloadResult object.
|
|
288
271
|
*/
|
|
289
|
-
|
|
290
|
-
|
|
291
272
|
async reloadAllExtensions() {
|
|
292
273
|
const runnerName = this.getName();
|
|
293
274
|
await this.wssBroadcast({
|
|
@@ -299,66 +280,60 @@ export class ChromiumExtensionRunner {
|
|
|
299
280
|
runnerName
|
|
300
281
|
}];
|
|
301
282
|
}
|
|
283
|
+
|
|
302
284
|
/**
|
|
303
285
|
* Reloads a single extension, collect any reload error and resolves to
|
|
304
286
|
* an array composed by a single ExtensionRunnerReloadResult object.
|
|
305
287
|
*/
|
|
306
|
-
|
|
307
|
-
|
|
308
288
|
async reloadExtensionBySourceDir(extensionSourceDir) {
|
|
309
289
|
// TODO(rpl): detect the extension ids assigned to the
|
|
310
290
|
// target extensions and map it to the extensions source dir
|
|
311
291
|
// (https://github.com/mozilla/web-ext/issues/1687).
|
|
312
292
|
return this.reloadAllExtensions();
|
|
313
293
|
}
|
|
294
|
+
|
|
314
295
|
/**
|
|
315
296
|
* Register a callback to be called when the runner has been exited
|
|
316
297
|
* (e.g. the Chromium instance exits or the user has requested web-ext
|
|
317
298
|
* to exit).
|
|
318
299
|
*/
|
|
319
|
-
|
|
320
|
-
|
|
321
300
|
registerCleanup(fn) {
|
|
322
301
|
this.cleanupCallbacks.add(fn);
|
|
323
302
|
}
|
|
303
|
+
|
|
324
304
|
/**
|
|
325
305
|
* Exits the runner, by closing the managed Chromium instance.
|
|
326
306
|
*/
|
|
327
|
-
|
|
328
|
-
|
|
329
307
|
async exit() {
|
|
330
|
-
this.exiting = true;
|
|
331
|
-
// being started.
|
|
308
|
+
this.exiting = true;
|
|
332
309
|
|
|
310
|
+
// Wait for the setup to complete if the extension runner is already
|
|
311
|
+
// being started.
|
|
333
312
|
if (this._promiseSetupDone) {
|
|
334
313
|
// Ignore initialization errors if any.
|
|
335
314
|
await this._promiseSetupDone.catch(err => {
|
|
336
315
|
log.debug(`ignored setup error on chromium runner shutdown: ${err}`);
|
|
337
316
|
});
|
|
338
317
|
}
|
|
339
|
-
|
|
340
318
|
if (this.chromiumInstance) {
|
|
341
319
|
await this.chromiumInstance.kill();
|
|
342
320
|
this.chromiumInstance = null;
|
|
343
321
|
}
|
|
344
|
-
|
|
345
322
|
if (this.wss) {
|
|
346
323
|
// Close all websocket clients, closing the WebSocketServer
|
|
347
324
|
// does not terminate the existing connection and it wouldn't
|
|
348
325
|
// resolve until all of the existing connections are closed.
|
|
349
326
|
for (const wssClient of ((_this$wss = this.wss) === null || _this$wss === void 0 ? void 0 : _this$wss.clients) || []) {
|
|
350
327
|
var _this$wss;
|
|
351
|
-
|
|
352
328
|
if (wssClient.readyState === WebSocket.OPEN) {
|
|
353
329
|
wssClient.terminate();
|
|
354
330
|
}
|
|
355
331
|
}
|
|
356
|
-
|
|
357
332
|
await new Promise(resolve => this.wss ? this.wss.close(resolve) : resolve());
|
|
358
333
|
this.wss = null;
|
|
359
|
-
}
|
|
360
|
-
|
|
334
|
+
}
|
|
361
335
|
|
|
336
|
+
// Call all the registered cleanup callbacks.
|
|
362
337
|
for (const fn of this.cleanupCallbacks) {
|
|
363
338
|
try {
|
|
364
339
|
fn();
|
|
@@ -367,6 +342,5 @@ export class ChromiumExtensionRunner {
|
|
|
367
342
|
}
|
|
368
343
|
}
|
|
369
344
|
}
|
|
370
|
-
|
|
371
345
|
}
|
|
372
346
|
//# sourceMappingURL=chromium.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chromium.js","names":["path","fs","asyncMkdirp","Launcher","ChromeLauncher","launch","defaultChromiumLaunch","WebSocket","WebSocketServer","createLogger","TempDir","isDirectory","fileExists","log","import","meta","url","EXCLUDED_CHROME_FLAGS","DEFAULT_CHROME_FLAGS","defaultFlags","filter","flag","includes","ChromiumExtensionRunner","constructor","params","chromiumLaunch","cleanupCallbacks","Set","getName","run","_promiseSetupDone","setupInstance","isUserDataDir","dirPath","localStatePath","join","defaultPath","isProfileDir","securePreferencesPath","getProfilePaths","chromiumProfile","userDataDir","profileDirName","isProfileDirAndNotUserData","dir","base","parse","wss","Promise","resolve","server","port","host","clientTracking","on","socket","err","debug","reloadManagerExtension","createReloadManagerExtension","extensions","concat","map","sourceDir","chromiumBinary","chromeFlags","push","args","keepProfileChanges","Error","tmpDir","create","tmpDirPath","copy","startingUrl","startUrl","startingUrls","Array","isArray","shift","chromiumInstance","enableExtensions","chromePath","ignoreDefaultFlags","process","once","exiting","info","exit","wssBroadcast","data","clients","cleanWebExtReloadComplete","client","removeEventListener","webExtReloadComplete","delete","message","msg","JSON","type","call","readyState","OPEN","addEventListener","send","stringify","size","registerCleanup","remove","extPath","Date","now","writeFile","manifest_version","name","version","permissions","background","scripts","wssInfo","address","bgPage","reloadAllExtensions","runnerName","stdout","write","toTimeString","reloadExtensionBySourceDir","extensionSourceDir","fn","add","catch","kill","wssClient","terminate","close","error"],"sources":["../../src/extension-runners/chromium.js"],"sourcesContent":["/* @flow */\n\n/**\n * This module provide an ExtensionRunner subclass that manage an extension executed\n * in a Chromium-based browser instance.\n */\n\nimport path from 'path';\n\nimport fs from 'fs-extra';\nimport asyncMkdirp from 'mkdirp';\nimport {\n Launcher as ChromeLauncher,\n launch as defaultChromiumLaunch,\n} from 'chrome-launcher';\nimport WebSocket, {WebSocketServer} from 'ws';\n\nimport {createLogger} from '../util/logger.js';\nimport {TempDir} from '../util/temp-dir.js';\nimport type {\n ExtensionRunnerParams,\n ExtensionRunnerReloadResult,\n} from './base';\nimport isDirectory from '../util/is-directory.js';\nimport fileExists from '../util/file-exists.js';\n\ntype ChromiumSpecificRunnerParams = {|\n chromiumBinary?: string,\n chromiumProfile?: string,\n chromiumLaunch?: typeof defaultChromiumLaunch,\n|};\n\nexport type ChromiumExtensionRunnerParams = {|\n ...ExtensionRunnerParams,\n // Chromium desktop CLI params.\n ...ChromiumSpecificRunnerParams,\n|};\n\nconst log = createLogger(import.meta.url);\n\nconst EXCLUDED_CHROME_FLAGS = [\n '--disable-extensions',\n '--mute-audio',\n];\n\nexport const DEFAULT_CHROME_FLAGS: Array<string> = ChromeLauncher.defaultFlags()\n .filter((flag) => !EXCLUDED_CHROME_FLAGS.includes(flag));\n\n/**\n * Implements an IExtensionRunner which manages a Chromium instance.\n */\nexport class ChromiumExtensionRunner {\n cleanupCallbacks: Set<Function>;\n params: ChromiumExtensionRunnerParams;\n chromiumInstance: ?ChromeLauncher;\n chromiumLaunch: typeof defaultChromiumLaunch;\n reloadManagerExtension: string;\n wss: ?WebSocketServer;\n exiting: boolean;\n _promiseSetupDone: ?Promise<void>;\n\n constructor(params: ChromiumExtensionRunnerParams) {\n const {\n chromiumLaunch = defaultChromiumLaunch,\n } = params;\n this.params = params;\n this.chromiumLaunch = chromiumLaunch;\n this.cleanupCallbacks = new Set();\n }\n\n // Method exported from the IExtensionRunner interface.\n\n /**\n * Returns the runner name.\n */\n getName(): string {\n return 'Chromium';\n }\n\n async run(): Promise<void> {\n // Run should never be called more than once.\n this._promiseSetupDone = this.setupInstance();\n await this._promiseSetupDone;\n }\n\n static async isUserDataDir(dirPath: string): Promise<boolean> {\n const localStatePath = path.join(dirPath, 'Local State');\n const defaultPath = path.join(dirPath, 'Default');\n // Local State and Default are typical for the user-data-dir\n return (await fileExists(localStatePath))\n && (await isDirectory(defaultPath));\n }\n\n static async isProfileDir(dirPath: string): Promise<boolean> {\n const securePreferencesPath = path.join(\n dirPath, 'Secure Preferences');\n //Secure Preferences is typical for a profile dir inside a user data dir\n return await fileExists(securePreferencesPath);\n }\n\n static async getProfilePaths(chromiumProfile: ?string): Promise<{\n userDataDir: ?string,\n profileDirName: ?string\n }> {\n if (!chromiumProfile) {\n return {\n userDataDir: null,\n profileDirName: null,\n };\n }\n\n const isProfileDirAndNotUserData =\n (await ChromiumExtensionRunner.isProfileDir(chromiumProfile))\n && !(await ChromiumExtensionRunner.isUserDataDir(chromiumProfile));\n\n if (isProfileDirAndNotUserData) {\n const {dir: userDataDir, base: profileDirName} =\n path.parse(chromiumProfile);\n return {\n userDataDir,\n profileDirName,\n };\n }\n\n return {\n userDataDir: chromiumProfile,\n profileDirName: null,\n };\n\n }\n\n /**\n * Setup the Chromium Profile and run a Chromium instance.\n */\n async setupInstance(): Promise<void> {\n // Start a websocket server on a free localhost TCP port.\n this.wss = await new Promise((resolve) => {\n const server = new WebSocketServer(\n // Use a ipv4 host so we don't need to escape ipv6 address\n // https://github.com/mozilla/web-ext/issues/2331\n {port: 0, host: '127.0.0.1', clientTracking: true},\n // Wait the server to be listening (so that the extension\n // runner can successfully retrieve server address and port).\n () => resolve(server));\n });\n\n // Prevent unhandled socket error (e.g. when chrome\n // is exiting, See https://github.com/websockets/ws/issues/1256).\n this.wss.on('connection', function(socket) {\n socket.on('error', (err) => {\n log.debug(`websocket connection error: ${err}`);\n });\n });\n\n // Create the extension that will manage the addon reloads\n this.reloadManagerExtension = await this.createReloadManagerExtension();\n\n // Start chrome pointing it to a given profile dir\n const extensions = [this.reloadManagerExtension].concat(\n this.params.extensions.map(({sourceDir}) => sourceDir)\n ).join(',');\n\n const {chromiumBinary} = this.params;\n\n log.debug('Starting Chromium instance...');\n\n if (chromiumBinary) {\n log.debug(`(chromiumBinary: ${chromiumBinary})`);\n }\n\n const chromeFlags = [...DEFAULT_CHROME_FLAGS];\n\n chromeFlags.push(`--load-extension=${extensions}`);\n\n if (this.params.args) {\n chromeFlags.push(...this.params.args);\n }\n\n // eslint-disable-next-line prefer-const\n let {userDataDir, profileDirName} =\n await ChromiumExtensionRunner.getProfilePaths(\n this.params.chromiumProfile);\n\n if (userDataDir && this.params.keepProfileChanges) {\n if (profileDirName\n && !(await ChromiumExtensionRunner.isUserDataDir(userDataDir))) {\n throw new Error('The profile you provided is not in a ' +\n 'user-data-dir. The changes cannot be kept. Please either ' +\n 'remove --keep-profile-changes or use a profile in a ' +\n 'user-data-dir directory');\n }\n } else if (!this.params.keepProfileChanges) {\n // the user provided an existing profile directory but doesn't want\n // the changes to be kept. we copy this directory to a temporary\n // user data dir.\n const tmpDir = new TempDir();\n await tmpDir.create();\n const tmpDirPath = tmpDir.path();\n\n if (userDataDir && profileDirName) {\n // copy profile dir to this temp user data dir.\n await fs.copy(path.join(\n userDataDir,\n profileDirName), path.join(\n tmpDirPath,\n profileDirName),\n );\n } else if (userDataDir) {\n await fs.copy(userDataDir, tmpDirPath);\n }\n userDataDir = tmpDirPath;\n }\n\n if (profileDirName) {\n chromeFlags.push(`--profile-directory=${profileDirName}`);\n }\n\n let startingUrl;\n if (this.params.startUrl) {\n const startingUrls = Array.isArray(this.params.startUrl) ?\n this.params.startUrl : [this.params.startUrl];\n startingUrl = startingUrls.shift();\n chromeFlags.push(...startingUrls);\n }\n\n this.chromiumInstance = await this.chromiumLaunch({\n enableExtensions: true,\n chromePath: chromiumBinary,\n chromeFlags,\n startingUrl,\n userDataDir,\n // Ignore default flags to keep the extension enabled.\n ignoreDefaultFlags: true,\n });\n\n this.chromiumInstance.process.once('close', () => {\n this.chromiumInstance = null;\n\n if (!this.exiting) {\n log.info('Exiting on Chromium instance disconnected.');\n this.exit();\n }\n });\n }\n\n async wssBroadcast(data: Object): Promise<void> {\n return new Promise((resolve) => {\n const clients = this.wss ? new Set(this.wss.clients) : new Set();\n\n function cleanWebExtReloadComplete() {\n const client = this;\n client.removeEventListener('message', webExtReloadComplete);\n client.removeEventListener('close', cleanWebExtReloadComplete);\n clients.delete(client);\n }\n\n const webExtReloadComplete = async (message) => {\n const msg = JSON.parse(message.data);\n\n if (msg.type === 'webExtReloadExtensionComplete') {\n for (const client of clients) {\n cleanWebExtReloadComplete.call(client);\n }\n resolve();\n }\n };\n\n for (const client of clients) {\n if (client.readyState === WebSocket.OPEN) {\n client.addEventListener('message', webExtReloadComplete);\n client.addEventListener('close', cleanWebExtReloadComplete);\n\n client.send(JSON.stringify(data));\n } else {\n clients.delete(client);\n }\n }\n\n if (clients.size === 0) {\n resolve();\n }\n });\n }\n\n async createReloadManagerExtension(): Promise<string> {\n const tmpDir = new TempDir();\n await tmpDir.create();\n this.registerCleanup(() => tmpDir.remove());\n\n const extPath = path.join(\n tmpDir.path(),\n `reload-manager-extension-${Date.now()}`\n );\n\n log.debug(`Creating reload-manager-extension in ${extPath}`);\n\n await asyncMkdirp(extPath);\n\n await fs.writeFile(\n path.join(extPath, 'manifest.json'),\n JSON.stringify({\n manifest_version: 2,\n name: 'web-ext Reload Manager Extension',\n version: '1.0',\n permissions: ['management', 'tabs'],\n background: {\n scripts: ['bg.js'],\n },\n })\n );\n\n // $FlowIgnore: this method is only called right after creating the server and so wss should be defined.\n const wssInfo = this.wss.address();\n\n const bgPage = `(function bgPage() {\n async function getAllDevExtensions() {\n const allExtensions = await new Promise(\n r => chrome.management.getAll(r));\n\n return allExtensions.filter((extension) => {\n return extension.enabled &&\n extension.installType === \"development\" &&\n extension.id !== chrome.runtime.id;\n });\n }\n\n const setEnabled = (extensionId, value) =>\n chrome.runtime.id == extensionId ?\n new Promise.resolve() :\n new Promise(r => chrome.management.setEnabled(extensionId, value, r));\n\n async function reloadExtension(extensionId) {\n await setEnabled(extensionId, false);\n await setEnabled(extensionId, true);\n }\n\n const ws = new window.WebSocket(\n \"ws://${wssInfo.address}:${wssInfo.port}\");\n\n ws.onmessage = async (evt) => {\n const msg = JSON.parse(evt.data);\n if (msg.type === 'webExtReloadAllExtensions') {\n const devExtensions = await getAllDevExtensions();\n await Promise.all(devExtensions.map(ext => reloadExtension(ext.id)));\n ws.send(JSON.stringify({ type: 'webExtReloadExtensionComplete' }));\n }\n };\n })()`;\n\n await fs.writeFile(path.join(extPath, 'bg.js'), bgPage);\n return extPath;\n }\n\n /**\n * Reloads all the extensions, collect any reload error and resolves to\n * an array composed by a single ExtensionRunnerReloadResult object.\n */\n async reloadAllExtensions(): Promise<Array<ExtensionRunnerReloadResult>> {\n const runnerName = this.getName();\n\n await this.wssBroadcast({\n type: 'webExtReloadAllExtensions',\n });\n\n process.stdout.write(\n `\\rLast extension reload: ${(new Date()).toTimeString()}`);\n log.debug('\\n');\n\n return [{runnerName}];\n }\n\n /**\n * Reloads a single extension, collect any reload error and resolves to\n * an array composed by a single ExtensionRunnerReloadResult object.\n */\n async reloadExtensionBySourceDir(\n extensionSourceDir: string // eslint-disable-line no-unused-vars\n ): Promise<Array<ExtensionRunnerReloadResult>> {\n // TODO(rpl): detect the extension ids assigned to the\n // target extensions and map it to the extensions source dir\n // (https://github.com/mozilla/web-ext/issues/1687).\n return this.reloadAllExtensions();\n }\n\n /**\n * Register a callback to be called when the runner has been exited\n * (e.g. the Chromium instance exits or the user has requested web-ext\n * to exit).\n */\n registerCleanup(fn: Function): void {\n this.cleanupCallbacks.add(fn);\n }\n\n /**\n * Exits the runner, by closing the managed Chromium instance.\n */\n async exit(): Promise<void> {\n this.exiting = true;\n\n // Wait for the setup to complete if the extension runner is already\n // being started.\n if (this._promiseSetupDone) {\n // Ignore initialization errors if any.\n await this._promiseSetupDone.catch((err) => {\n log.debug(`ignored setup error on chromium runner shutdown: ${err}`);\n });\n }\n\n if (this.chromiumInstance) {\n await this.chromiumInstance.kill();\n this.chromiumInstance = null;\n }\n\n if (this.wss) {\n // Close all websocket clients, closing the WebSocketServer\n // does not terminate the existing connection and it wouldn't\n // resolve until all of the existing connections are closed.\n for (const wssClient of this.wss?.clients || []) {\n if (wssClient.readyState === WebSocket.OPEN) {\n wssClient.terminate();\n }\n }\n await new Promise((resolve) =>\n this.wss ? this.wss.close(resolve) : resolve());\n this.wss = null;\n }\n\n // Call all the registered cleanup callbacks.\n for (const fn of this.cleanupCallbacks) {\n try {\n fn();\n } catch (error) {\n log.error(error);\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chromium.js","names":["path","fs","asyncMkdirp","Launcher","ChromeLauncher","launch","defaultChromiumLaunch","WebSocket","WebSocketServer","createLogger","TempDir","isDirectory","fileExists","log","import","meta","url","EXCLUDED_CHROME_FLAGS","DEFAULT_CHROME_FLAGS","defaultFlags","filter","flag","includes","ChromiumExtensionRunner","constructor","params","chromiumLaunch","cleanupCallbacks","Set","getName","run","_promiseSetupDone","setupInstance","isUserDataDir","dirPath","localStatePath","join","defaultPath","isProfileDir","securePreferencesPath","getProfilePaths","chromiumProfile","userDataDir","profileDirName","isProfileDirAndNotUserData","dir","base","parse","wss","Promise","resolve","server","port","host","clientTracking","on","socket","err","debug","reloadManagerExtension","createReloadManagerExtension","extensions","concat","map","sourceDir","chromiumBinary","chromeFlags","push","args","keepProfileChanges","Error","tmpDir","create","tmpDirPath","copy","startingUrl","startUrl","startingUrls","Array","isArray","shift","chromiumInstance","enableExtensions","chromePath","ignoreDefaultFlags","process","once","exiting","info","exit","wssBroadcast","data","clients","cleanWebExtReloadComplete","client","removeEventListener","webExtReloadComplete","delete","message","msg","JSON","type","call","readyState","OPEN","addEventListener","send","stringify","size","registerCleanup","remove","extPath","Date","now","writeFile","manifest_version","name","version","permissions","background","scripts","wssInfo","address","bgPage","reloadAllExtensions","runnerName","stdout","write","toTimeString","reloadExtensionBySourceDir","extensionSourceDir","fn","add","catch","kill","wssClient","terminate","close","error"],"sources":["../../src/extension-runners/chromium.js"],"sourcesContent":["/* @flow */\n\n/**\n * This module provide an ExtensionRunner subclass that manage an extension executed\n * in a Chromium-based browser instance.\n */\n\nimport path from 'path';\n\nimport fs from 'fs-extra';\nimport asyncMkdirp from 'mkdirp';\nimport {\n Launcher as ChromeLauncher,\n launch as defaultChromiumLaunch,\n} from 'chrome-launcher';\nimport WebSocket, {WebSocketServer} from 'ws';\n\nimport {createLogger} from '../util/logger.js';\nimport {TempDir} from '../util/temp-dir.js';\nimport type {\n ExtensionRunnerParams,\n ExtensionRunnerReloadResult,\n} from './base';\nimport isDirectory from '../util/is-directory.js';\nimport fileExists from '../util/file-exists.js';\n\ntype ChromiumSpecificRunnerParams = {|\n chromiumBinary?: string,\n chromiumProfile?: string,\n chromiumLaunch?: typeof defaultChromiumLaunch,\n|};\n\nexport type ChromiumExtensionRunnerParams = {|\n ...ExtensionRunnerParams,\n // Chromium desktop CLI params.\n ...ChromiumSpecificRunnerParams,\n|};\n\nconst log = createLogger(import.meta.url);\n\nconst EXCLUDED_CHROME_FLAGS = [\n '--disable-extensions',\n '--mute-audio',\n];\n\nexport const DEFAULT_CHROME_FLAGS: Array<string> = ChromeLauncher.defaultFlags()\n .filter((flag) => !EXCLUDED_CHROME_FLAGS.includes(flag));\n\n/**\n * Implements an IExtensionRunner which manages a Chromium instance.\n */\nexport class ChromiumExtensionRunner {\n cleanupCallbacks: Set<Function>;\n params: ChromiumExtensionRunnerParams;\n chromiumInstance: ?ChromeLauncher;\n chromiumLaunch: typeof defaultChromiumLaunch;\n reloadManagerExtension: string;\n wss: ?WebSocketServer;\n exiting: boolean;\n _promiseSetupDone: ?Promise<void>;\n\n constructor(params: ChromiumExtensionRunnerParams) {\n const {\n chromiumLaunch = defaultChromiumLaunch,\n } = params;\n this.params = params;\n this.chromiumLaunch = chromiumLaunch;\n this.cleanupCallbacks = new Set();\n }\n\n // Method exported from the IExtensionRunner interface.\n\n /**\n * Returns the runner name.\n */\n getName(): string {\n return 'Chromium';\n }\n\n async run(): Promise<void> {\n // Run should never be called more than once.\n this._promiseSetupDone = this.setupInstance();\n await this._promiseSetupDone;\n }\n\n static async isUserDataDir(dirPath: string): Promise<boolean> {\n const localStatePath = path.join(dirPath, 'Local State');\n const defaultPath = path.join(dirPath, 'Default');\n // Local State and Default are typical for the user-data-dir\n return (await fileExists(localStatePath))\n && (await isDirectory(defaultPath));\n }\n\n static async isProfileDir(dirPath: string): Promise<boolean> {\n const securePreferencesPath = path.join(\n dirPath, 'Secure Preferences');\n //Secure Preferences is typical for a profile dir inside a user data dir\n return await fileExists(securePreferencesPath);\n }\n\n static async getProfilePaths(chromiumProfile: ?string): Promise<{\n userDataDir: ?string,\n profileDirName: ?string\n }> {\n if (!chromiumProfile) {\n return {\n userDataDir: null,\n profileDirName: null,\n };\n }\n\n const isProfileDirAndNotUserData =\n (await ChromiumExtensionRunner.isProfileDir(chromiumProfile))\n && !(await ChromiumExtensionRunner.isUserDataDir(chromiumProfile));\n\n if (isProfileDirAndNotUserData) {\n const {dir: userDataDir, base: profileDirName} =\n path.parse(chromiumProfile);\n return {\n userDataDir,\n profileDirName,\n };\n }\n\n return {\n userDataDir: chromiumProfile,\n profileDirName: null,\n };\n\n }\n\n /**\n * Setup the Chromium Profile and run a Chromium instance.\n */\n async setupInstance(): Promise<void> {\n // Start a websocket server on a free localhost TCP port.\n this.wss = await new Promise((resolve) => {\n const server = new WebSocketServer(\n // Use a ipv4 host so we don't need to escape ipv6 address\n // https://github.com/mozilla/web-ext/issues/2331\n {port: 0, host: '127.0.0.1', clientTracking: true},\n // Wait the server to be listening (so that the extension\n // runner can successfully retrieve server address and port).\n () => resolve(server));\n });\n\n // Prevent unhandled socket error (e.g. when chrome\n // is exiting, See https://github.com/websockets/ws/issues/1256).\n this.wss.on('connection', function(socket) {\n socket.on('error', (err) => {\n log.debug(`websocket connection error: ${err}`);\n });\n });\n\n // Create the extension that will manage the addon reloads\n this.reloadManagerExtension = await this.createReloadManagerExtension();\n\n // Start chrome pointing it to a given profile dir\n const extensions = [this.reloadManagerExtension].concat(\n this.params.extensions.map(({sourceDir}) => sourceDir)\n ).join(',');\n\n const {chromiumBinary} = this.params;\n\n log.debug('Starting Chromium instance...');\n\n if (chromiumBinary) {\n log.debug(`(chromiumBinary: ${chromiumBinary})`);\n }\n\n const chromeFlags = [...DEFAULT_CHROME_FLAGS];\n\n chromeFlags.push(`--load-extension=${extensions}`);\n\n if (this.params.args) {\n chromeFlags.push(...this.params.args);\n }\n\n // eslint-disable-next-line prefer-const\n let {userDataDir, profileDirName} =\n await ChromiumExtensionRunner.getProfilePaths(\n this.params.chromiumProfile);\n\n if (userDataDir && this.params.keepProfileChanges) {\n if (profileDirName\n && !(await ChromiumExtensionRunner.isUserDataDir(userDataDir))) {\n throw new Error('The profile you provided is not in a ' +\n 'user-data-dir. The changes cannot be kept. Please either ' +\n 'remove --keep-profile-changes or use a profile in a ' +\n 'user-data-dir directory');\n }\n } else if (!this.params.keepProfileChanges) {\n // the user provided an existing profile directory but doesn't want\n // the changes to be kept. we copy this directory to a temporary\n // user data dir.\n const tmpDir = new TempDir();\n await tmpDir.create();\n const tmpDirPath = tmpDir.path();\n\n if (userDataDir && profileDirName) {\n // copy profile dir to this temp user data dir.\n await fs.copy(path.join(\n userDataDir,\n profileDirName), path.join(\n tmpDirPath,\n profileDirName),\n );\n } else if (userDataDir) {\n await fs.copy(userDataDir, tmpDirPath);\n }\n userDataDir = tmpDirPath;\n }\n\n if (profileDirName) {\n chromeFlags.push(`--profile-directory=${profileDirName}`);\n }\n\n let startingUrl;\n if (this.params.startUrl) {\n const startingUrls = Array.isArray(this.params.startUrl) ?\n this.params.startUrl : [this.params.startUrl];\n startingUrl = startingUrls.shift();\n chromeFlags.push(...startingUrls);\n }\n\n this.chromiumInstance = await this.chromiumLaunch({\n enableExtensions: true,\n chromePath: chromiumBinary,\n chromeFlags,\n startingUrl,\n userDataDir,\n // Ignore default flags to keep the extension enabled.\n ignoreDefaultFlags: true,\n });\n\n this.chromiumInstance.process.once('close', () => {\n this.chromiumInstance = null;\n\n if (!this.exiting) {\n log.info('Exiting on Chromium instance disconnected.');\n this.exit();\n }\n });\n }\n\n async wssBroadcast(data: Object): Promise<void> {\n return new Promise((resolve) => {\n const clients = this.wss ? new Set(this.wss.clients) : new Set();\n\n function cleanWebExtReloadComplete() {\n const client = this;\n client.removeEventListener('message', webExtReloadComplete);\n client.removeEventListener('close', cleanWebExtReloadComplete);\n clients.delete(client);\n }\n\n const webExtReloadComplete = async (message) => {\n const msg = JSON.parse(message.data);\n\n if (msg.type === 'webExtReloadExtensionComplete') {\n for (const client of clients) {\n cleanWebExtReloadComplete.call(client);\n }\n resolve();\n }\n };\n\n for (const client of clients) {\n if (client.readyState === WebSocket.OPEN) {\n client.addEventListener('message', webExtReloadComplete);\n client.addEventListener('close', cleanWebExtReloadComplete);\n\n client.send(JSON.stringify(data));\n } else {\n clients.delete(client);\n }\n }\n\n if (clients.size === 0) {\n resolve();\n }\n });\n }\n\n async createReloadManagerExtension(): Promise<string> {\n const tmpDir = new TempDir();\n await tmpDir.create();\n this.registerCleanup(() => tmpDir.remove());\n\n const extPath = path.join(\n tmpDir.path(),\n `reload-manager-extension-${Date.now()}`\n );\n\n log.debug(`Creating reload-manager-extension in ${extPath}`);\n\n await asyncMkdirp(extPath);\n\n await fs.writeFile(\n path.join(extPath, 'manifest.json'),\n JSON.stringify({\n manifest_version: 2,\n name: 'web-ext Reload Manager Extension',\n version: '1.0',\n permissions: ['management', 'tabs'],\n background: {\n scripts: ['bg.js'],\n },\n })\n );\n\n // $FlowIgnore: this method is only called right after creating the server and so wss should be defined.\n const wssInfo = this.wss.address();\n\n const bgPage = `(function bgPage() {\n async function getAllDevExtensions() {\n const allExtensions = await new Promise(\n r => chrome.management.getAll(r));\n\n return allExtensions.filter((extension) => {\n return extension.enabled &&\n extension.installType === \"development\" &&\n extension.id !== chrome.runtime.id;\n });\n }\n\n const setEnabled = (extensionId, value) =>\n chrome.runtime.id == extensionId ?\n new Promise.resolve() :\n new Promise(r => chrome.management.setEnabled(extensionId, value, r));\n\n async function reloadExtension(extensionId) {\n await setEnabled(extensionId, false);\n await setEnabled(extensionId, true);\n }\n\n const ws = new window.WebSocket(\n \"ws://${wssInfo.address}:${wssInfo.port}\");\n\n ws.onmessage = async (evt) => {\n const msg = JSON.parse(evt.data);\n if (msg.type === 'webExtReloadAllExtensions') {\n const devExtensions = await getAllDevExtensions();\n await Promise.all(devExtensions.map(ext => reloadExtension(ext.id)));\n ws.send(JSON.stringify({ type: 'webExtReloadExtensionComplete' }));\n }\n };\n })()`;\n\n await fs.writeFile(path.join(extPath, 'bg.js'), bgPage);\n return extPath;\n }\n\n /**\n * Reloads all the extensions, collect any reload error and resolves to\n * an array composed by a single ExtensionRunnerReloadResult object.\n */\n async reloadAllExtensions(): Promise<Array<ExtensionRunnerReloadResult>> {\n const runnerName = this.getName();\n\n await this.wssBroadcast({\n type: 'webExtReloadAllExtensions',\n });\n\n process.stdout.write(\n `\\rLast extension reload: ${(new Date()).toTimeString()}`);\n log.debug('\\n');\n\n return [{runnerName}];\n }\n\n /**\n * Reloads a single extension, collect any reload error and resolves to\n * an array composed by a single ExtensionRunnerReloadResult object.\n */\n async reloadExtensionBySourceDir(\n extensionSourceDir: string // eslint-disable-line no-unused-vars\n ): Promise<Array<ExtensionRunnerReloadResult>> {\n // TODO(rpl): detect the extension ids assigned to the\n // target extensions and map it to the extensions source dir\n // (https://github.com/mozilla/web-ext/issues/1687).\n return this.reloadAllExtensions();\n }\n\n /**\n * Register a callback to be called when the runner has been exited\n * (e.g. the Chromium instance exits or the user has requested web-ext\n * to exit).\n */\n registerCleanup(fn: Function): void {\n this.cleanupCallbacks.add(fn);\n }\n\n /**\n * Exits the runner, by closing the managed Chromium instance.\n */\n async exit(): Promise<void> {\n this.exiting = true;\n\n // Wait for the setup to complete if the extension runner is already\n // being started.\n if (this._promiseSetupDone) {\n // Ignore initialization errors if any.\n await this._promiseSetupDone.catch((err) => {\n log.debug(`ignored setup error on chromium runner shutdown: ${err}`);\n });\n }\n\n if (this.chromiumInstance) {\n await this.chromiumInstance.kill();\n this.chromiumInstance = null;\n }\n\n if (this.wss) {\n // Close all websocket clients, closing the WebSocketServer\n // does not terminate the existing connection and it wouldn't\n // resolve until all of the existing connections are closed.\n for (const wssClient of this.wss?.clients || []) {\n if (wssClient.readyState === WebSocket.OPEN) {\n wssClient.terminate();\n }\n }\n await new Promise((resolve) =>\n this.wss ? this.wss.close(resolve) : resolve());\n this.wss = null;\n }\n\n // Call all the registered cleanup callbacks.\n for (const fn of this.cleanupCallbacks) {\n try {\n fn();\n } catch (error) {\n log.error(error);\n }\n }\n }\n}\n"],"mappings":";;AAEA;AACA;AACA;AACA;;AAEA,OAAOA,IAAI,MAAM,MAAM;AAEvB,OAAOC,EAAE,MAAM,UAAU;AACzB,OAAOC,WAAW,MAAM,QAAQ;AAChC,SACEC,QAAQ,IAAIC,cAAc,EAC1BC,MAAM,IAAIC,qBAAqB,QAC1B,iBAAiB;AACxB,OAAOC,SAAS,IAAGC,eAAe,QAAO,IAAI;AAE7C,SAAQC,YAAY,QAAO,mBAAmB;AAC9C,SAAQC,OAAO,QAAO,qBAAqB;AAK3C,OAAOC,WAAW,MAAM,yBAAyB;AACjD,OAAOC,UAAU,MAAM,wBAAwB;AAc/C,MAAMC,GAAG,GAAGJ,YAAY,CAACK,MAAM,CAACC,IAAI,CAACC,GAAG,CAAC;AAEzC,MAAMC,qBAAqB,GAAG,CAC5B,sBAAsB,EACtB,cAAc,CACf;AAED,OAAO,MAAMC,oBAAmC,GAAGd,cAAc,CAACe,YAAY,EAAE,CAC7EC,MAAM,CAAEC,IAAI,IAAK,CAACJ,qBAAqB,CAACK,QAAQ,CAACD,IAAI,CAAC,CAAC;;AAE1D;AACA;AACA;AACA,OAAO,MAAME,uBAAuB,CAAC;EAUnCC,WAAW,CAACC,MAAqC,EAAE;IACjD,MAAM;MACJC,cAAc,GAAGpB;IACnB,CAAC,GAAGmB,MAAM;IACV,IAAI,CAACA,MAAM,GAAGA,MAAM;IACpB,IAAI,CAACC,cAAc,GAAGA,cAAc;IACpC,IAAI,CAACC,gBAAgB,GAAG,IAAIC,GAAG,EAAE;EACnC;;EAEA;;EAEA;AACF;AACA;EACEC,OAAO,GAAW;IAChB,OAAO,UAAU;EACnB;EAEA,MAAMC,GAAG,GAAkB;IACzB;IACA,IAAI,CAACC,iBAAiB,GAAG,IAAI,CAACC,aAAa,EAAE;IAC7C,MAAM,IAAI,CAACD,iBAAiB;EAC9B;EAEA,aAAaE,aAAa,CAACC,OAAe,EAAoB;IAC5D,MAAMC,cAAc,GAAGnC,IAAI,CAACoC,IAAI,CAACF,OAAO,EAAE,aAAa,CAAC;IACxD,MAAMG,WAAW,GAAGrC,IAAI,CAACoC,IAAI,CAACF,OAAO,EAAE,SAAS,CAAC;IACjD;IACA,OAAO,CAAC,MAAMtB,UAAU,CAACuB,cAAc,CAAC,MAClC,MAAMxB,WAAW,CAAC0B,WAAW,CAAC,CAAC;EACvC;EAEA,aAAaC,YAAY,CAACJ,OAAe,EAAoB;IAC3D,MAAMK,qBAAqB,GAAGvC,IAAI,CAACoC,IAAI,CACrCF,OAAO,EAAE,oBAAoB,CAAC;IAChC;IACA,OAAO,MAAMtB,UAAU,CAAC2B,qBAAqB,CAAC;EAChD;EAEA,aAAaC,eAAe,CAACC,eAAwB,EAGlD;IACD,IAAI,CAACA,eAAe,EAAE;MACpB,OAAO;QACLC,WAAW,EAAE,IAAI;QACjBC,cAAc,EAAE;MAClB,CAAC;IACH;IAEA,MAAMC,0BAA0B,GAC9B,CAAC,MAAMrB,uBAAuB,CAACe,YAAY,CAACG,eAAe,CAAC,KACzD,EAAE,MAAMlB,uBAAuB,CAACU,aAAa,CAACQ,eAAe,CAAC,CAAC;IAEpE,IAAIG,0BAA0B,EAAE;MAC9B,MAAM;QAACC,GAAG,EAAEH,WAAW;QAAEI,IAAI,EAAEH;MAAc,CAAC,GAC5C3C,IAAI,CAAC+C,KAAK,CAACN,eAAe,CAAC;MAC7B,OAAO;QACLC,WAAW;QACXC;MACF,CAAC;IACH;IAEA,OAAO;MACLD,WAAW,EAAED,eAAe;MAC5BE,cAAc,EAAE;IAClB,CAAC;EAEH;;EAEA;AACF;AACA;EACE,MAAMX,aAAa,GAAkB;IACnC;IACA,IAAI,CAACgB,GAAG,GAAG,MAAM,IAAIC,OAAO,CAAEC,OAAO,IAAK;MACxC,MAAMC,MAAM,GAAG,IAAI3C,eAAe;MAChC;MACA;MACA;QAAC4C,IAAI,EAAE,CAAC;QAAEC,IAAI,EAAE,WAAW;QAAEC,cAAc,EAAE;MAAI,CAAC;MAClD;MACA;MACA,MAAMJ,OAAO,CAACC,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC;;IAEF;IACA;IACA,IAAI,CAACH,GAAG,CAACO,EAAE,CAAC,YAAY,EAAE,UAASC,MAAM,EAAE;MACzCA,MAAM,CAACD,EAAE,CAAC,OAAO,EAAGE,GAAG,IAAK;QAC1B5C,GAAG,CAAC6C,KAAK,CAAE,+BAA8BD,GAAI,EAAC,CAAC;MACjD,CAAC,CAAC;IACJ,CAAC,CAAC;;IAEF;IACA,IAAI,CAACE,sBAAsB,GAAG,MAAM,IAAI,CAACC,4BAA4B,EAAE;;IAEvE;IACA,MAAMC,UAAU,GAAG,CAAC,IAAI,CAACF,sBAAsB,CAAC,CAACG,MAAM,CACrD,IAAI,CAACrC,MAAM,CAACoC,UAAU,CAACE,GAAG,CAAC,CAAC;MAACC;IAAS,CAAC,KAAKA,SAAS,CAAC,CACvD,CAAC5B,IAAI,CAAC,GAAG,CAAC;IAEX,MAAM;MAAC6B;IAAc,CAAC,GAAG,IAAI,CAACxC,MAAM;IAEpCZ,GAAG,CAAC6C,KAAK,CAAC,+BAA+B,CAAC;IAE1C,IAAIO,cAAc,EAAE;MAClBpD,GAAG,CAAC6C,KAAK,CAAE,oBAAmBO,cAAe,GAAE,CAAC;IAClD;IAEA,MAAMC,WAAW,GAAG,CAAC,GAAGhD,oBAAoB,CAAC;IAE7CgD,WAAW,CAACC,IAAI,CAAE,oBAAmBN,UAAW,EAAC,CAAC;IAElD,IAAI,IAAI,CAACpC,MAAM,CAAC2C,IAAI,EAAE;MACpBF,WAAW,CAACC,IAAI,CAAC,GAAG,IAAI,CAAC1C,MAAM,CAAC2C,IAAI,CAAC;IACvC;;IAEA;IACA,IAAI;MAAC1B,WAAW;MAAEC;IAAc,CAAC,GAC/B,MAAMpB,uBAAuB,CAACiB,eAAe,CAC3C,IAAI,CAACf,MAAM,CAACgB,eAAe,CAAC;IAEhC,IAAIC,WAAW,IAAI,IAAI,CAACjB,MAAM,CAAC4C,kBAAkB,EAAE;MACjD,IAAI1B,cAAc,IACb,EAAE,MAAMpB,uBAAuB,CAACU,aAAa,CAACS,WAAW,CAAC,CAAC,EAAE;QAChE,MAAM,IAAI4B,KAAK,CAAC,uCAAuC,GACrD,2DAA2D,GAC3D,sDAAsD,GACtD,yBAAyB,CAAC;MAC9B;IACF,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC7C,MAAM,CAAC4C,kBAAkB,EAAE;MAC1C;MACA;MACA;MACA,MAAME,MAAM,GAAG,IAAI7D,OAAO,EAAE;MAC5B,MAAM6D,MAAM,CAACC,MAAM,EAAE;MACrB,MAAMC,UAAU,GAAGF,MAAM,CAACvE,IAAI,EAAE;MAEhC,IAAI0C,WAAW,IAAIC,cAAc,EAAE;QACjC;QACA,MAAM1C,EAAE,CAACyE,IAAI,CAAC1E,IAAI,CAACoC,IAAI,CACrBM,WAAW,EACXC,cAAc,CAAC,EAAE3C,IAAI,CAACoC,IAAI,CAC1BqC,UAAU,EACV9B,cAAc,CAAC,CAChB;MACH,CAAC,MAAM,IAAID,WAAW,EAAE;QACtB,MAAMzC,EAAE,CAACyE,IAAI,CAAChC,WAAW,EAAE+B,UAAU,CAAC;MACxC;MACA/B,WAAW,GAAG+B,UAAU;IAC1B;IAEA,IAAI9B,cAAc,EAAE;MAClBuB,WAAW,CAACC,IAAI,CAAE,uBAAsBxB,cAAe,EAAC,CAAC;IAC3D;IAEA,IAAIgC,WAAW;IACf,IAAI,IAAI,CAAClD,MAAM,CAACmD,QAAQ,EAAE;MACxB,MAAMC,YAAY,GAAGC,KAAK,CAACC,OAAO,CAAC,IAAI,CAACtD,MAAM,CAACmD,QAAQ,CAAC,GACtD,IAAI,CAACnD,MAAM,CAACmD,QAAQ,GAAG,CAAC,IAAI,CAACnD,MAAM,CAACmD,QAAQ,CAAC;MAC/CD,WAAW,GAAGE,YAAY,CAACG,KAAK,EAAE;MAClCd,WAAW,CAACC,IAAI,CAAC,GAAGU,YAAY,CAAC;IACnC;IAEA,IAAI,CAACI,gBAAgB,GAAG,MAAM,IAAI,CAACvD,cAAc,CAAC;MAChDwD,gBAAgB,EAAE,IAAI;MACtBC,UAAU,EAAElB,cAAc;MAC1BC,WAAW;MACXS,WAAW;MACXjC,WAAW;MACX;MACA0C,kBAAkB,EAAE;IACtB,CAAC,CAAC;IAEF,IAAI,CAACH,gBAAgB,CAACI,OAAO,CAACC,IAAI,CAAC,OAAO,EAAE,MAAM;MAChD,IAAI,CAACL,gBAAgB,GAAG,IAAI;MAE5B,IAAI,CAAC,IAAI,CAACM,OAAO,EAAE;QACjB1E,GAAG,CAAC2E,IAAI,CAAC,4CAA4C,CAAC;QACtD,IAAI,CAACC,IAAI,EAAE;MACb;IACF,CAAC,CAAC;EACJ;EAEA,MAAMC,YAAY,CAACC,IAAY,EAAiB;IAC9C,OAAO,IAAI1C,OAAO,CAAEC,OAAO,IAAK;MAC9B,MAAM0C,OAAO,GAAG,IAAI,CAAC5C,GAAG,GAAG,IAAIpB,GAAG,CAAC,IAAI,CAACoB,GAAG,CAAC4C,OAAO,CAAC,GAAG,IAAIhE,GAAG,EAAE;MAEhE,SAASiE,yBAAyB,GAAG;QACnC,MAAMC,MAAM,GAAG,IAAI;QACnBA,MAAM,CAACC,mBAAmB,CAAC,SAAS,EAAEC,oBAAoB,CAAC;QAC3DF,MAAM,CAACC,mBAAmB,CAAC,OAAO,EAAEF,yBAAyB,CAAC;QAC9DD,OAAO,CAACK,MAAM,CAACH,MAAM,CAAC;MACxB;MAEA,MAAME,oBAAoB,GAAG,MAAOE,OAAO,IAAK;QAC9C,MAAMC,GAAG,GAAGC,IAAI,CAACrD,KAAK,CAACmD,OAAO,CAACP,IAAI,CAAC;QAEpC,IAAIQ,GAAG,CAACE,IAAI,KAAK,+BAA+B,EAAE;UAChD,KAAK,MAAMP,MAAM,IAAIF,OAAO,EAAE;YAC5BC,yBAAyB,CAACS,IAAI,CAACR,MAAM,CAAC;UACxC;UACA5C,OAAO,EAAE;QACX;MACF,CAAC;MAED,KAAK,MAAM4C,MAAM,IAAIF,OAAO,EAAE;QAC5B,IAAIE,MAAM,CAACS,UAAU,KAAKhG,SAAS,CAACiG,IAAI,EAAE;UACxCV,MAAM,CAACW,gBAAgB,CAAC,SAAS,EAAET,oBAAoB,CAAC;UACxDF,MAAM,CAACW,gBAAgB,CAAC,OAAO,EAAEZ,yBAAyB,CAAC;UAE3DC,MAAM,CAACY,IAAI,CAACN,IAAI,CAACO,SAAS,CAAChB,IAAI,CAAC,CAAC;QACnC,CAAC,MAAM;UACLC,OAAO,CAACK,MAAM,CAACH,MAAM,CAAC;QACxB;MACF;MAEA,IAAIF,OAAO,CAACgB,IAAI,KAAK,CAAC,EAAE;QACtB1D,OAAO,EAAE;MACX;IACF,CAAC,CAAC;EACJ;EAEA,MAAMU,4BAA4B,GAAoB;IACpD,MAAMW,MAAM,GAAG,IAAI7D,OAAO,EAAE;IAC5B,MAAM6D,MAAM,CAACC,MAAM,EAAE;IACrB,IAAI,CAACqC,eAAe,CAAC,MAAMtC,MAAM,CAACuC,MAAM,EAAE,CAAC;IAE3C,MAAMC,OAAO,GAAG/G,IAAI,CAACoC,IAAI,CACvBmC,MAAM,CAACvE,IAAI,EAAE,EACZ,4BAA2BgH,IAAI,CAACC,GAAG,EAAG,EAAC,CACzC;IAEDpG,GAAG,CAAC6C,KAAK,CAAE,wCAAuCqD,OAAQ,EAAC,CAAC;IAE5D,MAAM7G,WAAW,CAAC6G,OAAO,CAAC;IAE1B,MAAM9G,EAAE,CAACiH,SAAS,CAChBlH,IAAI,CAACoC,IAAI,CAAC2E,OAAO,EAAE,eAAe,CAAC,EACnCX,IAAI,CAACO,SAAS,CAAC;MACbQ,gBAAgB,EAAE,CAAC;MACnBC,IAAI,EAAE,kCAAkC;MACxCC,OAAO,EAAE,KAAK;MACdC,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;MACnCC,UAAU,EAAE;QACVC,OAAO,EAAE,CAAC,OAAO;MACnB;IACF,CAAC,CAAC,CACH;;IAED;IACA,MAAMC,OAAO,GAAG,IAAI,CAACzE,GAAG,CAAC0E,OAAO,EAAE;IAElC,MAAMC,MAAM,GAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAgBF,OAAO,CAACC,OAAQ,IAAGD,OAAO,CAACrE,IAAK;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;IAEL,MAAMnD,EAAE,CAACiH,SAAS,CAAClH,IAAI,CAACoC,IAAI,CAAC2E,OAAO,EAAE,OAAO,CAAC,EAAEY,MAAM,CAAC;IACvD,OAAOZ,OAAO;EAChB;;EAEA;AACF;AACA;AACA;EACE,MAAMa,mBAAmB,GAAgD;IACvE,MAAMC,UAAU,GAAG,IAAI,CAAChG,OAAO,EAAE;IAEjC,MAAM,IAAI,CAAC6D,YAAY,CAAC;MACtBW,IAAI,EAAE;IACR,CAAC,CAAC;IAEFhB,OAAO,CAACyC,MAAM,CAACC,KAAK,CACjB,4BAA4B,IAAIf,IAAI,EAAE,CAAEgB,YAAY,EAAG,EAAC,CAAC;IAC5DnH,GAAG,CAAC6C,KAAK,CAAC,IAAI,CAAC;IAEf,OAAO,CAAC;MAACmE;IAAU,CAAC,CAAC;EACvB;;EAEA;AACF;AACA;AACA;EACE,MAAMI,0BAA0B,CAC9BC,kBAA0B,EACmB;IAC7C;IACA;IACA;IACA,OAAO,IAAI,CAACN,mBAAmB,EAAE;EACnC;;EAEA;AACF;AACA;AACA;AACA;EACEf,eAAe,CAACsB,EAAY,EAAQ;IAClC,IAAI,CAACxG,gBAAgB,CAACyG,GAAG,CAACD,EAAE,CAAC;EAC/B;;EAEA;AACF;AACA;EACE,MAAM1C,IAAI,GAAkB;IAC1B,IAAI,CAACF,OAAO,GAAG,IAAI;;IAEnB;IACA;IACA,IAAI,IAAI,CAACxD,iBAAiB,EAAE;MAC1B;MACA,MAAM,IAAI,CAACA,iBAAiB,CAACsG,KAAK,CAAE5E,GAAG,IAAK;QAC1C5C,GAAG,CAAC6C,KAAK,CAAE,oDAAmDD,GAAI,EAAC,CAAC;MACtE,CAAC,CAAC;IACJ;IAEA,IAAI,IAAI,CAACwB,gBAAgB,EAAE;MACzB,MAAM,IAAI,CAACA,gBAAgB,CAACqD,IAAI,EAAE;MAClC,IAAI,CAACrD,gBAAgB,GAAG,IAAI;IAC9B;IAEA,IAAI,IAAI,CAACjC,GAAG,EAAE;MACZ;MACA;MACA;MACA,KAAK,MAAMuF,SAAS,IAAI,kBAAI,CAACvF,GAAG,8CAAR,UAAU4C,OAAO,KAAI,EAAE,EAAE;QAAA;QAC/C,IAAI2C,SAAS,CAAChC,UAAU,KAAKhG,SAAS,CAACiG,IAAI,EAAE;UAC3C+B,SAAS,CAACC,SAAS,EAAE;QACvB;MACF;MACA,MAAM,IAAIvF,OAAO,CAAEC,OAAO,IACxB,IAAI,CAACF,GAAG,GAAG,IAAI,CAACA,GAAG,CAACyF,KAAK,CAACvF,OAAO,CAAC,GAAGA,OAAO,EAAE,CAAC;MACjD,IAAI,CAACF,GAAG,GAAG,IAAI;IACjB;;IAEA;IACA,KAAK,MAAMmF,EAAE,IAAI,IAAI,CAACxG,gBAAgB,EAAE;MACtC,IAAI;QACFwG,EAAE,EAAE;MACN,CAAC,CAAC,OAAOO,KAAK,EAAE;QACd7H,GAAG,CAAC6H,KAAK,CAACA,KAAK,CAAC;MAClB;IACF;EACF;AACF"}
|