extension 4.1.21 → 4.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  exports.ids = [
3
- 84
3
+ 597
4
4
  ];
5
5
  exports.modules = {
6
6
  "./browsers/run-chromium/cdp/cdp-extension-controller/ensure.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
@@ -483,6 +483,73 @@ exports.modules = {
483
483
  this.host = host;
484
484
  }
485
485
  }
486
+ const EXTENSIONS_PAGE = 'chrome://extensions';
487
+ const READ_DEVELOPER_MODE = "new Promise((resolve) => chrome.developerPrivate.getProfileConfiguration((config) => resolve(!!config.inDeveloperMode)))";
488
+ const ENABLE_DEVELOPER_MODE = "new Promise((resolve) => chrome.developerPrivate.updateProfileConfiguration({inDeveloperMode: true}, () => resolve(!chrome.runtime.lastError)))";
489
+ function developerModeFromProfile(profilePath) {
490
+ try {
491
+ const securePath = external_node_path_.join(profilePath, 'Default', 'Secure Preferences');
492
+ const parsed = JSON.parse(external_node_fs_.readFileSync(securePath, 'utf-8'));
493
+ return parsed?.extensions?.ui?.developer_mode === true;
494
+ } catch {
495
+ return false;
496
+ }
497
+ }
498
+ function developerModeFlipIsSafe(browserArgs) {
499
+ return !browserArgs.some((arg)=>arg.startsWith('--no-startup-window'));
500
+ }
501
+ async function evaluateBoolean(transport, sessionId, expression) {
502
+ try {
503
+ const response = await transport.sendCommand('Runtime.evaluate', {
504
+ expression,
505
+ awaitPromise: true,
506
+ returnByValue: true
507
+ }, sessionId);
508
+ const value = response?.result?.value;
509
+ return 'boolean' == typeof value ? value : void 0;
510
+ } catch {
511
+ return;
512
+ }
513
+ }
514
+ async function ensureDeveloperMode(options) {
515
+ const { transport } = options;
516
+ const attempts = options.attempts ?? 10;
517
+ const delayMs = options.delayMs ?? 200;
518
+ const sleep = options.sleep ?? ((ms)=>new Promise((r)=>setTimeout(r, ms)));
519
+ let targetId = '';
520
+ try {
521
+ const created = await transport.sendCommand('Target.createTarget', {
522
+ url: EXTENSIONS_PAGE,
523
+ background: true
524
+ });
525
+ targetId = created?.targetId ?? '';
526
+ if (!targetId) return 'unavailable';
527
+ const attached = await transport.sendCommand('Target.attachToTarget', {
528
+ targetId,
529
+ flatten: true
530
+ });
531
+ const sessionId = attached?.sessionId ?? '';
532
+ if (!sessionId) return 'unavailable';
533
+ for(let attempt = 0; attempt < attempts; attempt++){
534
+ const current = await evaluateBoolean(transport, sessionId, READ_DEVELOPER_MODE);
535
+ if (true === current) return 'already-on';
536
+ if (false === current) {
537
+ const enabled = await evaluateBoolean(transport, sessionId, ENABLE_DEVELOPER_MODE);
538
+ return true === enabled ? 'enabled' : 'unavailable';
539
+ }
540
+ await sleep(delayMs);
541
+ }
542
+ return 'unavailable';
543
+ } catch {
544
+ return 'unavailable';
545
+ } finally{
546
+ if (targetId) try {
547
+ await transport.sendCommand('Target.closeTarget', {
548
+ targetId
549
+ });
550
+ } catch {}
551
+ }
552
+ }
486
553
  function isRecoverableBootstrapError(error) {
487
554
  const msg = String(error?.message || error || '').toLowerCase();
488
555
  return msg.includes('econnreset') || msg.includes('websocket is not open') || msg.includes('cdp transport is not open') || msg.includes('cdp connection closed') || msg.includes('cdp pipe closed') || msg.includes('socket hang up') || msg.includes('timed out') || msg.includes('no cdp websocket url');
@@ -784,6 +851,12 @@ exports.modules = {
784
851
  if (this.cdp) return;
785
852
  await this.connectFreshClient();
786
853
  }
854
+ async ensureDeveloperMode() {
855
+ if (!this.cdp) return 'unavailable';
856
+ return ensureDeveloperMode({
857
+ transport: this.cdp
858
+ });
859
+ }
787
860
  async openTab(url) {
788
861
  if (!this.cdp) return;
789
862
  await this.cdp.sendCommand('Target.createTarget', {
@@ -1110,6 +1183,10 @@ exports.modules = {
1110
1183
  });
1111
1184
  await cdpExtensionController.connect();
1112
1185
  if ((0, messaging._o)()) (0, messaging._w)(messages.M3('127.0.0.1', chromeRemoteDebugPort));
1186
+ if (userDataDir && developerModeFlipIsSafe(chromiumArgs) && !developerModeFromProfile(userDataDir)) {
1187
+ const developerMode = await cdpExtensionController.ensureDeveloperMode();
1188
+ if ((0, messaging._o)()) (0, messaging._w)(`[CDP] developer mode: ${developerMode}`);
1189
+ }
1113
1190
  try {
1114
1191
  if (extensionOutputPath && Number.isFinite(chromeRemoteDebugPort)) {
1115
1192
  const readyPath = external_node_path_.join(external_node_path_.dirname(extensionOutputPath), 'extension-js', external_node_path_.basename(extensionOutputPath), 'ready.json');
package/dist/browsers.cjs CHANGED
@@ -229,7 +229,7 @@ var __webpack_modules__ = {
229
229
  (0, _helpers_messaging__rspack_import_3._w)(_messages__rspack_import_5.io());
230
230
  }
231
231
  } catch {
232
- const provenanceNote = _messages__rspack_import_5.xX(opts.binaryProvenance);
232
+ const provenanceNote = _messages__rspack_import_5.xX(opts.binaryProvenance, opts.browser);
233
233
  (0, _helpers_messaging__rspack_import_3._w)(_messages__rspack_import_5.io());
234
234
  (0, _helpers_messaging__rspack_import_3._w)((0, _helpers_messaging__rspack_import_3.Nr)({
235
235
  rows: [
@@ -420,6 +420,7 @@ var __webpack_modules__ = {
420
420
  var pintor__rspack_import_8 = __webpack_require__("pintor");
421
421
  var pintor__rspack_import_8_default = /*#__PURE__*/ __webpack_require__.n(pintor__rspack_import_8);
422
422
  var _helpers_messaging__rspack_import_9 = __webpack_require__("./helpers/messaging.ts");
423
+ var _browser_family__rspack_import_10 = __webpack_require__("./browsers/browsers-lib/browser-family.ts");
423
424
  const require1 = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__);
424
425
  function getLoggingPrefix(type) {
425
426
  return (0, _helpers_messaging__rspack_import_9.Pl)(type);
@@ -828,9 +829,15 @@ var __webpack_modules__ = {
828
829
  if (rest.startsWith(node_path__rspack_import_3.sep) || rest.startsWith('/')) return `~${rest}`;
829
830
  return raw;
830
831
  }
831
- function binaryProvenanceNote(provenance) {
832
- if ('pinned' === provenance) return '(pinned with --chromium-binary)';
833
- return '';
832
+ function binaryProvenanceNote(provenance, browser) {
833
+ if ('pinned' !== provenance) return '';
834
+ return `(pinned with ${pinnedBinaryFlag(browser)})`;
835
+ }
836
+ function pinnedBinaryFlag(browser) {
837
+ const name = String(browser || '');
838
+ if ((0, _browser_family__rspack_import_10.M_)(name)) return '--gecko-binary';
839
+ if ('safari' === name || 'webkit-based' === name) return '--safari-binary';
840
+ return '--chromium-binary';
834
841
  }
835
842
  function runningInDevelopment(manifest, browser, message, browserVersionLine, updateSuffix, opts) {
836
843
  const capitalize = (str)=>str.charAt(0).toUpperCase() + str.slice(1);
@@ -866,7 +873,7 @@ var __webpack_modules__ = {
866
873
  const baseBrowserLabel = (0, _helpers_messaging__rspack_import_9.A6)(String(browser || 'unknown'), resolveBrowserVersionLine(browser, browserVersionLine, {
867
874
  pinned: opts?.binaryProvenance === 'pinned'
868
875
  }));
869
- const provenanceNote = binaryProvenanceNote(opts?.binaryProvenance);
876
+ const provenanceNote = binaryProvenanceNote(opts?.binaryProvenance, browser);
870
877
  const browserLabel = provenanceNote ? `${baseBrowserLabel} ${provenanceNote}` : baseBrowserLabel;
871
878
  const cleanId = String(id || '').trim();
872
879
  const includeExtensionId = opts?.includeExtensionId !== false;
@@ -1325,17 +1332,21 @@ var __webpack_modules__ = {
1325
1332
  var _messages__rspack_import_2 = __webpack_require__("./browsers/browsers-lib/messages.ts");
1326
1333
  const FORCE_KILL_GRACE_MS = 5000;
1327
1334
  const terminatedByUs = new WeakSet();
1335
+ const terminatedPids = new Set();
1328
1336
  function wasTerminatedByUs(child) {
1329
1337
  return !!child && terminatedByUs.has(child);
1330
1338
  }
1339
+ function wasPidTerminatedByUs(pid) {
1340
+ return 'number' == typeof pid && terminatedPids.has(pid);
1341
+ }
1331
1342
  function authorLog(line) {
1332
1343
  if (line && (0, _helpers_messaging__rspack_import_1._o)()) (0, _helpers_messaging__rspack_import_1._w)(line);
1333
1344
  }
1334
- function killWindowsTree(child, sync) {
1335
- if ('win32' !== process.platform) return;
1345
+ function killWindowsTree(pid, sync) {
1346
+ if ('win32' !== process.platform || !pid) return;
1336
1347
  const args = [
1337
1348
  '/PID',
1338
- String(child.pid),
1349
+ String(pid),
1339
1350
  '/T',
1340
1351
  '/F'
1341
1352
  ];
@@ -1350,10 +1361,37 @@ var __webpack_modules__ = {
1350
1361
  }).on('error', ()=>{});
1351
1362
  } catch {}
1352
1363
  }
1364
+ function signalPid(pid, signal) {
1365
+ try {
1366
+ process.kill(pid, signal);
1367
+ return true;
1368
+ } catch {
1369
+ return false;
1370
+ }
1371
+ }
1372
+ function gracefulTerminatePid(pid, browser) {
1373
+ if (!pid || terminatedPids.has(pid)) return;
1374
+ terminatedPids.add(pid);
1375
+ killWindowsTree(pid, false);
1376
+ authorLog(_messages__rspack_import_2.XH(browser));
1377
+ signalPid(pid, 'SIGTERM');
1378
+ const killTimer = setTimeout(()=>{
1379
+ authorLog(_messages__rspack_import_2.AG(browser));
1380
+ signalPid(pid, 'SIGKILL');
1381
+ }, FORCE_KILL_GRACE_MS);
1382
+ killTimer.unref?.();
1383
+ }
1384
+ function forceKillPidOnExit(pid, browser) {
1385
+ if (!pid) return;
1386
+ terminatedPids.add(pid);
1387
+ killWindowsTree(pid, true);
1388
+ authorLog(_messages__rspack_import_2.AG(browser));
1389
+ signalPid(pid, 'SIGKILL');
1390
+ }
1353
1391
  function gracefulTerminateChild(child, browser) {
1354
1392
  if (!child || child.killed) return;
1355
1393
  terminatedByUs.add(child);
1356
- killWindowsTree(child, false);
1394
+ killWindowsTree(child.pid, false);
1357
1395
  authorLog(_messages__rspack_import_2.XH(browser));
1358
1396
  child.kill('SIGTERM');
1359
1397
  const killTimer = setTimeout(()=>{
@@ -1367,7 +1405,7 @@ var __webpack_modules__ = {
1367
1405
  function forceKillChildOnExit(child, browser) {
1368
1406
  if (!child) return;
1369
1407
  terminatedByUs.add(child);
1370
- killWindowsTree(child, true);
1408
+ killWindowsTree(child.pid, true);
1371
1409
  try {
1372
1410
  authorLog(_messages__rspack_import_2.AG(browser));
1373
1411
  child.kill('SIGKILL');
@@ -1388,7 +1426,10 @@ var __webpack_modules__ = {
1388
1426
  $Y: ()=>forceKillChildOnExit,
1389
1427
  Df: ()=>gracefulTerminateChild,
1390
1428
  FA: ()=>isBenignSocketTeardown,
1391
- Op: ()=>wasTerminatedByUs
1429
+ Op: ()=>wasTerminatedByUs,
1430
+ PU: ()=>wasPidTerminatedByUs,
1431
+ Qx: ()=>forceKillPidOnExit,
1432
+ _T: ()=>gracefulTerminatePid
1392
1433
  });
1393
1434
  },
1394
1435
  "./browsers/browsers-lib/ready-message.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
@@ -1432,6 +1473,7 @@ var __webpack_modules__ = {
1432
1473
  const profilePath = String(details?.profilePath || '').trim();
1433
1474
  if (profilePath) ready.profilePath = profilePath;
1434
1475
  if ('number' == typeof details?.browserPid && Number.isFinite(details.browserPid)) ready.browserPid = details.browserPid;
1476
+ if ('number' == typeof details?.launcherPid && Number.isFinite(details.launcherPid)) ready.launcherPid = details.launcherPid;
1435
1477
  const extensionId = String(details?.extensionId || '').trim();
1436
1478
  if (extensionId) ready.extensionId = extensionId;
1437
1479
  const binary = String(details?.binary || '').trim();
@@ -2695,6 +2737,7 @@ var __webpack_modules__ = {
2695
2737
  '--enable-features=SidePanelUpdates',
2696
2738
  '--disable-features=DisableLoadExtensionCommandLineSwitch',
2697
2739
  '--disable-features=ExtensionDisableUnsupportedDeveloper',
2740
+ '--disable-features=SafetyHubExtensionsOffStoreTrigger',
2698
2741
  '--enable-unsafe-extension-debugging',
2699
2742
  '--silent-debugger-extension-api'
2700
2743
  ];
@@ -3523,7 +3566,7 @@ var __webpack_modules__ = {
3523
3566
  binaryProvenance
3524
3567
  };
3525
3568
  if (enableCdp) {
3526
- const mod = await __webpack_require__.e(84).then(__webpack_require__.bind(__webpack_require__, "./browsers/run-chromium/chromium-launch/setup-cdp-after-launch.ts"));
3569
+ const mod = await __webpack_require__.e(597).then(__webpack_require__.bind(__webpack_require__, "./browsers/run-chromium/chromium-launch/setup-cdp-after-launch.ts"));
3527
3570
  const CDP_SETUP_TIMEOUT_MS = 45000;
3528
3571
  await Promise.race([
3529
3572
  mod.setupCdpAfterLaunch(compilation, cdpConfig, chromiumConfig, pipeStreams),
@@ -3814,6 +3857,150 @@ var __webpack_modules__ = {
3814
3857
  var process_teardown = __webpack_require__("./browsers/browsers-lib/process-teardown.ts");
3815
3858
  var ready_message = __webpack_require__("./browsers/browsers-lib/ready-message.ts");
3816
3859
  var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
3860
+ var external_node_child_process_ = __webpack_require__("node:child_process");
3861
+ const PS_ROW = /^\s*(\d+)\s+(\d+)\s+(.*)$/;
3862
+ const HELPER_PROCESS = /plugin-container|(^|\s)-contentproc(\s|$)|(^|\s)-childID(\s|$)|--type=/;
3863
+ function runQuiet(bin, args) {
3864
+ try {
3865
+ const result = (0, external_node_child_process_.spawnSync)(bin, args, {
3866
+ encoding: 'utf-8',
3867
+ stdio: [
3868
+ 'ignore',
3869
+ 'pipe',
3870
+ 'ignore'
3871
+ ],
3872
+ windowsHide: true,
3873
+ maxBuffer: 67108864
3874
+ });
3875
+ if (result.error || 0 !== result.status) return null;
3876
+ return String(result.stdout || '');
3877
+ } catch {
3878
+ return null;
3879
+ }
3880
+ }
3881
+ function parseProcessRows(output) {
3882
+ const rows = [];
3883
+ for (const line of String(output || '').split(/\r?\n/)){
3884
+ const match = PS_ROW.exec(line.replace(/\r$/, ''));
3885
+ if (!match) continue;
3886
+ const pid = Number(match[1]);
3887
+ const ppid = Number(match[2]);
3888
+ if (Number.isFinite(pid) && Number.isFinite(ppid)) rows.push({
3889
+ pid,
3890
+ ppid,
3891
+ command: match[3].trim()
3892
+ });
3893
+ }
3894
+ return rows;
3895
+ }
3896
+ function appendLinuxProfileEnv(rows) {
3897
+ return rows.map((row)=>{
3898
+ try {
3899
+ const environ = external_node_fs_.readFileSync(`/proc/${row.pid}/environ`, 'utf-8');
3900
+ const entry = environ.split('\0').find((pair)=>pair.startsWith('XRE_PROFILE_PATH='));
3901
+ return entry ? {
3902
+ ...row,
3903
+ command: `${row.command} ${entry}`
3904
+ } : row;
3905
+ } catch {
3906
+ return row;
3907
+ }
3908
+ });
3909
+ }
3910
+ function listProcesses(platform = process.platform) {
3911
+ if ('win32' === platform) {
3912
+ const script = 'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId) $($_.CommandLine)" }';
3913
+ const output = runQuiet('powershell.exe', [
3914
+ '-NoProfile',
3915
+ '-NonInteractive',
3916
+ '-Command',
3917
+ script
3918
+ ]);
3919
+ return output ? parseProcessRows(output) : [];
3920
+ }
3921
+ if ('darwin' === platform) {
3922
+ const output = runQuiet('ps', [
3923
+ '-axEo',
3924
+ 'pid=,ppid=,command='
3925
+ ]);
3926
+ return output ? parseProcessRows(output) : [];
3927
+ }
3928
+ const output = runQuiet('ps', [
3929
+ '-axo',
3930
+ 'pid=,ppid=,command='
3931
+ ]);
3932
+ const rows = output ? parseProcessRows(output) : [];
3933
+ return 'linux' === platform ? appendLinuxProfileEnv(rows) : rows;
3934
+ }
3935
+ function resolve_live_pid_normalizePath(value) {
3936
+ return String(value || '').replace(/\\/g, '/').toLowerCase();
3937
+ }
3938
+ function carriesProfile(command, profilePath) {
3939
+ const haystack = resolve_live_pid_normalizePath(command);
3940
+ const needle = resolve_live_pid_normalizePath(profilePath);
3941
+ let from = 0;
3942
+ for(;;){
3943
+ const at = haystack.indexOf(needle, from);
3944
+ if (at < 0) return false;
3945
+ const before = 0 === at ? ' ' : haystack[at - 1];
3946
+ const after = haystack[at + needle.length] ?? ' ';
3947
+ if (/[\s="']/.test(before) && /[\s"']/.test(after)) return true;
3948
+ from = at + 1;
3949
+ }
3950
+ }
3951
+ function executableOf(command) {
3952
+ const trimmed = resolve_live_pid_normalizePath(command).trim();
3953
+ if (trimmed.startsWith('"')) {
3954
+ const end = trimmed.indexOf('"', 1);
3955
+ return end > 0 ? trimmed.slice(1, end) : trimmed;
3956
+ }
3957
+ const cut = trimmed.search(/\s(-|[a-z_][a-z0-9_]*=)/);
3958
+ return cut > 0 ? trimmed.slice(0, cut) : trimmed;
3959
+ }
3960
+ function findLiveBrowserPid(input) {
3961
+ const profilePath = String(input.profilePath || '').trim();
3962
+ const binary = resolve_live_pid_normalizePath(input.binary).trim();
3963
+ if (!profilePath || !binary) return null;
3964
+ const candidates = input.rows.filter((row)=>executableOf(row.command) === binary && carriesProfile(row.command, profilePath) && !HELPER_PROCESS.test(row.command));
3965
+ if (0 === candidates.length) return null;
3966
+ const launcher = input.launcherPid ? candidates.find((row)=>row.pid === input.launcherPid) : void 0;
3967
+ if (launcher) {
3968
+ const twin = candidates.find((row)=>row.pid !== launcher.pid);
3969
+ return twin ? twin.pid : launcher.pid;
3970
+ }
3971
+ const pids = new Set(candidates.map((row)=>row.pid));
3972
+ const roots = candidates.filter((row)=>!pids.has(row.ppid));
3973
+ return (roots[0] || candidates[0]).pid;
3974
+ }
3975
+ function isPidAlive(pid) {
3976
+ if (!pid || !Number.isFinite(pid)) return false;
3977
+ try {
3978
+ process.kill(pid, 0);
3979
+ return true;
3980
+ } catch (error) {
3981
+ return error?.code === 'EPERM';
3982
+ }
3983
+ }
3984
+ async function resolveLiveBrowserPid(input) {
3985
+ const list = input.list || listProcesses;
3986
+ const attempts = Math.max(1, input.attempts ?? 6);
3987
+ const intervalMs = input.intervalMs ?? 500;
3988
+ const sleep = input.sleep || ((ms)=>new Promise((r)=>{
3989
+ setTimeout(r, ms).unref?.();
3990
+ }));
3991
+ let last = null;
3992
+ for(let attempt = 0; attempt < attempts; attempt += 1){
3993
+ last = findLiveBrowserPid({
3994
+ profilePath: input.profilePath,
3995
+ binary: input.binary,
3996
+ launcherPid: input.launcherPid,
3997
+ rows: list()
3998
+ });
3999
+ if (last && last !== input.launcherPid) break;
4000
+ if (attempt < attempts - 1) await sleep(intervalMs);
4001
+ }
4002
+ return last;
4003
+ }
3817
4004
  var runtime_options = __webpack_require__("./browsers/browsers-lib/runtime-options.ts");
3818
4005
  var shared_utils = __webpack_require__("./browsers/browsers-lib/shared-utils.ts");
3819
4006
  function parseFlatpakBinary(binary) {
@@ -4091,7 +4278,12 @@ var __webpack_modules__ = {
4091
4278
  for (const instance of activeInstances)attemptCleanup(instance);
4092
4279
  }
4093
4280
  function forceKillAllOnExit() {
4094
- for (const instance of activeInstances)(0, process_teardown.$Y)(instance.childRef(), instance.browser);
4281
+ for (const instance of activeInstances){
4282
+ const child = instance.childRef();
4283
+ const livePid = instance.livePidRef();
4284
+ (0, process_teardown.$Y)(child, instance.browser);
4285
+ if (livePid && livePid !== child?.pid) (0, process_teardown.Qx)(livePid, instance.browser);
4286
+ }
4095
4287
  }
4096
4288
  function firstBrowserLabel() {
4097
4289
  for (const instance of activeInstances)return instance.browser;
@@ -4123,10 +4315,11 @@ var __webpack_modules__ = {
4123
4315
  process.exit(1);
4124
4316
  });
4125
4317
  }
4126
- function setupFirefoxProcessHandlers(browser, childRef, cleanupInstance) {
4318
+ function setupFirefoxProcessHandlers(browser, childRef, cleanupInstance, livePidRef = ()=>null) {
4127
4319
  const instance = {
4128
4320
  browser,
4129
4321
  childRef,
4322
+ livePidRef,
4130
4323
  cleanupInstance,
4131
4324
  isCleaningUp: false
4132
4325
  };
@@ -5212,7 +5405,6 @@ var __webpack_modules__ = {
5212
5405
  } catch {}
5213
5406
  return controller;
5214
5407
  }
5215
- var external_node_child_process_ = __webpack_require__("node:child_process");
5216
5408
  var wsl_support = __webpack_require__("./browsers/browsers-lib/wsl-support.ts");
5217
5409
  const LINUX_FIREFOX_PATHS = [
5218
5410
  '/usr/bin/firefox',
@@ -5482,13 +5674,12 @@ var __webpack_modules__ = {
5482
5674
  browserPid: this.child?.pid,
5483
5675
  extensionId: this.extensionOutputPath ? (0, banner.MT)(this.extensionOutputPath) : void 0
5484
5676
  });
5485
- this.child.on('close', ()=>{
5486
- (0, shared_utils.sW)(profilePath);
5487
- });
5488
5677
  this.wireChildLifecycle();
5678
+ this.trackLiveBrowserPid();
5489
5679
  let ctrl;
5490
5680
  try {
5491
5681
  ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
5682
+ this.trackLiveBrowserPid(1);
5492
5683
  } catch (error) {
5493
5684
  const reason = error?.extensionLoadRefusedReason;
5494
5685
  if (!reason) {
@@ -5583,24 +5774,89 @@ var __webpack_modules__ = {
5583
5774
  if (process.env.VITEST || process.env.VITEST_WORKER_ID) throw new Error('Firefox startup timed out');
5584
5775
  process.exit(1);
5585
5776
  });
5586
- let disposeProcessHandlers;
5587
5777
  child.on('close', (code)=>{
5588
- if (this.watchTimeout) {
5589
- clearTimeout(this.watchTimeout);
5590
- this.watchTimeout = void 0;
5591
- }
5592
- if ((0, messaging._o)()) this.ctx.logger?.info?.(messages.Th(this.host.browser));
5593
- if (!(0, process_teardown.Op)(child)) {
5594
- 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.`);
5595
- (0, ready_stamp.Wi)(this.extensionOutputPath, code);
5596
- }
5597
- this.cleanupInstance().catch((err)=>{
5598
- if ((0, messaging._o)()) this.ctx.logger?.error?.(`[browser] Cleanup error on child close: ${err?.message || err}`);
5599
- });
5600
- disposeProcessHandlers?.();
5778
+ const expected = (0, process_teardown.Op)(child);
5779
+ if (!expected && this.adoptHandedOffBrowser()) return;
5780
+ this.onBrowserGone(code, expected);
5601
5781
  });
5602
5782
  this.pipeChildOutput(child);
5603
- disposeProcessHandlers = setupFirefoxProcessHandlers(this.host.browser, ()=>this.child, ()=>this.cleanupInstance());
5783
+ this.disposeProcessHandlers = setupFirefoxProcessHandlers(this.host.browser, ()=>this.child, ()=>this.cleanupInstance(), ()=>this.livePid);
5784
+ }
5785
+ onBrowserGone(code, expected) {
5786
+ if (this.browserGone) return;
5787
+ this.browserGone = true;
5788
+ if (this.watchTimeout) {
5789
+ clearTimeout(this.watchTimeout);
5790
+ this.watchTimeout = void 0;
5791
+ }
5792
+ if (this.liveExitWatcher) {
5793
+ clearInterval(this.liveExitWatcher);
5794
+ this.liveExitWatcher = void 0;
5795
+ }
5796
+ if (this.host.launchProfilePath) (0, shared_utils.sW)(this.host.launchProfilePath);
5797
+ if ((0, messaging._o)()) this.ctx.logger?.info?.(messages.Th(this.host.browser));
5798
+ if (!expected) {
5799
+ 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.`);
5800
+ (0, ready_stamp.Wi)(this.extensionOutputPath, code);
5801
+ }
5802
+ this.cleanupInstance().catch((err)=>{
5803
+ if ((0, messaging._o)()) this.ctx.logger?.error?.(`[browser] Cleanup error on child close: ${err?.message || err}`);
5804
+ });
5805
+ this.disposeProcessHandlers?.();
5806
+ this.disposeProcessHandlers = void 0;
5807
+ }
5808
+ async trackLiveBrowserPid(attempts) {
5809
+ const child = this.child;
5810
+ const profilePath = this.host.launchProfilePath;
5811
+ if (!child?.pid || !profilePath || this.browserGone) return;
5812
+ try {
5813
+ const pid = await resolveLiveBrowserPid({
5814
+ profilePath,
5815
+ binary: child.spawnfile,
5816
+ launcherPid: child.pid,
5817
+ attempts
5818
+ });
5819
+ if (pid && pid !== child.pid && this.child === child) this.adoptLivePid(pid);
5820
+ } catch {}
5821
+ }
5822
+ adoptHandedOffBrowser() {
5823
+ const profilePath = this.host.launchProfilePath;
5824
+ const child = this.child;
5825
+ if (!profilePath || !child || this.browserGone) return false;
5826
+ try {
5827
+ const pid = this.livePid || findLiveBrowserPid({
5828
+ profilePath,
5829
+ binary: child.spawnfile,
5830
+ launcherPid: child.pid,
5831
+ rows: listProcesses()
5832
+ });
5833
+ if (!pid || pid === this.child?.pid || !isPidAlive(pid)) return false;
5834
+ this.adoptLivePid(pid);
5835
+ return true;
5836
+ } catch {
5837
+ return false;
5838
+ }
5839
+ }
5840
+ adoptLivePid(pid) {
5841
+ if (this.livePid === pid) return;
5842
+ this.livePid = pid;
5843
+ (0, ready_stamp.M)(this.extensionOutputPath, {
5844
+ browserPid: pid,
5845
+ launcherPid: this.child?.pid
5846
+ });
5847
+ if ((0, messaging._o)()) this.ctx.logger?.info?.(`[browser] ${this.host.browser} handed the session to pid ${pid} (launcher pid ${this.child?.pid}).`);
5848
+ this.watchLivePidExit();
5849
+ }
5850
+ watchLivePidExit() {
5851
+ if (this.liveExitWatcher) clearInterval(this.liveExitWatcher);
5852
+ this.liveExitWatcher = setInterval(()=>{
5853
+ const pid = this.livePid;
5854
+ if (!pid || isPidAlive(pid)) return;
5855
+ clearInterval(this.liveExitWatcher);
5856
+ this.liveExitWatcher = void 0;
5857
+ this.onBrowserGone(null, (0, process_teardown.PU)(pid));
5858
+ }, 1000);
5859
+ this.liveExitWatcher.unref?.();
5604
5860
  }
5605
5861
  async retryAddonInstall(compilation, debugPort) {
5606
5862
  try {
@@ -5635,6 +5891,7 @@ var __webpack_modules__ = {
5635
5891
  }
5636
5892
  async cleanupInstance() {
5637
5893
  (0, process_teardown.Df)(this.child, this.host.browser);
5894
+ if (this.livePid && this.livePid !== this.child?.pid) (0, process_teardown._T)(this.livePid, this.host.browser);
5638
5895
  }
5639
5896
  scheduleWatchTimeout() {
5640
5897
  if (this.watchTimeout) return;
@@ -5669,6 +5926,10 @@ var __webpack_modules__ = {
5669
5926
  firefox_launch_define_property(this, "child", null);
5670
5927
  firefox_launch_define_property(this, "watchTimeout", void 0);
5671
5928
  firefox_launch_define_property(this, "extensionOutputPath", void 0);
5929
+ firefox_launch_define_property(this, "livePid", null);
5930
+ firefox_launch_define_property(this, "liveExitWatcher", void 0);
5931
+ firefox_launch_define_property(this, "browserGone", false);
5932
+ firefox_launch_define_property(this, "disposeProcessHandlers", void 0);
5672
5933
  this.host = host;
5673
5934
  this.ctx = ctx;
5674
5935
  }