Themes werden nicht installiert!

  • Themes werden nicht installiert,es kommt folgende Fehlermeldung:
    Firefox konnte das Add-on von folgender Adresse nicht installieren:

    http://releases.mozilla.org/pub/mozilla.or…x-1.8.49-fx.jar

    Grund: Bei der Installation ist ein unbekannter Fehler aufgetreten.
    Schauen Sie in der Fehlerkonsole für weitere Informationen nach.
    -203


    Dies ist der Fehlercode:

    /**
    * Write the Extensions List
    */
    _updateExtensionsManifest: function() {
    // When an operation is performed that requires a component re-registration
    // (extension enabled/disabled, installed, uninstalled), we must write the
    // set of paths where extensions live so that the startup system can determine
    // where additional components, preferences, chrome manifests etc live.
    //
    // To do this we obtain a list of active extensions and themes and write
    // these to the extensions.ini file in the profile directory.
    var validExtensions = this._getActiveItems(Ci.nsIUpdateItem.TYPE_ANY -
    Ci.nsIUpdateItem.TYPE_THEME);
    var validThemes = this._getActiveItems(Ci.nsIUpdateItem.TYPE_THEME);

    var extensionsLocationsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSION_MANIFEST]);
    var fos = openSafeFileOutputStream(extensionsLocationsFile);

    var enabledItems = [];
    var extensionSectionHeader = "[ExtensionDirs]\r\n";
    fos.write(extensionSectionHeader, extensionSectionHeader.length);
    for (var i = 0; i < validExtensions.length; ++i) {
    var e = validExtensions[i];
    var itemLocation = e.location.getItemLocation(e.id).QueryInterface(Ci.nsILocalFile);
    var descriptor = getAbsoluteDescriptor(itemLocation);
    var line = "Extension" + i + "=" + descriptor + "\r\n";
    fos.write(line, line.length);
    enabledItems.push(e.id + ":" + e.version);
    }

    var themeSectionHeader = "[ThemeDirs]\r\n";
    fos.write(themeSectionHeader, themeSectionHeader.length);
    for (i = 0; i < validThemes.length; ++i) {
    var e = validThemes[i];
    var itemLocation = e.location.getItemLocation(e.id).QueryInterface(Ci.nsILocalFile);
    var descriptor = getAbsoluteDescriptor(itemLocation);
    var line = "Extension" + i + "=" + descriptor + "\r\n";
    fos.write(line, line.length);
    enabledItems.push(e.id + ":" + e.version);
    }

    closeSafeFileOutputStream(fos);

    // Cache the enabled list for annotating the crash report subsequently
    gPref.setCharPref(PREF_EM_ENABLED_ITEMS, enabledItems.join(","));
    },

    /**
    * Say whether or not the Extension List has changed (and thus whether or not
    * the system will have to restart the next time it is started).
    * Param val
    * true if the Extension List has changed, false otherwise.
    * @returns |val|
    */
    set _extensionListChanged(val) {
    // When an extension has an operation perform on it (e.g. install, upgrade,
    // disable, etc.) we are responsible for creating the .autoreg file and
    // nsAppRunner is responsible for removing it on restart. At some point it
    // may make sense to be able to cancel a registration but for now we only
    // create the file.
    try {
    var autoregFile = getFile(KEY_PROFILEDIR, [FILE_AUTOREG]);
    if (val && !autoregFile.exists())
    autoregFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
    }
    catch (e) {
    }
    return val;
    },

    /**
    * Gathers data about an item specified by the supplied Install Manifest
    * and determines whether or not it can be installed as-is. It makes this
    * determination by validating the item's GUID, Version, and determining
    * if it is compatible with this application.
    * Param installManifest
    * A nsIRDFDataSource representing the Install Manifest of the
    * item to be installed.
    * @return A JS Object with the following properties:
    * "id" The GUID of the Item being installed.
    * "version" The Version string of the Item being installed.
    * "name" The Name of the Item being installed.
    * "type" The nsIUpdateItem type of the Item being installed.
    * "targetApps" An array of TargetApplication Info Objects
    * with "id", "minVersion" and "maxVersion" properties,
    * representing applications targeted by this item.
    * "error" The result code:
    * INSTALLERROR_SUCCESS
    * no error, item can be installed
    * INSTALLERROR_INVALID_GUID
    * error, GUID is not well-formed
    * INSTALLERROR_INVALID_VERSION
    * error, Version is not well-formed
    * INSTALLERROR_INCOMPATIBLE_VERSION
    * error, item is not compatible with this version
    * of the application.
    * INSTALLERROR_INCOMPATIBLE_PLATFORM
    * error, item is not compatible with the operating
    * system or ABI the application was built for.
    * INSTALLERROR_INSECURE_UPDATE
    * error, item has no secure method of providing updates
    * INSTALLERROR_BLOCKLISTED
    * error, item is blocklisted
    */
    _getInstallData: function(installManifest) {
    var installData = { id : "",
    version : "",
    name : "",
    type : 0,
    error : INSTALLERROR_SUCCESS,
    targetApps : [],
    updateURL : "",
    updateKey : "",
    currentApp : null };

    // Fetch properties from the Install Manifest
    installData.id = getManifestProperty(installManifest, "id");
    installData.version = getManifestProperty(installManifest, "version");
    installData.name = getManifestProperty(installManifest, "name");
    installData.type = getAddonTypeFromInstallManifest(installManifest);
    installData.updateURL= getManifestProperty(installManifest, "updateURL");
    installData.updateKey= getManifestProperty(installManifest, "updateKey");

    /**
    * Reads a property off a Target Application resource
    * Param resource
    * The RDF Resource for a Target Application
    * Param property
    * The property (less EM_NS) to read
    * @returns The string literal value of the property.
    */
    function readTAProperty(resource, property) {
    return stringData(installManifest.GetTarget(resource, EM_R(property), true));
    }

    var targetApps = installManifest.GetTargets(gInstallManifestRoot,
    EM_R("targetApplication"),
    true);
    while (targetApps.hasMoreElements()) {
    var targetApp = targetApps.getNext();
    if (targetApp instanceof Ci.nsIRDFResource) {
    try {
    var data = { id : readTAProperty(targetApp, "id"),
    minVersion: readTAProperty(targetApp, "minVersion"),
    maxVersion: readTAProperty(targetApp, "maxVersion") };
    installData.targetApps.push(data);
    if ((data.id == gApp.ID) ||
    (data.id == TOOLKIT_ID) && !installData.currentApp)
    installData.currentApp = data;
    }
    catch (e) {
    continue;
    }
    }
    }

    // If the item specifies one or more target platforms, make sure our OS/ABI
    // combination is in the list - otherwise, refuse to install the item.
    var targetPlatforms = null;
    try {
    targetPlatforms = installManifest.GetTargets(gInstallManifestRoot,
    EM_R("targetPlatform"),
    true);
    } catch(e) {
    // No targetPlatform nodes, continue.
    }
    if (targetPlatforms != null && targetPlatforms.hasMoreElements()) {
    var foundMatchingOS = false;
    var foundMatchingOSAndABI = false;
    var requireABICompatibility = false;
    while (targetPlatforms.hasMoreElements()) {
    var targetPlatform = stringData(targetPlatforms.getNext());
    var os = targetPlatform.split("_")[0];
    var index = targetPlatform.indexOf("_");
    var abi = index != -1 ? targetPlatform.substr(index + 1) : null;
    if (os == gOSTarget) {
    foundMatchingOS = true;
    // The presence of any ABI part after our OS means ABI is important.
    if (abi != null) {
    requireABICompatibility = true;
    // If we don't know our ABI, we can't be compatible
    if (abi == gXPCOMABI && abi != UNKNOWN_XPCOM_ABI) {
    foundMatchingOSAndABI = true;
    break;
    }
    }
    }
    }
    if (!foundMatchingOS || (requireABICompatibility && !foundMatchingOSAndABI)) {
    installData.error = INSTALLERROR_INCOMPATIBLE_PLATFORM;
    return installData;
    }
    }

    // Validate the Item ID
    if (!gIDTest.test(installData.id)) {
    installData.error = INSTALLERROR_INVALID_GUID;
    return installData;
    }

    // Check that the add-on provides a secure update method.
    if (gCheckUpdateSecurity &&
    installData.updateURL &&
    installData.updateURL.substring(0, 6) != "https:" &&
    !installData.updateKey) {
    installData.error = INSTALLERROR_INSECURE_UPDATE;
    return installData;
    }

    // Check that the target application range allows compatibility with the app
    if (gCheckCompatibility &&
    !this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined)) {
    installData.error = INSTALLERROR_INCOMPATIBLE_VERSION;
    return installData;
    }

    // Check if the item is blocklisted.
    if (!gBlocklist)
    gBlocklist = Cc["@http://mozilla.org/extensions/blocklist;1"].
    getService(Ci.nsIBlocklistService);
    if (gBlocklist.isAddonBlocklisted(installData.id, installData.version,
    null, null))
    installData.error = INSTALLERROR_BLOCKLISTED;

    return installData;
    },

    /**
    * Installs an item from a XPI/JAR file.
    * This is the main entry point into the Install system from outside code
    * (e.g. XPInstall).
    * Param aXPIFile
    * The file to install from.
    * Param aInstallLocationKey
    * The name of the Install Location where this item should be
    * installed.
    */
    installItemFromFile: function(xpiFile, installLocationKey) {
    this.installItemFromFileInternal(xpiFile, installLocationKey, null);

    // If there are no compatibility checks running and no downloads in
    // progress then the install operations are complete.
    if (this._compatibilityCheckCount == 0 && this._transactions.length == 0) {
    for (var i = 0; i < this._installListeners.length; ++i)
    this._installListeners[i].onInstallsCompleted();
    }
    },

    /**
    * Installs an item from a XPI/JAR file.
    * Param aXPIFile
    * The file to install from.
    * Param aInstallLocationKey
    * The name of the Install Location where this item should be
    * installed.
    * Param aInstallManifest
    * An updated Install Manifest from the Version Update check.
    * Can be null when invoked from callers other than the Version
    * Update check.
    */
    installItemFromFileInternal: function(aXPIFile, aInstallLocationKey, aInstallManifest) {
    var em = this;
    /**
    * Gets the Install Location for an Item.
    * Param itemID
    * The GUID of the item to find an Install Location for.
    * @return An object implementing nsIInstallLocation which represents the
    * location where the specified item should be installed.
    * This can be:
    * 1. an object that corresponds to the location key supplied to
    * |installItemFromFileInternal|,
    * 2. the default install location (the App Profile Extensions Folder)
    * if no location key was supplied, or the location key supplied
    * was not in the set of registered locations
    * 3. null, if the location selected by 1 or 2 above does not support
    * installs from XPI/JAR files, or that location is not writable
    * with the current access privileges.
    */
    function getInstallLocation(itemID) {
    // Here I use "upgrade" to mean "install a different version of an item".
    var installLocation = em.getInstallLocation(itemID);
    if (!installLocation) {
    // This is not an "upgrade", since we don't have any location data for the
    // extension ID specified - that is, it's not in our database.

    // Caller supplied a key to a registered location, use that location
    // for the installation
    installLocation = InstallLocations.get(aInstallLocationKey);
    if (installLocation) {
    // If the specified location does not have a common metadata location
    // (e.g. extensions have no common root, or other location specified
    // by the location implementation) - e.g. for a Registry Key enumeration
    // location - we cannot install or upgrade using a XPI file, probably
    // because these application types will be handling upgrading themselves.
    // Just bail.
    if (!installLocation.location) {
    LOG("Install Location \"" + installLocation.name + "\" does not support " +
    "installation of items from XPI/JAR files. You must manage " +
    "installation and update of these items yourself.");
    installLocation = null;
    }
    }
    else {
    // In the absence of a preferred install location, just default to
    // the App-Profile
    installLocation = InstallLocations.get(KEY_APP_PROFILE);
    }
    }
    else {
    // This is an "upgrade", but not through the Update System, because the
    // Update code will not let an extension with an incompatible target
    // app version range through to this point. This is an "upgrade" in the
    // sense that the user found a different version of an installed extension
    // and installed it through the web interface, so we have metadata.

    // If the location is different, return the preferred location rather than
    // the location of the currently installed version, because we may be in
    // the situation where an item is being installed into the global app
    // dir when there's a version in the profile dir.
    if (installLocation.name != aInstallLocationKey)
    installLocation = InstallLocations.get(aInstallLocationKey);
    }
    if (!installLocation.canAccess) {
    LOG("Install Location\"" + installLocation.name + "\" cannot be written " +
    "to with your access privileges. Installation will not proceed.");
    installLocation = null;
    }
    return installLocation;
    }

    /**
    * Stages a XPI file in the default item location specified by other
    * applications when they registered with XulRunner if the item's
    * install manifest specified compatibility with them.
    */
    function stageXPIForOtherApps(xpiFile, installData) {
    for (var i = 0; i < installData.targetApps.length; ++i) {
    var targetApp = installData.targetApps[i];
    if (targetApp.id != gApp.ID && targetApp.id != TOOLKIT_ID) {
    /* XXXben uncomment when this works!
    var settingsThingy = Cc[].
    getService(Ci.nsIXULRunnerSettingsThingy);
    try {
    var appPrefix = "SOFTWARE\\Mozilla\\XULRunner\\Applications\\";
    var branch = settingsThingy.getBranch(appPrefix + targetApp.id);
    var path = branch.getProperty("ExtensionsLocation");
    var destination = Cc["@http://mozilla.org/file/local;1"].
    createInstance(Ci.nsILocalFile);
    destination.initWithPath(path);
    xpiFile.copyTo(file, xpiFile.leafName);
    }
    catch (e) {
    }
    */
    }
    }
    }

    /**
    * Extracts and then starts the install for extensions / themes contained
    * within a xpi.
    */
    function installMultiXPI(xpiFile, installData) {
    var fileURL = getURIFromFile(xpiFile).QueryInterface(Ci.nsIURL);
    if (fileURL.fileExtension.toLowerCase() != "xpi") {
    LOG("Invalid File Extension: Item: \"" + fileURL.fileName + "\" has an " +
    "invalid file extension. Only xpi file extensions are allowed for " +
    "multiple item packages.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("invalidFileExtTitle", [],
    "invalidFileExtMessage", [installData.name,
    fileURL.fileExtension,
    bundle.GetStringFromName("type-" + installData.type)]);
    return;
    }

    try {
    var zipReader = getZipReaderForFile(xpiFile);
    }
    catch (e) {
    LOG("installMultiXPI: failed to open xpi file: " + xpiFile.path);
    throw e;
    }

    var searchForEntries = ["*.xpi", "*.jar"];
    var files = [];
    for (var i = 0; i < searchForEntries.length; ++i) {
    var entries = zipReader.findEntries(searchForEntries[i]);
    while (entries.hasMore()) {
    var entryName = entries.getNext();
    var target = getFile(KEY_TEMPDIR, [entryName]);
    try {
    target.createUnique(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
    }
    catch (e) {
    LOG("installMultiXPI: failed to create target file for extraction " +
    " file = " + target.path + ", exception = " + e + "\n");
    }
    zipReader.extract(entryName, target);
    files.push(target);
    }
    }
    zipReader.close();

    if (files.length == 0) {
    LOG("Multiple Item Package: Item: \"" + fileURL.fileName + "\" does " +
    "not contain a valid package to install.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("missingPackageFilesTitle",
    [bundle.GetStringFromName("type-" + installData.type)],
    "missingPackageFilesMessage", [installData.name,
    bundle.GetStringFromName("type-" + installData.type)]);
    return;
    }

    for (i = 0; i < files.length; ++i) {
    em.installItemFromFileInternal(files[i], aInstallLocationKey, null);
    files[i].remove(false);
    }
    }

    /**
    * An observer for the Extension Update System.
    * @constructor
    */
    function IncompatibleObserver() {}
    IncompatibleObserver.prototype = {
    _xpi: null,
    _installManifest: null,

    /**
    * Ask the Extension Update System if there are any version updates for
    * this item that will allow it to be compatible with this version of
    * the Application.
    * Param item
    * An nsIUpdateItem representing the item being installed.
    * Param installManifest
    * The Install Manifest datasource for the item.
    * Param xpiFile
    * The staged source XPI file that contains the item. Cleaned
    * up by this process.
    * Param installRDF
    * The install.rdf file that was extracted from the xpi.
    */
    checkForUpdates: function(item, installManifest, xpiFile) {
    this._xpi = xpiFile;
    this._installManifest = installManifest;

    for (var i = 0; i < em._installListeners.length; ++i)
    em._installListeners[i].onCompatibilityCheckStarted(item);
    em._compatibilityCheckCount++;
    em.update([item], 1, Ci.nsIExtensionManager.UPDATE_CHECK_COMPATIBILITY, this);
    },

    /**
    * See nsIExtensionManager.idl
    */
    onUpdateStarted: function() {
    LOG("Phone Home Listener: Update Started");
    },

    /**
    * See nsIExtensionManager.idl
    */
    onUpdateEnded: function() {
    LOG("Phone Home Listener: Update Ended");
    },

    /**
    * See nsIExtensionManager.idl
    */
    onAddonUpdateStarted: function(addon) {
    if (!addon)
    throw Cr.NS_ERROR_INVALID_ARG;

    LOG("Phone Home Listener: Update For " + addon.id + " started");
    em.datasource.addIncompatibleUpdateItem(addon.name, this._xpi.path,
    addon.type, addon.version);
    },

    /**
    * See nsIExtensionManager.idl
    */
    onAddonUpdateEnded: function(addon, status) {
    if (!addon)
    throw Cr.NS_ERROR_INVALID_ARG;

    LOG("Phone Home Listener: Update For " + addon.id + " ended, status = " + status);
    em.datasource.removeDownload(this._xpi.path);
    LOG("Version Check Phone Home Completed");

    for (var i = 0; i < em._installListeners.length; ++i)
    em._installListeners[i].onCompatibilityCheckEnded(addon, status);

    // Only compatibility updates (e.g. STATUS_VERSIONINFO) are currently
    // supported
    if (status == Ci.nsIAddonUpdateCheckListener.STATUS_VERSIONINFO) {
    em.datasource.setTargetApplicationInfo(addon.id,
    addon.targetAppID,
    addon.minAppVersion,
    addon.maxAppVersion,
    this._installManifest);

    // Try and install again, but use the updated compatibility DB.
    // This will send out an apropriate onInstallEnded notification for us.
    em.installItemFromFileInternal(this._xpi, aInstallLocationKey,
    this._installManifest);

    // Add the updated compatibility info to the datasource if done
    if (StartupCache.entries[aInstallLocationKey][addon.id].op == OP_NONE) {
    em.datasource.setTargetApplicationInfo(addon.id,
    addon.targetAppID,
    addon.minAppVersion,
    addon.maxAppVersion,
    null);
    }
    else { // needs a restart
    // Add updatedMinVersion and updatedMaxVersion so it can be used
    // to update the datasource during the installation or upgrade.
    em.datasource.setUpdatedTargetAppInfo(addon.id,
    addon.targetAppID,
    addon.minAppVersion,
    addon.maxAppVersion,
    null);
    }
    }
    else {
    em.datasource.removeDownload(this._xpi.path);
    showIncompatibleError(installData);
    LOG("Add-on " + addon.id + " is incompatible with " +
    BundleManager.appName + " " + gApp.version + ", Toolkit " +
    gApp.platformVersion + ". Remote compatibility check did not " +
    "resolve this.");

    for (var i = 0; i < em._installListeners.length; ++i)
    em._installListeners[i].onInstallEnded(addon, INSTALLERROR_INCOMPATIBLE_VERSION);

    // We are responsible for cleaning up this file!
    InstallLocations.get(aInstallLocationKey).removeFile(this._xpi);
    }

    em._compatibilityCheckCount--;
    // If there are no more compatibility checks running and no downloads in
    // progress then the install operations are complete.
    if (em._compatibilityCheckCount == 0 && em._transactions.length == 0) {
    for (var i = 0; i < em._installListeners.length; ++i)
    em._installListeners[i].onInstallsCompleted();
    }
    },

    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonUpdateCheckListener])
    }

    var shouldPhoneHomeIfNecessary = false;
    if (!aInstallManifest) {
    // If we were not called with an Install Manifest, we were called from
    // some other path than the Phone Home system, so we do want to phone
    // home if the version is incompatible. As this is the first point in the
    // install process we must notify observers here.

    var addon = makeItem(getURIFromFile(aXPIFile).spec, "",
    aInstallLocationKey, "", "", "",
    getURIFromFile(aXPIFile).spec,
    "", "", "", "", 0, gApp.id);
    for (var i = 0; i < this._installListeners.length; ++i)
    this._installListeners[i].onInstallStarted(addon);

    shouldPhoneHomeIfNecessary = true;
    var installManifest = null;
    var installManifestFile = extractRDFFileToTempDir(aXPIFile,
    FILE_INSTALL_MANIFEST,
    true);
    if (installManifestFile.exists()) {
    installManifest = getInstallManifest(installManifestFile);
    installManifestFile.remove(false);
    }
    if (!installManifest) {
    LOG("The Install Manifest supplied by this item is not well-formed. " +
    "Installation will not proceed.");
    for (var i = 0; i < this._installListeners.length; ++i)
    this._installListeners[i].onInstallEnded(addon, INSTALLERROR_INVALID_MANIFEST);
    return;
    }
    }
    else
    installManifest = aInstallManifest;

    var installData = this._getInstallData(installManifest);
    // Recreate the add-on item with the full detail from the install manifest
    addon = makeItem(installData.id, installData.version,
    aInstallLocationKey,
    installData.currentApp ? installData.currentApp.minVersion : "",
    installData.currentApp ? installData.currentApp.maxVersion : "",
    installData.name,
    getURIFromFile(aXPIFile).spec,
    "", /* XPI Update Hash */
    "", /* Icon URL */
    installData.updateURL || "",
    installData.updateKey || "",
    installData.type,
    installData.currentApp ? installData.currentApp.id : "");

    switch (installData.error) {
    case INSTALLERROR_INCOMPATIBLE_VERSION:
    // Since the caller cleans up |aXPIFile|, and we're not yet sure whether or
    // not we need it (we may need it if a remote version bump that makes it
    // compatible is discovered by the call home) - so we must stage it for
    // later ourselves.
    if (shouldPhoneHomeIfNecessary && installData.currentApp) {
    var installLocation = getInstallLocation(installData.id, aInstallLocationKey);
    if (!installLocation)
    return;
    var stagedFile = installLocation.stageFile(aXPIFile, installData.id);
    (new IncompatibleObserver(this)).checkForUpdates(addon, installManifest,
    stagedFile);
    // Return early to prevent deletion of the install manifest file.
    return;
    }
    else {
    // XXXben Look up XULRunnerSettingsThingy to see if there is a registered
    // app that can handle this item, if so just stage and don't show
    // this error!
    showIncompatibleError(installData);
    LOG("Add-on " + installData.id + " is incompatible with " +
    BundleManager.appName + " " + gApp.version + ", Toolkit " +
    gApp.platformVersion + ". Remote compatibility check was not performed.");
    }
    break;
    case INSTALLERROR_SUCCESS:
    // Installation of multiple extensions / themes contained within a single xpi.
    if (installData.type == Ci.nsIUpdateItem.TYPE_MULTI_XPI) {
    installMultiXPI(aXPIFile, installData);
    break;
    }

    // Stage the extension's XPI so it can be extracted at the next restart.
    var installLocation = getInstallLocation(installData.id, aInstallLocationKey);
    if (!installLocation) {
    // No cleanup of any of the staged XPI files should be required here,
    // because this should only ever fail on the first recurse through
    // this function, BEFORE staging takes place... technically speaking
    // a location could become readonly during the phone home process,
    // but that's an edge case I don't care about.
    for (var i = 0; i < this._installListeners.length; ++i)
    this._installListeners[i].onInstallEnded(addon, INSTALLERROR_RESTRICTED);
    return;
    }

    // Stage a copy of the XPI/JAR file for our own evil purposes...
    stagedFile = installLocation.stageFile(aXPIFile, installData.id);

    var restartRequired = this.installRequiresRestart(installData.id,
    installData.type);
    // Determine which configuration function to use based on whether or not
    // there is data about this item in our datasource already - if there is
    // we want to upgrade, otherwise we install fresh.
    var ds = this.datasource;
    if (installData.id in ds.visibleItems && ds.visibleItems[installData.id]) {
    // We enter this function if any data corresponding to an existing GUID
    // is found, regardless of its Install Location. We need to check before
    // "upgrading" an item that Install Location of the new item is of equal
    // or higher priority than the old item, to make sure the datasource only
    // ever tracks metadata for active items.
    var oldInstallLocation = this.getInstallLocation(installData.id);
    if (oldInstallLocation.priority >= installLocation.priority) {
    this._upgradeItem(installManifest, installData.id, installLocation,
    installData.type);
    if (!restartRequired) {
    this._finalizeUpgrade(installData.id, installLocation);
    this._finalizeInstall(installData.id, stagedFile);
    }
    }
    }
    else {
    this._configureForthcomingItem(installManifest, installData.id,
    installLocation, installData.type);
    if (!restartRequired) {
    this._finalizeInstall(installData.id, stagedFile);
    if (installData.type == Ci.nsIUpdateItem.TYPE_THEME) {
    var internalName = this.datasource.getItemProperty(installData.id, "internalName");
    if (gPref.getBoolPref(PREF_EM_DSS_ENABLED)) {
    gPref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, internalName);
    }
    else {
    gPref.setBoolPref(PREF_DSS_SWITCHPENDING, true);
    gPref.setCharPref(PREF_DSS_SKIN_TO_SELECT, internalName);
    }
    }
    }
    }
    this._updateManifests(restartRequired);
    break;
    case INSTALLERROR_INVALID_GUID:
    LOG("Invalid GUID: Item has GUID: \"" + installData.id + "\"" +
    " which is not well-formed.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("incompatibleTitle",
    [bundle.GetStringFromName("type-" + installData.type)],
    "invalidGUIDMessage", [installData.name, installData.id]);
    break;
    case INSTALLERROR_INVALID_VERSION:
    LOG("Invalid Version: Item: \"" + installData.id + "\" has version " +
    installData.version + " which is not well-formed.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("incompatibleTitle",
    [bundle.GetStringFromName("type-" + installData.type)],
    "invalidVersionMessage", [installData.name, installData.version]);
    break;
    case INSTALLERROR_INCOMPATIBLE_PLATFORM:
    const osABI = gOSTarget + "_" + gXPCOMABI;
    LOG("Incompatible Platform: Item: \"" + installData.id + "\" is not " +
    "compatible with '" + osABI + "'.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("incompatibleTitle",
    [bundle.GetStringFromName("type-" + installData.type)],
    "incompatiblePlatformMessage",
    [installData.name, BundleManager.appName, osABI]);
    break;
    case INSTALLERROR_BLOCKLISTED:
    LOG("Blocklisted Item: Item: \"" + installData.id + "\" version " +
    installData.version + " was not installed.");
    showBlocklistMessage([installData], true);
    break;
    case INSTALLERROR_INSECURE_UPDATE:
    LOG("No secure updates: Item: \"" + installData.id + "\" version " +
    installData.version + " was not installed.");
    var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
    showMessage("incompatibleTitle",
    [bundle.GetStringFromName("type-" + installData.type)],
    "insecureUpdateMessage", [installData.name]);
    break;
    default:
    break;
    }

    // Check to see if this item supports other applications and in that case
    // stage the the XPI file in the location specified by those applications.
    stageXPIForOtherApps(aXPIFile, installData);

    // The install of this item is complete, notify observers
    for (var i = 0; i < this._installListeners.length; ++i)
    this._installListeners[i].onInstallEnded(addon, installData.error);
    },

    /**
    * Whether or not this type's installation/uninstallation requires
    * the application to be restarted.
    * Param id
    * The GUID of the item
    * Param type
    * The nsIUpdateItem type of the item
    * @returns true if installation of an item of this type requires a
    * restart.
    */
    installRequiresRestart: function(id, type) {
    switch (type) {
    case Ci.nsIUpdateItem.TYPE_THEME:
    var internalName = this.datasource.getItemProperty(id, "internalName");
    var needsRestart = false;
    if (gPref.prefHasUserValue(PREF_DSS_SKIN_TO_SELECT))
    needsRestart = internalName == gPref.getCharPref(PREF_DSS_SKIN_TO_SELECT);
    if (!needsRestart &&
    gPref.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN))
    needsRestart = internalName == gPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
    return needsRestart;
    }
    return ((type & Ci.nsIUpdateItem.TYPE_ADDON) > 0);
    },

    /**
    * Perform initial configuration on an item that has just or will be
    * installed. This inserts the item into the appropriate container in the
    * datasource, so that the application UI shows the item even if it will
    * not actually be installed until the next restart.
    * Param installManifest
    * The Install Manifest datasource that describes this item.
    * Param id
    * The GUID of this item.
    * Param installLocation
    * The Install Location where this item is installed.
    * Param type
    * The nsIUpdateItem type of this item.
    */
    _configureForthcomingItem: function(installManifest, id, installLocation, type) {
    var ds = this.datasource;
    ds.updateVisibleList(id, installLocation.name, false);

    var name = null;
    var localized = findClosestLocalizedResource(installManifest, gInstallManifestRoot);
    if (localized)
    name = installManifest.GetTarget(localized, EM_R("name"), true);
    else
    name = EM_L(getManifestProperty(installManifest, "name"));

    var props = { name : name,
    version : EM_L(getManifestProperty(installManifest, "version")),
    newVersion : EM_L(getManifestProperty(installManifest, "version")),
    installLocation : EM_L(installLocation.name),
    type : EM_I(type),
    availableUpdateURL : null,
    availableUpdateHash : null,
    availableUpdateVersion: null,
    availableUpdateInfo : null };
    ds.setItemProperties(id, props);
    ds.updateProperty(id, "availableUpdateURL");

    this._setOp(id, OP_NEEDS_INSTALL);

    // Insert it into the child list NOW rather than later because:
    // - extensions installed using the command line need to be a member
    // of a container during the install phase for the code to be able
    // to identify profile vs. global
    // - extensions installed through the UI should show some kind of
    // feedback to indicate their presence is forthcoming (i.e. they
    // will be available after a restart).
    ds.insertItemIntoContainer(id);

    this._notifyAction(id, EM_ITEM_INSTALLED);
    },

    /**
    * Perform configuration on an item that has just or will be upgraded.
    * Param installManifest
    * The Install Manifest datasource that describes this item.
    * Param itemID
    * The GUID of this item.
    * Param installLocation
    * The Install Location where this item is installed.
    * Param type
    * The nsIUpdateItem type of this item.
    */
    _upgradeItem: function (installManifest, id, installLocation, type) {
    // Don't change any props that would need to be reset if the install fails.
    // They will be reset as appropriate by the upgrade/install process.
    var ds = this.datasource;
    ds.updateVisibleList(id, installLocation.name, false);
    var props = { installLocation : EM_L(installLocation.name),
    type : EM_I(type),
    newVersion : EM_L(getManifestProperty(installManifest, "version")),
    availableUpdateURL : null,
    availableUpdateHash : null,
    availableUpdateVersion : null,
    availableUpdateInfo : null };
    ds.setItemProperties(id, props);
    ds.updateProperty(id, "availableUpdateURL");

    this._setOp(id, OP_NEEDS_UPGRADE);
    this._notifyAction(id, EM_ITEM_UPGRADED);
    },

    /**
    * Completes an Extension's installation.
    * Param id
    * The GUID of the Extension to install.
    * Param file
    * The XPI/JAR file to install from. If this is null, we try to
    * determine the stage file location from the ID.
    */
    _finalizeInstall: function(id, file) {
    var ds = this.datasource;
    var type = ds.getItemProperty(id, "type");
    if (id == 0 || id == -1) {
    ds.removeCorruptItem(id);
    return;
    }
    var installLocation = this.getInstallLocation(id);
    if (!installLocation) {
    // If the install location is null, that means we've reached the finalize
    // state without the item ever having metadata added for it, which implies
    // bogus data in the Startup Cache. Clear the entries and don't do anything
    // else.
    var entries = StartupCache.findEntries(id);
    for (var i = 0; i < entries.length; ++i) {
    var location = InstallLocations.get(entries[i].location);
    StartupCache.clearEntry(location, id);
    PendingOperations.clearItem(OP_NEEDS_INSTALL, id);
    }
    return;
    }
    var itemLocation = installLocation.getItemLocation(id);

    if (!file && "stageFile" in installLocation)
    file = installLocation.getStageFile(id);

    // If |file| is null or does not exist, the installer assumes the item is
    // a dropped-in directory.
    var installer = new Installer(this.datasource, id, installLocation, type);
    installer.installFromFile(file);

    // If the file was staged, we must clean it up ourselves, otherwise the
    // EM caller is responsible for doing so (e.g. XPInstall)
    if (file)
    installLocation.removeFile(file);

    // Clear the op flag from the Startup Cache and Pending Operations sets
    StartupCache.put(installLocation, id, OP_NONE, true);
    PendingOperations.clearItem(OP_NEEDS_INSTALL, id);
    },

    /**
    * Removes an item's metadata in preparation for an upgrade-install.
    * Param id
    * The GUID of the item to uninstall.
    * Param installLocation
    * The nsIInstallLocation of the item
    */
    _finalizeUpgrade: function(id, installLocation) {
    // Retrieve the item properties *BEFORE* we clean the resource!
    var ds = this.datasource;

    var stagedFile = null;
    if ("getStageFile" in installLocation)
    stagedFile = installLocation.getStageFile(id);

    if (stagedFile)
    var installRDF = extractRDFFileToTempDir(stagedFile, FILE_INSTALL_MANIFEST, true);
    else
    installRDF = installLocation.getItemFile(id, FILE_INSTALL_MANIFEST);
    if (installRDF.exists()) {
    var installManifest = getInstallManifest(installRDF);
    if (installManifest) {
    var type = getAddonTypeFromInstallManifest(installManifest);
    var userDisabled = ds.getItemProperty(id, "userDisabled") == "true";

    // Clean the item resource
    ds.removeItemMetadata(id);
    // Now set up the properties on the item to mimic an item in its
    // "initial state" for installation.
    this._configureForthcomingItem(installManifest, id, installLocation,
    type);
    if (userDisabled)
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
    }
    if (stagedFile)
    installRDF.remove(false);
    }
    // Clear the op flag from the Pending Operations set. Do NOT clear op flag in
    // the startup cache since this may have been reset to OP_NEEDS_INSTALL by
    // |_configureForthcomingItem|.
    PendingOperations.clearItem(OP_NEEDS_UPGRADE, id);
    },

    /**
    * Completes an item's uninstallation.
    * Param id
    * The GUID of the item to uninstall.
    */
    _finalizeUninstall: function(id) {
    var ds = this.datasource;

    var installLocation = this.getInstallLocation(id);
    if (!installLocation.itemIsManagedIndependently(id)) {
    try {
    // Having a callback that does nothing just causes the directory to be
    // removed.
    safeInstallOperation(id, installLocation,
    { data: null, callback: function() { } });
    }
    catch (e) {
    ERROR("_finalizeUninstall: failed to remove directory for item: " + id +
    " at Install Location: " + installLocation.name + ", rolling back uninstall");
    var manifest = installLocation.getItemFile(id, "FILE_INSTALL_MANIFEST");
    // If there is no manifest then either the rollback failed, or there was
    // no manifest in the first place. Either way this item is now invalid
    // and we shouldn't try to re-install it.
    if (manifest.exists()) {
    // Removal of the files failed, reset the uninstalled flag and rewrite
    // the install manifests so this item's components are registered.
    // Clear the op flag from the Startup Cache
    StartupCache.put(installLocation, id, OP_NONE, true);
    var restartRequired = this.installRequiresRestart(id, ds.getItemProperty(id, "type"))
    this._updateManifests(restartRequired);
    return;
    }
    }
    }
    else if (installLocation.name == KEY_APP_PROFILE ||
    installLocation.name == KEY_APP_GLOBAL ||
    installLocation.name == KEY_APP_SYSTEM_USER) {
    // Check for a pointer file and remove it if it exists
    var pointerFile = installLocation.location.clone();
    pointerFile.append(id);
    if (pointerFile.exists() && !pointerFile.isDirectory())
    pointerFile.remove(false);
    }

    // Clean the item resource
    ds.removeItemMetadata(id);

    // Do this LAST since inferences are made about an item based on
    // what container it's in.
    ds.removeItemFromContainer(id);

    // Clear the op flag from the Startup Cache and the Pending Operations set.
    StartupCache.clearEntry(installLocation, id);
    PendingOperations.clearItem(OP_NEEDS_UNINSTALL, id);
    },

    /**
    * Uninstalls an item. If the uninstallation cannot be performed immediately
    * it is scheduled for the next restart.
    * Param id
    * The GUID of the item to uninstall.
    */
    uninstallItem: function(id) {
    var ds = this.datasource;
    ds.updateDownloadState(PREFIX_ITEM_URI + id, null);
    if (!ds.isDownloadItem(id)) {
    var opType = ds.getItemProperty(id, "opType");
    var installLocation = this.getInstallLocation(id);
    // Removes any staged xpis for this item.
    if (opType == OP_NEEDS_UPGRADE || opType == OP_NEEDS_INSTALL) {
    var stageFile = installLocation.getStageFile(id);
    if (stageFile)
    installLocation.removeFile(stageFile);
    }
    // Addons with an opType of OP_NEEDS_INSTALL only have a staged xpi file
    // and are removed immediately since the uninstall can't be canceled.
    if (opType == OP_NEEDS_INSTALL) {
    ds.removeItemMetadata(id);
    ds.removeItemFromContainer(id);
    ds.updateVisibleList(id, null, true);
    StartupCache.clearEntry(installLocation, id);
    this._updateManifests(false);
    }
    else {
    if (opType == OP_NEEDS_UPGRADE)
    ds.setItemProperty(id, "newVersion", null);
    this._setOp(id, OP_NEEDS_UNINSTALL);
    var type = ds.getItemProperty(id, "type");
    var restartRequired = this.installRequiresRestart(id, type);
    if (!restartRequired) {
    this._finalizeUninstall(id);
    this._updateManifests(restartRequired);
    }
    }
    }
    else {
    // Bad download entry - uri is url, e.g. "http://www.foo.com/test.xpi"
    // ... just remove it from the list.
    ds.removeCorruptDLItem(id);
    }

    this._notifyAction(id, EM_ITEM_UNINSTALLED);
    },

    /* See nsIExtensionManager.idl */
    cancelInstallItem: function(id) {
    var ds = this.datasource;
    var opType = ds.getItemProperty(id, "opType");
    if (opType != OP_NEEDS_UPGRADE && opType != OP_NEEDS_INSTALL)
    return;

    ds.updateDownloadState(PREFIX_ITEM_URI + id, null);
    var installLocation = this.getInstallLocation(id);
    // Removes any staged xpis for this item.
    var stageFile = installLocation.getStageFile(id);
    if (stageFile)
    installLocation.removeFile(stageFile);
    // Addons with an opType of OP_NEEDS_INSTALL only have a staged xpi file
    // and just need to be removed completely from the ds.
    if (opType == OP_NEEDS_INSTALL) {
    ds.removeItemMetadata(id);
    ds.removeItemFromContainer(id);
    ds.updateVisibleList(id, null, true);
    StartupCache.clearEntry(installLocation, id);
    this._updateManifests(false);
    this._notifyAction(id, EM_ITEM_CANCEL);
    }
    else {
    // Clear upgrade information and reset any request to enable/disable.
    ds.setItemProperty(id, EM_R("newVersion"), null);
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    var userDisabled = ds.getItemProperty(id, "userDisabled");
    if (appDisabled == "true" || appDisabled == OP_NONE && userDisabled == OP_NONE) {
    this._setOp(id, OP_NONE);
    this._notifyAction(id, EM_ITEM_CANCEL);
    }
    else if (appDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NEEDS_DISABLE) {
    this._setOp(id, OP_NEEDS_DISABLE);
    this._notifyAction(id, EM_ITEM_DISABLED);
    }
    else if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE) {
    this._setOp(id, OP_NEEDS_ENABLE);
    this._notifyAction(id, EM_ITEM_ENABLED);
    }
    else {
    this._setOp(id, OP_NONE);
    this._notifyAction(id, EM_ITEM_CANCEL);
    }
    }
    },

    /**
    * Cancels a pending uninstall of an item
    * Param id
    * The ID of the item.
    */
    cancelUninstallItem: function(id) {
    var ds = this.datasource;
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    var userDisabled = ds.getItemProperty(id, "userDisabled");
    if (appDisabled == "true" || appDisabled == OP_NONE && userDisabled == OP_NONE) {
    this._setOp(id, OP_NONE);
    this._notifyAction(id, EM_ITEM_CANCEL);
    }
    else if (appDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NEEDS_DISABLE) {
    this._setOp(id, OP_NEEDS_DISABLE);
    this._notifyAction(id, EM_ITEM_DISABLED);
    }
    else if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE) {
    this._setOp(id, OP_NEEDS_ENABLE);
    this._notifyAction(id, EM_ITEM_ENABLED);
    }
    else {
    this._setOp(id, OP_NONE);
    this._notifyAction(id, EM_ITEM_CANCEL);
    }
    },

    /**
    * Sets the pending operation for a visible item.
    * Param id
    * The GUID of the item
    * Param op
    * The name of the operation to be performed
    */
    _setOp: function(id, op) {
    var location = this.getInstallLocation(id);
    StartupCache.put(location, id, op, true);
    PendingOperations.addItem(op, { locationKey: location.name, id: id });
    var ds = this.datasource;
    if (op == OP_NEEDS_INSTALL || op == OP_NEEDS_UPGRADE)
    ds.updateDownloadState(PREFIX_ITEM_URI + id, "success");

    ds.updateProperty(id, "opType");
    ds.updateProperty(id, "updateable");
    ds.updateProperty(id, "satisfiesDependencies");
    var restartRequired = this.installRequiresRestart(id, ds.getItemProperty(id, "type"))
    this._updateDependentItemsForID(id);
    this._updateManifests(restartRequired);
    },

    /**
    * Note on appDisabled and userDisabled property arcs.
    * The appDisabled and userDisabled RDF property arcs are used to store
    * the pending operation for app disabling and user disabling for an item as
    * well as the user and app disabled status after the pending operation has
    * been completed upon restart. When the appDisabled value changes the value
    * of userDisabled is reset to prevent the state of widgets and status
    * messages from being in an incorrect state.
    */

    /**
    * Enables an item for the application (e.g. the item satisfies all
    * requirements like app compatibility for it to be enabled). The appDisabled
    * property arc will be removed if the item will be app disabled on next
    * restart to cancel the app disabled operation for the item otherwise the
    * property value will be set to OP_NEEDS_ENABLE. The item's pending
    * operations are then evaluated in order to set the operation to perform
    * and notify the observers if the operation has been changed.
    * See "Note on appDisabled and userDisabled property arcs" above.
    * Param id
    * The ID of the item to be enabled by the application.
    */
    _appEnableItem: function(id) {
    var ds = this.datasource;
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    if (appDisabled == OP_NONE || appDisabled == OP_NEEDS_ENABLE)
    return;

    var opType = ds.getItemProperty(id, "opType");
    var userDisabled = ds.getItemProperty(id, "userDisabled");
    // reset user disabled if it has a pending operation to prevent the ui
    // state from getting confused as to an item's current state.
    if (userDisabled == OP_NEEDS_DISABLE)
    ds.setItemProperty(id, EM_R("userDisabled"), null);
    else if (userDisabled == OP_NEEDS_ENABLE)
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));

    if (appDisabled == OP_NEEDS_DISABLE)
    ds.setItemProperty(id, EM_R("appDisabled"), null);
    else if (appDisabled == "true")
    ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_ENABLE));

    // Don't set a new operation when there is a pending uninstall operation.
    if (opType == OP_NEEDS_UNINSTALL) {
    this._updateDependentItemsForID(id);
    return;
    }

    var operation, action;
    // if this item is already enabled or user disabled don't set a pending
    // operation - instead immediately enable it and reset the operation type
    // if needed.
    if (appDisabled == OP_NEEDS_DISABLE || appDisabled == OP_NONE ||
    userDisabled == "true") {
    if (opType != OP_NONE) {
    operation = OP_NONE;
    action = EM_ITEM_CANCEL;
    }
    }
    else {
    if (opType != OP_NEEDS_ENABLE) {
    operation = OP_NEEDS_ENABLE;
    action = EM_ITEM_ENABLED;
    }
    }

    if (action) {
    this._setOp(id, operation);
    this._notifyAction(id, action);
    }
    else {
    ds.updateProperty(id, "satisfiesDependencies");
    this._updateDependentItemsForID(id);
    }
    },

    /**
    * Disables an item for the application (e.g. the item doesn't satisfy all
    * requirements like app compatibility for it to be enabled). The appDisabled
    * property arc will be set to true if the item will be app enabled on next
    * restart to cancel the app enabled operation for the item otherwise the
    * property value will be set to OP_NEEDS_DISABLE. The item's pending
    * operations are then evaluated in order to set the operation to perform
    * and notify the observers if the operation has been changed.
    * See "Note on appDisabled and userDisabled property arcs" above.
    * Param id
    * The ID of the item to be disabled by the application.
    */
    _appDisableItem: function(id) {
    var ds = this.datasource;
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    if (appDisabled == "true" || appDisabled == OP_NEEDS_DISABLE)
    return;

    var opType = ds.getItemProperty(id, "opType");
    var userDisabled = ds.getItemProperty(id, "userDisabled");

    // reset user disabled if it has a pending operation to prevent the ui
    // state from getting confused as to an item's current state.
    if (userDisabled == OP_NEEDS_DISABLE)
    ds.setItemProperty(id, EM_R("userDisabled"), null);
    else if (userDisabled == OP_NEEDS_ENABLE)
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));

    if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE ||
    ds.getItemProperty(id, "userDisabled") == "true")
    ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
    else if (appDisabled == OP_NONE)
    ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_DISABLE));

    // Don't set a new operation when there is a pending uninstall operation.
    if (opType == OP_NEEDS_UNINSTALL) {
    this._updateDependentItemsForID(id);
    return;
    }

    var operation, action;
    // if this item is already disabled don't set a pending operation - instead
    // immediately disable it and reset the operation type if needed.
    if (appDisabled == OP_NEEDS_ENABLE || appDisabled == "true" ||
    userDisabled == OP_NEEDS_ENABLE || userDisabled == "true") {
    if (opType != OP_NONE) {
    operation = OP_NONE;
    action = EM_ITEM_CANCEL;
    }
    }
    else {
    if (opType != OP_NEEDS_DISABLE) {
    operation = OP_NEEDS_DISABLE;
    action = EM_ITEM_DISABLED;
    }
    }

    if (action) {
    this._setOp(id, operation);
    this._notifyAction(id, action);
    }
    else {
    ds.updateProperty(id, "satisfiesDependencies");
    this._updateDependentItemsForID(id);
    }
    },

    /**
    * Sets an item to be enabled by the user. If the item is already enabled this
    * clears the needs-enable operation for the next restart.
    * See "Note on appDisabled and userDisabled property arcs" above.
    * Param id
    * The ID of the item to be enabled by the user.
    */
    enableItem: function(id) {
    var ds = this.datasource;
    var opType = ds.getItemProperty(id, "opType");
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    var userDisabled = ds.getItemProperty(id, "userDisabled");

    var operation, action;
    // if this item is already enabled don't set a pending operation - instead
    // immediately enable it and reset the operation type if needed.
    if (appDisabled == OP_NONE &&
    userDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NONE) {
    if (userDisabled == OP_NEEDS_DISABLE)
    ds.setItemProperty(id, EM_R("userDisabled"), null);
    if (opType != OP_NONE) {
    operation = OP_NONE;
    action = EM_ITEM_CANCEL;
    }
    }
    else {
    if (userDisabled == "true")
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L(OP_NEEDS_ENABLE));
    if (opType != OP_NEEDS_ENABLE) {
    operation = OP_NEEDS_ENABLE;
    action = EM_ITEM_ENABLED;
    }
    }

    if (action) {
    this._setOp(id, operation);
    this._notifyAction(id, action);
    }
    else {
    ds.updateProperty(id, "satisfiesDependencies");
    this._updateDependentItemsForID(id);
    }
    },

    /**
    * Sets an item to be disabled by the user. If the item is already disabled
    * this clears the needs-disable operation for the next restart.
    * See "Note on appDisabled and userDisabled property arcs" above.
    * Param id
    * The ID of the item to be disabled by the user.
    */
    disableItem: function(id) {
    var ds = this.datasource;
    var opType = ds.getItemProperty(id, "opType");
    var appDisabled = ds.getItemProperty(id, "appDisabled");
    var userDisabled = ds.getItemProperty(id, "userDisabled");

    var operation, action;
    // if this item is already disabled don't set a pending operation - instead
    // immediately disable it and reset the operation type if needed.
    if (userDisabled == OP_NEEDS_ENABLE || userDisabled == "true" ||
    appDisabled == OP_NEEDS_ENABLE) {
    if (userDisabled != "true")
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
    if (opType != OP_NONE) {
    operation = OP_NONE;
    action = EM_ITEM_CANCEL;
    }
    }
    else {
    if (userDisabled == OP_NONE)
    ds.setItemProperty(id, EM_R("userDisabled"), EM_L(OP_NEEDS_DISABLE));
    if (opType != OP_NEEDS_DISABLE) {
    operation = OP_NEEDS_DISABLE;
    action = EM_ITEM_DISABLED;
    }
    }

    if (action) {
    this._setOp(id, operation);
    this._notifyAction(id, action);
    }
    else {
    ds.updateProperty(id, "satisfiesDependencies");
    this._updateDependentItemsForID(id);
    }
    },

    /**
    * Determines whether an item should be disabled by the application.
    * Param id
    * The ID of the item to check
    */
    _isUsableItem: function(id) {
    var ds = this.datasource;
    /* If we're not compatibility checking or if the item is compatible
    * and if it isn't blocklisted and has all dependencies satisfied then
    * proceed to the security check */
    if ((!gCheckCompatibility || ds.getItemProperty(id, "compatible") == "true") &&
    ds.getItemProperty(id, "blocklisted") == "false" &&
    ds.getItemProperty(id, "satisfiesDependencies") == "true") {

    // appManaged items aren't updated so no need to check update security.
    if (ds.getItemProperty(id, "appManaged") == "true")
    return true;

    /* If we are not ignoring update security then check that the item has
    * a secure update mechanism */
    return (!gCheckUpdateSecurity ||
    ds.getItemProperty(id, "providesUpdatesSecurely") == "true");
    }
    return false;
    },

    /**
    * Sets an item's dependent items disabled state for the app based on whether
    * its dependencies are met and the item is compatible.
    * Param id
    * The ID of the item whose dependent items will be checked
    */
    _updateDependentItemsForID: function(id) {
    var ds = this.datasource;
    var dependentItems = this.getDependentItemListForID(id, true, { });
    for (var i = 0; i < dependentItems.length; ++i) {
    var dependentID = dependentItems[i].id;
    ds.updateProperty(dependentID, "satisfiesDependencies");
    if (this._isUsableItem(dependentID))
    this._appEnableItem(dependentID);
    else
    this._appDisableItem(dependentID);
    }
    },

    /**
    * Notify observers of a change to an item that has been requested by the
    * user.
    */
    _notifyAction: function(id, reason) {
    gOS.notifyObservers(this.datasource.getItemForID(id),
    EM_ACTION_REQUESTED_TOPIC, reason);
    },

    /**
    * See nsIExtensionManager.idl
    */
    update: function(items, itemCount, updateCheckType, listener) {
    for (i = 0; i < itemCount; ++i) {
    var currItem = items[i];
    if (!currItem)
    throw Cr.NS_ERROR_ILLEGAL_VALUE;
    }

    if (items.length == 0)
    items = this.getItemList(Ci.nsIUpdateItem.TYPE_ANY, { });

    var updater = new ExtensionItemUpdater(this);
    updater.checkForUpdates(items, items.length, updateCheckType, listener);
    },


    /**
    * Checks for changes to the blocklist using the local blocklist file,
    * application disables / enables items that have been added / removed from
    * the blocklist, and if there are additions to the blocklist this will
    * inform the user by displaying a list of the items added.
    *
    * XXXrstrong - this method is not terribly useful and was added so we ca

    5 Mal editiert, zuletzt von presla (4. Mai 2009 um 14:15)