extension 4.0.15 → 4.0.16
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 +16 -6
- package/dist/49.cjs +142 -12
- package/dist/browsers.cjs +190 -23
- package/dist/cli.cjs +190 -23
- package/dist/extension/browsers/browsers-lib/banner.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/messages.d.ts +2 -0
- package/dist/extension/browsers/browsers-lib/ready-stamp.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/shared-utils.d.ts +1 -0
- package/dist/extension/browsers/browsers-types.d.ts +6 -0
- package/dist/extension/browsers/index.d.ts +6 -1
- package/dist/extension/browsers/run-chromium/cdp/cdp-extension-controller/ensure.d.ts +11 -0
- package/dist/extension/browsers/run-chromium/cdp/cdp-extension-controller/index.d.ts +5 -0
- package/dist/extension/browsers/run-chromium/chromium-launch/index.d.ts +6 -1
- package/dist/extension/browsers/run-chromium/chromium-types.d.ts +2 -0
- package/dist/extension/browsers/run-firefox/firefox-launch/index.d.ts +2 -0
- package/dist/extension/browsers/run-firefox/firefox-types.d.ts +4 -1
- package/dist/extension/browsers/run-firefox/rdp/rdp-extension-controller/index.d.ts +1 -0
- package/dist/extension/browsers/run-firefox/rdp/remote-firefox/addons-install.d.ts +7 -0
- package/dist/extension/browsers/run-firefox/rdp/remote-firefox/index.d.ts +2 -0
- package/package.json +7 -7
- package/dist/extension/browsers/browsers-lib/html-merge.d.ts +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -85,6 +85,14 @@ var __webpack_modules__ = {
|
|
|
85
85
|
if (fromBrowserSpecificSettings) return fromBrowserSpecificSettings;
|
|
86
86
|
return toNormalizedId(manifest?.applications?.gecko?.id);
|
|
87
87
|
}
|
|
88
|
+
function expectedChromiumExtensionId(outPath) {
|
|
89
|
+
try {
|
|
90
|
+
const manifest = JSON.parse(node_fs__rspack_import_1.readFileSync(node_path__rspack_import_2.join(outPath, 'manifest.json'), 'utf-8'));
|
|
91
|
+
const fromKey = deriveChromiumExtensionIdFromManifest(manifest);
|
|
92
|
+
if (fromKey) return fromKey;
|
|
93
|
+
} catch {}
|
|
94
|
+
return deriveChromiumExtensionIdFromPath(outPath);
|
|
95
|
+
}
|
|
88
96
|
function resolveExtensionId(args) {
|
|
89
97
|
const fromInfo = toNormalizedId(args.info?.extensionId);
|
|
90
98
|
if (fromInfo) return fromInfo;
|
|
@@ -210,7 +218,8 @@ var __webpack_modules__ = {
|
|
|
210
218
|
}
|
|
211
219
|
__webpack_require__.d(__webpack_exports__, {
|
|
212
220
|
MK: ()=>printProdBannerOnce,
|
|
213
|
-
ai: ()=>printDevBannerOnce
|
|
221
|
+
ai: ()=>printDevBannerOnce,
|
|
222
|
+
vy: ()=>expectedChromiumExtensionId
|
|
214
223
|
});
|
|
215
224
|
},
|
|
216
225
|
"./browsers/browsers-lib/browser-family.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
@@ -476,6 +485,12 @@ var __webpack_modules__ = {
|
|
|
476
485
|
function chromiumManifestLoadBlockers(extensionPath, blockers) {
|
|
477
486
|
return `${getLoggingPrefix('warn')} ${pintor__rspack_import_8_default().brightYellow('This manifest declares shapes Chrome refuses, the whole extension will not load.')}\n${pintor__rspack_import_8_default().gray('PATH')} ${pintor__rspack_import_8_default().underline(extensionPath)}\n` + blockers.map((blocker)=>`${pintor__rspack_import_8_default().gray('REASON')} ${pintor__rspack_import_8_default().red(blocker)}\n`).join('') + "Chrome rejects the extension at load, so no service worker or content script ever runs. Fix these in the source manifest.";
|
|
478
487
|
}
|
|
488
|
+
function chromiumExtensionLoadRefused(extensionPath, reason) {
|
|
489
|
+
return `${getLoggingPrefix('error')} ${pintor__rspack_import_8_default().red('The browser refused to load this extension, so it is NOT running.')}\n${pintor__rspack_import_8_default().gray('PATH')} ${pintor__rspack_import_8_default().underline(extensionPath)}\n` + (reason ? `${pintor__rspack_import_8_default().gray('REASON')} ${pintor__rspack_import_8_default().red(reason)}\n` : '') + "No service worker, content script, or page from this extension will run, and no Extension ID was assigned. Fix the reason above and save. If the browser does not pick it up, restart the dev session.";
|
|
490
|
+
}
|
|
491
|
+
function geckoAddonLoadRefused(addonPath, reason) {
|
|
492
|
+
return `${getLoggingPrefix('error')} ${pintor__rspack_import_8_default().red('The browser refused to load this add-on, so it is NOT running.')}\n${pintor__rspack_import_8_default().gray('PATH')} ${pintor__rspack_import_8_default().underline(addonPath)}\n` + (reason ? `${pintor__rspack_import_8_default().gray('REASON')} ${pintor__rspack_import_8_default().red(reason)}\n` : '') + "No background script, content script, or page from this add-on will run, and no Extension ID was assigned. Fix the reason above and save. If the browser does not pick it up, restart the dev session.";
|
|
493
|
+
}
|
|
479
494
|
function devChannelSnapshotInUse(binaryPath) {
|
|
480
495
|
return `${getLoggingPrefix('warn')} ${pintor__rspack_import_8_default().brightYellow('Running a Chromium tip-of-tree snapshot (dev channel, not a stable release).')}\n${pintor__rspack_import_8_default().gray('PATH')} ${pintor__rspack_import_8_default().underline(binaryPath)}\nBehavior may differ from stable Chrome. Install a stable browser or remove the snapshot to stop using it.`;
|
|
481
496
|
}
|
|
@@ -835,6 +850,7 @@ var __webpack_modules__ = {
|
|
|
835
850
|
Cs: ()=>safariToolchainMissing,
|
|
836
851
|
Dh: ()=>invalidGeckoBinaryPath,
|
|
837
852
|
E8: ()=>chromiumDryRunFlags,
|
|
853
|
+
EE: ()=>geckoAddonLoadRefused,
|
|
838
854
|
EI: ()=>firefoxRdpClientConnected,
|
|
839
855
|
F1: ()=>cdpClientBrowserConnectionEstablished,
|
|
840
856
|
F4: ()=>enhancedProcessManagementUncaughtException,
|
|
@@ -852,6 +868,7 @@ var __webpack_modules__ = {
|
|
|
852
868
|
L: ()=>chromiumManifestLoadBlockers,
|
|
853
869
|
LD: ()=>firefoxDryRunBinary,
|
|
854
870
|
M3: ()=>cdpClientConnected,
|
|
871
|
+
M6: ()=>chromiumExtensionLoadRefused,
|
|
855
872
|
MG: ()=>prettyPuppeteerInstallGuidance,
|
|
856
873
|
MQ: ()=>firefoxFailedToStart,
|
|
857
874
|
MS: ()=>errorConnectingToBrowser,
|
|
@@ -1224,6 +1241,21 @@ var __webpack_modules__ = {
|
|
|
1224
1241
|
node_fs__rspack_import_0.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
|
|
1225
1242
|
} catch {}
|
|
1226
1243
|
}
|
|
1244
|
+
function stampReadyExtensionLoadRefused(extensionOutputPath, reason) {
|
|
1245
|
+
try {
|
|
1246
|
+
if (!extensionOutputPath) return;
|
|
1247
|
+
const readyPath = readyPathFor(extensionOutputPath);
|
|
1248
|
+
if (!node_fs__rspack_import_0.existsSync(readyPath)) return;
|
|
1249
|
+
const ready = JSON.parse(node_fs__rspack_import_0.readFileSync(readyPath, 'utf-8'));
|
|
1250
|
+
ready.status = 'error';
|
|
1251
|
+
ready.code = 'extension_load_refused';
|
|
1252
|
+
const browserLabel = String(ready.browser || 'the browser');
|
|
1253
|
+
ready.message = `${browserLabel.charAt(0).toUpperCase() + browserLabel.slice(1)} refused to load the extension at ${extensionOutputPath}${reason ? `: ${reason}` : ''}`;
|
|
1254
|
+
ready.extensionLoadRefusedAt = new Date().toISOString();
|
|
1255
|
+
if (reason) ready.extensionLoadRefusedReason = reason;
|
|
1256
|
+
node_fs__rspack_import_0.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
|
|
1257
|
+
} catch {}
|
|
1258
|
+
}
|
|
1227
1259
|
function stampReadyBrowserExited(extensionOutputPath, code) {
|
|
1228
1260
|
try {
|
|
1229
1261
|
if (!extensionOutputPath) return;
|
|
@@ -1241,8 +1273,9 @@ var __webpack_modules__ = {
|
|
|
1241
1273
|
} catch {}
|
|
1242
1274
|
}
|
|
1243
1275
|
__webpack_require__.d(__webpack_exports__, {
|
|
1244
|
-
|
|
1245
|
-
|
|
1276
|
+
Wi: ()=>stampReadyBrowserExited,
|
|
1277
|
+
__: ()=>stampReadyRdpPort,
|
|
1278
|
+
sf: ()=>stampReadyExtensionLoadRefused
|
|
1246
1279
|
});
|
|
1247
1280
|
},
|
|
1248
1281
|
"./browsers/browsers-lib/resolve-profile.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
@@ -1448,6 +1481,9 @@ var __webpack_modules__ = {
|
|
|
1448
1481
|
function parseEnvBrowserFlags(raw) {
|
|
1449
1482
|
return String(raw || '').split(/\s+/).map((flag)=>flag.trim()).filter(Boolean);
|
|
1450
1483
|
}
|
|
1484
|
+
function isHeadlessGuardRequested(env = process.env) {
|
|
1485
|
+
return /^(1|true)$/i.test(String(env.EXTENSION_HEADLESS || '').trim());
|
|
1486
|
+
}
|
|
1451
1487
|
function mergeChromiumFeatureSwitches(flags) {
|
|
1452
1488
|
const merged = [];
|
|
1453
1489
|
const featureValues = {
|
|
@@ -1608,6 +1644,7 @@ var __webpack_modules__ = {
|
|
|
1608
1644
|
Py: ()=>mergeChromiumFeatureSwitches,
|
|
1609
1645
|
RE: ()=>markManagedEphemeralProfile,
|
|
1610
1646
|
W0: ()=>parseEnvBrowserFlags,
|
|
1647
|
+
ZO: ()=>isHeadlessGuardRequested,
|
|
1611
1648
|
aY: ()=>findAvailablePortNear,
|
|
1612
1649
|
jl: ()=>deriveDebugPortWithInstance,
|
|
1613
1650
|
ov: ()=>filterBrowserFlags,
|
|
@@ -1688,6 +1725,20 @@ var __webpack_modules__ = {
|
|
|
1688
1725
|
let cdpController;
|
|
1689
1726
|
if (enableCdp) cdpController = ctx.getController?.();
|
|
1690
1727
|
return {
|
|
1728
|
+
getExtensionLoadRefusal () {
|
|
1729
|
+
return launcher.getExtensionLoadRefusal();
|
|
1730
|
+
},
|
|
1731
|
+
async retryExtensionLoad () {
|
|
1732
|
+
if (!cdpController?.verifyGuestLoaded) return {
|
|
1733
|
+
status: 'unknown'
|
|
1734
|
+
};
|
|
1735
|
+
const outcome = await cdpController.verifyGuestLoaded();
|
|
1736
|
+
if ('loaded' === outcome.status) {
|
|
1737
|
+
launcher.clearExtensionLoadRefusal();
|
|
1738
|
+
await launcher.printBannerOnRecovery();
|
|
1739
|
+
}
|
|
1740
|
+
return outcome;
|
|
1741
|
+
},
|
|
1691
1742
|
async enableUnifiedLogging (logOpts) {
|
|
1692
1743
|
if (cdpController?.enableUnifiedLogging) await cdpController.enableUnifiedLogging({
|
|
1693
1744
|
level: logOpts.level,
|
|
@@ -1719,7 +1770,8 @@ var __webpack_modules__ = {
|
|
|
1719
1770
|
logTimestamps: opts.logTimestamps,
|
|
1720
1771
|
logColor: opts.logColor,
|
|
1721
1772
|
logUrl: opts.logUrl,
|
|
1722
|
-
logTab: opts.logTab
|
|
1773
|
+
logTab: opts.logTab,
|
|
1774
|
+
logSink: opts.logSink
|
|
1723
1775
|
};
|
|
1724
1776
|
const pluginOptions = {
|
|
1725
1777
|
extension: opts.extensionsToLoad,
|
|
@@ -1747,6 +1799,15 @@ var __webpack_modules__ = {
|
|
|
1747
1799
|
await launcher.runOnce(compilationLike, launchRequest);
|
|
1748
1800
|
const rdpController = ctx.getController?.();
|
|
1749
1801
|
return {
|
|
1802
|
+
getExtensionLoadRefusal () {
|
|
1803
|
+
return firefoxOpts.extensionLoadRefused || null;
|
|
1804
|
+
},
|
|
1805
|
+
async retryExtensionLoad () {
|
|
1806
|
+
if (!firefoxOpts.retryAddonInstall) return {
|
|
1807
|
+
status: 'unknown'
|
|
1808
|
+
};
|
|
1809
|
+
return await firefoxOpts.retryAddonInstall();
|
|
1810
|
+
},
|
|
1750
1811
|
async enableUnifiedLogging (logOpts) {
|
|
1751
1812
|
if (!rdpController?.enableUnifiedLogging) return;
|
|
1752
1813
|
await rdpController.enableUnifiedLogging({
|
|
@@ -2093,6 +2154,7 @@ var __webpack_modules__ = {
|
|
|
2093
2154
|
var output_binaries_resolver = __webpack_require__("./browsers/browsers-lib/output-binaries-resolver.ts");
|
|
2094
2155
|
var process_teardown = __webpack_require__("./browsers/browsers-lib/process-teardown.ts");
|
|
2095
2156
|
var ready_message = __webpack_require__("./browsers/browsers-lib/ready-message.ts");
|
|
2157
|
+
var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
|
|
2096
2158
|
var runtime_options = __webpack_require__("./browsers/browsers-lib/runtime-options.ts");
|
|
2097
2159
|
var shared_utils = __webpack_require__("./browsers/browsers-lib/shared-utils.ts");
|
|
2098
2160
|
var discovery = __webpack_require__("./browsers/run-chromium/cdp/discovery.ts");
|
|
@@ -2325,6 +2387,7 @@ var __webpack_modules__ = {
|
|
|
2325
2387
|
...configOptions.browserFlags || [],
|
|
2326
2388
|
...(0, shared_utils.W0)(process.env.EXTENSION_BROWSER_FLAGS)
|
|
2327
2389
|
];
|
|
2390
|
+
if ((0, shared_utils.ZO)() && !baseFlags.some((flag)=>flag.startsWith('--headless'))) baseFlags.push('--headless=new');
|
|
2328
2391
|
return (0, shared_utils.Py)(baseFlags);
|
|
2329
2392
|
}
|
|
2330
2393
|
function logChromiumDryRun(browserBinaryLocation, chromiumConfig) {
|
|
@@ -2505,7 +2568,6 @@ var __webpack_modules__ = {
|
|
|
2505
2568
|
throw error;
|
|
2506
2569
|
}
|
|
2507
2570
|
}
|
|
2508
|
-
var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
|
|
2509
2571
|
function _define_property(obj, key, value) {
|
|
2510
2572
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
2511
2573
|
value: value,
|
|
@@ -2536,6 +2598,15 @@ var __webpack_modules__ = {
|
|
|
2536
2598
|
});
|
|
2537
2599
|
}
|
|
2538
2600
|
class ChromiumLaunchPlugin {
|
|
2601
|
+
getExtensionLoadRefusal() {
|
|
2602
|
+
return this.extensionLoadRefused || null;
|
|
2603
|
+
}
|
|
2604
|
+
clearExtensionLoadRefusal() {
|
|
2605
|
+
this.extensionLoadRefused = void 0;
|
|
2606
|
+
}
|
|
2607
|
+
async printBannerOnRecovery() {
|
|
2608
|
+
await this.bannerOnRecovery?.();
|
|
2609
|
+
}
|
|
2539
2610
|
async runOnce(compilation, opts) {
|
|
2540
2611
|
if (!this.logger) this.logger = {
|
|
2541
2612
|
info: (...a)=>console.log(...a),
|
|
@@ -2560,7 +2631,7 @@ var __webpack_modules__ = {
|
|
|
2560
2631
|
if (this.didLaunch) return;
|
|
2561
2632
|
await this.launchChromium(stats.compilation);
|
|
2562
2633
|
this.didLaunch = true;
|
|
2563
|
-
if (!this.didReportReady) console.log((0, ready_message.G)(stats.compilation.options.mode, this.options.browser));
|
|
2634
|
+
if (!this.didReportReady && !this.extensionLoadRefused) console.log((0, ready_message.G)(stats.compilation.options.mode, this.options.browser));
|
|
2564
2635
|
} catch (error) {
|
|
2565
2636
|
try {
|
|
2566
2637
|
this.logger.error(messages._D(this.options.browser, error));
|
|
@@ -2927,10 +2998,13 @@ var __webpack_modules__ = {
|
|
|
2927
2998
|
}
|
|
2928
2999
|
if (!portReady && 'true' === process.env.EXTENSION_AUTHOR_MODE) this.logger.warn?.(`[browser] Debug port ${selectedPort} not bound after spawn. CDP may fail`);
|
|
2929
3000
|
}
|
|
2930
|
-
|
|
3001
|
+
const reportReady = ()=>{
|
|
3002
|
+
if ('development' !== compilation.options.mode) return;
|
|
3003
|
+
if (this.didReportReady || this.extensionLoadRefused) return;
|
|
2931
3004
|
console.log((0, ready_message.G)(compilation.options.mode, this.options.browser));
|
|
2932
3005
|
this.didReportReady = true;
|
|
2933
|
-
}
|
|
3006
|
+
};
|
|
3007
|
+
if (!enableCdp) reportReady();
|
|
2934
3008
|
try {
|
|
2935
3009
|
const mode = compilation?.options?.mode || 'development';
|
|
2936
3010
|
try {
|
|
@@ -2962,12 +3036,18 @@ var __webpack_modules__ = {
|
|
|
2962
3036
|
mod.setupCdpAfterLaunch(compilation, cdpConfig, chromiumConfig, pipeStreams),
|
|
2963
3037
|
new Promise((_, reject)=>setTimeout(()=>reject(new Error(`CDP setup did not complete within ${CDP_SETUP_TIMEOUT_MS / 1000}s. Chrome likely rejected the extension at launch, open chrome://extensions in the dev browser window for the exact error. Common causes: MV3 content_security_policy with 'unsafe-inline', manifest keys Chrome does not support, or manifest references to files missing from the output. Reload/HMR cannot attach until this is fixed.`)), CDP_SETUP_TIMEOUT_MS).unref?.())
|
|
2964
3038
|
]);
|
|
3039
|
+
if (cdpConfig.extensionLoadRefused) {
|
|
3040
|
+
this.extensionLoadRefused = cdpConfig.extensionLoadRefused;
|
|
3041
|
+
this.bannerOnRecovery = cdpConfig.printBannerOnRecovery;
|
|
3042
|
+
}
|
|
2965
3043
|
if (cdpConfig.cdpController) this.ctx.setController(cdpConfig.cdpController);
|
|
2966
3044
|
}
|
|
3045
|
+
reportReady();
|
|
2967
3046
|
} catch (error) {
|
|
2968
3047
|
const message = String(error && error.message);
|
|
2969
3048
|
const hint = /timed out|did not complete/i.test(message) ? " Chrome likely rejected the extension at launch, open chrome://extensions in the dev browser window for the exact error. Common causes: MV3 content_security_policy with 'unsafe-inline', manifest keys Chrome does not support, or manifest references to missing files. Reload/HMR cannot attach until this is fixed." : '';
|
|
2970
3049
|
console.error(`[browser] ${message}${hint}`);
|
|
3050
|
+
reportReady();
|
|
2971
3051
|
}
|
|
2972
3052
|
}
|
|
2973
3053
|
async launchWithDirectSpawn(binary, chromeFlags, usePipe = false) {
|
|
@@ -3018,7 +3098,7 @@ var __webpack_modules__ = {
|
|
|
3018
3098
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.logger.info(messages.nn(code || 0));
|
|
3019
3099
|
if (!(0, process_teardown.Op)(child)) {
|
|
3020
3100
|
this.logger.error(this.closeHandlerContext?.isDevMode ? `[browser] ${this.options.browser} exited mid-session (code ${code ?? 'unknown'}). The dev server is still running but reloads cannot be delivered, restart "extension dev" to relaunch the browser.` : `[browser] ${this.options.browser} exited (code ${code ?? 'unknown'}); the preview session is over.`);
|
|
3021
|
-
(0, ready_stamp.
|
|
3101
|
+
(0, ready_stamp.Wi)(this.closeHandlerContext?.extensionOutputPath, code);
|
|
3022
3102
|
}
|
|
3023
3103
|
disposeSignalHandlers?.();
|
|
3024
3104
|
const userDataDir = launchArgs.find((arg)=>arg.startsWith('--user-data-dir='))?.slice('--user-data-dir='.length).replace(/^"|"$/g, '');
|
|
@@ -3053,6 +3133,8 @@ var __webpack_modules__ = {
|
|
|
3053
3133
|
_define_property(this, "ctx", void 0);
|
|
3054
3134
|
_define_property(this, "didLaunch", void 0);
|
|
3055
3135
|
_define_property(this, "didReportReady", void 0);
|
|
3136
|
+
_define_property(this, "extensionLoadRefused", void 0);
|
|
3137
|
+
_define_property(this, "bannerOnRecovery", void 0);
|
|
3056
3138
|
_define_property(this, "logger", void 0);
|
|
3057
3139
|
_define_property(this, "closeHandlerContext", void 0);
|
|
3058
3140
|
this.options = options;
|
|
@@ -3243,7 +3325,7 @@ var __webpack_modules__ = {
|
|
|
3243
3325
|
} : null;
|
|
3244
3326
|
}
|
|
3245
3327
|
function isFirefoxHeadlessRequested(env = process.env) {
|
|
3246
|
-
return /^(1|true)$/i.test(String(env.MOZ_HEADLESS || '').trim());
|
|
3328
|
+
return /^(1|true)$/i.test(String(env.MOZ_HEADLESS || '').trim()) || (0, shared_utils.ZO)(env);
|
|
3247
3329
|
}
|
|
3248
3330
|
class FirefoxBinaryDetector {
|
|
3249
3331
|
static generateFirefoxArgs(binaryPath, profilePath, debugPort, additionalArgs = [], headless = false) {
|
|
@@ -3265,7 +3347,9 @@ var __webpack_modules__ = {
|
|
|
3265
3347
|
'-start-debugger-server',
|
|
3266
3348
|
String(debugPort)
|
|
3267
3349
|
] : [],
|
|
3268
|
-
|
|
3350
|
+
...headless ? [] : [
|
|
3351
|
+
'--foreground'
|
|
3352
|
+
],
|
|
3269
3353
|
...additionalArgs
|
|
3270
3354
|
];
|
|
3271
3355
|
return {
|
|
@@ -3286,7 +3370,9 @@ var __webpack_modules__ = {
|
|
|
3286
3370
|
'-start-debugger-server',
|
|
3287
3371
|
String(debugPort)
|
|
3288
3372
|
] : [],
|
|
3289
|
-
|
|
3373
|
+
...headless ? [] : [
|
|
3374
|
+
'--foreground'
|
|
3375
|
+
],
|
|
3290
3376
|
...additionalArgs
|
|
3291
3377
|
];
|
|
3292
3378
|
return {
|
|
@@ -3529,6 +3615,22 @@ var __webpack_modules__ = {
|
|
|
3529
3615
|
if (external_node_fs_.existsSync(external_node_path_.join(distFirefox, 'manifest.json'))) return distFirefox;
|
|
3530
3616
|
return candidate;
|
|
3531
3617
|
}
|
|
3618
|
+
function classifyAddonInstallFailure(error) {
|
|
3619
|
+
if (error instanceof Error) return {
|
|
3620
|
+
status: 'unknown'
|
|
3621
|
+
};
|
|
3622
|
+
const reply = error;
|
|
3623
|
+
if (!reply || 'object' != typeof reply || !reply.error) return {
|
|
3624
|
+
status: 'unknown'
|
|
3625
|
+
};
|
|
3626
|
+
const reason = String(reply.message || '').trim();
|
|
3627
|
+
return reason ? {
|
|
3628
|
+
status: 'refused',
|
|
3629
|
+
reason
|
|
3630
|
+
} : {
|
|
3631
|
+
status: 'unknown'
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3532
3634
|
async function getAddonsActorWithRetry(client, cached, tries = 40, delayMs = 250) {
|
|
3533
3635
|
if (cached) return cached;
|
|
3534
3636
|
let addonsActor;
|
|
@@ -4185,6 +4287,9 @@ var __webpack_modules__ = {
|
|
|
4185
4287
|
const RETRY_INTERVAL = constants.FQ;
|
|
4186
4288
|
const RETRY_LOG_EVERY_N_ATTEMPTS = 10;
|
|
4187
4289
|
class RemoteFirefox {
|
|
4290
|
+
getAddonInstallRefusalReason() {
|
|
4291
|
+
return this.addonInstallRefusalReason || null;
|
|
4292
|
+
}
|
|
4188
4293
|
selectPrimaryAddonPath(compilation, candidateAddonPaths) {
|
|
4189
4294
|
const normalizedOutputPath = String(compilation?.options?.output?.path || '').replace(/\\/g, '/');
|
|
4190
4295
|
if (normalizedOutputPath) {
|
|
@@ -4274,6 +4379,8 @@ var __webpack_modules__ = {
|
|
|
4274
4379
|
if (primaryAddonPath && String(addonPath) === String(primaryAddonPath) && maybeId) primaryUserAddonId = maybeId;
|
|
4275
4380
|
if (isManager) await waitForManagerWelcome(client);
|
|
4276
4381
|
} catch (err) {
|
|
4382
|
+
const outcome = classifyAddonInstallFailure(err);
|
|
4383
|
+
if ('refused' === outcome.status) this.addonInstallRefusalReason = outcome.reason;
|
|
4277
4384
|
const message = requestErrorToMessage(err);
|
|
4278
4385
|
throw new Error(messages.WV(this.options.browser, message));
|
|
4279
4386
|
}
|
|
@@ -4321,6 +4428,7 @@ var __webpack_modules__ = {
|
|
|
4321
4428
|
remote_firefox_define_property(this, "cachedAddonsActor", void 0);
|
|
4322
4429
|
remote_firefox_define_property(this, "lastInstalledAddonPath", void 0);
|
|
4323
4430
|
remote_firefox_define_property(this, "derivedExtensionId", void 0);
|
|
4431
|
+
remote_firefox_define_property(this, "addonInstallRefusalReason", void 0);
|
|
4324
4432
|
this.options = configOptions;
|
|
4325
4433
|
}
|
|
4326
4434
|
}
|
|
@@ -4338,6 +4446,9 @@ var __webpack_modules__ = {
|
|
|
4338
4446
|
async ensureLoaded(compilation) {
|
|
4339
4447
|
await this.remote.installAddons(compilation);
|
|
4340
4448
|
}
|
|
4449
|
+
getAddonInstallRefusalReason() {
|
|
4450
|
+
return this.remote.getAddonInstallRefusalReason();
|
|
4451
|
+
}
|
|
4341
4452
|
async enableUnifiedLogging(opts) {
|
|
4342
4453
|
await this.remote.enableUnifiedLogging(opts);
|
|
4343
4454
|
}
|
|
@@ -4370,6 +4481,7 @@ var __webpack_modules__ = {
|
|
|
4370
4481
|
return await fn();
|
|
4371
4482
|
} catch (error) {
|
|
4372
4483
|
lastError = error;
|
|
4484
|
+
if (controller.getAddonInstallRefusalReason()) break;
|
|
4373
4485
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) try {
|
|
4374
4486
|
const msg = error?.message || String(error);
|
|
4375
4487
|
console.warn(`[browser] Firefox RDP setup retry ${i + 1}/${attempts}: ${msg}`);
|
|
@@ -4379,10 +4491,19 @@ var __webpack_modules__ = {
|
|
|
4379
4491
|
}
|
|
4380
4492
|
throw lastError;
|
|
4381
4493
|
};
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4494
|
+
const withRefusalReason = (error)=>{
|
|
4495
|
+
const reason = controller.getAddonInstallRefusalReason();
|
|
4496
|
+
if (reason && error && 'object' == typeof error) error.extensionLoadRefusedReason = reason;
|
|
4497
|
+
return error;
|
|
4498
|
+
};
|
|
4499
|
+
try {
|
|
4500
|
+
if (plugin.rdpController) await retry(()=>plugin.rdpController.ensureLoaded?.(compilation) || Promise.resolve());
|
|
4501
|
+
else {
|
|
4502
|
+
await retry(()=>controller.ensureLoaded(compilation));
|
|
4503
|
+
plugin.rdpController = controller;
|
|
4504
|
+
}
|
|
4505
|
+
} catch (error) {
|
|
4506
|
+
throw withRefusalReason(error);
|
|
4386
4507
|
}
|
|
4387
4508
|
return controller;
|
|
4388
4509
|
}
|
|
@@ -4465,8 +4586,8 @@ var __webpack_modules__ = {
|
|
|
4465
4586
|
error: (...a)=>console.error(...a),
|
|
4466
4587
|
debug: (...a)=>console?.debug?.(...a)
|
|
4467
4588
|
};
|
|
4468
|
-
if ('development' === options.mode) this.ctx.logger?.info?.((0, ready_message.G)(options.mode, this.host.browser));
|
|
4469
4589
|
await this.launch(compilation, options);
|
|
4590
|
+
if ('development' === options.mode && !this.host.extensionLoadRefused) this.ctx.logger?.info?.((0, ready_message.G)(options.mode, this.host.browser));
|
|
4470
4591
|
this.ctx.didLaunch = true;
|
|
4471
4592
|
}
|
|
4472
4593
|
apply(compiler) {
|
|
@@ -4486,8 +4607,8 @@ var __webpack_modules__ = {
|
|
|
4486
4607
|
return;
|
|
4487
4608
|
}
|
|
4488
4609
|
if (this.ctx.didLaunch) return void done();
|
|
4489
|
-
if ('development' === stats.compilation.options.mode) console.log((0, ready_message.G)(stats.compilation.options.mode, this.host.browser));
|
|
4490
4610
|
await this.launch(stats.compilation, (0, runtime_options.zU)(this.host, stats.compilation.options.mode));
|
|
4611
|
+
if ('development' === stats.compilation.options.mode && !this.host.extensionLoadRefused) console.log((0, ready_message.G)(stats.compilation.options.mode, this.host.browser));
|
|
4491
4612
|
this.ctx.didLaunch = true;
|
|
4492
4613
|
} catch (error) {
|
|
4493
4614
|
this.ctx.logger?.error?.(messages.MQ(error));
|
|
@@ -4648,10 +4769,24 @@ var __webpack_modules__ = {
|
|
|
4648
4769
|
(0, shared_utils.sW)(profilePath);
|
|
4649
4770
|
});
|
|
4650
4771
|
this.wireChildLifecycle();
|
|
4651
|
-
|
|
4772
|
+
let ctrl;
|
|
4773
|
+
try {
|
|
4774
|
+
ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
|
|
4775
|
+
} catch (error) {
|
|
4776
|
+
const reason = error?.extensionLoadRefusedReason;
|
|
4777
|
+
if (!reason) {
|
|
4778
|
+
(0, ready_stamp.__)(this.extensionOutputPath, debugPort);
|
|
4779
|
+
throw error;
|
|
4780
|
+
}
|
|
4781
|
+
this.reportAddonLoadRefused(reason);
|
|
4782
|
+
this.host.retryAddonInstall = ()=>this.retryAddonInstall(compilation, debugPort);
|
|
4783
|
+
(0, ready_stamp.__)(this.extensionOutputPath, debugPort);
|
|
4784
|
+
this.scheduleWatchTimeout();
|
|
4785
|
+
return;
|
|
4786
|
+
}
|
|
4652
4787
|
this.host.rdpController = ctrl;
|
|
4653
4788
|
this.ctx.setController(ctrl);
|
|
4654
|
-
(0, ready_stamp.
|
|
4789
|
+
(0, ready_stamp.__)(this.extensionOutputPath, debugPort);
|
|
4655
4790
|
this.scheduleWatchTimeout();
|
|
4656
4791
|
try {
|
|
4657
4792
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) {
|
|
@@ -4672,12 +4807,14 @@ var __webpack_modules__ = {
|
|
|
4672
4807
|
...'win32' === process.platform ? [
|
|
4673
4808
|
'-wait-for-browser'
|
|
4674
4809
|
] : [],
|
|
4675
|
-
|
|
4810
|
+
...isFirefoxHeadlessRequested() ? [] : [
|
|
4811
|
+
'--foreground'
|
|
4812
|
+
],
|
|
4676
4813
|
...firefoxArgs
|
|
4677
4814
|
];
|
|
4678
4815
|
this.child = await this.spawnFirefoxChild(binaryPath, args, wslFallbackBinary);
|
|
4679
4816
|
this.wireChildLifecycle();
|
|
4680
|
-
if (debugPort > 0) (0, ready_stamp.
|
|
4817
|
+
if (debugPort > 0) (0, ready_stamp.__)(this.extensionOutputPath, debugPort);
|
|
4681
4818
|
this.scheduleWatchTimeout();
|
|
4682
4819
|
}
|
|
4683
4820
|
}
|
|
@@ -4730,7 +4867,7 @@ var __webpack_modules__ = {
|
|
|
4730
4867
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.ctx.logger?.info?.(messages.Th(this.host.browser));
|
|
4731
4868
|
if (!(0, process_teardown.Op)(child)) {
|
|
4732
4869
|
this.ctx.logger?.error?.(`[browser] ${this.host.browser} exited (code ${code ?? 'unknown'}) without being asked to. The add-on may have been rejected or the browser crashed; the session cannot be driven.`);
|
|
4733
|
-
(0, ready_stamp.
|
|
4870
|
+
(0, ready_stamp.Wi)(this.extensionOutputPath, code);
|
|
4734
4871
|
}
|
|
4735
4872
|
this.cleanupInstance().catch((err)=>{
|
|
4736
4873
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.ctx.logger?.error?.(`[browser] Cleanup error on child close: ${err?.message || err}`);
|
|
@@ -4740,6 +4877,36 @@ var __webpack_modules__ = {
|
|
|
4740
4877
|
this.pipeChildOutput(child);
|
|
4741
4878
|
disposeProcessHandlers = setupFirefoxProcessHandlers(this.host.browser, ()=>this.child, ()=>this.cleanupInstance());
|
|
4742
4879
|
}
|
|
4880
|
+
async retryAddonInstall(compilation, debugPort) {
|
|
4881
|
+
try {
|
|
4882
|
+
const ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
|
|
4883
|
+
this.host.rdpController = ctrl;
|
|
4884
|
+
this.ctx.setController(ctrl);
|
|
4885
|
+
this.host.extensionLoadRefused = void 0;
|
|
4886
|
+
return {
|
|
4887
|
+
status: 'loaded'
|
|
4888
|
+
};
|
|
4889
|
+
} catch (error) {
|
|
4890
|
+
const reason = error?.extensionLoadRefusedReason;
|
|
4891
|
+
return reason ? {
|
|
4892
|
+
status: 'refused',
|
|
4893
|
+
reason
|
|
4894
|
+
} : {
|
|
4895
|
+
status: 'unknown'
|
|
4896
|
+
};
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4899
|
+
reportAddonLoadRefused(reason) {
|
|
4900
|
+
const refusedPath = this.extensionOutputPath || '';
|
|
4901
|
+
console.error(messages.EE(refusedPath, reason));
|
|
4902
|
+
this.host.logSink?.({
|
|
4903
|
+
level: 'error',
|
|
4904
|
+
text: `extension_load_refused: ${refusedPath}${reason ? ` - ${reason}` : ''}`,
|
|
4905
|
+
source: 'browser'
|
|
4906
|
+
});
|
|
4907
|
+
(0, ready_stamp.sf)(this.extensionOutputPath, reason);
|
|
4908
|
+
this.host.extensionLoadRefused = reason;
|
|
4909
|
+
}
|
|
4743
4910
|
async cleanupInstance() {
|
|
4744
4911
|
(0, process_teardown.Df)(this.child, this.host.browser);
|
|
4745
4912
|
}
|
|
@@ -20,6 +20,8 @@ export declare function mv3BackgroundScriptsNotSupportedByChromium(extensionPath
|
|
|
20
20
|
export declare function unsupportedManifestVersionOnChromium(extensionPath: string, declared: unknown): string;
|
|
21
21
|
export declare function chromiumInvalidMatchPatterns(extensionPath: string, patterns: string[]): string;
|
|
22
22
|
export declare function chromiumManifestLoadBlockers(extensionPath: string, blockers: string[]): string;
|
|
23
|
+
export declare function chromiumExtensionLoadRefused(extensionPath: string, reason: string): string;
|
|
24
|
+
export declare function geckoAddonLoadRefused(addonPath: string, reason: string): string;
|
|
23
25
|
export declare function devChannelSnapshotInUse(binaryPath: string): string;
|
|
24
26
|
export declare function browserLaunchError(browser: Browser, error: unknown): string;
|
|
25
27
|
export declare function enhancedProcessManagementCleanup(browser: Browser): string;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare function stampReadyRdpPort(extensionOutputPath: string | undefined, rdpPort: number): void;
|
|
2
|
+
export declare function stampReadyExtensionLoadRefused(extensionOutputPath: string | undefined, reason: string): void;
|
|
2
3
|
export declare function stampReadyBrowserExited(extensionOutputPath: string | undefined, code: number | null): void;
|
|
@@ -15,6 +15,7 @@ export declare function chooseChromiumBinaryPreferringStable(opts: {
|
|
|
15
15
|
preferManagedSnapshot?: boolean;
|
|
16
16
|
}): ChromiumBinaryChoice;
|
|
17
17
|
export declare function parseEnvBrowserFlags(raw: string | undefined | null): string[];
|
|
18
|
+
export declare function isHeadlessGuardRequested(env?: NodeJS.ProcessEnv): boolean;
|
|
18
19
|
export declare function mergeChromiumFeatureSwitches(flags: string[]): string[];
|
|
19
20
|
export declare function findAvailablePortNear(startPort: number, maxAttempts?: number, host?: string): Promise<number>;
|
|
20
21
|
export declare function prepareChromiumProfileForLaunch(profilePath: string): {
|
|
@@ -185,6 +185,11 @@ export interface BrowserLogSinkEvent {
|
|
|
185
185
|
}
|
|
186
186
|
/** Host-provided sink for {@link BrowserLogSinkEvent}s. Must never throw. */
|
|
187
187
|
export type BrowserLogSink = (event: BrowserLogSinkEvent) => void;
|
|
188
|
+
export interface ExtensionLoadRetryResult {
|
|
189
|
+
status: 'loaded' | 'refused' | 'unknown';
|
|
190
|
+
reason?: string;
|
|
191
|
+
extensionId?: string;
|
|
192
|
+
}
|
|
188
193
|
export interface Controller {
|
|
189
194
|
enableUnifiedLogging: (opts: {
|
|
190
195
|
level?: string;
|
|
@@ -199,4 +204,5 @@ export interface Controller {
|
|
|
199
204
|
method?: string;
|
|
200
205
|
params?: unknown;
|
|
201
206
|
}) => void) => void;
|
|
207
|
+
verifyGuestLoaded?: () => Promise<ExtensionLoadRetryResult>;
|
|
202
208
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BrowserLogSink, BrowserType } from './browsers-types';
|
|
1
|
+
import type { BrowserLogSink, BrowserType, ExtensionLoadRetryResult } from './browsers-types';
|
|
2
2
|
export type { BrowserType, CompilationLike, Controller } from './browsers-types';
|
|
3
3
|
/**
|
|
4
4
|
* Options for launching a browser with an extension loaded.
|
|
@@ -49,6 +49,7 @@ export interface BrowserLaunchOptions {
|
|
|
49
49
|
* (`setupFirefoxProcessHandlers` / Chromium equivalents). The controller is
|
|
50
50
|
* deliberately not responsible for teardown, so there is no `close()`.
|
|
51
51
|
*/
|
|
52
|
+
export type { ExtensionLoadRetryResult };
|
|
52
53
|
export interface BrowserController {
|
|
53
54
|
enableUnifiedLogging(opts: {
|
|
54
55
|
level?: string;
|
|
@@ -59,6 +60,10 @@ export interface BrowserController {
|
|
|
59
60
|
urlFilter?: string;
|
|
60
61
|
tabFilter?: number | string;
|
|
61
62
|
}): Promise<void>;
|
|
63
|
+
/** The browser's refusal reason for this session, or null when it loaded. */
|
|
64
|
+
getExtensionLoadRefusal?(): string | null;
|
|
65
|
+
/** Re-offer the current dist. Only ever called while the session is refused. */
|
|
66
|
+
retryExtensionLoad?(): Promise<ExtensionLoadRetryResult>;
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
69
|
* Launch a browser with the given extension(s) loaded.
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { CDPClient } from '../cdp-client';
|
|
2
2
|
export declare function uninstallStaleUnpackedLoads(cdp: CDPClient, profilePath: string | undefined, outPath: string): Promise<string[]>;
|
|
3
|
+
export declare function declaresBackgroundContext(outPath: string): boolean;
|
|
4
|
+
export type LoadUnpackedOutcome = {
|
|
5
|
+
status: 'loaded';
|
|
6
|
+
extensionId: string;
|
|
7
|
+
} | {
|
|
8
|
+
status: 'refused';
|
|
9
|
+
reason: string;
|
|
10
|
+
} | {
|
|
11
|
+
status: 'unknown';
|
|
12
|
+
};
|
|
13
|
+
export declare function loadUnpacked(cdp: CDPClient, outPath: string): Promise<LoadUnpackedOutcome>;
|
|
3
14
|
export declare function loadUnpackedIfNeeded(cdp: CDPClient, outPath: string): Promise<string | null>;
|
|
4
15
|
export declare function readManifestInfo(outPath: string): {
|
|
5
16
|
name?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Readable, Writable } from 'node:stream';
|
|
2
2
|
import type { BrowserLogSink } from '../../../browsers-types';
|
|
3
3
|
import type { CdpProtocolMessage } from '../../chromium-types';
|
|
4
|
+
import { type LoadUnpackedOutcome } from './ensure';
|
|
4
5
|
interface ExtensionInfoResult {
|
|
5
6
|
extensionId: string;
|
|
6
7
|
name?: string;
|
|
@@ -16,6 +17,7 @@ export declare class CDPExtensionController {
|
|
|
16
17
|
private readonly logSink?;
|
|
17
18
|
private cdp;
|
|
18
19
|
private extensionId;
|
|
20
|
+
private loadRefusalReason;
|
|
19
21
|
constructor(args: {
|
|
20
22
|
outPath: string;
|
|
21
23
|
browser: 'chrome' | 'edge' | 'chromium-based';
|
|
@@ -28,6 +30,9 @@ export declare class CDPExtensionController {
|
|
|
28
30
|
});
|
|
29
31
|
connect(): Promise<void>;
|
|
30
32
|
openTab(url: string): Promise<void>;
|
|
33
|
+
verifyGuestLoaded(): Promise<LoadUnpackedOutcome>;
|
|
34
|
+
getLoadRefusalReason(): string | null;
|
|
35
|
+
private waitForExtensionTarget;
|
|
31
36
|
ensureLoaded(): Promise<ExtensionInfoResult>;
|
|
32
37
|
private deriveExtensionIdFromTargets;
|
|
33
38
|
private classifyOwnership;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { stampReadyBrowserExited } from '../../browsers-lib/ready-stamp';
|
|
1
2
|
import type { CompilationLike } from '../../browsers-types';
|
|
2
3
|
import type { ChromiumContext } from '../chromium-context';
|
|
3
4
|
import type { ChromiumLaunchOptions } from '../chromium-types';
|
|
4
|
-
import { stampReadyBrowserExited } from '../../browsers-lib/ready-stamp';
|
|
5
5
|
export { stampReadyBrowserExited };
|
|
6
6
|
/**
|
|
7
7
|
* ChromiumLaunchPlugin
|
|
@@ -17,6 +17,11 @@ export declare class ChromiumLaunchPlugin {
|
|
|
17
17
|
private readonly ctx;
|
|
18
18
|
private didLaunch;
|
|
19
19
|
private didReportReady;
|
|
20
|
+
private extensionLoadRefused;
|
|
21
|
+
getExtensionLoadRefusal(): string | null;
|
|
22
|
+
clearExtensionLoadRefusal(): void;
|
|
23
|
+
printBannerOnRecovery(): Promise<void>;
|
|
24
|
+
private bannerOnRecovery;
|
|
20
25
|
private logger;
|
|
21
26
|
private closeHandlerContext;
|
|
22
27
|
constructor(options: ChromiumLaunchOptions, ctx: ChromiumContext);
|
|
@@ -41,6 +41,8 @@ export interface ChromiumPluginRuntime extends ChromiumLaunchOptions {
|
|
|
41
41
|
bannerPrintedOnce?: boolean;
|
|
42
42
|
cdpController?: Controller;
|
|
43
43
|
browserVersionLine?: string;
|
|
44
|
+
extensionLoadRefused?: string;
|
|
45
|
+
printBannerOnRecovery?: () => Promise<void>;
|
|
44
46
|
}
|
|
45
47
|
export interface ChromiumLogger {
|
|
46
48
|
level?: LogLevel | 'off' | string;
|
|
@@ -26,6 +26,8 @@ export declare class FirefoxLaunchPlugin {
|
|
|
26
26
|
private spawnFirefoxChild;
|
|
27
27
|
private pipeChildOutput;
|
|
28
28
|
private wireChildLifecycle;
|
|
29
|
+
private retryAddonInstall;
|
|
30
|
+
private reportAddonLoadRefused;
|
|
29
31
|
private cleanupInstance;
|
|
30
32
|
private scheduleWatchTimeout;
|
|
31
33
|
private printInstallHint;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginInterface } from '../browsers-types';
|
|
1
|
+
import type { BrowserLogSink, ExtensionLoadRetryResult, PluginInterface } from '../browsers-types';
|
|
2
2
|
import type { FirefoxRDPController } from './rdp/rdp-extension-controller';
|
|
3
3
|
export type FirefoxPluginLike = Pick<PluginInterface, 'extension' | 'browserFlags' | 'profile' | 'persistProfile' | 'keepProfileChanges' | 'copyFromProfile' | 'preferences' | 'startingUrl' | 'geckoBinary' | 'instanceId' | 'port' | 'logLevel' | 'logContexts' | 'logFormat' | 'logTimestamps' | 'logColor' | 'logUrl' | 'logTab' | 'dryRun'> & {
|
|
4
4
|
browser: PluginInterface['browser'];
|
|
@@ -6,4 +6,7 @@ export type FirefoxPluginLike = Pick<PluginInterface, 'extension' | 'browserFlag
|
|
|
6
6
|
export interface FirefoxPluginRuntime extends FirefoxPluginLike {
|
|
7
7
|
rdpController?: FirefoxRDPController;
|
|
8
8
|
browserVersionLine?: string;
|
|
9
|
+
logSink?: BrowserLogSink;
|
|
10
|
+
extensionLoadRefused?: string;
|
|
11
|
+
retryAddonInstall?: () => Promise<ExtensionLoadRetryResult>;
|
|
9
12
|
}
|