Architecture, conventions and pitfalls of the Firefox Windows installers (stub, full, helper/uninstaller, MSI, MSIX) built with NSIS. Use when reading, writing, reviewing or debugging .nsi/.nsh files, anything under browser/installer/windows/ or toolkit/mozapps/installer/windows/nsis/, NSIS plugins in other-licenses/nsis/, or installer/uninstall telemetry.
71
86%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
The Firefox installer gets users running Firefox as quickly and reliably as possible while integrating it into the Windows system. It is built on the NSIS (Nullsoft Scriptable Install System) framework, which compiles scripts in its custom language (plus native C++ plugins) into executable installers.
We build four installer types: Stub Installer, Full Installer, MSI package (wraps the full installer), and MSIX package. There is also helper.exe (uninstaller + post-update + shortcuts).
Read references/build-and-test.md before building or testing changes,
references/telemetry.md before touching install/uninstall pings,
references/full-installer-options.md for the full installer's CLI/INI
surface, references/file-map.md to locate a script/include/doc by topic,
references/uac.md before touching elevation logic, and
references/stub-installer.md before touching the stub's HTML/CSS/JS UI or
the WebBrowser plugin.
The default installer most users see. A tiny download (200-300 KB) that downloads and runs the full installer silently. It offers no options except an optional profile cleanup prompt for returning users.
browser/installer/windows/nsis/stub.nsicreateProfileCleanup and createInstall)IWebBrowser2
control (IE-based), driven by the custom WebBrowser NSIS plugin.onInit checks system requirements, selects 32/64-bit, looks for
existing install to pave over, displays UAC prompt, initializes variables
and GUIcreateProfileCleanup optionally offers profile cleanup (same as
about:support refresh)createInstall draws the download/install UI, starts a blurb rotation
timer, runs StartDownloadStartDownload kicks off the InetBgDl plugin for background download,
starts OnDownload timer (200ms)OnDownload checks download status, retries on failure, updates progress
bar, verifies and runs the full installer when completeCheckInstall waits for the full installer to exit, deletes the fileFinishInstall copies post-signing data, waits for the app to launch
(requesting profile cleanup if selected)64-bit is selected only if: (1) 64-bit OS, (2) strictly more than 2 GB RAM, (3) no incompatible third-party software. Otherwise 32-bit.
Automatically selected based on privileges. Rejecting the UAC prompt results in a per-user installation. No UI for this choice.
Looks for existing same-channel installation with matching architecture to pave over. If none found, uses a hard-coded default based on architecture, scope, and channel.
Shown when helpful. Two variants: "paveover" (overwriting existing install) and "reinstall" (new install). Triggers the same refresh as about:support. Decision logic:
IE8 rendering mode, limited image/font formats, fixed window size, and the
ShowPage/RegisterCustomFunction/CreateTimer WebBrowser plugin API used
to drive the HTML/CSS/JS UI — see references/stub-installer.md.
The "real" installer that does the actual work. Uses a traditional wizard interface. Can be launched by the stub or standalone. Produces setup.exe (30-40 MB).
browser/installer/windows/nsis/installer.nsitoolkit/mozapps/installer/windows/nsis/common.nshinstallation_telemetry.json to the install location (read by
Firefox for telemetry) — see references/telemetry.mdreferences/telemetry.mdreferences/full-installer-options.mdContains the uninstaller, a post-update routine, and default browser/shortcut
utilities. Two main files: uninstaller.nsi (entry point + uninstall logic)
and shared.nsh (other functions).
Philosophy: remove everything the installer creates, nothing it doesn't.
precomplete file
(auto-generated list of all app files)references/telemetry.mdRuns after an update via /PostUpdate switch. Maintains system integration
objects (version numbers in registry, shortcut renaming, maintenance service
updates, DLL registration).
Key facts:
/ShowShortcuts — add application to Open With/HideShortcuts — remove application from Open With/SetAsDefaultAppGlobal — make default browser (system-wide)/SetAsDefaultAppUser — make default browser (invoked by Firefox
preferences)On Windows 10+, SetAsDefault commands only write necessary registry entries since the settings app controls defaults. ShowShortcuts/HideShortcuts are never called (SPAD control panel no longer exists).
A Windows Installer package for enterprise deployment. NOT a "true" MSI — it
wraps the full installer. The full installer runs inside the MSI just as if
launched normally. Built with WiX tools from
browser/installer/windows/msi/installer.wxs.
./mach repackage msiA full participant in the Windows modern app packaging system. Distributed, installed, updated, repaired, and uninstalled entirely via that system. Firefox's built-in updater is always disabled in MSIX. Better than MSI for Windows 10+ enterprise deployment.
./mach repackage msix--unsigned (Windows 11 only)--sign or ./mach repackage sign-msixresources.pri generated with makepri.exe from Windows SDKInstaller scripts and includes live under
browser/installer/windows/nsis/ (browser-level) and
toolkit/mozapps/installer/windows/nsis/ (toolkit-level shared utilities in
common.nsh), with NSIS plugins in other-licenses/nsis/Plugins/,
branding in browser/branding/*/branding.nsi, and docs in
browser/installer/windows/docs/. See references/file-map.md for the
full breakdown by script, include, branding, build config, localization,
plugins, UI content, repackaging, and tests.
Var VarName at the top of files, outside any section or
functionVar /GLOBAL inside sections/functions; it works but is
non-idiomatic$VarName syntax$INSTDIR (install directory), $EXEDIR
(executable directory)${DefineName} syntax!undef a define when no longer needed to prevent accidental reuse${BrandFullName} (product name), ${FileMainEXE} (main
exe name)${GetLongPath} returns the correctly formatted full path for a file$0-$9 and $R0-$R9 for temporary storage$0, then $1, $2, $3, etc. (same
for $R0, $R1, $R2, ...)$R9 for macro return values!define MacroName "!insertmacro MacroName"
shorthand so callers use ${MacroName} syntaxExch and Pop$R9) or a user
variable${If}, ${ElseIf}, ${Else}, ${EndIf}, ${Unless} are LogicLib macros
that simplify conditional logic (replacing raw IntCmp/StrCmp gotos).
Key constraint: LogicLib does NOT work recursively — a macro meant to be
used as a right-hand operand of ${If} cannot itself use ${If}
internally. For such macros, use raw IntCmp, StrCmp, or similar
comparison instructions instead.
!ifdef, !ifndef, !if, !else, !endif for compile-time
conditionalsIfFileExists for runtime file checksdefines.nsi.in uses @VARIABLE@ substitution (Mozilla build system
preprocessor)MOZ_MAINTENANCE_SERVICE, MOZ_BITS_DOWNLOAD,
MOZ_DEFAULT_BROWSER_AGENT, HAVE_64BIT_BUILD, _ARM64_${If} ${Errors} after operationsIfFileExists before file operationsClearErrors at the start of functions/macros that check the error
flagClearErrors can mask bugs if used carelessly; use with caution${MacroName} (e.g., ${SetHandlers},
${TouchStartMenuShortcut})un. prefix (e.g.,
un.CheckForFilesInUse)shared.nsh and create both prefixed and unprefixed variantsCall FunctionName or Call un.FunctionNameClearErrors before operations that may fail, then check
${If} ${Errors}SetShellVarContextSetShellVarContext all makes shell folder constants ($DESKTOP,
$SMPROGRAMS, etc.) resolve to all-users paths AND sets SHCTX to HKLM
for registry operationsSetShellVarContext current makes them resolve to current-user paths AND
sets SHCTX to HKCUSHCTX root key)SHCTXcontrol_utils.nsh provides helpers: ${SetShellVarContextToValue} and
${SwapShellVarContext}; comment style for inline comments (not # comment)Delete, not RMDir (which is for directories)All installer scripts declare RequestExecutionLevel user — they do NOT
request admin at startup. Elevation is lazy, via the UAC plugin
(${ElevateUAC}/${UnloadUAC} in common.nsh), and degrades gracefully to
HKCU if declined or unavailable. After elevation, scripts test-write to
HKLM to decide $RegHive (HKLM or HKCU) and set SetShellVarContext
accordingly. When elevated code needs to run a user-level operation (e.g.
shortcuts), it uses the dual-context pattern: detect the /UAC: flag with
${GetOptions}, and if present, call UAC::ExecCodeSegment on a
GetFunctionAddress of the user-level function instead of calling it
directly.
See references/uac.md for the full UAC plugin function reference, the
elevation flow, the dual-context code pattern, and how elevation differs
between the full installer, stub installer, and uninstaller.
6821231
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.