CtrlK
BlogDocsLog inGet started
Tessl Logo

installer

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

Quality

86%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Firefox Windows Installer

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.


1. Architecture

1.1 Stub installer

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.

  • Main script: browser/installer/windows/nsis/stub.nsi
  • Structure is unusual for NSIS: one empty section, no predefined pages; all work is in two custom pages (createProfileCleanup and createInstall)
  • GUI is built with HTML/CSS/JS rendered via the Windows IWebBrowser2 control (IE-based), driven by the custom WebBrowser NSIS plugin

Execution flow

  1. .onInit checks system requirements, selects 32/64-bit, looks for existing install to pave over, displays UAC prompt, initializes variables and GUI
  2. createProfileCleanup optionally offers profile cleanup (same as about:support refresh)
  3. createInstall draws the download/install UI, starts a blurb rotation timer, runs StartDownload
  4. StartDownload kicks off the InetBgDl plugin for background download, starts OnDownload timer (200ms)
  5. OnDownload checks download status, retries on failure, updates progress bar, verifies and runs the full installer when complete
  6. CheckInstall waits for the full installer to exit, deletes the file
  7. FinishInstall copies post-signing data, waits for the app to launch (requesting profile cleanup if selected)
  8. Once the app is running and has shown a window, the stub sends its ping and exits

Architecture selection

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.

Install scope

Automatically selected based on privileges. Rejecting the UAC prompt results in a per-user installation. No UI for this choice.

Install location

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.

Profile cleanup

Shown when helpful. Two variants: "paveover" (overwriting existing install) and "reinstall" (new install). Triggers the same refresh as about:support. Decision logic:

  1. Find default Firefox profile; if none, skip
  2. Look for existing same-channel installation via registry
  3. Check if default profile's last-used version is 2+ versions behind current; if so, show prompt

GUI and WebBrowser plugin

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.

1.2 Full installer

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).

  • Main script: browser/installer/windows/nsis/installer.nsi
  • Heavy lifting in toolkit/mozapps/installer/windows/nsis/common.nsh
  • Writes installation_telemetry.json to the install location (read by Firefox for telemetry) — see references/telemetry.md
  • If not launched by the stub, sends an install ping on exit — see references/telemetry.md
  • Can access NSIS plugins written in C++
  • Full CLI/INI option surface: references/full-installer-options.md

1.3 Helper (helper.exe)

Contains 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).

Uninstaller

Philosophy: remove everything the installer creates, nothing it doesn't.

  • Removes all installer-created registry entries (including very old versions)
  • Deletes known application files using the precomplete file (auto-generated list of all app files)
  • Leaves the install directory if any unknown files remain
  • Runs maintenance service uninstaller if this was the only Firefox using it
  • Cancels any BITS jobs for this installation
  • Uploads the uninstall ping — see references/telemetry.md
  • Profiles and user-generated files are NOT removed

PostUpdate

Runs after an update via /PostUpdate switch. Maintains system integration objects (version numbers in registry, shortcut renaming, maintenance service updates, DLL registration).

Key facts:

  • Runs after its own code has been updated, so changes take effect immediately in the first build containing a patch
  • Runs twice: once elevated (HKLM entries) and once as regular user (HKCU entries)
  • Windows-only; cannot fix things up on other platforms

Default browser and shortcut handling

  • /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).

1.4 MSI package

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.

  • Build: ./mach repackage msi
  • Most of the WiX file passes MSI properties through as CLI arguments to the full installer
  • Cannot uninstall Firefox via standard MSI tools (limitation of the wrapper approach)

1.5 MSIX package

A 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.

  • Build: ./mach repackage msix
  • Can build unsigned packages with --unsigned (Windows 11 only)
  • Can sign locally with --sign or ./mach repackage sign-msix
  • Channel-specific paths and assets (branding-aware)
  • Langpacks included as distribution extensions in shippable builds
  • resources.pri generated with makepri.exe from Windows SDK

2. File locations

Installer 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.


3. NSIS language guide

3.1 Variables

  • Declared with Var VarName at the top of files, outside any section or function
  • Avoid Var /GLOBAL inside sections/functions; it works but is non-idiomatic
  • All variables are global, mutable, and only known at install-time
  • Access with $VarName syntax
  • Key built-in variables: $INSTDIR (install directory), $EXEDIR (executable directory)

3.2 Defines (symbols)

  • Compile-time constants, accessed with ${DefineName} syntax
  • !undef a define when no longer needed to prevent accidental reuse
  • Key defines: ${BrandFullName} (product name), ${FileMainEXE} (main exe name)
  • ${GetLongPath} returns the correctly formatted full path for a file

3.3 Registers

  • NSIS has registers $0-$9 and $R0-$R9 for temporary storage
  • Registers are global — a value set in a macro/function persists after execution
  • CRITICAL: always push registers before use and pop after, in FILO order, to avoid clobbering
  • Use registers in ascending order: $0, then $1, $2, $3, etc. (same for $R0, $R1, $R2, ...)
  • Convention: use $R9 for macro return values

3.4 Functions vs macros

  • Functions: for code invoked repeatedly; cannot take arguments directly; use slightly less disk space
  • Macros: copy-paste code at invocation site; can take arguments; beware of variable clobbering since the code is inlined
  • Prefer macros when arguments are needed
  • Convention: define a !define MacroName "!insertmacro MacroName" shorthand so callers use ${MacroName} syntax
  • Functions cannot take arguments natively; use stack Push/Pop or global variables instead

3.5 Parameter passing and return values

  • Stack-based: parameters pushed onto stack, functions use Exch and Pop
  • Push/Pop must follow FILO (First In Last Out) order
  • Return values: assign to a register (convention: $R9) or a user variable
  • Macros have no built-in return mechanism; use registers or global variables

3.6 LogicLib and conditional logic

${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.

3.7 Conditional compilation

  • !ifdef, !ifndef, !if, !else, !endif for compile-time conditionals
  • IfFileExists for runtime file checks
  • defines.nsi.in uses @VARIABLE@ substitution (Mozilla build system preprocessor)
  • Key defines: MOZ_MAINTENANCE_SERVICE, MOZ_BITS_DOWNLOAD, MOZ_DEFAULT_BROWSER_AGENT, HAVE_64BIT_BUILD, _ARM64_

3.8 Error handling

  • Check errors with ${If} ${Errors} after operations
  • Use IfFileExists before file operations
  • Call ClearErrors at the start of functions/macros that check the error flag
  • ClearErrors can mask bugs if used carelessly; use with caution
  • Registry operations should handle missing keys gracefully
  • Common pattern: try HKLM, check errors, fall back to HKCU

4. Codebase conventions

4.1 Naming

  • Standard macros: ${MacroName} (e.g., ${SetHandlers}, ${TouchStartMenuShortcut})
  • Uninstaller-only variants MUST use the un. prefix (e.g., un.CheckForFilesInUse)
  • If a macro is used in both installer and uninstaller, place it in shared.nsh and create both prefixed and unprefixed variants
  • Functions are called with Call FunctionName or Call un.FunctionName

4.2 Registry operations

  • Two main root keys: HKLM (system-wide, admin) and HKCU (current user only)
  • NSIS auto-creates parent keys when writing subkeys/values
  • Common value types: REG_SZ (string) and REG_DWORD (32-bit integer)
  • Write default value of a key by using empty string as value name
  • HKLM writes may fail in unelevated installs; always write fallback code for HKCU
  • Use ClearErrors before operations that may fail, then check ${If} ${Errors}
  • Prefer commands that take an explicit root key argument over relying on SetShellVarContext

4.3 SetShellVarContext

  • SetShellVarContext all makes shell folder constants ($DESKTOP, $SMPROGRAMS, etc.) resolve to all-users paths AND sets SHCTX to HKLM for registry operations
  • SetShellVarContext current makes them resolve to current-user paths AND sets SHCTX to HKCU
  • It affects BOTH file system paths and registry operations (via the SHCTX root key)
  • Misreading or forgetting to restore the context is a common source of bugs
  • When possible, favor registry commands that explicitly take a root key (HKLM/HKCU) over relying on SHCTX
  • If you must use it, always restore it to the expected value after your operation
  • control_utils.nsh provides helpers: ${SetShellVarContextToValue} and ${SwapShellVarContext}

4.4 Code style

  • Keep lines reasonable in length
  • Use ; comment style for inline comments (not # comment)
  • Section organization follows: PreInit -> Init -> InstallTypes -> Pages -> Sections -> Functions
  • Delete files with Delete, not RMDir (which is for directories)

5. UAC and elevation

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.

Repository
mozilla/firefox-aidev-plugins
Last updated
First committed

Is this your skill?

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.