wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import {promisify} from 'node:util';
import {execFile} from 'node:child_process';
 
const execFileAsync = promisify(execFile);
 
// TODO: Fix the casing of bundle identifiers in the next major version.
 
// Windows doesn't have browser IDs in the same way macOS/Linux does so we give fake
// ones that look real and match the macOS/Linux versions for cross-platform apps.
const windowsBrowserProgIds = {
    MSEdgeHTM: {name: 'Edge', id: 'com.microsoft.edge'}, // The missing `L` is correct.
    MSEdgeBHTML: {name: 'Edge Beta', id: 'com.microsoft.edge.beta'},
    MSEdgeDHTML: {name: 'Edge Dev', id: 'com.microsoft.edge.dev'},
    AppXq0fevzme2pys62n3e0fbqa7peapykr8v: {name: 'Edge', id: 'com.microsoft.edge.old'},
    ChromeHTML: {name: 'Chrome', id: 'com.google.chrome'},
    ChromeBHTML: {name: 'Chrome Beta', id: 'com.google.chrome.beta'},
    ChromeDHTML: {name: 'Chrome Dev', id: 'com.google.chrome.dev'},
    ChromiumHTM: {name: 'Chromium', id: 'org.chromium.Chromium'},
    BraveHTML: {name: 'Brave', id: 'com.brave.Browser'},
    BraveBHTML: {name: 'Brave Beta', id: 'com.brave.Browser.beta'},
    BraveDHTML: {name: 'Brave Dev', id: 'com.brave.Browser.dev'},
    BraveSSHTM: {name: 'Brave Nightly', id: 'com.brave.Browser.nightly'},
    FirefoxURL: {name: 'Firefox', id: 'org.mozilla.firefox'},
    OperaStable: {name: 'Opera', id: 'com.operasoftware.Opera'},
    VivaldiHTM: {name: 'Vivaldi', id: 'com.vivaldi.Vivaldi'},
    'IE.HTTP': {name: 'Internet Explorer', id: 'com.microsoft.ie'},
};
 
export const _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
 
export class UnknownBrowserError extends Error {}
 
export default async function defaultBrowser(_execFileAsync = execFileAsync) {
    const {stdout} = await _execFileAsync('reg', [
        'QUERY',
        ' HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice',
        '/v',
        'ProgId',
    ]);
 
    const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
    if (!match) {
        throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
    }
 
    const {id} = match.groups;
 
    // Windows can append a hash suffix to ProgIds using a dot or hyphen
    // (e.g., `ChromeHTML.ABC123`, `FirefoxURL-6F193CCC56814779`).
    // Try exact match first, then try without the suffix.
    const dotIndex = id.lastIndexOf('.');
    const hyphenIndex = id.lastIndexOf('-');
    const baseIdByDot = dotIndex === -1 ? undefined : id.slice(0, dotIndex);
    const baseIdByHyphen = hyphenIndex === -1 ? undefined : id.slice(0, hyphenIndex);
 
    return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? {name: id, id};
}