Extension Manager:Overview

From MozillaWiki
Jump to: navigation, search

This page gives an overview of the extension manager component focusing on the elements in nsExtensionManager.js.in.

The goals of the extension manager are:

  • To allow the user to install add-ons
  • To allow third-party applications to integrate add-ons
  • To allow the user to enable/disable/uninstall add-ons
  • To let the user know that updates are available to add-ons and to install them.
  • To tell the XUL runtime what add-ons are installed so that it can load components and chrome from them

This overview is correct as of Gecko 1.9.1 though it should go without saying that it is in some places a simplification to ease understanding and the source code is the definitive guide to specific behaviours.

Dramatis Personæ

Code Players

ExtensionManager plays the main role of nsIExtensionManager as well as the lesser roles nsIObserver and nsITimerCallback

ExtensionsDataSource is a tour de force as the middle-man between the rest of the code and the RDF based datasource holding the add-on meta-data.

DirectoryInstallLocation and WinRegInstallLocation play the lesser parts of nsIInstallLocations, the latter only appearing in the Windows performance of this piece.

InstallLocations plays a singleton role maintaining a list of known install locations.

PendingOperations, also a singleton, remembers any operation that is waiting to be performed for each add-on.

StartupCache is a singleton as well and is used to read and write the extensions.cache file.

File Players

extensions.rdf appears in the profile and holds all known meta-data about installed add-ons, drawn from their install manifests as well as subsequent update checks and disabled state.

extensions.cache also appears in the profile and remembers the last modification time of each known add-on as well as any operations waiting to be performed.

extensions.ini is in the profile as well and keeps the final list of what add-ons are enabled, allowing libxul to know where to load components and chrome from.

Character Interactions

General Overview

The extension manager is a regular xpcom component. Its job is to manage the list of installed add-ons. Every add-on is installed in an install location (represented by a nsIInstallLocation).

An install location simply contains a list of add-ons registered with it and where the add-on's files are stored. There are currently two types of install locations, the most common is the DirectoryInstallLocation which exists as a single directory in the filesystem. All registered add-ons appear inside this directory as directories named after the add-on's ID. The other type is the WinRegInstallLocation which only exists on Windows. This looks for add-on registrations in the windows registry, the add-on's files can be held anywhere on disk.

Install locations have priorities. If the same add-on (as determined by ID) appears in multiple install locations then the actual instance used is the one in the install location with the highest priority.

The install locations vary between platform, and since custom locations can be registered potentially by application too, however the normal platforms see 4 (OSX) or 5 standard install locations.

In general whenever the working set of add-ons changes (either through install, upgrade, enable, disable or uninstall) then the application must be restarted to ensure chrome and components are loaded/unloaded from the relevant add-ons. The main exception to this is for themes which aren't the currently used theme. The procedure followed is to mark the changes necessary in the startup cache and then after restart perform the necessary operations and then restart the process again to bring the changes into effect.

Platform Interactions

The XUL platform interacts with the extension manager during startup to allow any pending operations to complete and so to update the working set of add-ons. More specifically the following sequence occurs during a startup:

  1. nsAppRunner selects the profile for this run of the application and dispatches profile-after-change to the EM
  2. This initialises the EM, loads registered install locations and necessary settings from the preferences.
  3. nsAppRunner checks the command line for -install-global-extension and -install-global-extension, if either are present it calls the EM to install the items into the application install location.
  4. If the application version has changed since the last run with the current profile then nsAppRunner calls checkForMismatches so the EM can perform any necessary migration and check whether any items should be enabled or disabled due to the application version change.
  5. nsAppRunner calls start for the EM to perform any pending operations. Depending on the result of this startup will either continue or a restart will be performed.

XPInstall Interactions

XPInstall is the component used to download XPI files and if they are signed to verify that the file has not been tampered with. It is also the component that detects initial attempts to install XPI files by websites.

Installs From Content

When a new item is to be installed from content, either by a call to InstallTrigger.install or by browsing to an file with the XPI mimetype the nsXPInstallManager component is loaded to offer the new item to the user. A new instance of this component is created for each install (a group of XPI files installed with a single InstallTrigger.install call counts as a single install).

When the user agrees to the install nsXPInstallmanager checks for the presence of an already visible install progress dialog. If one doesn't exist then a dialog will be opened, passing the details of the new item to be downloaded. If a dialog is already installed then an observer notification with the topic xpinstall-download-started will be dispatched. The progress dialog is expected to have registered an observer for this topic.

The progress dialog should then call nsIExtensionManager.addDownloads. This will register the new downloads in the datasource. It will also call the nsXPInstallManager with the observer notification xpinstall-progress to tell it that the progress dialog is visible, to start the actual download and to pass progress notifications to the EM.

The EM will then receive progress notifications until the download is complete, updating the download state in the RDF.

Once download is complete nsXPInstallManager will call nsIExtensionManager.installItemFromFile to perform the actual install of the XPI file.

Upgrades

When installing updates to add-ons through the UI, and to a certain extent new items, nsIExtensionManager.addDownloads should be called with the list of items to be installed. This will create a new instance of nsXPInstallManager to download and verify the signature for the items. Once downloaded as above this will call nsIExtensionManager.installItemFromFile to install the file.

User Interactions

The user interacts with the extension manager through the add-ons manager UI. The UI is built using raw access to the underlying ExtensionsDataSource with a, RDF template.

Actions During Startup

There are a few key entry points into the extension manager during the startup of the application.

checkForMismatches

This method is called when the XUL runtime has determined that the application has been changed (either in terms of version or platform or location in the filesystem) since the last run with this profile.

The extension manager's principal goal in this method is to identify whether any of the installed add-ons should be enabled or disabled. This may happen because the version number of the application has changed so add-ons may no longer be compatible, or they may now be considered blocklisted. Any add-ons that change state will be marked to be enabled/disabled and then a following call to _finishOperations will complete the changes and a restart will be forced.

This method also has the secondary function of detecting when add-ons are installed in an install location that is no longer known about. This can happen when switching between different distributions of the same application (for example Ubuntu provides additional install locations not present in the regular Mozilla build of Firefox).

After all of these checks are complete a dialog will be displayed that performs compatibility and update checks for installed add-ons that aren't compatible with the current version of the application.

start

start is always called during the startup of an application where the extension manager is enabled. Its first task is to check to see if there have been changes to the add-ons registered with the install locations by calling _checkForFileChanges. Following this it performs any pending operations in _finishOperations.

_checkForFileChanges

The StartupCache has a list of the known add-ons and their modification time during the last run of the application. The code iterates through all the install locations. For each find all the currently registered add-ons. If an add-on isn't in the startup cache then it is marked to be installed. If an add-on is in the startup cache but has a different modification time (taken from the directory of the add-ons files) then it is marked to be updated. Finally the startup cache entries for the install location are iterated to find any add-ons that no longer exist. These are marked to be uninstalled.

_finishOperations

This method works on the list of pending operations held in PendingOperations which would have been loaded from extensions.cache. The method loops until there are no more operations pending. For each loop it:

  1. Clears any pending enable or disable operations (the update of manifests that comes later actually enforces the enable/disable)
  2. Finalises any pending upgrade which involves moving the old files out of the way and the new files into place, then loading the install manifest into the RDF datasource.
  3. Finalises any pending installs which involves moving the files into place and then loading the install manifest into the RDF datasource.
  4. Finalises any pending uninstalls which involves removing the add-on's files, removing the data from the RDF datasource then checking whether another version of this add-on exists in a lower priority install location and if so importing that add-on's install manifest into the RDF datasource.

After the looping is complete the extensions.ini, extensions.cache and extensions.rdf files are flushed to disk. Also a .autoreg file is created in the profile when necessary to indicate to the XUL runtime that components need to be reregistered. The start method indicates to the runtime when a restart is necessary to bring the changes into place.

Add-on Management Stories

These are high level descriptions of the sequence of events that is followed in order to perform a specific tasks. Much of the nitty gritty error handling is skipped.

Add-on Install

  1. Install starts with the installItemFromFile method
  2. The install manifest is parsed and the data is checked for compatibility
    1. If the add-on is not compatible with the application then a remote update check is performed.
    2. If the remote check shows the add-on is compatible then that information is stored in the RDF datasource to be ready after the restart.
  3. The add-on's XPI file is placed in a staging area in the target install location.
  4. A small amount of the information from the install manifest is added to the RDF datasource and the item is marked as needs-install.
  5. The user must then restart the application.
  6. During the restart the pending install is finalised, all the remaining install manifest data is added to the datasource and any updated compatiblity information included.
  7. The runtime restarts the process again to ensure any new components are registered.

Add-on Enable

  1. enableItem is called to enable an item.
  2. It updates the userDisabled RDF property and marks the item as needs-enable
  3. The user must then restart the application.
  4. During the restart extensions.ini is updated to add the add-on to the list of usable bundles.
  5. The runtime restarts the process again to ensure any new components are registered.

Add-on Disable

  1. disableItem is called to disable an item.
  2. It updates the userDisabled RDF property and marks the item as needs-disable
  3. The user must then restart the application.
  4. During the restart extensions.ini is updated to remove the add-on to the list of usable bundles.
  5. The runtime restarts the process again to ensure any components are unregistered.

Add-on Upgrade

Upgrade proceeds in basically the same way as an install except that during the restart the old files are moved aside before the new files are moved into place.

Add-on Uninstall

  1. uninstallItem is called to uninstall an item.
  2. It marks the item as needs-uninstall
  3. The user must then restart the application.
  4. During the restart the add-on's files are removed, the data removed from the RDF datasource and the startup cache and extensions.ini is updated to remove the add-on to the list of usable bundles.
  5. The runtime restarts the process again to ensure any components are unregistered.