toolkit/mozapps/extensions/AddonManager.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/mozapps/extensions/AddonManager.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2814 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +const Cc = Components.classes;
    1.11 +const Ci = Components.interfaces;
    1.12 +const Cr = Components.results;
    1.13 +const Cu = Components.utils;
    1.14 +
    1.15 +// Cannot use Services.appinfo here, or else xpcshell-tests will blow up, as
    1.16 +// most tests later register different nsIAppInfo implementations, which
    1.17 +// wouldn't be reflected in Services.appinfo anymore, as the lazy getter
    1.18 +// underlying it would have been initialized if we used it here.
    1.19 +if ("@mozilla.org/xre/app-info;1" in Cc) {
    1.20 +  let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
    1.21 +  if (runtime.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
    1.22 +    // Refuse to run in child processes.
    1.23 +    throw new Error("You cannot use the AddonManager in child processes!");
    1.24 +  }
    1.25 +}
    1.26 +
    1.27 +
    1.28 +const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
    1.29 +const PREF_DEFAULT_PROVIDERS_ENABLED  = "extensions.defaultProviders.enabled";
    1.30 +const PREF_EM_UPDATE_ENABLED          = "extensions.update.enabled";
    1.31 +const PREF_EM_LAST_APP_VERSION        = "extensions.lastAppVersion";
    1.32 +const PREF_EM_LAST_PLATFORM_VERSION   = "extensions.lastPlatformVersion";
    1.33 +const PREF_EM_AUTOUPDATE_DEFAULT      = "extensions.update.autoUpdateDefault";
    1.34 +const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
    1.35 +const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
    1.36 +const PREF_EM_UPDATE_BACKGROUND_URL   = "extensions.update.background.url";
    1.37 +const PREF_APP_UPDATE_ENABLED         = "app.update.enabled";
    1.38 +const PREF_APP_UPDATE_AUTO            = "app.update.auto";
    1.39 +const PREF_EM_HOTFIX_ID               = "extensions.hotfix.id";
    1.40 +const PREF_EM_HOTFIX_LASTVERSION      = "extensions.hotfix.lastVersion";
    1.41 +const PREF_EM_HOTFIX_URL              = "extensions.hotfix.url";
    1.42 +const PREF_EM_CERT_CHECKATTRIBUTES    = "extensions.hotfix.cert.checkAttributes";
    1.43 +const PREF_EM_HOTFIX_CERTS            = "extensions.hotfix.certs.";
    1.44 +const PREF_MATCH_OS_LOCALE            = "intl.locale.matchOS";
    1.45 +const PREF_SELECTED_LOCALE            = "general.useragent.locale";
    1.46 +const UNKNOWN_XPCOM_ABI               = "unknownABI";
    1.47 +
    1.48 +const UPDATE_REQUEST_VERSION          = 2;
    1.49 +const CATEGORY_UPDATE_PARAMS          = "extension-update-params";
    1.50 +
    1.51 +const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
    1.52 +
    1.53 +const KEY_PROFILEDIR                  = "ProfD";
    1.54 +const KEY_APPDIR                      = "XCurProcD";
    1.55 +const FILE_BLOCKLIST                  = "blocklist.xml";
    1.56 +
    1.57 +const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
    1.58 +const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
    1.59 +#ifdef MOZ_COMPATIBILITY_NIGHTLY
    1.60 +var PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + ".nightly";
    1.61 +#else
    1.62 +var PREF_EM_CHECK_COMPATIBILITY;
    1.63 +#endif
    1.64 +
    1.65 +const TOOLKIT_ID                      = "toolkit@mozilla.org";
    1.66 +
    1.67 +const VALID_TYPES_REGEXP = /^[\w\-]+$/;
    1.68 +
    1.69 +Cu.import("resource://gre/modules/Services.jsm");
    1.70 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.71 +Cu.import("resource://gre/modules/AsyncShutdown.jsm");
    1.72 +
    1.73 +XPCOMUtils.defineLazyModuleGetter(this, "Promise",
    1.74 +                                  "resource://gre/modules/Promise.jsm");
    1.75 +XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
    1.76 +                                  "resource://gre/modules/addons/AddonRepository.jsm");
    1.77 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
    1.78 +                                  "resource://gre/modules/FileUtils.jsm");
    1.79 +
    1.80 +XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
    1.81 +  let certUtils = {};
    1.82 +  Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
    1.83 +  return certUtils;
    1.84 +});
    1.85 +
    1.86 +
    1.87 +this.EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
    1.88 +
    1.89 +const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
    1.90 +
    1.91 +// A list of providers to load by default
    1.92 +const DEFAULT_PROVIDERS = [
    1.93 +  "resource://gre/modules/addons/XPIProvider.jsm",
    1.94 +  "resource://gre/modules/LightweightThemeManager.jsm"
    1.95 +];
    1.96 +
    1.97 +Cu.import("resource://gre/modules/Log.jsm");
    1.98 +// Configure a logger at the parent 'addons' level to format
    1.99 +// messages for all the modules under addons.*
   1.100 +const PARENT_LOGGER_ID = "addons";
   1.101 +let parentLogger = Log.repository.getLogger(PARENT_LOGGER_ID);
   1.102 +parentLogger.level = Log.Level.Warn;
   1.103 +let formatter = new Log.BasicFormatter();
   1.104 +// Set parent logger (and its children) to append to
   1.105 +// the Javascript section of the Browser Console
   1.106 +parentLogger.addAppender(new Log.ConsoleAppender(formatter));
   1.107 +// Set parent logger (and its children) to
   1.108 +// also append to standard out
   1.109 +parentLogger.addAppender(new Log.DumpAppender(formatter));
   1.110 +
   1.111 +// Create a new logger (child of 'addons' logger)
   1.112 +// for use by the Addons Manager
   1.113 +const LOGGER_ID = "addons.manager";
   1.114 +let logger = Log.repository.getLogger(LOGGER_ID);
   1.115 +
   1.116 +// Provide the ability to enable/disable logging
   1.117 +// messages at runtime.
   1.118 +// If the "extensions.logging.enabled" preference is
   1.119 +// missing or 'false', messages at the WARNING and higher
   1.120 +// severity should be logged to the JS console and standard error.
   1.121 +// If "extensions.logging.enabled" is set to 'true', messages
   1.122 +// at DEBUG and higher should go to JS console and standard error.
   1.123 +const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
   1.124 +const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
   1.125 +
   1.126 +/**
   1.127 + * Preference listener which listens for a change in the
   1.128 + * "extensions.logging.enabled" preference and changes the logging level of the
   1.129 + * parent 'addons' level logger accordingly.
   1.130 + */
   1.131 +var PrefObserver = {
   1.132 +    init: function PrefObserver_init() {
   1.133 +      Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
   1.134 +      Services.obs.addObserver(this, "xpcom-shutdown", false);
   1.135 +      this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
   1.136 +    },
   1.137 +
   1.138 +    observe: function PrefObserver_observe(aSubject, aTopic, aData) {
   1.139 +      if (aTopic == "xpcom-shutdown") {
   1.140 +        Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
   1.141 +        Services.obs.removeObserver(this, "xpcom-shutdown");
   1.142 +      }
   1.143 +      else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
   1.144 +        let debugLogEnabled = false;
   1.145 +        try {
   1.146 +          debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
   1.147 +        }
   1.148 +        catch (e) {
   1.149 +        }
   1.150 +        if (debugLogEnabled) {
   1.151 +          parentLogger.level = Log.Level.Debug;
   1.152 +        }
   1.153 +        else {
   1.154 +          parentLogger.level = Log.Level.Warn;
   1.155 +        }
   1.156 +      }
   1.157 +    }
   1.158 +};
   1.159 +
   1.160 +PrefObserver.init();
   1.161 +
   1.162 +/**
   1.163 + * Calls a callback method consuming any thrown exception. Any parameters after
   1.164 + * the callback parameter will be passed to the callback.
   1.165 + *
   1.166 + * @param  aCallback
   1.167 + *         The callback method to call
   1.168 + */
   1.169 +function safeCall(aCallback, ...aArgs) {
   1.170 +  try {
   1.171 +    aCallback.apply(null, aArgs);
   1.172 +  }
   1.173 +  catch (e) {
   1.174 +    logger.warn("Exception calling callback", e);
   1.175 +  }
   1.176 +}
   1.177 +
   1.178 +/**
   1.179 + * Calls a method on a provider if it exists and consumes any thrown exception.
   1.180 + * Any parameters after the dflt parameter are passed to the provider's method.
   1.181 + *
   1.182 + * @param  aProvider
   1.183 + *         The provider to call
   1.184 + * @param  aMethod
   1.185 + *         The method name to call
   1.186 + * @param  aDefault
   1.187 + *         A default return value if the provider does not implement the named
   1.188 + *         method or throws an error.
   1.189 + * @return the return value from the provider or dflt if the provider does not
   1.190 + *         implement method or throws an error
   1.191 + */
   1.192 +function callProvider(aProvider, aMethod, aDefault, ...aArgs) {
   1.193 +  if (!(aMethod in aProvider))
   1.194 +    return aDefault;
   1.195 +
   1.196 +  try {
   1.197 +    return aProvider[aMethod].apply(aProvider, aArgs);
   1.198 +  }
   1.199 +  catch (e) {
   1.200 +    logger.error("Exception calling provider " + aMethod, e);
   1.201 +    return aDefault;
   1.202 +  }
   1.203 +}
   1.204 +
   1.205 +/**
   1.206 + * Gets the currently selected locale for display.
   1.207 + * @return  the selected locale or "en-US" if none is selected
   1.208 + */
   1.209 +function getLocale() {
   1.210 +  try {
   1.211 +    if (Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE))
   1.212 +      return Services.locale.getLocaleComponentForUserAgent();
   1.213 +  }
   1.214 +  catch (e) { }
   1.215 +
   1.216 +  try {
   1.217 +    let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
   1.218 +                                                Ci.nsIPrefLocalizedString);
   1.219 +    if (locale)
   1.220 +      return locale;
   1.221 +  }
   1.222 +  catch (e) { }
   1.223 +
   1.224 +  try {
   1.225 +    return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
   1.226 +  }
   1.227 +  catch (e) { }
   1.228 +
   1.229 +  return "en-US";
   1.230 +}
   1.231 +
   1.232 +/**
   1.233 + * A helper class to repeatedly call a listener with each object in an array
   1.234 + * optionally checking whether the object has a method in it.
   1.235 + *
   1.236 + * @param  aObjects
   1.237 + *         The array of objects to iterate through
   1.238 + * @param  aMethod
   1.239 + *         An optional method name, if not null any objects without this method
   1.240 + *         will not be passed to the listener
   1.241 + * @param  aListener
   1.242 + *         A listener implementing nextObject and noMoreObjects methods. The
   1.243 + *         former will be called with the AsyncObjectCaller as the first
   1.244 + *         parameter and the object as the second. noMoreObjects will be passed
   1.245 + *         just the AsyncObjectCaller
   1.246 + */
   1.247 +function AsyncObjectCaller(aObjects, aMethod, aListener) {
   1.248 +  this.objects = aObjects.slice(0);
   1.249 +  this.method = aMethod;
   1.250 +  this.listener = aListener;
   1.251 +
   1.252 +  this.callNext();
   1.253 +}
   1.254 +
   1.255 +AsyncObjectCaller.prototype = {
   1.256 +  objects: null,
   1.257 +  method: null,
   1.258 +  listener: null,
   1.259 +
   1.260 +  /**
   1.261 +   * Passes the next object to the listener or calls noMoreObjects if there
   1.262 +   * are none left.
   1.263 +   */
   1.264 +  callNext: function AOC_callNext() {
   1.265 +    if (this.objects.length == 0) {
   1.266 +      this.listener.noMoreObjects(this);
   1.267 +      return;
   1.268 +    }
   1.269 +
   1.270 +    let object = this.objects.shift();
   1.271 +    if (!this.method || this.method in object)
   1.272 +      this.listener.nextObject(this, object);
   1.273 +    else
   1.274 +      this.callNext();
   1.275 +  }
   1.276 +};
   1.277 +
   1.278 +/**
   1.279 + * This represents an author of an add-on (e.g. creator or developer)
   1.280 + *
   1.281 + * @param  aName
   1.282 + *         The name of the author
   1.283 + * @param  aURL
   1.284 + *         The URL of the author's profile page
   1.285 + */
   1.286 +function AddonAuthor(aName, aURL) {
   1.287 +  this.name = aName;
   1.288 +  this.url = aURL;
   1.289 +}
   1.290 +
   1.291 +AddonAuthor.prototype = {
   1.292 +  name: null,
   1.293 +  url: null,
   1.294 +
   1.295 +  // Returns the author's name, defaulting to the empty string
   1.296 +  toString: function AddonAuthor_toString() {
   1.297 +    return this.name || "";
   1.298 +  }
   1.299 +}
   1.300 +
   1.301 +/**
   1.302 + * This represents an screenshot for an add-on
   1.303 + *
   1.304 + * @param  aURL
   1.305 + *         The URL to the full version of the screenshot
   1.306 + * @param  aWidth
   1.307 + *         The width in pixels of the screenshot
   1.308 + * @param  aHeight
   1.309 + *         The height in pixels of the screenshot
   1.310 + * @param  aThumbnailURL
   1.311 + *         The URL to the thumbnail version of the screenshot
   1.312 + * @param  aThumbnailWidth
   1.313 + *         The width in pixels of the thumbnail version of the screenshot
   1.314 + * @param  aThumbnailHeight
   1.315 + *         The height in pixels of the thumbnail version of the screenshot
   1.316 + * @param  aCaption
   1.317 + *         The caption of the screenshot
   1.318 + */
   1.319 +function AddonScreenshot(aURL, aWidth, aHeight, aThumbnailURL,
   1.320 +                         aThumbnailWidth, aThumbnailHeight, aCaption) {
   1.321 +  this.url = aURL;
   1.322 +  if (aWidth) this.width = aWidth;
   1.323 +  if (aHeight) this.height = aHeight;
   1.324 +  if (aThumbnailURL) this.thumbnailURL = aThumbnailURL;
   1.325 +  if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth;
   1.326 +  if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight;
   1.327 +  if (aCaption) this.caption = aCaption;
   1.328 +}
   1.329 +
   1.330 +AddonScreenshot.prototype = {
   1.331 +  url: null,
   1.332 +  width: null,
   1.333 +  height: null,
   1.334 +  thumbnailURL: null,
   1.335 +  thumbnailWidth: null,
   1.336 +  thumbnailHeight: null,
   1.337 +  caption: null,
   1.338 +
   1.339 +  // Returns the screenshot URL, defaulting to the empty string
   1.340 +  toString: function AddonScreenshot_toString() {
   1.341 +    return this.url || "";
   1.342 +  }
   1.343 +}
   1.344 +
   1.345 +
   1.346 +/**
   1.347 + * This represents a compatibility override for an addon.
   1.348 + *
   1.349 + * @param  aType
   1.350 + *         Overrride type - "compatible" or "incompatible"
   1.351 + * @param  aMinVersion
   1.352 + *         Minimum version of the addon to match
   1.353 + * @param  aMaxVersion
   1.354 + *         Maximum version of the addon to match
   1.355 + * @param  aAppID
   1.356 + *         Application ID used to match appMinVersion and appMaxVersion
   1.357 + * @param  aAppMinVersion
   1.358 + *         Minimum version of the application to match
   1.359 + * @param  aAppMaxVersion
   1.360 + *         Maximum version of the application to match
   1.361 + */
   1.362 +function AddonCompatibilityOverride(aType, aMinVersion, aMaxVersion, aAppID,
   1.363 +                                    aAppMinVersion, aAppMaxVersion) {
   1.364 +  this.type = aType;
   1.365 +  this.minVersion = aMinVersion;
   1.366 +  this.maxVersion = aMaxVersion;
   1.367 +  this.appID = aAppID;
   1.368 +  this.appMinVersion = aAppMinVersion;
   1.369 +  this.appMaxVersion = aAppMaxVersion;
   1.370 +}
   1.371 +
   1.372 +AddonCompatibilityOverride.prototype = {
   1.373 +  /**
   1.374 +   * Type of override - "incompatible" or "compatible".
   1.375 +   * Only "incompatible" is supported for now.
   1.376 +   */
   1.377 +  type: null,
   1.378 +
   1.379 +  /**
   1.380 +   * Min version of the addon to match.
   1.381 +   */
   1.382 +  minVersion: null,
   1.383 +
   1.384 +  /**
   1.385 +   * Max version of the addon to match.
   1.386 +   */
   1.387 +  maxVersion: null,
   1.388 +
   1.389 +  /**
   1.390 +   * Application ID to match.
   1.391 +   */
   1.392 +  appID: null,
   1.393 +
   1.394 +  /**
   1.395 +   * Min version of the application to match.
   1.396 +   */
   1.397 +  appMinVersion: null,
   1.398 +
   1.399 +  /**
   1.400 +   * Max version of the application to match.
   1.401 +   */
   1.402 +  appMaxVersion: null
   1.403 +};
   1.404 +
   1.405 +
   1.406 +/**
   1.407 + * A type of add-on, used by the UI to determine how to display different types
   1.408 + * of add-ons.
   1.409 + *
   1.410 + * @param  aID
   1.411 + *         The add-on type ID
   1.412 + * @param  aLocaleURI
   1.413 + *         The URI of a localized properties file to get the displayable name
   1.414 + *         for the type from
   1.415 + * @param  aLocaleKey
   1.416 + *         The key for the string in the properties file or the actual display
   1.417 + *         name if aLocaleURI is null. Include %ID% to include the type ID in
   1.418 + *         the key
   1.419 + * @param  aViewType
   1.420 + *         The optional type of view to use in the UI
   1.421 + * @param  aUIPriority
   1.422 + *         The priority is used by the UI to list the types in order. Lower
   1.423 + *         values push the type higher in the list.
   1.424 + * @param  aFlags
   1.425 + *         An option set of flags that customize the display of the add-on in
   1.426 + *         the UI.
   1.427 + */
   1.428 +function AddonType(aID, aLocaleURI, aLocaleKey, aViewType, aUIPriority, aFlags) {
   1.429 +  if (!aID)
   1.430 +    throw Components.Exception("An AddonType must have an ID", Cr.NS_ERROR_INVALID_ARG);
   1.431 +
   1.432 +  if (aViewType && aUIPriority === undefined)
   1.433 +    throw Components.Exception("An AddonType with a defined view must have a set UI priority",
   1.434 +                               Cr.NS_ERROR_INVALID_ARG);
   1.435 +
   1.436 +  if (!aLocaleKey)
   1.437 +    throw Components.Exception("An AddonType must have a displayable name",
   1.438 +                               Cr.NS_ERROR_INVALID_ARG);
   1.439 +
   1.440 +  this.id = aID;
   1.441 +  this.uiPriority = aUIPriority;
   1.442 +  this.viewType = aViewType;
   1.443 +  this.flags = aFlags;
   1.444 +
   1.445 +  if (aLocaleURI) {
   1.446 +    this.__defineGetter__("name", function nameGetter() {
   1.447 +      delete this.name;
   1.448 +      let bundle = Services.strings.createBundle(aLocaleURI);
   1.449 +      this.name = bundle.GetStringFromName(aLocaleKey.replace("%ID%", aID));
   1.450 +      return this.name;
   1.451 +    });
   1.452 +  }
   1.453 +  else {
   1.454 +    this.name = aLocaleKey;
   1.455 +  }
   1.456 +}
   1.457 +
   1.458 +var gStarted = false;
   1.459 +var gStartupComplete = false;
   1.460 +var gCheckCompatibility = true;
   1.461 +var gStrictCompatibility = true;
   1.462 +var gCheckUpdateSecurityDefault = true;
   1.463 +var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
   1.464 +var gUpdateEnabled = true;
   1.465 +var gAutoUpdateDefault = true;
   1.466 +var gHotfixID = null;
   1.467 +
   1.468 +/**
   1.469 + * This is the real manager, kept here rather than in AddonManager to keep its
   1.470 + * contents hidden from API users.
   1.471 + */
   1.472 +var AddonManagerInternal = {
   1.473 +  managerListeners: [],
   1.474 +  installListeners: [],
   1.475 +  addonListeners: [],
   1.476 +  typeListeners: [],
   1.477 +  providers: [],
   1.478 +  types: {},
   1.479 +  startupChanges: {},
   1.480 +  // Store telemetry details per addon provider
   1.481 +  telemetryDetails: {},
   1.482 +
   1.483 +
   1.484 +  // A read-only wrapper around the types dictionary
   1.485 +  typesProxy: Proxy.create({
   1.486 +    getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) {
   1.487 +      if (!(aName in AddonManagerInternal.types))
   1.488 +        return undefined;
   1.489 +
   1.490 +      return {
   1.491 +        value: AddonManagerInternal.types[aName].type,
   1.492 +        writable: false,
   1.493 +        configurable: false,
   1.494 +        enumerable: true
   1.495 +      }
   1.496 +    },
   1.497 +
   1.498 +    getPropertyDescriptor: function typesProxy_getPropertyDescriptor(aName) {
   1.499 +      return this.getOwnPropertyDescriptor(aName);
   1.500 +    },
   1.501 +
   1.502 +    getOwnPropertyNames: function typesProxy_getOwnPropertyNames() {
   1.503 +      return Object.keys(AddonManagerInternal.types);
   1.504 +    },
   1.505 +
   1.506 +    getPropertyNames: function typesProxy_getPropertyNames() {
   1.507 +      return this.getOwnPropertyNames();
   1.508 +    },
   1.509 +
   1.510 +    delete: function typesProxy_delete(aName) {
   1.511 +      // Not allowed to delete properties
   1.512 +      return false;
   1.513 +    },
   1.514 +
   1.515 +    defineProperty: function typesProxy_defineProperty(aName, aProperty) {
   1.516 +      // Ignore attempts to define properties
   1.517 +    },
   1.518 +
   1.519 +    fix: function typesProxy_fix(){
   1.520 +      return undefined;
   1.521 +    },
   1.522 +
   1.523 +    // Despite MDC's claims to the contrary, it is required that this trap
   1.524 +    // be defined
   1.525 +    enumerate: function typesProxy_enumerate() {
   1.526 +      // All properties are enumerable
   1.527 +      return this.getPropertyNames();
   1.528 +    }
   1.529 +  }),
   1.530 +
   1.531 +  recordTimestamp: function AMI_recordTimestamp(name, value) {
   1.532 +    this.TelemetryTimestamps.add(name, value);
   1.533 +  },
   1.534 +
   1.535 +  validateBlocklist: function AMI_validateBlocklist() {
   1.536 +    let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
   1.537 +
   1.538 +    // If there is no application shipped blocklist then there is nothing to do
   1.539 +    if (!appBlocklist.exists())
   1.540 +      return;
   1.541 +
   1.542 +    let profileBlocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
   1.543 +
   1.544 +    // If there is no blocklist in the profile then copy the application shipped
   1.545 +    // one there
   1.546 +    if (!profileBlocklist.exists()) {
   1.547 +      try {
   1.548 +        appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
   1.549 +      }
   1.550 +      catch (e) {
   1.551 +        logger.warn("Failed to copy the application shipped blocklist to the profile", e);
   1.552 +      }
   1.553 +      return;
   1.554 +    }
   1.555 +
   1.556 +    let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
   1.557 +                     createInstance(Ci.nsIFileInputStream);
   1.558 +    try {
   1.559 +      let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
   1.560 +                    createInstance(Ci.nsIConverterInputStream);
   1.561 +      fileStream.init(appBlocklist, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
   1.562 +      cstream.init(fileStream, "UTF-8", 0, 0);
   1.563 +
   1.564 +      let data = "";
   1.565 +      let str = {};
   1.566 +      let read = 0;
   1.567 +      do {
   1.568 +        read = cstream.readString(0xffffffff, str);
   1.569 +        data += str.value;
   1.570 +      } while (read != 0);
   1.571 +
   1.572 +      let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
   1.573 +                   createInstance(Ci.nsIDOMParser);
   1.574 +      var doc = parser.parseFromString(data, "text/xml");
   1.575 +    }
   1.576 +    catch (e) {
   1.577 +      logger.warn("Application shipped blocklist could not be loaded", e);
   1.578 +      return;
   1.579 +    }
   1.580 +    finally {
   1.581 +      try {
   1.582 +        fileStream.close();
   1.583 +      }
   1.584 +      catch (e) {
   1.585 +        logger.warn("Unable to close blocklist file stream", e);
   1.586 +      }
   1.587 +    }
   1.588 +
   1.589 +    // If the namespace is incorrect then ignore the application shipped
   1.590 +    // blocklist
   1.591 +    if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
   1.592 +      logger.warn("Application shipped blocklist has an unexpected namespace (" +
   1.593 +                  doc.documentElement.namespaceURI + ")");
   1.594 +      return;
   1.595 +    }
   1.596 +
   1.597 +    // If there is no lastupdate information then ignore the application shipped
   1.598 +    // blocklist
   1.599 +    if (!doc.documentElement.hasAttribute("lastupdate"))
   1.600 +      return;
   1.601 +
   1.602 +    // If the application shipped blocklist is older than the profile blocklist
   1.603 +    // then do nothing
   1.604 +    if (doc.documentElement.getAttribute("lastupdate") <=
   1.605 +        profileBlocklist.lastModifiedTime)
   1.606 +      return;
   1.607 +
   1.608 +    // Otherwise copy the application shipped blocklist to the profile
   1.609 +    try {
   1.610 +      appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
   1.611 +    }
   1.612 +    catch (e) {
   1.613 +      logger.warn("Failed to copy the application shipped blocklist to the profile", e);
   1.614 +    }
   1.615 +  },
   1.616 +
   1.617 +  /**
   1.618 +   * Initializes the AddonManager, loading any known providers and initializing
   1.619 +   * them.
   1.620 +   */
   1.621 +  startup: function AMI_startup() {
   1.622 +    try {
   1.623 +      if (gStarted)
   1.624 +        return;
   1.625 +
   1.626 +      this.recordTimestamp("AMI_startup_begin");
   1.627 +
   1.628 +      // clear this for xpcshell test restarts
   1.629 +      for (let provider in this.telemetryDetails)
   1.630 +        delete this.telemetryDetails[provider];
   1.631 +
   1.632 +      let appChanged = undefined;
   1.633 +
   1.634 +      let oldAppVersion = null;
   1.635 +      try {
   1.636 +        oldAppVersion = Services.prefs.getCharPref(PREF_EM_LAST_APP_VERSION);
   1.637 +        appChanged = Services.appinfo.version != oldAppVersion;
   1.638 +      }
   1.639 +      catch (e) { }
   1.640 +
   1.641 +      let oldPlatformVersion = null;
   1.642 +      try {
   1.643 +        oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION);
   1.644 +      }
   1.645 +      catch (e) { }
   1.646 +
   1.647 +      if (appChanged !== false) {
   1.648 +        logger.debug("Application has been upgraded");
   1.649 +        Services.prefs.setCharPref(PREF_EM_LAST_APP_VERSION,
   1.650 +                                   Services.appinfo.version);
   1.651 +        Services.prefs.setCharPref(PREF_EM_LAST_PLATFORM_VERSION,
   1.652 +                                   Services.appinfo.platformVersion);
   1.653 +        Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION,
   1.654 +                                  (appChanged === undefined ? 0 : -1));
   1.655 +        this.validateBlocklist();
   1.656 +      }
   1.657 +
   1.658 +#ifndef MOZ_COMPATIBILITY_NIGHTLY
   1.659 +      PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
   1.660 +                                    Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
   1.661 +#endif
   1.662 +
   1.663 +      try {
   1.664 +        gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
   1.665 +      } catch (e) {}
   1.666 +      Services.prefs.addObserver(PREF_EM_CHECK_COMPATIBILITY, this, false);
   1.667 +
   1.668 +      try {
   1.669 +        gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
   1.670 +      } catch (e) {}
   1.671 +      Services.prefs.addObserver(PREF_EM_STRICT_COMPATIBILITY, this, false);
   1.672 +
   1.673 +      try {
   1.674 +        let defaultBranch = Services.prefs.getDefaultBranch("");
   1.675 +        gCheckUpdateSecurityDefault = defaultBranch.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
   1.676 +      } catch(e) {}
   1.677 +
   1.678 +      try {
   1.679 +        gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
   1.680 +      } catch (e) {}
   1.681 +      Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
   1.682 +
   1.683 +      try {
   1.684 +        gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
   1.685 +      } catch (e) {}
   1.686 +      Services.prefs.addObserver(PREF_EM_UPDATE_ENABLED, this, false);
   1.687 +
   1.688 +      try {
   1.689 +        gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
   1.690 +      } catch (e) {}
   1.691 +      Services.prefs.addObserver(PREF_EM_AUTOUPDATE_DEFAULT, this, false);
   1.692 +
   1.693 +      try {
   1.694 +        gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
   1.695 +      } catch (e) {}
   1.696 +      Services.prefs.addObserver(PREF_EM_HOTFIX_ID, this, false);
   1.697 +
   1.698 +      let defaultProvidersEnabled = true;
   1.699 +      try {
   1.700 +        defaultProvidersEnabled = Services.prefs.getBoolPref(PREF_DEFAULT_PROVIDERS_ENABLED);
   1.701 +      } catch (e) {}
   1.702 +      AddonManagerPrivate.recordSimpleMeasure("default_providers", defaultProvidersEnabled);
   1.703 +
   1.704 +      // Ensure all default providers have had a chance to register themselves
   1.705 +      if (defaultProvidersEnabled) {
   1.706 +        for (let url of DEFAULT_PROVIDERS) {
   1.707 +          try {
   1.708 +            let scope = {};
   1.709 +            Components.utils.import(url, scope);
   1.710 +            // Sanity check - make sure the provider exports a symbol that
   1.711 +            // has a 'startup' method
   1.712 +            let syms = Object.keys(scope);
   1.713 +            if ((syms.length < 1) ||
   1.714 +                (typeof scope[syms[0]].startup != "function")) {
   1.715 +              logger.warn("Provider " + url + " has no startup()");
   1.716 +              AddonManagerPrivate.recordException("AMI", "provider " + url, "no startup()");
   1.717 +            }
   1.718 +            logger.debug("Loaded provider scope for " + url + ": " + Object.keys(scope).toSource());
   1.719 +          }
   1.720 +          catch (e) {
   1.721 +            AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
   1.722 +            logger.error("Exception loading default provider \"" + url + "\"", e);
   1.723 +          }
   1.724 +        };
   1.725 +      }
   1.726 +
   1.727 +      // Load any providers registered in the category manager
   1.728 +      let catman = Cc["@mozilla.org/categorymanager;1"].
   1.729 +                   getService(Ci.nsICategoryManager);
   1.730 +      let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
   1.731 +      while (entries.hasMoreElements()) {
   1.732 +        let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
   1.733 +        let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
   1.734 +
   1.735 +        try {
   1.736 +          Components.utils.import(url, {});
   1.737 +        }
   1.738 +        catch (e) {
   1.739 +          AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
   1.740 +          logger.error("Exception loading provider " + entry + " from category \"" +
   1.741 +                url + "\"", e);
   1.742 +        }
   1.743 +      }
   1.744 +
   1.745 +      // Register our shutdown handler with the AsyncShutdown manager
   1.746 +      AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
   1.747 +                                                   this.shutdown.bind(this));
   1.748 +
   1.749 +      // Once we start calling providers we must allow all normal methods to work.
   1.750 +      gStarted = true;
   1.751 +
   1.752 +      this.callProviders("startup", appChanged, oldAppVersion,
   1.753 +                         oldPlatformVersion);
   1.754 +
   1.755 +      // If this is a new profile just pretend that there were no changes
   1.756 +      if (appChanged === undefined) {
   1.757 +        for (let type in this.startupChanges)
   1.758 +          delete this.startupChanges[type];
   1.759 +      }
   1.760 +
   1.761 +      gStartupComplete = true;
   1.762 +      this.recordTimestamp("AMI_startup_end");
   1.763 +    }
   1.764 +    catch (e) {
   1.765 +      logger.error("startup failed", e);
   1.766 +      AddonManagerPrivate.recordException("AMI", "startup failed", e);
   1.767 +    }
   1.768 +  },
   1.769 +
   1.770 +  /**
   1.771 +   * Registers a new AddonProvider.
   1.772 +   *
   1.773 +   * @param  aProvider
   1.774 +   *         The provider to register
   1.775 +   * @param  aTypes
   1.776 +   *         An optional array of add-on types
   1.777 +   */
   1.778 +  registerProvider: function AMI_registerProvider(aProvider, aTypes) {
   1.779 +    if (!aProvider || typeof aProvider != "object")
   1.780 +      throw Components.Exception("aProvider must be specified",
   1.781 +                                 Cr.NS_ERROR_INVALID_ARG);
   1.782 +
   1.783 +    if (aTypes && !Array.isArray(aTypes))
   1.784 +      throw Components.Exception("aTypes must be an array or null",
   1.785 +                                 Cr.NS_ERROR_INVALID_ARG);
   1.786 +
   1.787 +    this.providers.push(aProvider);
   1.788 +
   1.789 +    if (aTypes) {
   1.790 +      aTypes.forEach(function(aType) {
   1.791 +        if (!(aType.id in this.types)) {
   1.792 +          if (!VALID_TYPES_REGEXP.test(aType.id)) {
   1.793 +            logger.warn("Ignoring invalid type " + aType.id);
   1.794 +            return;
   1.795 +          }
   1.796 +
   1.797 +          this.types[aType.id] = {
   1.798 +            type: aType,
   1.799 +            providers: [aProvider]
   1.800 +          };
   1.801 +
   1.802 +          let typeListeners = this.typeListeners.slice(0);
   1.803 +          for (let listener of typeListeners) {
   1.804 +            safeCall(function listenerSafeCall() {
   1.805 +              listener.onTypeAdded(aType);
   1.806 +            });
   1.807 +          }
   1.808 +        }
   1.809 +        else {
   1.810 +          this.types[aType.id].providers.push(aProvider);
   1.811 +        }
   1.812 +      }, this);
   1.813 +    }
   1.814 +
   1.815 +    // If we're registering after startup call this provider's startup.
   1.816 +    if (gStarted)
   1.817 +      callProvider(aProvider, "startup");
   1.818 +  },
   1.819 +
   1.820 +  /**
   1.821 +   * Unregisters an AddonProvider.
   1.822 +   *
   1.823 +   * @param  aProvider
   1.824 +   *         The provider to unregister
   1.825 +   */
   1.826 +  unregisterProvider: function AMI_unregisterProvider(aProvider) {
   1.827 +    if (!aProvider || typeof aProvider != "object")
   1.828 +      throw Components.Exception("aProvider must be specified",
   1.829 +                                 Cr.NS_ERROR_INVALID_ARG);
   1.830 +
   1.831 +    let pos = 0;
   1.832 +    while (pos < this.providers.length) {
   1.833 +      if (this.providers[pos] == aProvider)
   1.834 +        this.providers.splice(pos, 1);
   1.835 +      else
   1.836 +        pos++;
   1.837 +    }
   1.838 +
   1.839 +    for (let type in this.types) {
   1.840 +      this.types[type].providers = this.types[type].providers.filter(function filterProvider(p) p != aProvider);
   1.841 +      if (this.types[type].providers.length == 0) {
   1.842 +        let oldType = this.types[type].type;
   1.843 +        delete this.types[type];
   1.844 +
   1.845 +        let typeListeners = this.typeListeners.slice(0);
   1.846 +        for (let listener of typeListeners) {
   1.847 +          safeCall(function listenerSafeCall() {
   1.848 +            listener.onTypeRemoved(oldType);
   1.849 +          });
   1.850 +        }
   1.851 +      }
   1.852 +    }
   1.853 +
   1.854 +    // If we're unregistering after startup call this provider's shutdown.
   1.855 +    if (gStarted)
   1.856 +      callProvider(aProvider, "shutdown");
   1.857 +  },
   1.858 +
   1.859 +  /**
   1.860 +   * Calls a method on all registered providers if it exists and consumes any
   1.861 +   * thrown exception. Return values are ignored. Any parameters after the
   1.862 +   * method parameter are passed to the provider's method.
   1.863 +   *
   1.864 +   * @param  aMethod
   1.865 +   *         The method name to call
   1.866 +   * @see    callProvider
   1.867 +   */
   1.868 +  callProviders: function AMI_callProviders(aMethod, ...aArgs) {
   1.869 +    if (!aMethod || typeof aMethod != "string")
   1.870 +      throw Components.Exception("aMethod must be a non-empty string",
   1.871 +                                 Cr.NS_ERROR_INVALID_ARG);
   1.872 +
   1.873 +    let providers = this.providers.slice(0);
   1.874 +    for (let provider of providers) {
   1.875 +      try {
   1.876 +        if (aMethod in provider)
   1.877 +          provider[aMethod].apply(provider, aArgs);
   1.878 +      }
   1.879 +      catch (e) {
   1.880 +        AddonManagerPrivate.recordException("AMI", "provider " + aMethod, e);
   1.881 +        logger.error("Exception calling provider " + aMethod, e);
   1.882 +      }
   1.883 +    }
   1.884 +  },
   1.885 +
   1.886 +  /**
   1.887 +   * Calls a method on all registered providers, if the provider implements
   1.888 +   * the method. The called method is expected to return a promise, and
   1.889 +   * callProvidersAsync returns a promise that resolves when every provider
   1.890 +   * method has either resolved or rejected. Rejection reasons are logged
   1.891 +   * but otherwise ignored. Return values are ignored. Any parameters after the
   1.892 +   * method parameter are passed to the provider's method.
   1.893 +   *
   1.894 +   * @param  aMethod
   1.895 +   *         The method name to call
   1.896 +   * @see    callProvider
   1.897 +   */
   1.898 +  callProvidersAsync: function AMI_callProviders(aMethod, ...aArgs) {
   1.899 +    if (!aMethod || typeof aMethod != "string")
   1.900 +      throw Components.Exception("aMethod must be a non-empty string",
   1.901 +                                 Cr.NS_ERROR_INVALID_ARG);
   1.902 +
   1.903 +    let allProviders = [];
   1.904 +
   1.905 +    let providers = this.providers.slice(0);
   1.906 +    for (let provider of providers) {
   1.907 +      try {
   1.908 +        if (aMethod in provider) {
   1.909 +          // Resolve a new promise with the result of the method, to handle both
   1.910 +          // methods that return values (or nothing) and methods that return promises.
   1.911 +          let providerResult = provider[aMethod].apply(provider, aArgs);
   1.912 +          let nextPromise = Promise.resolve(providerResult);
   1.913 +          // Log and swallow the errors from methods that do return promises.
   1.914 +          nextPromise = nextPromise.then(
   1.915 +              null,
   1.916 +              e => logger.error("Exception calling provider " + aMethod, e));
   1.917 +          allProviders.push(nextPromise);
   1.918 +        }
   1.919 +      }
   1.920 +      catch (e) {
   1.921 +        logger.error("Exception calling provider " + aMethod, e);
   1.922 +      }
   1.923 +    }
   1.924 +    // Because we use promise.then to catch and log all errors above, Promise.all()
   1.925 +    // will never exit early because of a rejection.
   1.926 +    return Promise.all(allProviders);
   1.927 +  },
   1.928 +
   1.929 +  /**
   1.930 +   * Shuts down the addon manager and all registered providers, this must clean
   1.931 +   * up everything in order for automated tests to fake restarts.
   1.932 +   * @return Promise{null} that resolves when all providers and dependent modules
   1.933 +   *                       have finished shutting down
   1.934 +   */
   1.935 +  shutdown: function AMI_shutdown() {
   1.936 +    logger.debug("shutdown");
   1.937 +    // Clean up listeners
   1.938 +    Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
   1.939 +    Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
   1.940 +    Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
   1.941 +    Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
   1.942 +    Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
   1.943 +    Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
   1.944 +
   1.945 +    // Only shut down providers if they've been started. Shut down
   1.946 +    // AddonRepository after providers (if any).
   1.947 +    let shuttingDown = null;
   1.948 +    if (gStarted) {
   1.949 +      shuttingDown = this.callProvidersAsync("shutdown")
   1.950 +        .then(null,
   1.951 +              err => logger.error("Failure during async provider shutdown", err))
   1.952 +        .then(() => AddonRepository.shutdown());
   1.953 +    }
   1.954 +    else {
   1.955 +      shuttingDown = AddonRepository.shutdown();
   1.956 +    }
   1.957 +
   1.958 +      shuttingDown.then(val => logger.debug("Async provider shutdown done"),
   1.959 +                        err => logger.error("Failure during AddonRepository shutdown", err))
   1.960 +      .then(() => {
   1.961 +        this.managerListeners.splice(0, this.managerListeners.length);
   1.962 +        this.installListeners.splice(0, this.installListeners.length);
   1.963 +        this.addonListeners.splice(0, this.addonListeners.length);
   1.964 +        this.typeListeners.splice(0, this.typeListeners.length);
   1.965 +        for (let type in this.startupChanges)
   1.966 +          delete this.startupChanges[type];
   1.967 +        gStarted = false;
   1.968 +        gStartupComplete = false;
   1.969 +      });
   1.970 +    return shuttingDown;
   1.971 +  },
   1.972 +
   1.973 +  /**
   1.974 +   * Notified when a preference we're interested in has changed.
   1.975 +   *
   1.976 +   * @see nsIObserver
   1.977 +   */
   1.978 +  observe: function AMI_observe(aSubject, aTopic, aData) {
   1.979 +    switch (aData) {
   1.980 +      case PREF_EM_CHECK_COMPATIBILITY: {
   1.981 +        let oldValue = gCheckCompatibility;
   1.982 +        try {
   1.983 +          gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
   1.984 +        } catch(e) {
   1.985 +          gCheckCompatibility = true;
   1.986 +        }
   1.987 +
   1.988 +        this.callManagerListeners("onCompatibilityModeChanged");
   1.989 +
   1.990 +        if (gCheckCompatibility != oldValue)
   1.991 +          this.updateAddonAppDisabledStates();
   1.992 +
   1.993 +        break;
   1.994 +      }
   1.995 +      case PREF_EM_STRICT_COMPATIBILITY: {
   1.996 +        let oldValue = gStrictCompatibility;
   1.997 +        try {
   1.998 +          gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
   1.999 +        } catch(e) {
  1.1000 +          gStrictCompatibility = true;
  1.1001 +        }
  1.1002 +
  1.1003 +        this.callManagerListeners("onCompatibilityModeChanged");
  1.1004 +
  1.1005 +        if (gStrictCompatibility != oldValue)
  1.1006 +          this.updateAddonAppDisabledStates();
  1.1007 +
  1.1008 +        break;
  1.1009 +      }
  1.1010 +      case PREF_EM_CHECK_UPDATE_SECURITY: {
  1.1011 +        let oldValue = gCheckUpdateSecurity;
  1.1012 +        try {
  1.1013 +          gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
  1.1014 +        } catch(e) {
  1.1015 +          gCheckUpdateSecurity = true;
  1.1016 +        }
  1.1017 +
  1.1018 +        this.callManagerListeners("onCheckUpdateSecurityChanged");
  1.1019 +
  1.1020 +        if (gCheckUpdateSecurity != oldValue)
  1.1021 +          this.updateAddonAppDisabledStates();
  1.1022 +
  1.1023 +        break;
  1.1024 +      }
  1.1025 +      case PREF_EM_UPDATE_ENABLED: {
  1.1026 +        let oldValue = gUpdateEnabled;
  1.1027 +        try {
  1.1028 +          gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
  1.1029 +        } catch(e) {
  1.1030 +          gUpdateEnabled = true;
  1.1031 +        }
  1.1032 +
  1.1033 +        this.callManagerListeners("onUpdateModeChanged");
  1.1034 +        break;
  1.1035 +      }
  1.1036 +      case PREF_EM_AUTOUPDATE_DEFAULT: {
  1.1037 +        let oldValue = gAutoUpdateDefault;
  1.1038 +        try {
  1.1039 +          gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
  1.1040 +        } catch(e) {
  1.1041 +          gAutoUpdateDefault = true;
  1.1042 +        }
  1.1043 +
  1.1044 +        this.callManagerListeners("onUpdateModeChanged");
  1.1045 +        break;
  1.1046 +      }
  1.1047 +      case PREF_EM_HOTFIX_ID: {
  1.1048 +        try {
  1.1049 +          gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
  1.1050 +        } catch(e) {
  1.1051 +          gHotfixID = null;
  1.1052 +        }
  1.1053 +        break;
  1.1054 +      }
  1.1055 +    }
  1.1056 +  },
  1.1057 +
  1.1058 +  /**
  1.1059 +   * Replaces %...% strings in an addon url (update and updateInfo) with
  1.1060 +   * appropriate values.
  1.1061 +   *
  1.1062 +   * @param  aAddon
  1.1063 +   *         The Addon representing the add-on
  1.1064 +   * @param  aUri
  1.1065 +   *         The string representation of the URI to escape
  1.1066 +   * @param  aAppVersion
  1.1067 +   *         The optional application version to use for %APP_VERSION%
  1.1068 +   * @return The appropriately escaped URI.
  1.1069 +   */
  1.1070 +  escapeAddonURI: function AMI_escapeAddonURI(aAddon, aUri, aAppVersion)
  1.1071 +  {
  1.1072 +    if (!aAddon || typeof aAddon != "object")
  1.1073 +      throw Components.Exception("aAddon must be an Addon object",
  1.1074 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1075 +
  1.1076 +    if (!aUri || typeof aUri != "string")
  1.1077 +      throw Components.Exception("aUri must be a non-empty string",
  1.1078 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1079 +
  1.1080 +    if (aAppVersion && typeof aAppVersion != "string")
  1.1081 +      throw Components.Exception("aAppVersion must be a string or null",
  1.1082 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1083 +
  1.1084 +    var addonStatus = aAddon.userDisabled || aAddon.softDisabled ? "userDisabled"
  1.1085 +                                                                 : "userEnabled";
  1.1086 +
  1.1087 +    if (!aAddon.isCompatible)
  1.1088 +      addonStatus += ",incompatible";
  1.1089 +    if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
  1.1090 +      addonStatus += ",blocklisted";
  1.1091 +    if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  1.1092 +      addonStatus += ",softblocked";
  1.1093 +
  1.1094 +    try {
  1.1095 +      var xpcomABI = Services.appinfo.XPCOMABI;
  1.1096 +    } catch (ex) {
  1.1097 +      xpcomABI = UNKNOWN_XPCOM_ABI;
  1.1098 +    }
  1.1099 +
  1.1100 +    let uri = aUri.replace(/%ITEM_ID%/g, aAddon.id);
  1.1101 +    uri = uri.replace(/%ITEM_VERSION%/g, aAddon.version);
  1.1102 +    uri = uri.replace(/%ITEM_STATUS%/g, addonStatus);
  1.1103 +    uri = uri.replace(/%APP_ID%/g, Services.appinfo.ID);
  1.1104 +    uri = uri.replace(/%APP_VERSION%/g, aAppVersion ? aAppVersion :
  1.1105 +                                                      Services.appinfo.version);
  1.1106 +    uri = uri.replace(/%REQ_VERSION%/g, UPDATE_REQUEST_VERSION);
  1.1107 +    uri = uri.replace(/%APP_OS%/g, Services.appinfo.OS);
  1.1108 +    uri = uri.replace(/%APP_ABI%/g, xpcomABI);
  1.1109 +    uri = uri.replace(/%APP_LOCALE%/g, getLocale());
  1.1110 +    uri = uri.replace(/%CURRENT_APP_VERSION%/g, Services.appinfo.version);
  1.1111 +
  1.1112 +    // Replace custom parameters (names of custom parameters must have at
  1.1113 +    // least 3 characters to prevent lookups for something like %D0%C8)
  1.1114 +    var catMan = null;
  1.1115 +    uri = uri.replace(/%(\w{3,})%/g, function parameterReplace(aMatch, aParam) {
  1.1116 +      if (!catMan) {
  1.1117 +        catMan = Cc["@mozilla.org/categorymanager;1"].
  1.1118 +                 getService(Ci.nsICategoryManager);
  1.1119 +      }
  1.1120 +
  1.1121 +      try {
  1.1122 +        var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, aParam);
  1.1123 +        var paramHandler = Cc[contractID].getService(Ci.nsIPropertyBag2);
  1.1124 +        return paramHandler.getPropertyAsAString(aParam);
  1.1125 +      }
  1.1126 +      catch(e) {
  1.1127 +        return aMatch;
  1.1128 +      }
  1.1129 +    });
  1.1130 +
  1.1131 +    // escape() does not properly encode + symbols in any embedded FVF strings.
  1.1132 +    return uri.replace(/\+/g, "%2B");
  1.1133 +  },
  1.1134 +
  1.1135 +  /**
  1.1136 +   * Performs a background update check by starting an update for all add-ons
  1.1137 +   * that can be updated.
  1.1138 +   */
  1.1139 +  backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
  1.1140 +    if (!gStarted)
  1.1141 +      throw Components.Exception("AddonManager is not initialized",
  1.1142 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1143 +
  1.1144 +    let hotfixID = this.hotfixID;
  1.1145 +
  1.1146 +    let checkHotfix = hotfixID &&
  1.1147 +                      Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
  1.1148 +                      Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
  1.1149 +
  1.1150 +    if (!this.updateEnabled && !checkHotfix)
  1.1151 +      return;
  1.1152 +
  1.1153 +    Services.obs.notifyObservers(null, "addons-background-update-start", null);
  1.1154 +
  1.1155 +    // Start this from one to ensure the whole of this function completes before
  1.1156 +    // we can send the complete notification. Some parts can in some cases
  1.1157 +    // complete synchronously before later parts have a chance to increment
  1.1158 +    // pendingUpdates.
  1.1159 +    let pendingUpdates = 1;
  1.1160 +
  1.1161 +    function notifyComplete() {
  1.1162 +      if (--pendingUpdates == 0) {
  1.1163 +        Services.obs.notifyObservers(null,
  1.1164 +                                     "addons-background-update-complete",
  1.1165 +                                     null);
  1.1166 +      }
  1.1167 +    }
  1.1168 +
  1.1169 +    if (this.updateEnabled) {
  1.1170 +      let scope = {};
  1.1171 +      Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
  1.1172 +      scope.LightweightThemeManager.updateCurrentTheme();
  1.1173 +
  1.1174 +      pendingUpdates++;
  1.1175 +      this.getAllAddons(function getAddonsCallback(aAddons) {
  1.1176 +        // If there is a known hotfix then exclude it from the list of add-ons to update.
  1.1177 +        var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
  1.1178 +
  1.1179 +        // Repopulate repository cache first, to ensure compatibility overrides
  1.1180 +        // are up to date before checking for addon updates.
  1.1181 +        AddonRepository.backgroundUpdateCheck(
  1.1182 +                     ids, function BUC_backgroundUpdateCheckCallback() {
  1.1183 +          pendingUpdates += aAddons.length;
  1.1184 +          aAddons.forEach(function BUC_forEachCallback(aAddon) {
  1.1185 +            if (aAddon.id == hotfixID) {
  1.1186 +              notifyComplete();
  1.1187 +              return;
  1.1188 +            }
  1.1189 +
  1.1190 +            // Check all add-ons for updates so that any compatibility updates will
  1.1191 +            // be applied
  1.1192 +            aAddon.findUpdates({
  1.1193 +              onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
  1.1194 +                // Start installing updates when the add-on can be updated and
  1.1195 +                // background updates should be applied.
  1.1196 +                if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
  1.1197 +                    AddonManager.shouldAutoUpdate(aAddon)) {
  1.1198 +                  aInstall.install();
  1.1199 +                }
  1.1200 +              },
  1.1201 +
  1.1202 +              onUpdateFinished: notifyComplete
  1.1203 +            }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
  1.1204 +          });
  1.1205 +
  1.1206 +          notifyComplete();
  1.1207 +        });
  1.1208 +      });
  1.1209 +    }
  1.1210 +
  1.1211 +    if (checkHotfix) {
  1.1212 +      var hotfixVersion = "";
  1.1213 +      try {
  1.1214 +        hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
  1.1215 +      }
  1.1216 +      catch (e) { }
  1.1217 +
  1.1218 +      let url = null;
  1.1219 +      if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
  1.1220 +        url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
  1.1221 +      else
  1.1222 +        url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
  1.1223 +
  1.1224 +      // Build the URI from a fake add-on data.
  1.1225 +      url = AddonManager.escapeAddonURI({
  1.1226 +        id: hotfixID,
  1.1227 +        version: hotfixVersion,
  1.1228 +        userDisabled: false,
  1.1229 +        appDisabled: false
  1.1230 +      }, url);
  1.1231 +
  1.1232 +      pendingUpdates++;
  1.1233 +      Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
  1.1234 +      AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
  1.1235 +        onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) {
  1.1236 +          let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates);
  1.1237 +          if (!update) {
  1.1238 +            notifyComplete();
  1.1239 +            return;
  1.1240 +          }
  1.1241 +
  1.1242 +          // If the available version isn't newer than the last installed
  1.1243 +          // version then ignore it.
  1.1244 +          if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
  1.1245 +            notifyComplete();
  1.1246 +            return;
  1.1247 +          }
  1.1248 +
  1.1249 +          logger.debug("Downloading hotfix version " + update.version);
  1.1250 +          AddonManager.getInstallForURL(update.updateURL,
  1.1251 +                                       function BUC_getInstallForURL(aInstall) {
  1.1252 +            aInstall.addListener({
  1.1253 +              onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
  1.1254 +                try {
  1.1255 +                  if (!Services.prefs.getBoolPref(PREF_EM_CERT_CHECKATTRIBUTES))
  1.1256 +                    return;
  1.1257 +                }
  1.1258 +                catch (e) {
  1.1259 +                  // By default don't do certificate checks.
  1.1260 +                  return;
  1.1261 +                }
  1.1262 +
  1.1263 +                try {
  1.1264 +                  CertUtils.validateCert(aInstall.certificate,
  1.1265 +                                         CertUtils.readCertPrefs(PREF_EM_HOTFIX_CERTS));
  1.1266 +                }
  1.1267 +                catch (e) {
  1.1268 +                  logger.warn("The hotfix add-on was not signed by the expected " +
  1.1269 +                       "certificate and so will not be installed.");
  1.1270 +                  aInstall.cancel();
  1.1271 +                }
  1.1272 +              },
  1.1273 +
  1.1274 +              onInstallEnded: function BUC_onInstallEnded(aInstall) {
  1.1275 +                // Remember the last successfully installed version.
  1.1276 +                Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
  1.1277 +                                           aInstall.version);
  1.1278 +              },
  1.1279 +
  1.1280 +              onInstallCancelled: function BUC_onInstallCancelled(aInstall) {
  1.1281 +                // Revert to the previous version if the installation was
  1.1282 +                // cancelled.
  1.1283 +                Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
  1.1284 +                                           hotfixVersion);
  1.1285 +              }
  1.1286 +            });
  1.1287 +
  1.1288 +            aInstall.install();
  1.1289 +
  1.1290 +            notifyComplete();
  1.1291 +          }, "application/x-xpinstall", update.updateHash, null,
  1.1292 +             null, update.version);
  1.1293 +        },
  1.1294 +
  1.1295 +        onUpdateCheckError: notifyComplete
  1.1296 +      });
  1.1297 +    }
  1.1298 +
  1.1299 +    notifyComplete();
  1.1300 +  },
  1.1301 +
  1.1302 +  /**
  1.1303 +   * Adds a add-on to the list of detected changes for this startup. If
  1.1304 +   * addStartupChange is called multiple times for the same add-on in the same
  1.1305 +   * startup then only the most recent change will be remembered.
  1.1306 +   *
  1.1307 +   * @param  aType
  1.1308 +   *         The type of change as a string. Providers can define their own
  1.1309 +   *         types of changes or use the existing defined STARTUP_CHANGE_*
  1.1310 +   *         constants
  1.1311 +   * @param  aID
  1.1312 +   *         The ID of the add-on
  1.1313 +   */
  1.1314 +  addStartupChange: function AMI_addStartupChange(aType, aID) {
  1.1315 +    if (!aType || typeof aType != "string")
  1.1316 +      throw Components.Exception("aType must be a non-empty string",
  1.1317 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1318 +
  1.1319 +    if (!aID || typeof aID != "string")
  1.1320 +      throw Components.Exception("aID must be a non-empty string",
  1.1321 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1322 +
  1.1323 +    if (gStartupComplete)
  1.1324 +      return;
  1.1325 +
  1.1326 +    // Ensure that an ID is only listed in one type of change
  1.1327 +    for (let type in this.startupChanges)
  1.1328 +      this.removeStartupChange(type, aID);
  1.1329 +
  1.1330 +    if (!(aType in this.startupChanges))
  1.1331 +      this.startupChanges[aType] = [];
  1.1332 +    this.startupChanges[aType].push(aID);
  1.1333 +  },
  1.1334 +
  1.1335 +  /**
  1.1336 +   * Removes a startup change for an add-on.
  1.1337 +   *
  1.1338 +   * @param  aType
  1.1339 +   *         The type of change
  1.1340 +   * @param  aID
  1.1341 +   *         The ID of the add-on
  1.1342 +   */
  1.1343 +  removeStartupChange: function AMI_removeStartupChange(aType, aID) {
  1.1344 +    if (!aType || typeof aType != "string")
  1.1345 +      throw Components.Exception("aType must be a non-empty string",
  1.1346 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1347 +
  1.1348 +    if (!aID || typeof aID != "string")
  1.1349 +      throw Components.Exception("aID must be a non-empty string",
  1.1350 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1351 +
  1.1352 +    if (gStartupComplete)
  1.1353 +      return;
  1.1354 +
  1.1355 +    if (!(aType in this.startupChanges))
  1.1356 +      return;
  1.1357 +
  1.1358 +    this.startupChanges[aType] = this.startupChanges[aType].filter(
  1.1359 +                                 function filterItem(aItem) aItem != aID);
  1.1360 +  },
  1.1361 +
  1.1362 +  /**
  1.1363 +   * Calls all registered AddonManagerListeners with an event. Any parameters
  1.1364 +   * after the method parameter are passed to the listener.
  1.1365 +   *
  1.1366 +   * @param  aMethod
  1.1367 +   *         The method on the listeners to call
  1.1368 +   */
  1.1369 +  callManagerListeners: function AMI_callManagerListeners(aMethod, ...aArgs) {
  1.1370 +    if (!gStarted)
  1.1371 +      throw Components.Exception("AddonManager is not initialized",
  1.1372 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1373 +
  1.1374 +    if (!aMethod || typeof aMethod != "string")
  1.1375 +      throw Components.Exception("aMethod must be a non-empty string",
  1.1376 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1377 +
  1.1378 +    let managerListeners = this.managerListeners.slice(0);
  1.1379 +    for (let listener of managerListeners) {
  1.1380 +      try {
  1.1381 +        if (aMethod in listener)
  1.1382 +          listener[aMethod].apply(listener, aArgs);
  1.1383 +      }
  1.1384 +      catch (e) {
  1.1385 +        logger.warn("AddonManagerListener threw exception when calling " + aMethod, e);
  1.1386 +      }
  1.1387 +    }
  1.1388 +  },
  1.1389 +
  1.1390 +  /**
  1.1391 +   * Calls all registered InstallListeners with an event. Any parameters after
  1.1392 +   * the extraListeners parameter are passed to the listener.
  1.1393 +   *
  1.1394 +   * @param  aMethod
  1.1395 +   *         The method on the listeners to call
  1.1396 +   * @param  aExtraListeners
  1.1397 +   *         An optional array of extra InstallListeners to also call
  1.1398 +   * @return false if any of the listeners returned false, true otherwise
  1.1399 +   */
  1.1400 +  callInstallListeners: function AMI_callInstallListeners(aMethod,
  1.1401 +                                 aExtraListeners, ...aArgs) {
  1.1402 +    if (!gStarted)
  1.1403 +      throw Components.Exception("AddonManager is not initialized",
  1.1404 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1405 +
  1.1406 +    if (!aMethod || typeof aMethod != "string")
  1.1407 +      throw Components.Exception("aMethod must be a non-empty string",
  1.1408 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1409 +
  1.1410 +    if (aExtraListeners && !Array.isArray(aExtraListeners))
  1.1411 +      throw Components.Exception("aExtraListeners must be an array or null",
  1.1412 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1413 +
  1.1414 +    let result = true;
  1.1415 +    let listeners;
  1.1416 +    if (aExtraListeners)
  1.1417 +      listeners = aExtraListeners.concat(this.installListeners);
  1.1418 +    else
  1.1419 +      listeners = this.installListeners.slice(0);
  1.1420 +
  1.1421 +    for (let listener of listeners) {
  1.1422 +      try {
  1.1423 +        if (aMethod in listener) {
  1.1424 +          if (listener[aMethod].apply(listener, aArgs) === false)
  1.1425 +            result = false;
  1.1426 +        }
  1.1427 +      }
  1.1428 +      catch (e) {
  1.1429 +        logger.warn("InstallListener threw exception when calling " + aMethod, e);
  1.1430 +      }
  1.1431 +    }
  1.1432 +    return result;
  1.1433 +  },
  1.1434 +
  1.1435 +  /**
  1.1436 +   * Calls all registered AddonListeners with an event. Any parameters after
  1.1437 +   * the method parameter are passed to the listener.
  1.1438 +   *
  1.1439 +   * @param  aMethod
  1.1440 +   *         The method on the listeners to call
  1.1441 +   */
  1.1442 +  callAddonListeners: function AMI_callAddonListeners(aMethod, ...aArgs) {
  1.1443 +    if (!gStarted)
  1.1444 +      throw Components.Exception("AddonManager is not initialized",
  1.1445 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1446 +
  1.1447 +    if (!aMethod || typeof aMethod != "string")
  1.1448 +      throw Components.Exception("aMethod must be a non-empty string",
  1.1449 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1450 +
  1.1451 +    let addonListeners = this.addonListeners.slice(0);
  1.1452 +    for (let listener of addonListeners) {
  1.1453 +      try {
  1.1454 +        if (aMethod in listener)
  1.1455 +          listener[aMethod].apply(listener, aArgs);
  1.1456 +      }
  1.1457 +      catch (e) {
  1.1458 +        logger.warn("AddonListener threw exception when calling " + aMethod, e);
  1.1459 +      }
  1.1460 +    }
  1.1461 +  },
  1.1462 +
  1.1463 +  /**
  1.1464 +   * Notifies all providers that an add-on has been enabled when that type of
  1.1465 +   * add-on only supports a single add-on being enabled at a time. This allows
  1.1466 +   * the providers to disable theirs if necessary.
  1.1467 +   *
  1.1468 +   * @param  aID
  1.1469 +   *         The ID of the enabled add-on
  1.1470 +   * @param  aType
  1.1471 +   *         The type of the enabled add-on
  1.1472 +   * @param  aPendingRestart
  1.1473 +   *         A boolean indicating if the change will only take place the next
  1.1474 +   *         time the application is restarted
  1.1475 +   */
  1.1476 +  notifyAddonChanged: function AMI_notifyAddonChanged(aID, aType, aPendingRestart) {
  1.1477 +    if (!gStarted)
  1.1478 +      throw Components.Exception("AddonManager is not initialized",
  1.1479 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1480 +
  1.1481 +    if (aID && typeof aID != "string")
  1.1482 +      throw Components.Exception("aID must be a string or null",
  1.1483 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1484 +
  1.1485 +    if (!aType || typeof aType != "string")
  1.1486 +      throw Components.Exception("aType must be a non-empty string",
  1.1487 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1488 +
  1.1489 +    this.callProviders("addonChanged", aID, aType, aPendingRestart);
  1.1490 +  },
  1.1491 +
  1.1492 +  /**
  1.1493 +   * Notifies all providers they need to update the appDisabled property for
  1.1494 +   * their add-ons in response to an application change such as a blocklist
  1.1495 +   * update.
  1.1496 +   */
  1.1497 +  updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
  1.1498 +    if (!gStarted)
  1.1499 +      throw Components.Exception("AddonManager is not initialized",
  1.1500 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1501 +
  1.1502 +    this.callProviders("updateAddonAppDisabledStates");
  1.1503 +  },
  1.1504 +
  1.1505 +  /**
  1.1506 +   * Notifies all providers that the repository has updated its data for
  1.1507 +   * installed add-ons.
  1.1508 +   *
  1.1509 +   * @param  aCallback
  1.1510 +   *         Function to call when operation is complete.
  1.1511 +   */
  1.1512 +  updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
  1.1513 +    if (!gStarted)
  1.1514 +      throw Components.Exception("AddonManager is not initialized",
  1.1515 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1516 +
  1.1517 +    if (typeof aCallback != "function")
  1.1518 +      throw Components.Exception("aCallback must be a function",
  1.1519 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1520 +
  1.1521 +    new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", {
  1.1522 +      nextObject: function updateAddonRepositoryData_nextObject(aCaller, aProvider) {
  1.1523 +        callProvider(aProvider,
  1.1524 +                     "updateAddonRepositoryData",
  1.1525 +                     null,
  1.1526 +                     aCaller.callNext.bind(aCaller));
  1.1527 +      },
  1.1528 +      noMoreObjects: function updateAddonRepositoryData_noMoreObjects(aCaller) {
  1.1529 +        safeCall(aCallback);
  1.1530 +        // only tests should care about this
  1.1531 +        Services.obs.notifyObservers(null, "TEST:addon-repository-data-updated", null);
  1.1532 +      }
  1.1533 +    });
  1.1534 +  },
  1.1535 +
  1.1536 +  /**
  1.1537 +   * Asynchronously gets an AddonInstall for a URL.
  1.1538 +   *
  1.1539 +   * @param  aUrl
  1.1540 +   *         The string represenation of the URL the add-on is located at
  1.1541 +   * @param  aCallback
  1.1542 +   *         A callback to pass the AddonInstall to
  1.1543 +   * @param  aMimetype
  1.1544 +   *         The mimetype of the add-on
  1.1545 +   * @param  aHash
  1.1546 +   *         An optional hash of the add-on
  1.1547 +   * @param  aName
  1.1548 +   *         An optional placeholder name while the add-on is being downloaded
  1.1549 +   * @param  aIcons
  1.1550 +   *         Optional placeholder icons while the add-on is being downloaded
  1.1551 +   * @param  aVersion
  1.1552 +   *         An optional placeholder version while the add-on is being downloaded
  1.1553 +   * @param  aLoadGroup
  1.1554 +   *         An optional nsILoadGroup to associate any network requests with
  1.1555 +   * @throws if the aUrl, aCallback or aMimetype arguments are not specified
  1.1556 +   */
  1.1557 +  getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype,
  1.1558 +                                                  aHash, aName, aIcons,
  1.1559 +                                                  aVersion, aLoadGroup) {
  1.1560 +    if (!gStarted)
  1.1561 +      throw Components.Exception("AddonManager is not initialized",
  1.1562 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1563 +
  1.1564 +    if (!aUrl || typeof aUrl != "string")
  1.1565 +      throw Components.Exception("aURL must be a non-empty string",
  1.1566 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1567 +
  1.1568 +    if (typeof aCallback != "function")
  1.1569 +      throw Components.Exception("aCallback must be a function",
  1.1570 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1571 +
  1.1572 +    if (!aMimetype || typeof aMimetype != "string")
  1.1573 +      throw Components.Exception("aMimetype must be a non-empty string",
  1.1574 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1575 +
  1.1576 +    if (aHash && typeof aHash != "string")
  1.1577 +      throw Components.Exception("aHash must be a string or null",
  1.1578 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1579 +
  1.1580 +    if (aName && typeof aName != "string")
  1.1581 +      throw Components.Exception("aName must be a string or null",
  1.1582 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1583 +
  1.1584 +    if (aIcons) {
  1.1585 +      if (typeof aIcons == "string")
  1.1586 +        aIcons = { "32": aIcons };
  1.1587 +      else if (typeof aIcons != "object")
  1.1588 +        throw Components.Exception("aIcons must be a string, an object or null",
  1.1589 +                                   Cr.NS_ERROR_INVALID_ARG);
  1.1590 +    } else {
  1.1591 +      aIcons = {};
  1.1592 +    }
  1.1593 +
  1.1594 +    if (aVersion && typeof aVersion != "string")
  1.1595 +      throw Components.Exception("aVersion must be a string or null",
  1.1596 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1597 +
  1.1598 +    if (aLoadGroup && (!(aLoadGroup instanceof Ci.nsILoadGroup)))
  1.1599 +      throw Components.Exception("aLoadGroup must be a nsILoadGroup or null",
  1.1600 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1601 +
  1.1602 +    let providers = this.providers.slice(0);
  1.1603 +    for (let provider of providers) {
  1.1604 +      if (callProvider(provider, "supportsMimetype", false, aMimetype)) {
  1.1605 +        callProvider(provider, "getInstallForURL", null,
  1.1606 +                     aUrl, aHash, aName, aIcons, aVersion, aLoadGroup,
  1.1607 +                     function  getInstallForURL_safeCall(aInstall) {
  1.1608 +          safeCall(aCallback, aInstall);
  1.1609 +        });
  1.1610 +        return;
  1.1611 +      }
  1.1612 +    }
  1.1613 +    safeCall(aCallback, null);
  1.1614 +  },
  1.1615 +
  1.1616 +  /**
  1.1617 +   * Asynchronously gets an AddonInstall for an nsIFile.
  1.1618 +   *
  1.1619 +   * @param  aFile
  1.1620 +   *         The nsIFile where the add-on is located
  1.1621 +   * @param  aCallback
  1.1622 +   *         A callback to pass the AddonInstall to
  1.1623 +   * @param  aMimetype
  1.1624 +   *         An optional mimetype hint for the add-on
  1.1625 +   * @throws if the aFile or aCallback arguments are not specified
  1.1626 +   */
  1.1627 +  getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) {
  1.1628 +    if (!gStarted)
  1.1629 +      throw Components.Exception("AddonManager is not initialized",
  1.1630 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1631 +
  1.1632 +    if (!(aFile instanceof Ci.nsIFile))
  1.1633 +      throw Components.Exception("aFile must be a nsIFile",
  1.1634 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1635 +
  1.1636 +    if (typeof aCallback != "function")
  1.1637 +      throw Components.Exception("aCallback must be a function",
  1.1638 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1639 +
  1.1640 +    if (aMimetype && typeof aMimetype != "string")
  1.1641 +      throw Components.Exception("aMimetype must be a string or null",
  1.1642 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1643 +
  1.1644 +    new AsyncObjectCaller(this.providers, "getInstallForFile", {
  1.1645 +      nextObject: function getInstallForFile_nextObject(aCaller, aProvider) {
  1.1646 +        callProvider(aProvider, "getInstallForFile", null, aFile,
  1.1647 +                     function getInstallForFile_safeCall(aInstall) {
  1.1648 +          if (aInstall)
  1.1649 +            safeCall(aCallback, aInstall);
  1.1650 +          else
  1.1651 +            aCaller.callNext();
  1.1652 +        });
  1.1653 +      },
  1.1654 +
  1.1655 +      noMoreObjects: function getInstallForFile_noMoreObjects(aCaller) {
  1.1656 +        safeCall(aCallback, null);
  1.1657 +      }
  1.1658 +    });
  1.1659 +  },
  1.1660 +
  1.1661 +  /**
  1.1662 +   * Asynchronously gets all current AddonInstalls optionally limiting to a list
  1.1663 +   * of types.
  1.1664 +   *
  1.1665 +   * @param  aTypes
  1.1666 +   *         An optional array of types to retrieve. Each type is a string name
  1.1667 +   * @param  aCallback
  1.1668 +   *         A callback which will be passed an array of AddonInstalls
  1.1669 +   * @throws If the aCallback argument is not specified
  1.1670 +   */
  1.1671 +  getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) {
  1.1672 +    if (!gStarted)
  1.1673 +      throw Components.Exception("AddonManager is not initialized",
  1.1674 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1675 +
  1.1676 +    if (aTypes && !Array.isArray(aTypes))
  1.1677 +      throw Components.Exception("aTypes must be an array or null",
  1.1678 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1679 +
  1.1680 +    if (typeof aCallback != "function")
  1.1681 +      throw Components.Exception("aCallback must be a function",
  1.1682 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1683 +
  1.1684 +    let installs = [];
  1.1685 +
  1.1686 +    new AsyncObjectCaller(this.providers, "getInstallsByTypes", {
  1.1687 +      nextObject: function getInstallsByTypes_nextObject(aCaller, aProvider) {
  1.1688 +        callProvider(aProvider, "getInstallsByTypes", null, aTypes,
  1.1689 +                     function getInstallsByTypes_safeCall(aProviderInstalls) {
  1.1690 +          installs = installs.concat(aProviderInstalls);
  1.1691 +          aCaller.callNext();
  1.1692 +        });
  1.1693 +      },
  1.1694 +
  1.1695 +      noMoreObjects: function getInstallsByTypes_noMoreObjects(aCaller) {
  1.1696 +        safeCall(aCallback, installs);
  1.1697 +      }
  1.1698 +    });
  1.1699 +  },
  1.1700 +
  1.1701 +  /**
  1.1702 +   * Asynchronously gets all current AddonInstalls.
  1.1703 +   *
  1.1704 +   * @param  aCallback
  1.1705 +   *         A callback which will be passed an array of AddonInstalls
  1.1706 +   */
  1.1707 +  getAllInstalls: function AMI_getAllInstalls(aCallback) {
  1.1708 +    if (!gStarted)
  1.1709 +      throw Components.Exception("AddonManager is not initialized",
  1.1710 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1711 +
  1.1712 +    this.getInstallsByTypes(null, aCallback);
  1.1713 +  },
  1.1714 +
  1.1715 +  /**
  1.1716 +   * Synchronously map a URI to the corresponding Addon ID.
  1.1717 +   *
  1.1718 +   * Mappable URIs are limited to in-application resources belonging to the
  1.1719 +   * add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
  1.1720 +   * but do not include URIs from meta data, such as the add-on homepage.
  1.1721 +   *
  1.1722 +   * @param  aURI
  1.1723 +   *         nsIURI to map to an addon id
  1.1724 +   * @return string containing the Addon ID or null
  1.1725 +   * @see    amIAddonManager.mapURIToAddonID
  1.1726 +   */
  1.1727 +  mapURIToAddonID: function AMI_mapURIToAddonID(aURI) {
  1.1728 +    if (!(aURI instanceof Ci.nsIURI)) {
  1.1729 +      throw Components.Exception("aURI is not a nsIURI",
  1.1730 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1731 +    }
  1.1732 +    // Try all providers
  1.1733 +    let providers = this.providers.slice(0);
  1.1734 +    for (let provider of providers) {
  1.1735 +      var id = callProvider(provider, "mapURIToAddonID", null, aURI);
  1.1736 +      if (id !== null) {
  1.1737 +        return id;
  1.1738 +      }
  1.1739 +    }
  1.1740 +
  1.1741 +    return null;
  1.1742 +  },
  1.1743 +
  1.1744 +  /**
  1.1745 +   * Checks whether installation is enabled for a particular mimetype.
  1.1746 +   *
  1.1747 +   * @param  aMimetype
  1.1748 +   *         The mimetype to check
  1.1749 +   * @return true if installation is enabled for the mimetype
  1.1750 +   */
  1.1751 +  isInstallEnabled: function AMI_isInstallEnabled(aMimetype) {
  1.1752 +    if (!gStarted)
  1.1753 +      throw Components.Exception("AddonManager is not initialized",
  1.1754 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1755 +
  1.1756 +    if (!aMimetype || typeof aMimetype != "string")
  1.1757 +      throw Components.Exception("aMimetype must be a non-empty string",
  1.1758 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1759 +
  1.1760 +    let providers = this.providers.slice(0);
  1.1761 +    for (let provider of providers) {
  1.1762 +      if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
  1.1763 +          callProvider(provider, "isInstallEnabled"))
  1.1764 +        return true;
  1.1765 +    }
  1.1766 +    return false;
  1.1767 +  },
  1.1768 +
  1.1769 +  /**
  1.1770 +   * Checks whether a particular source is allowed to install add-ons of a
  1.1771 +   * given mimetype.
  1.1772 +   *
  1.1773 +   * @param  aMimetype
  1.1774 +   *         The mimetype of the add-on
  1.1775 +   * @param  aURI
  1.1776 +   *         The optional nsIURI of the source
  1.1777 +   * @return true if the source is allowed to install this mimetype
  1.1778 +   */
  1.1779 +  isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aURI) {
  1.1780 +    if (!gStarted)
  1.1781 +      throw Components.Exception("AddonManager is not initialized",
  1.1782 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1783 +
  1.1784 +    if (!aMimetype || typeof aMimetype != "string")
  1.1785 +      throw Components.Exception("aMimetype must be a non-empty string",
  1.1786 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1787 +
  1.1788 +    if (aURI && !(aURI instanceof Ci.nsIURI))
  1.1789 +      throw Components.Exception("aURI must be a nsIURI or null",
  1.1790 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1791 +
  1.1792 +    let providers = this.providers.slice(0);
  1.1793 +    for (let provider of providers) {
  1.1794 +      if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
  1.1795 +          callProvider(provider, "isInstallAllowed", null, aURI))
  1.1796 +        return true;
  1.1797 +    }
  1.1798 +    return false;
  1.1799 +  },
  1.1800 +
  1.1801 +  /**
  1.1802 +   * Starts installation of an array of AddonInstalls notifying the registered
  1.1803 +   * web install listener of blocked or started installs.
  1.1804 +   *
  1.1805 +   * @param  aMimetype
  1.1806 +   *         The mimetype of add-ons being installed
  1.1807 +   * @param  aSource
  1.1808 +   *         The optional nsIDOMWindow that started the installs
  1.1809 +   * @param  aURI
  1.1810 +   *         The optional nsIURI that started the installs
  1.1811 +   * @param  aInstalls
  1.1812 +   *         The array of AddonInstalls to be installed
  1.1813 +   */
  1.1814 +  installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype,
  1.1815 +                                                                  aSource,
  1.1816 +                                                                  aURI,
  1.1817 +                                                                  aInstalls) {
  1.1818 +    if (!gStarted)
  1.1819 +      throw Components.Exception("AddonManager is not initialized",
  1.1820 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1821 +
  1.1822 +    if (!aMimetype || typeof aMimetype != "string")
  1.1823 +      throw Components.Exception("aMimetype must be a non-empty string",
  1.1824 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1825 +
  1.1826 +    if (aSource && !(aSource instanceof Ci.nsIDOMWindow))
  1.1827 +      throw Components.Exception("aSource must be a nsIDOMWindow or null",
  1.1828 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1829 +
  1.1830 +    if (aURI && !(aURI instanceof Ci.nsIURI))
  1.1831 +      throw Components.Exception("aURI must be a nsIURI or null",
  1.1832 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1833 +
  1.1834 +    if (!Array.isArray(aInstalls))
  1.1835 +      throw Components.Exception("aInstalls must be an array",
  1.1836 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1837 +
  1.1838 +    if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
  1.1839 +      logger.warn("No web installer available, cancelling all installs");
  1.1840 +      aInstalls.forEach(function(aInstall) {
  1.1841 +        aInstall.cancel();
  1.1842 +      });
  1.1843 +      return;
  1.1844 +    }
  1.1845 +
  1.1846 +    try {
  1.1847 +      let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
  1.1848 +                        getService(Ci.amIWebInstallListener);
  1.1849 +
  1.1850 +      if (!this.isInstallEnabled(aMimetype, aURI)) {
  1.1851 +        weblistener.onWebInstallDisabled(aSource, aURI, aInstalls,
  1.1852 +                                         aInstalls.length);
  1.1853 +      }
  1.1854 +      else if (!this.isInstallAllowed(aMimetype, aURI)) {
  1.1855 +        if (weblistener.onWebInstallBlocked(aSource, aURI, aInstalls,
  1.1856 +                                            aInstalls.length)) {
  1.1857 +          aInstalls.forEach(function(aInstall) {
  1.1858 +            aInstall.install();
  1.1859 +          });
  1.1860 +        }
  1.1861 +      }
  1.1862 +      else if (weblistener.onWebInstallRequested(aSource, aURI, aInstalls,
  1.1863 +                                                   aInstalls.length)) {
  1.1864 +        aInstalls.forEach(function(aInstall) {
  1.1865 +          aInstall.install();
  1.1866 +        });
  1.1867 +      }
  1.1868 +    }
  1.1869 +    catch (e) {
  1.1870 +      // In the event that the weblistener throws during instantiation or when
  1.1871 +      // calling onWebInstallBlocked or onWebInstallRequested all of the
  1.1872 +      // installs should get cancelled.
  1.1873 +      logger.warn("Failure calling web installer", e);
  1.1874 +      aInstalls.forEach(function(aInstall) {
  1.1875 +        aInstall.cancel();
  1.1876 +      });
  1.1877 +    }
  1.1878 +  },
  1.1879 +
  1.1880 +  /**
  1.1881 +   * Adds a new InstallListener if the listener is not already registered.
  1.1882 +   *
  1.1883 +   * @param  aListener
  1.1884 +   *         The InstallListener to add
  1.1885 +   */
  1.1886 +  addInstallListener: function AMI_addInstallListener(aListener) {
  1.1887 +    if (!aListener || typeof aListener != "object")
  1.1888 +      throw Components.Exception("aListener must be a InstallListener object",
  1.1889 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1890 +
  1.1891 +    if (!this.installListeners.some(function addInstallListener_matchListener(i) {
  1.1892 +      return i == aListener; }))
  1.1893 +      this.installListeners.push(aListener);
  1.1894 +  },
  1.1895 +
  1.1896 +  /**
  1.1897 +   * Removes an InstallListener if the listener is registered.
  1.1898 +   *
  1.1899 +   * @param  aListener
  1.1900 +   *         The InstallListener to remove
  1.1901 +   */
  1.1902 +  removeInstallListener: function AMI_removeInstallListener(aListener) {
  1.1903 +    if (!aListener || typeof aListener != "object")
  1.1904 +      throw Components.Exception("aListener must be a InstallListener object",
  1.1905 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1906 +
  1.1907 +    let pos = 0;
  1.1908 +    while (pos < this.installListeners.length) {
  1.1909 +      if (this.installListeners[pos] == aListener)
  1.1910 +        this.installListeners.splice(pos, 1);
  1.1911 +      else
  1.1912 +        pos++;
  1.1913 +    }
  1.1914 +  },
  1.1915 +
  1.1916 +  /**
  1.1917 +   * Asynchronously gets an add-on with a specific ID.
  1.1918 +   *
  1.1919 +   * @param  aID
  1.1920 +   *         The ID of the add-on to retrieve
  1.1921 +   * @param  aCallback
  1.1922 +   *         The callback to pass the retrieved add-on to
  1.1923 +   * @throws if the aID or aCallback arguments are not specified
  1.1924 +   */
  1.1925 +  getAddonByID: function AMI_getAddonByID(aID, aCallback) {
  1.1926 +    if (!gStarted)
  1.1927 +      throw Components.Exception("AddonManager is not initialized",
  1.1928 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1929 +
  1.1930 +    if (!aID || typeof aID != "string")
  1.1931 +      throw Components.Exception("aID must be a non-empty string",
  1.1932 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1933 +
  1.1934 +    if (typeof aCallback != "function")
  1.1935 +      throw Components.Exception("aCallback must be a function",
  1.1936 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1937 +
  1.1938 +    new AsyncObjectCaller(this.providers, "getAddonByID", {
  1.1939 +      nextObject: function getAddonByID_nextObject(aCaller, aProvider) {
  1.1940 +        callProvider(aProvider, "getAddonByID", null, aID,
  1.1941 +                    function getAddonByID_safeCall(aAddon) {
  1.1942 +          if (aAddon)
  1.1943 +            safeCall(aCallback, aAddon);
  1.1944 +          else
  1.1945 +            aCaller.callNext();
  1.1946 +        });
  1.1947 +      },
  1.1948 +
  1.1949 +      noMoreObjects: function getAddonByID_noMoreObjects(aCaller) {
  1.1950 +        safeCall(aCallback, null);
  1.1951 +      }
  1.1952 +    });
  1.1953 +  },
  1.1954 +
  1.1955 +  /**
  1.1956 +   * Asynchronously get an add-on with a specific Sync GUID.
  1.1957 +   *
  1.1958 +   * @param  aGUID
  1.1959 +   *         String GUID of add-on to retrieve
  1.1960 +   * @param  aCallback
  1.1961 +   *         The callback to pass the retrieved add-on to.
  1.1962 +   * @throws if the aGUID or aCallback arguments are not specified
  1.1963 +   */
  1.1964 +  getAddonBySyncGUID: function AMI_getAddonBySyncGUID(aGUID, aCallback) {
  1.1965 +    if (!gStarted)
  1.1966 +      throw Components.Exception("AddonManager is not initialized",
  1.1967 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.1968 +
  1.1969 +    if (!aGUID || typeof aGUID != "string")
  1.1970 +      throw Components.Exception("aGUID must be a non-empty string",
  1.1971 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1972 +
  1.1973 +    if (typeof aCallback != "function")
  1.1974 +      throw Components.Exception("aCallback must be a function",
  1.1975 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.1976 +
  1.1977 +    new AsyncObjectCaller(this.providers, "getAddonBySyncGUID", {
  1.1978 +      nextObject: function getAddonBySyncGUID_nextObject(aCaller, aProvider) {
  1.1979 +        callProvider(aProvider, "getAddonBySyncGUID", null, aGUID,
  1.1980 +                    function getAddonBySyncGUID_safeCall(aAddon) {
  1.1981 +          if (aAddon) {
  1.1982 +            safeCall(aCallback, aAddon);
  1.1983 +          } else {
  1.1984 +            aCaller.callNext();
  1.1985 +          }
  1.1986 +        });
  1.1987 +      },
  1.1988 +
  1.1989 +      noMoreObjects: function getAddonBySyncGUID_noMoreObjects(aCaller) {
  1.1990 +        safeCall(aCallback, null);
  1.1991 +      }
  1.1992 +    });
  1.1993 +  },
  1.1994 +
  1.1995 +  /**
  1.1996 +   * Asynchronously gets an array of add-ons.
  1.1997 +   *
  1.1998 +   * @param  aIDs
  1.1999 +   *         The array of IDs to retrieve
  1.2000 +   * @param  aCallback
  1.2001 +   *         The callback to pass an array of Addons to
  1.2002 +   * @throws if the aID or aCallback arguments are not specified
  1.2003 +   */
  1.2004 +  getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) {
  1.2005 +    if (!gStarted)
  1.2006 +      throw Components.Exception("AddonManager is not initialized",
  1.2007 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.2008 +
  1.2009 +    if (!Array.isArray(aIDs))
  1.2010 +      throw Components.Exception("aIDs must be an array",
  1.2011 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2012 +
  1.2013 +    if (typeof aCallback != "function")
  1.2014 +      throw Components.Exception("aCallback must be a function",
  1.2015 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2016 +
  1.2017 +    let addons = [];
  1.2018 +
  1.2019 +    new AsyncObjectCaller(aIDs, null, {
  1.2020 +      nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
  1.2021 +        AddonManagerInternal.getAddonByID(aID,
  1.2022 +                             function getAddonsByIDs_getAddonByID(aAddon) {
  1.2023 +          addons.push(aAddon);
  1.2024 +          aCaller.callNext();
  1.2025 +        });
  1.2026 +      },
  1.2027 +
  1.2028 +      noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) {
  1.2029 +        safeCall(aCallback, addons);
  1.2030 +      }
  1.2031 +    });
  1.2032 +  },
  1.2033 +
  1.2034 +  /**
  1.2035 +   * Asynchronously gets add-ons of specific types.
  1.2036 +   *
  1.2037 +   * @param  aTypes
  1.2038 +   *         An optional array of types to retrieve. Each type is a string name
  1.2039 +   * @param  aCallback
  1.2040 +   *         The callback to pass an array of Addons to.
  1.2041 +   * @throws if the aCallback argument is not specified
  1.2042 +   */
  1.2043 +  getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) {
  1.2044 +    if (!gStarted)
  1.2045 +      throw Components.Exception("AddonManager is not initialized",
  1.2046 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.2047 +
  1.2048 +    if (aTypes && !Array.isArray(aTypes))
  1.2049 +      throw Components.Exception("aTypes must be an array or null",
  1.2050 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2051 +
  1.2052 +    if (typeof aCallback != "function")
  1.2053 +      throw Components.Exception("aCallback must be a function",
  1.2054 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2055 +
  1.2056 +    let addons = [];
  1.2057 +
  1.2058 +    new AsyncObjectCaller(this.providers, "getAddonsByTypes", {
  1.2059 +      nextObject: function getAddonsByTypes_nextObject(aCaller, aProvider) {
  1.2060 +        callProvider(aProvider, "getAddonsByTypes", null, aTypes,
  1.2061 +                     function getAddonsByTypes_concatAddons(aProviderAddons) {
  1.2062 +          addons = addons.concat(aProviderAddons);
  1.2063 +          aCaller.callNext();
  1.2064 +        });
  1.2065 +      },
  1.2066 +
  1.2067 +      noMoreObjects: function getAddonsByTypes_noMoreObjects(aCaller) {
  1.2068 +        safeCall(aCallback, addons);
  1.2069 +      }
  1.2070 +    });
  1.2071 +  },
  1.2072 +
  1.2073 +  /**
  1.2074 +   * Asynchronously gets all installed add-ons.
  1.2075 +   *
  1.2076 +   * @param  aCallback
  1.2077 +   *         A callback which will be passed an array of Addons
  1.2078 +   */
  1.2079 +  getAllAddons: function AMI_getAllAddons(aCallback) {
  1.2080 +    if (!gStarted)
  1.2081 +      throw Components.Exception("AddonManager is not initialized",
  1.2082 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.2083 +
  1.2084 +    if (typeof aCallback != "function")
  1.2085 +      throw Components.Exception("aCallback must be a function",
  1.2086 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2087 +
  1.2088 +    this.getAddonsByTypes(null, aCallback);
  1.2089 +  },
  1.2090 +
  1.2091 +  /**
  1.2092 +   * Asynchronously gets add-ons that have operations waiting for an application
  1.2093 +   * restart to complete.
  1.2094 +   *
  1.2095 +   * @param  aTypes
  1.2096 +   *         An optional array of types to retrieve. Each type is a string name
  1.2097 +   * @param  aCallback
  1.2098 +   *         The callback to pass the array of Addons to
  1.2099 +   * @throws if the aCallback argument is not specified
  1.2100 +   */
  1.2101 +  getAddonsWithOperationsByTypes:
  1.2102 +  function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
  1.2103 +    if (!gStarted)
  1.2104 +      throw Components.Exception("AddonManager is not initialized",
  1.2105 +                                 Cr.NS_ERROR_NOT_INITIALIZED);
  1.2106 +
  1.2107 +    if (aTypes && !Array.isArray(aTypes))
  1.2108 +      throw Components.Exception("aTypes must be an array or null",
  1.2109 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2110 +
  1.2111 +    if (typeof aCallback != "function")
  1.2112 +      throw Components.Exception("aCallback must be a function",
  1.2113 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2114 +
  1.2115 +    let addons = [];
  1.2116 +
  1.2117 +    new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", {
  1.2118 +      nextObject: function getAddonsWithOperationsByTypes_nextObject
  1.2119 +                           (aCaller, aProvider) {
  1.2120 +        callProvider(aProvider, "getAddonsWithOperationsByTypes", null, aTypes,
  1.2121 +                     function getAddonsWithOperationsByTypes_concatAddons
  1.2122 +                              (aProviderAddons) {
  1.2123 +          addons = addons.concat(aProviderAddons);
  1.2124 +          aCaller.callNext();
  1.2125 +        });
  1.2126 +      },
  1.2127 +
  1.2128 +      noMoreObjects: function getAddonsWithOperationsByTypes_noMoreObjects(caller) {
  1.2129 +        safeCall(aCallback, addons);
  1.2130 +      }
  1.2131 +    });
  1.2132 +  },
  1.2133 +
  1.2134 +  /**
  1.2135 +   * Adds a new AddonManagerListener if the listener is not already registered.
  1.2136 +   *
  1.2137 +   * @param  aListener
  1.2138 +   *         The listener to add
  1.2139 +   */
  1.2140 +  addManagerListener: function AMI_addManagerListener(aListener) {
  1.2141 +    if (!aListener || typeof aListener != "object")
  1.2142 +      throw Components.Exception("aListener must be an AddonManagerListener object",
  1.2143 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2144 +
  1.2145 +    if (!this.managerListeners.some(function addManagerListener_matchListener(i) {
  1.2146 +      return i == aListener; }))
  1.2147 +      this.managerListeners.push(aListener);
  1.2148 +  },
  1.2149 +
  1.2150 +  /**
  1.2151 +   * Removes an AddonManagerListener if the listener is registered.
  1.2152 +   *
  1.2153 +   * @param  aListener
  1.2154 +   *         The listener to remove
  1.2155 +   */
  1.2156 +  removeManagerListener: function AMI_removeManagerListener(aListener) {
  1.2157 +    if (!aListener || typeof aListener != "object")
  1.2158 +      throw Components.Exception("aListener must be an AddonManagerListener object",
  1.2159 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2160 +
  1.2161 +    let pos = 0;
  1.2162 +    while (pos < this.managerListeners.length) {
  1.2163 +      if (this.managerListeners[pos] == aListener)
  1.2164 +        this.managerListeners.splice(pos, 1);
  1.2165 +      else
  1.2166 +        pos++;
  1.2167 +    }
  1.2168 +  },
  1.2169 +
  1.2170 +  /**
  1.2171 +   * Adds a new AddonListener if the listener is not already registered.
  1.2172 +   *
  1.2173 +   * @param  aListener
  1.2174 +   *         The AddonListener to add
  1.2175 +   */
  1.2176 +  addAddonListener: function AMI_addAddonListener(aListener) {
  1.2177 +    if (!aListener || typeof aListener != "object")
  1.2178 +      throw Components.Exception("aListener must be an AddonListener object",
  1.2179 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2180 +
  1.2181 +    if (!this.addonListeners.some(function addAddonListener_matchListener(i) {
  1.2182 +      return i == aListener; }))
  1.2183 +      this.addonListeners.push(aListener);
  1.2184 +  },
  1.2185 +
  1.2186 +  /**
  1.2187 +   * Removes an AddonListener if the listener is registered.
  1.2188 +   *
  1.2189 +   * @param  aListener
  1.2190 +   *         The AddonListener to remove
  1.2191 +   */
  1.2192 +  removeAddonListener: function AMI_removeAddonListener(aListener) {
  1.2193 +    if (!aListener || typeof aListener != "object")
  1.2194 +      throw Components.Exception("aListener must be an AddonListener object",
  1.2195 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2196 +
  1.2197 +    let pos = 0;
  1.2198 +    while (pos < this.addonListeners.length) {
  1.2199 +      if (this.addonListeners[pos] == aListener)
  1.2200 +        this.addonListeners.splice(pos, 1);
  1.2201 +      else
  1.2202 +        pos++;
  1.2203 +    }
  1.2204 +  },
  1.2205 +
  1.2206 +  /**
  1.2207 +   * Adds a new TypeListener if the listener is not already registered.
  1.2208 +   *
  1.2209 +   * @param  aListener
  1.2210 +   *         The TypeListener to add
  1.2211 +   */
  1.2212 +  addTypeListener: function AMI_addTypeListener(aListener) {
  1.2213 +    if (!aListener || typeof aListener != "object")
  1.2214 +      throw Components.Exception("aListener must be a TypeListener object",
  1.2215 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2216 +
  1.2217 +    if (!this.typeListeners.some(function addTypeListener_matchListener(i) {
  1.2218 +      return i == aListener; }))
  1.2219 +      this.typeListeners.push(aListener);
  1.2220 +  },
  1.2221 +
  1.2222 +  /**
  1.2223 +   * Removes an TypeListener if the listener is registered.
  1.2224 +   *
  1.2225 +   * @param  aListener
  1.2226 +   *         The TypeListener to remove
  1.2227 +   */
  1.2228 +  removeTypeListener: function AMI_removeTypeListener(aListener) {
  1.2229 +    if (!aListener || typeof aListener != "object")
  1.2230 +      throw Components.Exception("aListener must be a TypeListener object",
  1.2231 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2232 +
  1.2233 +    let pos = 0;
  1.2234 +    while (pos < this.typeListeners.length) {
  1.2235 +      if (this.typeListeners[pos] == aListener)
  1.2236 +        this.typeListeners.splice(pos, 1);
  1.2237 +      else
  1.2238 +        pos++;
  1.2239 +    }
  1.2240 +  },
  1.2241 +
  1.2242 +  get addonTypes() {
  1.2243 +    return this.typesProxy;
  1.2244 +  },
  1.2245 +
  1.2246 +  get autoUpdateDefault() {
  1.2247 +    return gAutoUpdateDefault;
  1.2248 +  },
  1.2249 +
  1.2250 +  set autoUpdateDefault(aValue) {
  1.2251 +    aValue = !!aValue;
  1.2252 +    if (aValue != gAutoUpdateDefault)
  1.2253 +      Services.prefs.setBoolPref(PREF_EM_AUTOUPDATE_DEFAULT, aValue);
  1.2254 +    return aValue;
  1.2255 +  },
  1.2256 +
  1.2257 +  get checkCompatibility() {
  1.2258 +    return gCheckCompatibility;
  1.2259 +  },
  1.2260 +
  1.2261 +  set checkCompatibility(aValue) {
  1.2262 +    aValue = !!aValue;
  1.2263 +    if (aValue != gCheckCompatibility) {
  1.2264 +      if (!aValue)
  1.2265 +        Services.prefs.setBoolPref(PREF_EM_CHECK_COMPATIBILITY, false);
  1.2266 +      else
  1.2267 +        Services.prefs.clearUserPref(PREF_EM_CHECK_COMPATIBILITY);
  1.2268 +    }
  1.2269 +    return aValue;
  1.2270 +  },
  1.2271 +
  1.2272 +  get strictCompatibility() {
  1.2273 +    return gStrictCompatibility;
  1.2274 +  },
  1.2275 +
  1.2276 +  set strictCompatibility(aValue) {
  1.2277 +    aValue = !!aValue;
  1.2278 +    if (aValue != gStrictCompatibility)
  1.2279 +      Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, aValue);
  1.2280 +    return aValue;
  1.2281 +  },
  1.2282 +
  1.2283 +  get checkUpdateSecurityDefault() {
  1.2284 +    return gCheckUpdateSecurityDefault;
  1.2285 +  },
  1.2286 +
  1.2287 +  get checkUpdateSecurity() {
  1.2288 +    return gCheckUpdateSecurity;
  1.2289 +  },
  1.2290 +
  1.2291 +  set checkUpdateSecurity(aValue) {
  1.2292 +    aValue = !!aValue;
  1.2293 +    if (aValue != gCheckUpdateSecurity) {
  1.2294 +      if (aValue != gCheckUpdateSecurityDefault)
  1.2295 +        Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, aValue);
  1.2296 +      else
  1.2297 +        Services.prefs.clearUserPref(PREF_EM_CHECK_UPDATE_SECURITY);
  1.2298 +    }
  1.2299 +    return aValue;
  1.2300 +  },
  1.2301 +
  1.2302 +  get updateEnabled() {
  1.2303 +    return gUpdateEnabled;
  1.2304 +  },
  1.2305 +
  1.2306 +  set updateEnabled(aValue) {
  1.2307 +    aValue = !!aValue;
  1.2308 +    if (aValue != gUpdateEnabled)
  1.2309 +      Services.prefs.setBoolPref(PREF_EM_UPDATE_ENABLED, aValue);
  1.2310 +    return aValue;
  1.2311 +  },
  1.2312 +
  1.2313 +  get hotfixID() {
  1.2314 +    return gHotfixID;
  1.2315 +  },
  1.2316 +};
  1.2317 +
  1.2318 +/**
  1.2319 + * Should not be used outside of core Mozilla code. This is a private API for
  1.2320 + * the startup and platform integration code to use. Refer to the methods on
  1.2321 + * AddonManagerInternal for documentation however note that these methods are
  1.2322 + * subject to change at any time.
  1.2323 + */
  1.2324 +this.AddonManagerPrivate = {
  1.2325 +  startup: function AMP_startup() {
  1.2326 +    AddonManagerInternal.startup();
  1.2327 +  },
  1.2328 +
  1.2329 +  registerProvider: function AMP_registerProvider(aProvider, aTypes) {
  1.2330 +    AddonManagerInternal.registerProvider(aProvider, aTypes);
  1.2331 +  },
  1.2332 +
  1.2333 +  unregisterProvider: function AMP_unregisterProvider(aProvider) {
  1.2334 +    AddonManagerInternal.unregisterProvider(aProvider);
  1.2335 +  },
  1.2336 +
  1.2337 +  backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
  1.2338 +    AddonManagerInternal.backgroundUpdateCheck();
  1.2339 +  },
  1.2340 +
  1.2341 +  addStartupChange: function AMP_addStartupChange(aType, aID) {
  1.2342 +    AddonManagerInternal.addStartupChange(aType, aID);
  1.2343 +  },
  1.2344 +
  1.2345 +  removeStartupChange: function AMP_removeStartupChange(aType, aID) {
  1.2346 +    AddonManagerInternal.removeStartupChange(aType, aID);
  1.2347 +  },
  1.2348 +
  1.2349 +  notifyAddonChanged: function AMP_notifyAddonChanged(aID, aType, aPendingRestart) {
  1.2350 +    AddonManagerInternal.notifyAddonChanged(aID, aType, aPendingRestart);
  1.2351 +  },
  1.2352 +
  1.2353 +  updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() {
  1.2354 +    AddonManagerInternal.updateAddonAppDisabledStates();
  1.2355 +  },
  1.2356 +
  1.2357 +  updateAddonRepositoryData: function AMP_updateAddonRepositoryData(aCallback) {
  1.2358 +    AddonManagerInternal.updateAddonRepositoryData(aCallback);
  1.2359 +  },
  1.2360 +
  1.2361 +  callInstallListeners: function AMP_callInstallListeners(...aArgs) {
  1.2362 +    return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal,
  1.2363 +                                                           aArgs);
  1.2364 +  },
  1.2365 +
  1.2366 +  callAddonListeners: function AMP_callAddonListeners(...aArgs) {
  1.2367 +    AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);
  1.2368 +  },
  1.2369 +
  1.2370 +  AddonAuthor: AddonAuthor,
  1.2371 +
  1.2372 +  AddonScreenshot: AddonScreenshot,
  1.2373 +
  1.2374 +  AddonCompatibilityOverride: AddonCompatibilityOverride,
  1.2375 +
  1.2376 +  AddonType: AddonType,
  1.2377 +
  1.2378 +  recordTimestamp: function AMP_recordTimestamp(name, value) {
  1.2379 +    AddonManagerInternal.recordTimestamp(name, value);
  1.2380 +  },
  1.2381 +
  1.2382 +  _simpleMeasures: {},
  1.2383 +  recordSimpleMeasure: function AMP_recordSimpleMeasure(name, value) {
  1.2384 +    this._simpleMeasures[name] = value;
  1.2385 +  },
  1.2386 +
  1.2387 +  recordException: function AMP_recordException(aModule, aContext, aException) {
  1.2388 +    let report = {
  1.2389 +      module: aModule,
  1.2390 +      context: aContext
  1.2391 +    };
  1.2392 +
  1.2393 +    if (typeof aException == "number") {
  1.2394 +      report.message = Components.Exception("", aException).name;
  1.2395 +    }
  1.2396 +    else {
  1.2397 +      report.message = aException.toString();
  1.2398 +      if (aException.fileName) {
  1.2399 +        report.file = aException.fileName;
  1.2400 +        report.line = aException.lineNumber;
  1.2401 +      }
  1.2402 +    }
  1.2403 +
  1.2404 +    this._simpleMeasures.exception = report;
  1.2405 +  },
  1.2406 +
  1.2407 +  getSimpleMeasures: function AMP_getSimpleMeasures() {
  1.2408 +    return this._simpleMeasures;
  1.2409 +  },
  1.2410 +
  1.2411 +  getTelemetryDetails: function AMP_getTelemetryDetails() {
  1.2412 +    return AddonManagerInternal.telemetryDetails;
  1.2413 +  },
  1.2414 +
  1.2415 +  setTelemetryDetails: function AMP_setTelemetryDetails(aProvider, aDetails) {
  1.2416 +    AddonManagerInternal.telemetryDetails[aProvider] = aDetails;
  1.2417 +  },
  1.2418 +
  1.2419 +  // Start a timer, record a simple measure of the time interval when
  1.2420 +  // timer.done() is called
  1.2421 +  simpleTimer: function(aName) {
  1.2422 +    let startTime = Date.now();
  1.2423 +    return {
  1.2424 +      done: () => this.recordSimpleMeasure(aName, Date.now() - startTime)
  1.2425 +    };
  1.2426 +  },
  1.2427 +
  1.2428 +  /**
  1.2429 +   * Helper to call update listeners when no update is available.
  1.2430 +   *
  1.2431 +   * This can be used as an implementation for Addon.findUpdates() when
  1.2432 +   * no update mechanism is available.
  1.2433 +   */
  1.2434 +  callNoUpdateListeners: function (addon, listener, reason, appVersion, platformVersion) {
  1.2435 +    if ("onNoCompatibilityUpdateAvailable" in listener) {
  1.2436 +      safeCall(listener.onNoCompatibilityUpdateAvailable.bind(listener), addon);
  1.2437 +    }
  1.2438 +    if ("onNoUpdateAvailable" in listener) {
  1.2439 +      safeCall(listener.onNoUpdateAvailable.bind(listener), addon);
  1.2440 +    }
  1.2441 +    if ("onUpdateFinished" in listener) {
  1.2442 +      safeCall(listener.onUpdateFinished.bind(listener), addon);
  1.2443 +    }
  1.2444 +  },
  1.2445 +};
  1.2446 +
  1.2447 +/**
  1.2448 + * This is the public API that UI and developers should be calling. All methods
  1.2449 + * just forward to AddonManagerInternal.
  1.2450 + */
  1.2451 +this.AddonManager = {
  1.2452 +  // Constants for the AddonInstall.state property
  1.2453 +  // The install is available for download.
  1.2454 +  STATE_AVAILABLE: 0,
  1.2455 +  // The install is being downloaded.
  1.2456 +  STATE_DOWNLOADING: 1,
  1.2457 +  // The install is checking for compatibility information.
  1.2458 +  STATE_CHECKING: 2,
  1.2459 +  // The install is downloaded and ready to install.
  1.2460 +  STATE_DOWNLOADED: 3,
  1.2461 +  // The download failed.
  1.2462 +  STATE_DOWNLOAD_FAILED: 4,
  1.2463 +  // The add-on is being installed.
  1.2464 +  STATE_INSTALLING: 5,
  1.2465 +  // The add-on has been installed.
  1.2466 +  STATE_INSTALLED: 6,
  1.2467 +  // The install failed.
  1.2468 +  STATE_INSTALL_FAILED: 7,
  1.2469 +  // The install has been cancelled.
  1.2470 +  STATE_CANCELLED: 8,
  1.2471 +
  1.2472 +  // Constants representing different types of errors while downloading an
  1.2473 +  // add-on.
  1.2474 +  // The download failed due to network problems.
  1.2475 +  ERROR_NETWORK_FAILURE: -1,
  1.2476 +  // The downloaded file did not match the provided hash.
  1.2477 +  ERROR_INCORRECT_HASH: -2,
  1.2478 +  // The downloaded file seems to be corrupted in some way.
  1.2479 +  ERROR_CORRUPT_FILE: -3,
  1.2480 +  // An error occured trying to write to the filesystem.
  1.2481 +  ERROR_FILE_ACCESS: -4,
  1.2482 +
  1.2483 +  // These must be kept in sync with AddonUpdateChecker.
  1.2484 +  // No error was encountered.
  1.2485 +  UPDATE_STATUS_NO_ERROR: 0,
  1.2486 +  // The update check timed out
  1.2487 +  UPDATE_STATUS_TIMEOUT: -1,
  1.2488 +  // There was an error while downloading the update information.
  1.2489 +  UPDATE_STATUS_DOWNLOAD_ERROR: -2,
  1.2490 +  // The update information was malformed in some way.
  1.2491 +  UPDATE_STATUS_PARSE_ERROR: -3,
  1.2492 +  // The update information was not in any known format.
  1.2493 +  UPDATE_STATUS_UNKNOWN_FORMAT: -4,
  1.2494 +  // The update information was not correctly signed or there was an SSL error.
  1.2495 +  UPDATE_STATUS_SECURITY_ERROR: -5,
  1.2496 +  // The update was cancelled.
  1.2497 +  UPDATE_STATUS_CANCELLED: -6,
  1.2498 +
  1.2499 +  // Constants to indicate why an update check is being performed
  1.2500 +  // Update check has been requested by the user.
  1.2501 +  UPDATE_WHEN_USER_REQUESTED: 1,
  1.2502 +  // Update check is necessary to see if the Addon is compatibile with a new
  1.2503 +  // version of the application.
  1.2504 +  UPDATE_WHEN_NEW_APP_DETECTED: 2,
  1.2505 +  // Update check is necessary because a new application has been installed.
  1.2506 +  UPDATE_WHEN_NEW_APP_INSTALLED: 3,
  1.2507 +  // Update check is a regular background update check.
  1.2508 +  UPDATE_WHEN_PERIODIC_UPDATE: 16,
  1.2509 +  // Update check is needed to check an Addon that is being installed.
  1.2510 +  UPDATE_WHEN_ADDON_INSTALLED: 17,
  1.2511 +
  1.2512 +  // Constants for operations in Addon.pendingOperations
  1.2513 +  // Indicates that the Addon has no pending operations.
  1.2514 +  PENDING_NONE: 0,
  1.2515 +  // Indicates that the Addon will be enabled after the application restarts.
  1.2516 +  PENDING_ENABLE: 1,
  1.2517 +  // Indicates that the Addon will be disabled after the application restarts.
  1.2518 +  PENDING_DISABLE: 2,
  1.2519 +  // Indicates that the Addon will be uninstalled after the application restarts.
  1.2520 +  PENDING_UNINSTALL: 4,
  1.2521 +  // Indicates that the Addon will be installed after the application restarts.
  1.2522 +  PENDING_INSTALL: 8,
  1.2523 +  PENDING_UPGRADE: 16,
  1.2524 +
  1.2525 +  // Constants for operations in Addon.operationsRequiringRestart
  1.2526 +  // Indicates that restart isn't required for any operation.
  1.2527 +  OP_NEEDS_RESTART_NONE: 0,
  1.2528 +  // Indicates that restart is required for enabling the addon.
  1.2529 +  OP_NEEDS_RESTART_ENABLE: 1,
  1.2530 +  // Indicates that restart is required for disabling the addon.
  1.2531 +  OP_NEEDS_RESTART_DISABLE: 2,
  1.2532 +  // Indicates that restart is required for uninstalling the addon.
  1.2533 +  OP_NEEDS_RESTART_UNINSTALL: 4,
  1.2534 +  // Indicates that restart is required for installing the addon.
  1.2535 +  OP_NEEDS_RESTART_INSTALL: 8,
  1.2536 +
  1.2537 +  // Constants for permissions in Addon.permissions.
  1.2538 +  // Indicates that the Addon can be uninstalled.
  1.2539 +  PERM_CAN_UNINSTALL: 1,
  1.2540 +  // Indicates that the Addon can be enabled by the user.
  1.2541 +  PERM_CAN_ENABLE: 2,
  1.2542 +  // Indicates that the Addon can be disabled by the user.
  1.2543 +  PERM_CAN_DISABLE: 4,
  1.2544 +  // Indicates that the Addon can be upgraded.
  1.2545 +  PERM_CAN_UPGRADE: 8,
  1.2546 +  // Indicates that the Addon can be set to be optionally enabled
  1.2547 +  // on a case-by-case basis.
  1.2548 +  PERM_CAN_ASK_TO_ACTIVATE: 16,
  1.2549 +
  1.2550 +  // General descriptions of where items are installed.
  1.2551 +  // Installed in this profile.
  1.2552 +  SCOPE_PROFILE: 1,
  1.2553 +  // Installed for all of this user's profiles.
  1.2554 +  SCOPE_USER: 2,
  1.2555 +  // Installed and owned by the application.
  1.2556 +  SCOPE_APPLICATION: 4,
  1.2557 +  // Installed for all users of the computer.
  1.2558 +  SCOPE_SYSTEM: 8,
  1.2559 +  // The combination of all scopes.
  1.2560 +  SCOPE_ALL: 15,
  1.2561 +
  1.2562 +  // 1-15 are different built-in views for the add-on type
  1.2563 +  VIEW_TYPE_LIST: "list",
  1.2564 +
  1.2565 +  TYPE_UI_HIDE_EMPTY: 16,
  1.2566 +  // Indicates that this add-on type supports the ask-to-activate state.
  1.2567 +  // That is, add-ons of this type can be set to be optionally enabled
  1.2568 +  // on a case-by-case basis.
  1.2569 +  TYPE_SUPPORTS_ASK_TO_ACTIVATE: 32,
  1.2570 +
  1.2571 +  // Constants for Addon.applyBackgroundUpdates.
  1.2572 +  // Indicates that the Addon should not update automatically.
  1.2573 +  AUTOUPDATE_DISABLE: 0,
  1.2574 +  // Indicates that the Addon should update automatically only if
  1.2575 +  // that's the global default.
  1.2576 +  AUTOUPDATE_DEFAULT: 1,
  1.2577 +  // Indicates that the Addon should update automatically.
  1.2578 +  AUTOUPDATE_ENABLE: 2,
  1.2579 +
  1.2580 +  // Constants for how Addon options should be shown.
  1.2581 +  // Options will be opened in a new window
  1.2582 +  OPTIONS_TYPE_DIALOG: 1,
  1.2583 +  // Options will be displayed within the AM detail view
  1.2584 +  OPTIONS_TYPE_INLINE: 2,
  1.2585 +  // Options will be displayed in a new tab, if possible
  1.2586 +  OPTIONS_TYPE_TAB: 3,
  1.2587 +  // Same as OPTIONS_TYPE_INLINE, but no Preferences button will be shown.
  1.2588 +  // Used to indicate that only non-interactive information will be shown.
  1.2589 +  OPTIONS_TYPE_INLINE_INFO: 4,
  1.2590 +
  1.2591 +  // Constants for displayed or hidden options notifications
  1.2592 +  // Options notification will be displayed
  1.2593 +  OPTIONS_NOTIFICATION_DISPLAYED: "addon-options-displayed",
  1.2594 +  // Options notification will be hidden
  1.2595 +  OPTIONS_NOTIFICATION_HIDDEN: "addon-options-hidden",
  1.2596 +
  1.2597 +  // Constants for getStartupChanges, addStartupChange and removeStartupChange
  1.2598 +  // Add-ons that were detected as installed during startup. Doesn't include
  1.2599 +  // add-ons that were pending installation the last time the application ran.
  1.2600 +  STARTUP_CHANGE_INSTALLED: "installed",
  1.2601 +  // Add-ons that were detected as changed during startup. This includes an
  1.2602 +  // add-on moving to a different location, changing version or just having
  1.2603 +  // been detected as possibly changed.
  1.2604 +  STARTUP_CHANGE_CHANGED: "changed",
  1.2605 +  // Add-ons that were detected as uninstalled during startup. Doesn't include
  1.2606 +  // add-ons that were pending uninstallation the last time the application ran.
  1.2607 +  STARTUP_CHANGE_UNINSTALLED: "uninstalled",
  1.2608 +  // Add-ons that were detected as disabled during startup, normally because of
  1.2609 +  // an application change making an add-on incompatible. Doesn't include
  1.2610 +  // add-ons that were pending being disabled the last time the application ran.
  1.2611 +  STARTUP_CHANGE_DISABLED: "disabled",
  1.2612 +  // Add-ons that were detected as enabled during startup, normally because of
  1.2613 +  // an application change making an add-on compatible. Doesn't include
  1.2614 +  // add-ons that were pending being enabled the last time the application ran.
  1.2615 +  STARTUP_CHANGE_ENABLED: "enabled",
  1.2616 +
  1.2617 +  // Constants for the Addon.userDisabled property
  1.2618 +  // Indicates that the userDisabled state of this add-on is currently
  1.2619 +  // ask-to-activate. That is, it can be conditionally enabled on a
  1.2620 +  // case-by-case basis.
  1.2621 +  STATE_ASK_TO_ACTIVATE: "askToActivate",
  1.2622 +
  1.2623 +#ifdef MOZ_EM_DEBUG
  1.2624 +  get __AddonManagerInternal__() {
  1.2625 +    return AddonManagerInternal;
  1.2626 +  },
  1.2627 +#endif
  1.2628 +
  1.2629 +  getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
  1.2630 +                                                 aHash, aName, aIcons,
  1.2631 +                                                 aVersion, aLoadGroup) {
  1.2632 +    AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
  1.2633 +                                          aName, aIcons, aVersion, aLoadGroup);
  1.2634 +  },
  1.2635 +
  1.2636 +  getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
  1.2637 +    AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
  1.2638 +  },
  1.2639 +
  1.2640 +  /**
  1.2641 +   * Gets an array of add-on IDs that changed during the most recent startup.
  1.2642 +   *
  1.2643 +   * @param  aType
  1.2644 +   *         The type of startup change to get
  1.2645 +   * @return An array of add-on IDs
  1.2646 +   */
  1.2647 +  getStartupChanges: function AM_getStartupChanges(aType) {
  1.2648 +    if (!(aType in AddonManagerInternal.startupChanges))
  1.2649 +      return [];
  1.2650 +    return AddonManagerInternal.startupChanges[aType].slice(0);
  1.2651 +  },
  1.2652 +
  1.2653 +  getAddonByID: function AM_getAddonByID(aID, aCallback) {
  1.2654 +    AddonManagerInternal.getAddonByID(aID, aCallback);
  1.2655 +  },
  1.2656 +
  1.2657 +  getAddonBySyncGUID: function AM_getAddonBySyncGUID(aGUID, aCallback) {
  1.2658 +    AddonManagerInternal.getAddonBySyncGUID(aGUID, aCallback);
  1.2659 +  },
  1.2660 +
  1.2661 +  getAddonsByIDs: function AM_getAddonsByIDs(aIDs, aCallback) {
  1.2662 +    AddonManagerInternal.getAddonsByIDs(aIDs, aCallback);
  1.2663 +  },
  1.2664 +
  1.2665 +  getAddonsWithOperationsByTypes:
  1.2666 +  function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) {
  1.2667 +    AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback);
  1.2668 +  },
  1.2669 +
  1.2670 +  getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) {
  1.2671 +    AddonManagerInternal.getAddonsByTypes(aTypes, aCallback);
  1.2672 +  },
  1.2673 +
  1.2674 +  getAllAddons: function AM_getAllAddons(aCallback) {
  1.2675 +    AddonManagerInternal.getAllAddons(aCallback);
  1.2676 +  },
  1.2677 +
  1.2678 +  getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) {
  1.2679 +    AddonManagerInternal.getInstallsByTypes(aTypes, aCallback);
  1.2680 +  },
  1.2681 +
  1.2682 +  getAllInstalls: function AM_getAllInstalls(aCallback) {
  1.2683 +    AddonManagerInternal.getAllInstalls(aCallback);
  1.2684 +  },
  1.2685 +
  1.2686 +  mapURIToAddonID: function AM_mapURIToAddonID(aURI) {
  1.2687 +    return AddonManagerInternal.mapURIToAddonID(aURI);
  1.2688 +  },
  1.2689 +
  1.2690 +  isInstallEnabled: function AM_isInstallEnabled(aType) {
  1.2691 +    return AddonManagerInternal.isInstallEnabled(aType);
  1.2692 +  },
  1.2693 +
  1.2694 +  isInstallAllowed: function AM_isInstallAllowed(aType, aUri) {
  1.2695 +    return AddonManagerInternal.isInstallAllowed(aType, aUri);
  1.2696 +  },
  1.2697 +
  1.2698 +  installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aSource,
  1.2699 +                                                                 aUri, aInstalls) {
  1.2700 +    AddonManagerInternal.installAddonsFromWebpage(aType, aSource, aUri, aInstalls);
  1.2701 +  },
  1.2702 +
  1.2703 +  addManagerListener: function AM_addManagerListener(aListener) {
  1.2704 +    AddonManagerInternal.addManagerListener(aListener);
  1.2705 +  },
  1.2706 +
  1.2707 +  removeManagerListener: function AM_removeManagerListener(aListener) {
  1.2708 +    AddonManagerInternal.removeManagerListener(aListener);
  1.2709 +  },
  1.2710 +
  1.2711 +  addInstallListener: function AM_addInstallListener(aListener) {
  1.2712 +    AddonManagerInternal.addInstallListener(aListener);
  1.2713 +  },
  1.2714 +
  1.2715 +  removeInstallListener: function AM_removeInstallListener(aListener) {
  1.2716 +    AddonManagerInternal.removeInstallListener(aListener);
  1.2717 +  },
  1.2718 +
  1.2719 +  addAddonListener: function AM_addAddonListener(aListener) {
  1.2720 +    AddonManagerInternal.addAddonListener(aListener);
  1.2721 +  },
  1.2722 +
  1.2723 +  removeAddonListener: function AM_removeAddonListener(aListener) {
  1.2724 +    AddonManagerInternal.removeAddonListener(aListener);
  1.2725 +  },
  1.2726 +
  1.2727 +  addTypeListener: function AM_addTypeListener(aListener) {
  1.2728 +    AddonManagerInternal.addTypeListener(aListener);
  1.2729 +  },
  1.2730 +
  1.2731 +  removeTypeListener: function AM_removeTypeListener(aListener) {
  1.2732 +    AddonManagerInternal.removeTypeListener(aListener);
  1.2733 +  },
  1.2734 +
  1.2735 +  get addonTypes() {
  1.2736 +    return AddonManagerInternal.addonTypes;
  1.2737 +  },
  1.2738 +
  1.2739 +  /**
  1.2740 +   * Determines whether an Addon should auto-update or not.
  1.2741 +   *
  1.2742 +   * @param  aAddon
  1.2743 +   *         The Addon representing the add-on
  1.2744 +   * @return true if the addon should auto-update, false otherwise.
  1.2745 +   */
  1.2746 +  shouldAutoUpdate: function AM_shouldAutoUpdate(aAddon) {
  1.2747 +    if (!aAddon || typeof aAddon != "object")
  1.2748 +      throw Components.Exception("aAddon must be specified",
  1.2749 +                                 Cr.NS_ERROR_INVALID_ARG);
  1.2750 +
  1.2751 +    if (!("applyBackgroundUpdates" in aAddon))
  1.2752 +      return false;
  1.2753 +    if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)
  1.2754 +      return true;
  1.2755 +    if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE)
  1.2756 +      return false;
  1.2757 +    return this.autoUpdateDefault;
  1.2758 +  },
  1.2759 +
  1.2760 +  get checkCompatibility() {
  1.2761 +    return AddonManagerInternal.checkCompatibility;
  1.2762 +  },
  1.2763 +
  1.2764 +  set checkCompatibility(aValue) {
  1.2765 +    AddonManagerInternal.checkCompatibility = aValue;
  1.2766 +  },
  1.2767 +
  1.2768 +  get strictCompatibility() {
  1.2769 +    return AddonManagerInternal.strictCompatibility;
  1.2770 +  },
  1.2771 +
  1.2772 +  set strictCompatibility(aValue) {
  1.2773 +    AddonManagerInternal.strictCompatibility = aValue;
  1.2774 +  },
  1.2775 +
  1.2776 +  get checkUpdateSecurityDefault() {
  1.2777 +    return AddonManagerInternal.checkUpdateSecurityDefault;
  1.2778 +  },
  1.2779 +
  1.2780 +  get checkUpdateSecurity() {
  1.2781 +    return AddonManagerInternal.checkUpdateSecurity;
  1.2782 +  },
  1.2783 +
  1.2784 +  set checkUpdateSecurity(aValue) {
  1.2785 +    AddonManagerInternal.checkUpdateSecurity = aValue;
  1.2786 +  },
  1.2787 +
  1.2788 +  get updateEnabled() {
  1.2789 +    return AddonManagerInternal.updateEnabled;
  1.2790 +  },
  1.2791 +
  1.2792 +  set updateEnabled(aValue) {
  1.2793 +    AddonManagerInternal.updateEnabled = aValue;
  1.2794 +  },
  1.2795 +
  1.2796 +  get autoUpdateDefault() {
  1.2797 +    return AddonManagerInternal.autoUpdateDefault;
  1.2798 +  },
  1.2799 +
  1.2800 +  set autoUpdateDefault(aValue) {
  1.2801 +    AddonManagerInternal.autoUpdateDefault = aValue;
  1.2802 +  },
  1.2803 +
  1.2804 +  get hotfixID() {
  1.2805 +    return AddonManagerInternal.hotfixID;
  1.2806 +  },
  1.2807 +
  1.2808 +  escapeAddonURI: function AM_escapeAddonURI(aAddon, aUri, aAppVersion) {
  1.2809 +    return AddonManagerInternal.escapeAddonURI(aAddon, aUri, aAppVersion);
  1.2810 +  }
  1.2811 +};
  1.2812 +
  1.2813 +// load the timestamps module into AddonManagerInternal
  1.2814 +Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", AddonManagerInternal);
  1.2815 +Object.freeze(AddonManagerInternal);
  1.2816 +Object.freeze(AddonManagerPrivate);
  1.2817 +Object.freeze(AddonManager);

mercurial