Skip to content

Commit 400345d

Browse files
committed
Make two-step Firefox startup work on Windows
1 parent 279223b commit 400345d

File tree

2 files changed

+31
-9
lines changed

2 files changed

+31
-9
lines changed

src/interceptors/fresh-firefox.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { HtkConfig } from '../config';
88

99
import { getAvailableBrowsers, launchBrowser, BrowserInstance } from '../browsers';
1010
import { CertCheckServer } from '../cert-check-server';
11-
import { delay } from '../util';
11+
import { delay, windowsKill } from '../util';
1212
import { Interceptor } from '.';
1313
import { reportError } from '../error-tracking';
1414

@@ -50,7 +50,9 @@ export class FreshFirefox implements Interceptor {
5050
proxyPort?: number,
5151
existingPrefs = {}
5252
) {
53-
const browser = await launchBrowser(certCheckServer.checkCertUrl, {
53+
const initialUrl = certCheckServer.checkCertUrl;
54+
55+
const browser = await launchBrowser(initialUrl, {
5456
browser: 'firefox',
5557
profile: this.firefoxProfilePath,
5658
proxy: proxyPort ? `127.0.0.1:${proxyPort}` : undefined,
@@ -120,6 +122,17 @@ export class FreshFirefox implements Interceptor {
120122
if (browser.process.stdout) browser.process.stdout.pipe(process.stdout);
121123
if (browser.process.stderr) browser.process.stderr.pipe(process.stderr);
122124

125+
const normalStop = browser.stop.bind(browser);
126+
browser.stop = async function () {
127+
if (process.platform === "win32") {
128+
// Firefox spawns a child process on Windows, and doesn't let us kill it at all.
129+
// To fix this, we kill all firefox instances that were started with this exact same URL.
130+
await windowsKill(`CommandLine Like '%\\\\firefox.exe%${initialUrl}'`);
131+
} else {
132+
normalStop();
133+
}
134+
};
135+
123136
return browser;
124137
}
125138

@@ -160,14 +173,11 @@ export class FreshFirefox implements Interceptor {
160173

161174
let existingPrefs: _.Dictionary<any> = {};
162175

163-
if (await canAccess(firefoxPrefsFile) === false && process.platform !== 'win32') {
176+
if (await canAccess(firefoxPrefsFile) === false) {
164177
/*
165178
First time, we do a separate pre-usage startup & stop, without the proxy, for certificate setup.
166179
This helps avoid initial Firefox profile setup request noise, and tidies up some awkward UX where
167180
firefox likes to open extra welcome windows/tabs on first run.
168-
169-
Unfortunately, Windows doesn't seem to play nicely with this (because it spawns separate child
170-
processes that we can't reliable kill), so we have to fall back to normal behaviour in that case.
171181
*/
172182
await this.setupFirefoxProfile();
173183
}
@@ -215,8 +225,8 @@ export class FreshFirefox implements Interceptor {
215225
async deactivate(proxyPort: number | string) {
216226
if (this.isActive(proxyPort)) {
217227
const browser = browsers[proxyPort];
218-
const closePromise = new Promise((resolve) => browser!.process.once('close', resolve));
219-
browser!.stop();
228+
const closePromise = new Promise((resolve) => browser.process.once('close', resolve));
229+
browser.stop();
220230
await closePromise;
221231
}
222232
}

src/util.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { spawn } from 'child_process';
2+
13
export function delay(durationMs: number): Promise<void> {
24
return new Promise((resolve) => setTimeout(resolve, durationMs));
35
}
@@ -20,4 +22,14 @@ export const ALLOWED_ORIGINS = IS_PROD_BUILD
2022
/^https?:\/\/localhost(:\d+)?$/,
2123
/^http:\/\/local\.httptoolkit\.tech(:\d+)?$/,
2224
/^https:\/\/app\.httptoolkit\.tech$/
23-
]
25+
]
26+
27+
export async function windowsKill(processMatcher: string) {
28+
await spawn('wmic', [
29+
'Path', 'win32_process',
30+
'Where', processMatcher,
31+
'Call', 'Terminate'
32+
], {
33+
stdio: 'inherit'
34+
});
35+
}

0 commit comments

Comments
 (0)