Written April 2018
In-App Update Process¶
This document details how Firefox checks for and installs updates. The focus is how it does this in the Windows Operating System. This was written in preparation for creating a Background Update Agent, and therefore does not cover that update mechanism.
Definitions¶
MAR file¶
An archive format containing update data. They are signed to prevent being tampered with.
Partial MAR file¶
A MAR file containing a binary diff between the installed and updated versions of Firefox. This is the preferred update mechanism because the file sizes are much smaller.
Complete MAR file¶
A MAR file containing a full copy of the application. This is typically used if updating from a partial MAR has failed or if the user is updating from such an old version that no partial MAR exists to upgrade directly from that version to the current version.
Update directory¶
Directory where update files are saved. Location is platform-specific, but can be determined with this code:
Services.dirsvc.get("UpdRootD", Ci.nsIFile);
Update status file¶
A file written to and read from, in part, to communicate between Firefox and the updater. The file will contain a short string indicating the current status. If that status is “failed”, it will also contain an error code. A list of recognized status strings can be found here. The error codes used for failure status can be found here.
Documentation¶
Detailed Update Steps¶
An update check is initiated shortly after Firefox launches. First, an update XML document is retrieved by an internal XHR request. The XML is saved in the update directory. Some very basic sanity checks are performed to ensure that this file is valid and describes an update that should be applied.
The URL used to download the update XML requires some variable substitution and communicates to the server information about the system and the currently installed version of Firefox:
https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%(noBug1296630v1)(nowebsense)/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml
On my system, this becomes:https://aus5.mozilla.org/update/6/Firefox/61.0a1/20180419100148/WINNT_x86_64-msvc-x64/en-US/nightly/Windows_NT 10.0.0.0.16299.371 (x64)(noBug1296630v1)(nowebsense)/ISET:SSE4_2,MEM:32676/default/default/update.xml
Balrog uses this information to determine what XML data to send in response. Common criteria used in making this decision include:
Is the system capable of using a 64-bit MAR? Does it have enough RAM to make it “worth it”?
Does the system support processor features that are required for newer versions of Firefox?
Are updates currently being throttled? If so, Balrog may indicate to some clients that there is not an update yet. Note that manual checks for update will add a “force” query parameter which will force Balrog to report any updates, regardless of throttling.
Balrog will send an XML document in response. If an update is available, it will contain the URL for a complete MAR. If a partial MAR is available, the URL for that will be contained as well.
If an update is available, Firefox downloads the update MAR file.
Currently, downloads are performed using nsIIncrementalDownloader, but ideally we would use a less-special restartable downloader (Bug 1348087)
Files are stored in the “update directory” (see Definitions, above)
If Firefox is closed before download completes, the download resumes the next time Firefox launches.
During this time, the update status file will contain “downloading”
When the download completes, the MAR file stays in the update directory. The signature/file integrity is not yet verified.
The action taken now differs depending on whether update staging is enabled (controlled by the pref:
app.update.staging.enabled
). Staging is currently enabled by default in all cases.If staging is enabled:
Firefox runs the updater, which copies the install files into a directory within the installation and updates them
If privilege elevation will be needed and the Mozilla Maintenance Service is available, the update status file will now contain “applied-service”
Otherwise, the update status file will now contain “applied”
If staging is disabled:
If privilege elevation will not be needed to install the update, the update status file will now contain “pending”
If privilege elevation will be needed and the Mozilla Maintenance Service is available, the update status file will now contain “pending-service”
If privilege elevation will be needed and the Mozilla Maintenance Service is not available, the update status file will now contain either “pending” (on Windows) or “pending-elevate” (on Mac).
Update ping sent with reason=”ready”.
Firefox waits to be closed so that the update can be installed.
If the user keeps the browser open for more than 4 days, a green arrow will be displayed on the menu icon, prompting the user to restart to update.
If the user keeps the browser open for 8 days, a doorhanger will prompt for a restart.
(Note: These are the delays for the release channel, the prefs that control this have different values on other channels. On Nightly, for example, the green arrow has no delay and the doorhanger delay is 12 hours)
When Firefox next launches, it reads the update status file very early in startup. Upon discovering one of the “pending” statuses, it launches the updater and exits. This updater process will be running without privilege elevation.
The updater reads the update status file and, if the status was a “pending” status, it changes the status to “applying”.
If the status read was “pending-service” or “applied-service”, the updater contacts the Mozilla Maintenance Service and requests that a privileged updater be started.
This process is retried in the event of failure, but the updater will continue if multiple attempts all result in failure
The original updater waits for the privileged updater process to exit
If the status read was “pending-elevate” or “applied”, or if the updater was unable to get a privileged updater from the Mozilla Maintenance Service, a new process is started with elevated privileges. This causes a UAC prompt to display. The original updater waits for the privileged updater to exit.
If the UAC prompt was declined, the updater writes an error state and exits.
When Firefox starts, it returns the status to a “pending” state, and then it returns to step 5
If this process repeats 2 times, we prompt the user to download the Firefox installer in order to update (but Firefox returns to step 5 nevertheless)
The update is now attempted. If the status was one of the “pending” statuses, this will involve patching or overwriting installation files with data from the MAR file. If the status was one of the “applied” statuses, this will involve overwriting files in the installation directory with the patched (staged) files in the update directory.
If a privileged updater was not needed, the original updater process attempts to install the update.
If a privileged updater was started, it attempts to install the update. Afterwards it exits and the original, unprivileged updater process resumes
The updater writes the status to the update status file (either “succeeded” or “failed”), starts Firefox, and exits.
Firefox starts and reads the update status file.
On update success:
An update ping (with reason=”success”) is sent
Firefox reads the XML file out of the update directory and, if it indicates that a particular URL should be shown after that update, it does so.
On update failure, Firefox checks whether this was a partial MAR or a complete MAR by checking the “selected” attribute in the XML.
If it was a partial MAR, Firefox downloads the complete MAR and retries the update (i.e. goes back to step 2, but downloads a different MAR)
If it was a complete MAR, Firefox notifies the user of the failure and gives them a link to the current version.