toolkit/mozapps/extensions/AddonManager.jsm

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 const Cc = Components.classes;
     8 const Ci = Components.interfaces;
     9 const Cr = Components.results;
    10 const Cu = Components.utils;
    12 // Cannot use Services.appinfo here, or else xpcshell-tests will blow up, as
    13 // most tests later register different nsIAppInfo implementations, which
    14 // wouldn't be reflected in Services.appinfo anymore, as the lazy getter
    15 // underlying it would have been initialized if we used it here.
    16 if ("@mozilla.org/xre/app-info;1" in Cc) {
    17   let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
    18   if (runtime.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
    19     // Refuse to run in child processes.
    20     throw new Error("You cannot use the AddonManager in child processes!");
    21   }
    22 }
    25 const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
    26 const PREF_DEFAULT_PROVIDERS_ENABLED  = "extensions.defaultProviders.enabled";
    27 const PREF_EM_UPDATE_ENABLED          = "extensions.update.enabled";
    28 const PREF_EM_LAST_APP_VERSION        = "extensions.lastAppVersion";
    29 const PREF_EM_LAST_PLATFORM_VERSION   = "extensions.lastPlatformVersion";
    30 const PREF_EM_AUTOUPDATE_DEFAULT      = "extensions.update.autoUpdateDefault";
    31 const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
    32 const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
    33 const PREF_EM_UPDATE_BACKGROUND_URL   = "extensions.update.background.url";
    34 const PREF_APP_UPDATE_ENABLED         = "app.update.enabled";
    35 const PREF_APP_UPDATE_AUTO            = "app.update.auto";
    36 const PREF_EM_HOTFIX_ID               = "extensions.hotfix.id";
    37 const PREF_EM_HOTFIX_LASTVERSION      = "extensions.hotfix.lastVersion";
    38 const PREF_EM_HOTFIX_URL              = "extensions.hotfix.url";
    39 const PREF_EM_CERT_CHECKATTRIBUTES    = "extensions.hotfix.cert.checkAttributes";
    40 const PREF_EM_HOTFIX_CERTS            = "extensions.hotfix.certs.";
    41 const PREF_MATCH_OS_LOCALE            = "intl.locale.matchOS";
    42 const PREF_SELECTED_LOCALE            = "general.useragent.locale";
    43 const UNKNOWN_XPCOM_ABI               = "unknownABI";
    45 const UPDATE_REQUEST_VERSION          = 2;
    46 const CATEGORY_UPDATE_PARAMS          = "extension-update-params";
    48 const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
    50 const KEY_PROFILEDIR                  = "ProfD";
    51 const KEY_APPDIR                      = "XCurProcD";
    52 const FILE_BLOCKLIST                  = "blocklist.xml";
    54 const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
    55 const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
    56 #ifdef MOZ_COMPATIBILITY_NIGHTLY
    57 var PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + ".nightly";
    58 #else
    59 var PREF_EM_CHECK_COMPATIBILITY;
    60 #endif
    62 const TOOLKIT_ID                      = "toolkit@mozilla.org";
    64 const VALID_TYPES_REGEXP = /^[\w\-]+$/;
    66 Cu.import("resource://gre/modules/Services.jsm");
    67 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    68 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
    70 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
    71                                   "resource://gre/modules/Promise.jsm");
    72 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
    73                                   "resource://gre/modules/addons/AddonRepository.jsm");
    74 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
    75                                   "resource://gre/modules/FileUtils.jsm");
    77 XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
    78   let certUtils = {};
    79   Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
    80   return certUtils;
    81 });
    84 this.EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
    86 const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
    88 // A list of providers to load by default
    89 const DEFAULT_PROVIDERS = [
    90   "resource://gre/modules/addons/XPIProvider.jsm",
    91   "resource://gre/modules/LightweightThemeManager.jsm"
    92 ];
    94 Cu.import("resource://gre/modules/Log.jsm");
    95 // Configure a logger at the parent 'addons' level to format
    96 // messages for all the modules under addons.*
    97 const PARENT_LOGGER_ID = "addons";
    98 let parentLogger = Log.repository.getLogger(PARENT_LOGGER_ID);
    99 parentLogger.level = Log.Level.Warn;
   100 let formatter = new Log.BasicFormatter();
   101 // Set parent logger (and its children) to append to
   102 // the Javascript section of the Browser Console
   103 parentLogger.addAppender(new Log.ConsoleAppender(formatter));
   104 // Set parent logger (and its children) to
   105 // also append to standard out
   106 parentLogger.addAppender(new Log.DumpAppender(formatter));
   108 // Create a new logger (child of 'addons' logger)
   109 // for use by the Addons Manager
   110 const LOGGER_ID = "addons.manager";
   111 let logger = Log.repository.getLogger(LOGGER_ID);
   113 // Provide the ability to enable/disable logging
   114 // messages at runtime.
   115 // If the "extensions.logging.enabled" preference is
   116 // missing or 'false', messages at the WARNING and higher
   117 // severity should be logged to the JS console and standard error.
   118 // If "extensions.logging.enabled" is set to 'true', messages
   119 // at DEBUG and higher should go to JS console and standard error.
   120 const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
   121 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
   123 /**
   124  * Preference listener which listens for a change in the
   125  * "extensions.logging.enabled" preference and changes the logging level of the
   126  * parent 'addons' level logger accordingly.
   127  */
   128 var PrefObserver = {
   129     init: function PrefObserver_init() {
   130       Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
   131       Services.obs.addObserver(this, "xpcom-shutdown", false);
   132       this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
   133     },
   135     observe: function PrefObserver_observe(aSubject, aTopic, aData) {
   136       if (aTopic == "xpcom-shutdown") {
   137         Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
   138         Services.obs.removeObserver(this, "xpcom-shutdown");
   139       }
   140       else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
   141         let debugLogEnabled = false;
   142         try {
   143           debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
   144         }
   145         catch (e) {
   146         }
   147         if (debugLogEnabled) {
   148           parentLogger.level = Log.Level.Debug;
   149         }
   150         else {
   151           parentLogger.level = Log.Level.Warn;
   152         }
   153       }
   154     }
   155 };
   157 PrefObserver.init();
   159 /**
   160  * Calls a callback method consuming any thrown exception. Any parameters after
   161  * the callback parameter will be passed to the callback.
   162  *
   163  * @param  aCallback
   164  *         The callback method to call
   165  */
   166 function safeCall(aCallback, ...aArgs) {
   167   try {
   168     aCallback.apply(null, aArgs);
   169   }
   170   catch (e) {
   171     logger.warn("Exception calling callback", e);
   172   }
   173 }
   175 /**
   176  * Calls a method on a provider if it exists and consumes any thrown exception.
   177  * Any parameters after the dflt parameter are passed to the provider's method.
   178  *
   179  * @param  aProvider
   180  *         The provider to call
   181  * @param  aMethod
   182  *         The method name to call
   183  * @param  aDefault
   184  *         A default return value if the provider does not implement the named
   185  *         method or throws an error.
   186  * @return the return value from the provider or dflt if the provider does not
   187  *         implement method or throws an error
   188  */
   189 function callProvider(aProvider, aMethod, aDefault, ...aArgs) {
   190   if (!(aMethod in aProvider))
   191     return aDefault;
   193   try {
   194     return aProvider[aMethod].apply(aProvider, aArgs);
   195   }
   196   catch (e) {
   197     logger.error("Exception calling provider " + aMethod, e);
   198     return aDefault;
   199   }
   200 }
   202 /**
   203  * Gets the currently selected locale for display.
   204  * @return  the selected locale or "en-US" if none is selected
   205  */
   206 function getLocale() {
   207   try {
   208     if (Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE))
   209       return Services.locale.getLocaleComponentForUserAgent();
   210   }
   211   catch (e) { }
   213   try {
   214     let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
   215                                                 Ci.nsIPrefLocalizedString);
   216     if (locale)
   217       return locale;
   218   }
   219   catch (e) { }
   221   try {
   222     return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
   223   }
   224   catch (e) { }
   226   return "en-US";
   227 }
   229 /**
   230  * A helper class to repeatedly call a listener with each object in an array
   231  * optionally checking whether the object has a method in it.
   232  *
   233  * @param  aObjects
   234  *         The array of objects to iterate through
   235  * @param  aMethod
   236  *         An optional method name, if not null any objects without this method
   237  *         will not be passed to the listener
   238  * @param  aListener
   239  *         A listener implementing nextObject and noMoreObjects methods. The
   240  *         former will be called with the AsyncObjectCaller as the first
   241  *         parameter and the object as the second. noMoreObjects will be passed
   242  *         just the AsyncObjectCaller
   243  */
   244 function AsyncObjectCaller(aObjects, aMethod, aListener) {
   245   this.objects = aObjects.slice(0);
   246   this.method = aMethod;
   247   this.listener = aListener;
   249   this.callNext();
   250 }
   252 AsyncObjectCaller.prototype = {
   253   objects: null,
   254   method: null,
   255   listener: null,
   257   /**
   258    * Passes the next object to the listener or calls noMoreObjects if there
   259    * are none left.
   260    */
   261   callNext: function AOC_callNext() {
   262     if (this.objects.length == 0) {
   263       this.listener.noMoreObjects(this);
   264       return;
   265     }
   267     let object = this.objects.shift();
   268     if (!this.method || this.method in object)
   269       this.listener.nextObject(this, object);
   270     else
   271       this.callNext();
   272   }
   273 };
   275 /**
   276  * This represents an author of an add-on (e.g. creator or developer)
   277  *
   278  * @param  aName
   279  *         The name of the author
   280  * @param  aURL
   281  *         The URL of the author's profile page
   282  */
   283 function AddonAuthor(aName, aURL) {
   284   this.name = aName;
   285   this.url = aURL;
   286 }
   288 AddonAuthor.prototype = {
   289   name: null,
   290   url: null,
   292   // Returns the author's name, defaulting to the empty string
   293   toString: function AddonAuthor_toString() {
   294     return this.name || "";
   295   }
   296 }
   298 /**
   299  * This represents an screenshot for an add-on
   300  *
   301  * @param  aURL
   302  *         The URL to the full version of the screenshot
   303  * @param  aWidth
   304  *         The width in pixels of the screenshot
   305  * @param  aHeight
   306  *         The height in pixels of the screenshot
   307  * @param  aThumbnailURL
   308  *         The URL to the thumbnail version of the screenshot
   309  * @param  aThumbnailWidth
   310  *         The width in pixels of the thumbnail version of the screenshot
   311  * @param  aThumbnailHeight
   312  *         The height in pixels of the thumbnail version of the screenshot
   313  * @param  aCaption
   314  *         The caption of the screenshot
   315  */
   316 function AddonScreenshot(aURL, aWidth, aHeight, aThumbnailURL,
   317                          aThumbnailWidth, aThumbnailHeight, aCaption) {
   318   this.url = aURL;
   319   if (aWidth) this.width = aWidth;
   320   if (aHeight) this.height = aHeight;
   321   if (aThumbnailURL) this.thumbnailURL = aThumbnailURL;
   322   if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth;
   323   if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight;
   324   if (aCaption) this.caption = aCaption;
   325 }
   327 AddonScreenshot.prototype = {
   328   url: null,
   329   width: null,
   330   height: null,
   331   thumbnailURL: null,
   332   thumbnailWidth: null,
   333   thumbnailHeight: null,
   334   caption: null,
   336   // Returns the screenshot URL, defaulting to the empty string
   337   toString: function AddonScreenshot_toString() {
   338     return this.url || "";
   339   }
   340 }
   343 /**
   344  * This represents a compatibility override for an addon.
   345  *
   346  * @param  aType
   347  *         Overrride type - "compatible" or "incompatible"
   348  * @param  aMinVersion
   349  *         Minimum version of the addon to match
   350  * @param  aMaxVersion
   351  *         Maximum version of the addon to match
   352  * @param  aAppID
   353  *         Application ID used to match appMinVersion and appMaxVersion
   354  * @param  aAppMinVersion
   355  *         Minimum version of the application to match
   356  * @param  aAppMaxVersion
   357  *         Maximum version of the application to match
   358  */
   359 function AddonCompatibilityOverride(aType, aMinVersion, aMaxVersion, aAppID,
   360                                     aAppMinVersion, aAppMaxVersion) {
   361   this.type = aType;
   362   this.minVersion = aMinVersion;
   363   this.maxVersion = aMaxVersion;
   364   this.appID = aAppID;
   365   this.appMinVersion = aAppMinVersion;
   366   this.appMaxVersion = aAppMaxVersion;
   367 }
   369 AddonCompatibilityOverride.prototype = {
   370   /**
   371    * Type of override - "incompatible" or "compatible".
   372    * Only "incompatible" is supported for now.
   373    */
   374   type: null,
   376   /**
   377    * Min version of the addon to match.
   378    */
   379   minVersion: null,
   381   /**
   382    * Max version of the addon to match.
   383    */
   384   maxVersion: null,
   386   /**
   387    * Application ID to match.
   388    */
   389   appID: null,
   391   /**
   392    * Min version of the application to match.
   393    */
   394   appMinVersion: null,
   396   /**
   397    * Max version of the application to match.
   398    */
   399   appMaxVersion: null
   400 };
   403 /**
   404  * A type of add-on, used by the UI to determine how to display different types
   405  * of add-ons.
   406  *
   407  * @param  aID
   408  *         The add-on type ID
   409  * @param  aLocaleURI
   410  *         The URI of a localized properties file to get the displayable name
   411  *         for the type from
   412  * @param  aLocaleKey
   413  *         The key for the string in the properties file or the actual display
   414  *         name if aLocaleURI is null. Include %ID% to include the type ID in
   415  *         the key
   416  * @param  aViewType
   417  *         The optional type of view to use in the UI
   418  * @param  aUIPriority
   419  *         The priority is used by the UI to list the types in order. Lower
   420  *         values push the type higher in the list.
   421  * @param  aFlags
   422  *         An option set of flags that customize the display of the add-on in
   423  *         the UI.
   424  */
   425 function AddonType(aID, aLocaleURI, aLocaleKey, aViewType, aUIPriority, aFlags) {
   426   if (!aID)
   427     throw Components.Exception("An AddonType must have an ID", Cr.NS_ERROR_INVALID_ARG);
   429   if (aViewType && aUIPriority === undefined)
   430     throw Components.Exception("An AddonType with a defined view must have a set UI priority",
   431                                Cr.NS_ERROR_INVALID_ARG);
   433   if (!aLocaleKey)
   434     throw Components.Exception("An AddonType must have a displayable name",
   435                                Cr.NS_ERROR_INVALID_ARG);
   437   this.id = aID;
   438   this.uiPriority = aUIPriority;
   439   this.viewType = aViewType;
   440   this.flags = aFlags;
   442   if (aLocaleURI) {
   443     this.__defineGetter__("name", function nameGetter() {
   444       delete this.name;
   445       let bundle = Services.strings.createBundle(aLocaleURI);
   446       this.name = bundle.GetStringFromName(aLocaleKey.replace("%ID%", aID));
   447       return this.name;
   448     });
   449   }
   450   else {
   451     this.name = aLocaleKey;
   452   }
   453 }
   455 var gStarted = false;
   456 var gStartupComplete = false;
   457 var gCheckCompatibility = true;
   458 var gStrictCompatibility = true;
   459 var gCheckUpdateSecurityDefault = true;
   460 var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
   461 var gUpdateEnabled = true;
   462 var gAutoUpdateDefault = true;
   463 var gHotfixID = null;
   465 /**
   466  * This is the real manager, kept here rather than in AddonManager to keep its
   467  * contents hidden from API users.
   468  */
   469 var AddonManagerInternal = {
   470   managerListeners: [],
   471   installListeners: [],
   472   addonListeners: [],
   473   typeListeners: [],
   474   providers: [],
   475   types: {},
   476   startupChanges: {},
   477   // Store telemetry details per addon provider
   478   telemetryDetails: {},
   481   // A read-only wrapper around the types dictionary
   482   typesProxy: Proxy.create({
   483     getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) {
   484       if (!(aName in AddonManagerInternal.types))
   485         return undefined;
   487       return {
   488         value: AddonManagerInternal.types[aName].type,
   489         writable: false,
   490         configurable: false,
   491         enumerable: true
   492       }
   493     },
   495     getPropertyDescriptor: function typesProxy_getPropertyDescriptor(aName) {
   496       return this.getOwnPropertyDescriptor(aName);
   497     },
   499     getOwnPropertyNames: function typesProxy_getOwnPropertyNames() {
   500       return Object.keys(AddonManagerInternal.types);
   501     },
   503     getPropertyNames: function typesProxy_getPropertyNames() {
   504       return this.getOwnPropertyNames();
   505     },
   507     delete: function typesProxy_delete(aName) {
   508       // Not allowed to delete properties
   509       return false;
   510     },
   512     defineProperty: function typesProxy_defineProperty(aName, aProperty) {
   513       // Ignore attempts to define properties
   514     },
   516     fix: function typesProxy_fix(){
   517       return undefined;
   518     },
   520     // Despite MDC's claims to the contrary, it is required that this trap
   521     // be defined
   522     enumerate: function typesProxy_enumerate() {
   523       // All properties are enumerable
   524       return this.getPropertyNames();
   525     }
   526   }),
   528   recordTimestamp: function AMI_recordTimestamp(name, value) {
   529     this.TelemetryTimestamps.add(name, value);
   530   },
   532   validateBlocklist: function AMI_validateBlocklist() {
   533     let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
   535     // If there is no application shipped blocklist then there is nothing to do
   536     if (!appBlocklist.exists())
   537       return;
   539     let profileBlocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
   541     // If there is no blocklist in the profile then copy the application shipped
   542     // one there
   543     if (!profileBlocklist.exists()) {
   544       try {
   545         appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
   546       }
   547       catch (e) {
   548         logger.warn("Failed to copy the application shipped blocklist to the profile", e);
   549       }
   550       return;
   551     }
   553     let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
   554                      createInstance(Ci.nsIFileInputStream);
   555     try {
   556       let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
   557                     createInstance(Ci.nsIConverterInputStream);
   558       fileStream.init(appBlocklist, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
   559       cstream.init(fileStream, "UTF-8", 0, 0);
   561       let data = "";
   562       let str = {};
   563       let read = 0;
   564       do {
   565         read = cstream.readString(0xffffffff, str);
   566         data += str.value;
   567       } while (read != 0);
   569       let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
   570                    createInstance(Ci.nsIDOMParser);
   571       var doc = parser.parseFromString(data, "text/xml");
   572     }
   573     catch (e) {
   574       logger.warn("Application shipped blocklist could not be loaded", e);
   575       return;
   576     }
   577     finally {
   578       try {
   579         fileStream.close();
   580       }
   581       catch (e) {
   582         logger.warn("Unable to close blocklist file stream", e);
   583       }
   584     }
   586     // If the namespace is incorrect then ignore the application shipped
   587     // blocklist
   588     if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
   589       logger.warn("Application shipped blocklist has an unexpected namespace (" +
   590                   doc.documentElement.namespaceURI + ")");
   591       return;
   592     }
   594     // If there is no lastupdate information then ignore the application shipped
   595     // blocklist
   596     if (!doc.documentElement.hasAttribute("lastupdate"))
   597       return;
   599     // If the application shipped blocklist is older than the profile blocklist
   600     // then do nothing
   601     if (doc.documentElement.getAttribute("lastupdate") <=
   602         profileBlocklist.lastModifiedTime)
   603       return;
   605     // Otherwise copy the application shipped blocklist to the profile
   606     try {
   607       appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
   608     }
   609     catch (e) {
   610       logger.warn("Failed to copy the application shipped blocklist to the profile", e);
   611     }
   612   },
   614   /**
   615    * Initializes the AddonManager, loading any known providers and initializing
   616    * them.
   617    */
   618   startup: function AMI_startup() {
   619     try {
   620       if (gStarted)
   621         return;
   623       this.recordTimestamp("AMI_startup_begin");
   625       // clear this for xpcshell test restarts
   626       for (let provider in this.telemetryDetails)
   627         delete this.telemetryDetails[provider];
   629       let appChanged = undefined;
   631       let oldAppVersion = null;
   632       try {
   633         oldAppVersion = Services.prefs.getCharPref(PREF_EM_LAST_APP_VERSION);
   634         appChanged = Services.appinfo.version != oldAppVersion;
   635       }
   636       catch (e) { }
   638       let oldPlatformVersion = null;
   639       try {
   640         oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION);
   641       }
   642       catch (e) { }
   644       if (appChanged !== false) {
   645         logger.debug("Application has been upgraded");
   646         Services.prefs.setCharPref(PREF_EM_LAST_APP_VERSION,
   647                                    Services.appinfo.version);
   648         Services.prefs.setCharPref(PREF_EM_LAST_PLATFORM_VERSION,
   649                                    Services.appinfo.platformVersion);
   650         Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION,
   651                                   (appChanged === undefined ? 0 : -1));
   652         this.validateBlocklist();
   653       }
   655 #ifndef MOZ_COMPATIBILITY_NIGHTLY
   656       PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
   657                                     Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
   658 #endif
   660       try {
   661         gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
   662       } catch (e) {}
   663       Services.prefs.addObserver(PREF_EM_CHECK_COMPATIBILITY, this, false);
   665       try {
   666         gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
   667       } catch (e) {}
   668       Services.prefs.addObserver(PREF_EM_STRICT_COMPATIBILITY, this, false);
   670       try {
   671         let defaultBranch = Services.prefs.getDefaultBranch("");
   672         gCheckUpdateSecurityDefault = defaultBranch.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
   673       } catch(e) {}
   675       try {
   676         gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
   677       } catch (e) {}
   678       Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
   680       try {
   681         gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
   682       } catch (e) {}
   683       Services.prefs.addObserver(PREF_EM_UPDATE_ENABLED, this, false);
   685       try {
   686         gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
   687       } catch (e) {}
   688       Services.prefs.addObserver(PREF_EM_AUTOUPDATE_DEFAULT, this, false);
   690       try {
   691         gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
   692       } catch (e) {}
   693       Services.prefs.addObserver(PREF_EM_HOTFIX_ID, this, false);
   695       let defaultProvidersEnabled = true;
   696       try {
   697         defaultProvidersEnabled = Services.prefs.getBoolPref(PREF_DEFAULT_PROVIDERS_ENABLED);
   698       } catch (e) {}
   699       AddonManagerPrivate.recordSimpleMeasure("default_providers", defaultProvidersEnabled);
   701       // Ensure all default providers have had a chance to register themselves
   702       if (defaultProvidersEnabled) {
   703         for (let url of DEFAULT_PROVIDERS) {
   704           try {
   705             let scope = {};
   706             Components.utils.import(url, scope);
   707             // Sanity check - make sure the provider exports a symbol that
   708             // has a 'startup' method
   709             let syms = Object.keys(scope);
   710             if ((syms.length < 1) ||
   711                 (typeof scope[syms[0]].startup != "function")) {
   712               logger.warn("Provider " + url + " has no startup()");
   713               AddonManagerPrivate.recordException("AMI", "provider " + url, "no startup()");
   714             }
   715             logger.debug("Loaded provider scope for " + url + ": " + Object.keys(scope).toSource());
   716           }
   717           catch (e) {
   718             AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
   719             logger.error("Exception loading default provider \"" + url + "\"", e);
   720           }
   721         };
   722       }
   724       // Load any providers registered in the category manager
   725       let catman = Cc["@mozilla.org/categorymanager;1"].
   726                    getService(Ci.nsICategoryManager);
   727       let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
   728       while (entries.hasMoreElements()) {
   729         let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
   730         let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
   732         try {
   733           Components.utils.import(url, {});
   734         }
   735         catch (e) {
   736           AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
   737           logger.error("Exception loading provider " + entry + " from category \"" +
   738                 url + "\"", e);
   739         }
   740       }
   742       // Register our shutdown handler with the AsyncShutdown manager
   743       AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
   744                                                    this.shutdown.bind(this));
   746       // Once we start calling providers we must allow all normal methods to work.
   747       gStarted = true;
   749       this.callProviders("startup", appChanged, oldAppVersion,
   750                          oldPlatformVersion);
   752       // If this is a new profile just pretend that there were no changes
   753       if (appChanged === undefined) {
   754         for (let type in this.startupChanges)
   755           delete this.startupChanges[type];
   756       }
   758       gStartupComplete = true;
   759       this.recordTimestamp("AMI_startup_end");
   760     }
   761     catch (e) {
   762       logger.error("startup failed", e);
   763       AddonManagerPrivate.recordException("AMI", "startup failed", e);
   764     }
   765   },
   767   /**
   768    * Registers a new AddonProvider.
   769    *
   770    * @param  aProvider
   771    *         The provider to register
   772    * @param  aTypes
   773    *         An optional array of add-on types
   774    */
   775   registerProvider: function AMI_registerProvider(aProvider, aTypes) {
   776     if (!aProvider || typeof aProvider != "object")
   777       throw Components.Exception("aProvider must be specified",
   778                                  Cr.NS_ERROR_INVALID_ARG);
   780     if (aTypes && !Array.isArray(aTypes))
   781       throw Components.Exception("aTypes must be an array or null",
   782                                  Cr.NS_ERROR_INVALID_ARG);
   784     this.providers.push(aProvider);
   786     if (aTypes) {
   787       aTypes.forEach(function(aType) {
   788         if (!(aType.id in this.types)) {
   789           if (!VALID_TYPES_REGEXP.test(aType.id)) {
   790             logger.warn("Ignoring invalid type " + aType.id);
   791             return;
   792           }
   794           this.types[aType.id] = {
   795             type: aType,
   796             providers: [aProvider]
   797           };
   799           let typeListeners = this.typeListeners.slice(0);
   800           for (let listener of typeListeners) {
   801             safeCall(function listenerSafeCall() {
   802               listener.onTypeAdded(aType);
   803             });
   804           }
   805         }
   806         else {
   807           this.types[aType.id].providers.push(aProvider);
   808         }
   809       }, this);
   810     }
   812     // If we're registering after startup call this provider's startup.
   813     if (gStarted)
   814       callProvider(aProvider, "startup");
   815   },
   817   /**
   818    * Unregisters an AddonProvider.
   819    *
   820    * @param  aProvider
   821    *         The provider to unregister
   822    */
   823   unregisterProvider: function AMI_unregisterProvider(aProvider) {
   824     if (!aProvider || typeof aProvider != "object")
   825       throw Components.Exception("aProvider must be specified",
   826                                  Cr.NS_ERROR_INVALID_ARG);
   828     let pos = 0;
   829     while (pos < this.providers.length) {
   830       if (this.providers[pos] == aProvider)
   831         this.providers.splice(pos, 1);
   832       else
   833         pos++;
   834     }
   836     for (let type in this.types) {
   837       this.types[type].providers = this.types[type].providers.filter(function filterProvider(p) p != aProvider);
   838       if (this.types[type].providers.length == 0) {
   839         let oldType = this.types[type].type;
   840         delete this.types[type];
   842         let typeListeners = this.typeListeners.slice(0);
   843         for (let listener of typeListeners) {
   844           safeCall(function listenerSafeCall() {
   845             listener.onTypeRemoved(oldType);
   846           });
   847         }
   848       }
   849     }
   851     // If we're unregistering after startup call this provider's shutdown.
   852     if (gStarted)
   853       callProvider(aProvider, "shutdown");
   854   },
   856   /**
   857    * Calls a method on all registered providers if it exists and consumes any
   858    * thrown exception. Return values are ignored. Any parameters after the
   859    * method parameter are passed to the provider's method.
   860    *
   861    * @param  aMethod
   862    *         The method name to call
   863    * @see    callProvider
   864    */
   865   callProviders: function AMI_callProviders(aMethod, ...aArgs) {
   866     if (!aMethod || typeof aMethod != "string")
   867       throw Components.Exception("aMethod must be a non-empty string",
   868                                  Cr.NS_ERROR_INVALID_ARG);
   870     let providers = this.providers.slice(0);
   871     for (let provider of providers) {
   872       try {
   873         if (aMethod in provider)
   874           provider[aMethod].apply(provider, aArgs);
   875       }
   876       catch (e) {
   877         AddonManagerPrivate.recordException("AMI", "provider " + aMethod, e);
   878         logger.error("Exception calling provider " + aMethod, e);
   879       }
   880     }
   881   },
   883   /**
   884    * Calls a method on all registered providers, if the provider implements
   885    * the method. The called method is expected to return a promise, and
   886    * callProvidersAsync returns a promise that resolves when every provider
   887    * method has either resolved or rejected. Rejection reasons are logged
   888    * but otherwise ignored. Return values are ignored. Any parameters after the
   889    * method parameter are passed to the provider's method.
   890    *
   891    * @param  aMethod
   892    *         The method name to call
   893    * @see    callProvider
   894    */
   895   callProvidersAsync: function AMI_callProviders(aMethod, ...aArgs) {
   896     if (!aMethod || typeof aMethod != "string")
   897       throw Components.Exception("aMethod must be a non-empty string",
   898                                  Cr.NS_ERROR_INVALID_ARG);
   900     let allProviders = [];
   902     let providers = this.providers.slice(0);
   903     for (let provider of providers) {
   904       try {
   905         if (aMethod in provider) {
   906           // Resolve a new promise with the result of the method, to handle both
   907           // methods that return values (or nothing) and methods that return promises.
   908           let providerResult = provider[aMethod].apply(provider, aArgs);
   909           let nextPromise = Promise.resolve(providerResult);
   910           // Log and swallow the errors from methods that do return promises.
   911           nextPromise = nextPromise.then(
   912               null,
   913               e => logger.error("Exception calling provider " + aMethod, e));
   914           allProviders.push(nextPromise);
   915         }
   916       }
   917       catch (e) {
   918         logger.error("Exception calling provider " + aMethod, e);
   919       }
   920     }
   921     // Because we use promise.then to catch and log all errors above, Promise.all()
   922     // will never exit early because of a rejection.
   923     return Promise.all(allProviders);
   924   },
   926   /**
   927    * Shuts down the addon manager and all registered providers, this must clean
   928    * up everything in order for automated tests to fake restarts.
   929    * @return Promise{null} that resolves when all providers and dependent modules
   930    *                       have finished shutting down
   931    */
   932   shutdown: function AMI_shutdown() {
   933     logger.debug("shutdown");
   934     // Clean up listeners
   935     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
   936     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
   937     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
   938     Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
   939     Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
   940     Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
   942     // Only shut down providers if they've been started. Shut down
   943     // AddonRepository after providers (if any).
   944     let shuttingDown = null;
   945     if (gStarted) {
   946       shuttingDown = this.callProvidersAsync("shutdown")
   947         .then(null,
   948               err => logger.error("Failure during async provider shutdown", err))
   949         .then(() => AddonRepository.shutdown());
   950     }
   951     else {
   952       shuttingDown = AddonRepository.shutdown();
   953     }
   955       shuttingDown.then(val => logger.debug("Async provider shutdown done"),
   956                         err => logger.error("Failure during AddonRepository shutdown", err))
   957       .then(() => {
   958         this.managerListeners.splice(0, this.managerListeners.length);
   959         this.installListeners.splice(0, this.installListeners.length);
   960         this.addonListeners.splice(0, this.addonListeners.length);
   961         this.typeListeners.splice(0, this.typeListeners.length);
   962         for (let type in this.startupChanges)
   963           delete this.startupChanges[type];
   964         gStarted = false;
   965         gStartupComplete = false;
   966       });
   967     return shuttingDown;
   968   },
   970   /**
   971    * Notified when a preference we're interested in has changed.
   972    *
   973    * @see nsIObserver
   974    */
   975   observe: function AMI_observe(aSubject, aTopic, aData) {
   976     switch (aData) {
   977       case PREF_EM_CHECK_COMPATIBILITY: {
   978         let oldValue = gCheckCompatibility;
   979         try {
   980           gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
   981         } catch(e) {
   982           gCheckCompatibility = true;
   983         }
   985         this.callManagerListeners("onCompatibilityModeChanged");
   987         if (gCheckCompatibility != oldValue)
   988           this.updateAddonAppDisabledStates();
   990         break;
   991       }
   992       case PREF_EM_STRICT_COMPATIBILITY: {
   993         let oldValue = gStrictCompatibility;
   994         try {
   995           gStrictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
   996         } catch(e) {
   997           gStrictCompatibility = true;
   998         }
  1000         this.callManagerListeners("onCompatibilityModeChanged");
  1002         if (gStrictCompatibility != oldValue)
  1003           this.updateAddonAppDisabledStates();
  1005         break;
  1007       case PREF_EM_CHECK_UPDATE_SECURITY: {
  1008         let oldValue = gCheckUpdateSecurity;
  1009         try {
  1010           gCheckUpdateSecurity = Services.prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
  1011         } catch(e) {
  1012           gCheckUpdateSecurity = true;
  1015         this.callManagerListeners("onCheckUpdateSecurityChanged");
  1017         if (gCheckUpdateSecurity != oldValue)
  1018           this.updateAddonAppDisabledStates();
  1020         break;
  1022       case PREF_EM_UPDATE_ENABLED: {
  1023         let oldValue = gUpdateEnabled;
  1024         try {
  1025           gUpdateEnabled = Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED);
  1026         } catch(e) {
  1027           gUpdateEnabled = true;
  1030         this.callManagerListeners("onUpdateModeChanged");
  1031         break;
  1033       case PREF_EM_AUTOUPDATE_DEFAULT: {
  1034         let oldValue = gAutoUpdateDefault;
  1035         try {
  1036           gAutoUpdateDefault = Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
  1037         } catch(e) {
  1038           gAutoUpdateDefault = true;
  1041         this.callManagerListeners("onUpdateModeChanged");
  1042         break;
  1044       case PREF_EM_HOTFIX_ID: {
  1045         try {
  1046           gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
  1047         } catch(e) {
  1048           gHotfixID = null;
  1050         break;
  1053   },
  1055   /**
  1056    * Replaces %...% strings in an addon url (update and updateInfo) with
  1057    * appropriate values.
  1059    * @param  aAddon
  1060    *         The Addon representing the add-on
  1061    * @param  aUri
  1062    *         The string representation of the URI to escape
  1063    * @param  aAppVersion
  1064    *         The optional application version to use for %APP_VERSION%
  1065    * @return The appropriately escaped URI.
  1066    */
  1067   escapeAddonURI: function AMI_escapeAddonURI(aAddon, aUri, aAppVersion)
  1069     if (!aAddon || typeof aAddon != "object")
  1070       throw Components.Exception("aAddon must be an Addon object",
  1071                                  Cr.NS_ERROR_INVALID_ARG);
  1073     if (!aUri || typeof aUri != "string")
  1074       throw Components.Exception("aUri must be a non-empty string",
  1075                                  Cr.NS_ERROR_INVALID_ARG);
  1077     if (aAppVersion && typeof aAppVersion != "string")
  1078       throw Components.Exception("aAppVersion must be a string or null",
  1079                                  Cr.NS_ERROR_INVALID_ARG);
  1081     var addonStatus = aAddon.userDisabled || aAddon.softDisabled ? "userDisabled"
  1082                                                                  : "userEnabled";
  1084     if (!aAddon.isCompatible)
  1085       addonStatus += ",incompatible";
  1086     if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
  1087       addonStatus += ",blocklisted";
  1088     if (aAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  1089       addonStatus += ",softblocked";
  1091     try {
  1092       var xpcomABI = Services.appinfo.XPCOMABI;
  1093     } catch (ex) {
  1094       xpcomABI = UNKNOWN_XPCOM_ABI;
  1097     let uri = aUri.replace(/%ITEM_ID%/g, aAddon.id);
  1098     uri = uri.replace(/%ITEM_VERSION%/g, aAddon.version);
  1099     uri = uri.replace(/%ITEM_STATUS%/g, addonStatus);
  1100     uri = uri.replace(/%APP_ID%/g, Services.appinfo.ID);
  1101     uri = uri.replace(/%APP_VERSION%/g, aAppVersion ? aAppVersion :
  1102                                                       Services.appinfo.version);
  1103     uri = uri.replace(/%REQ_VERSION%/g, UPDATE_REQUEST_VERSION);
  1104     uri = uri.replace(/%APP_OS%/g, Services.appinfo.OS);
  1105     uri = uri.replace(/%APP_ABI%/g, xpcomABI);
  1106     uri = uri.replace(/%APP_LOCALE%/g, getLocale());
  1107     uri = uri.replace(/%CURRENT_APP_VERSION%/g, Services.appinfo.version);
  1109     // Replace custom parameters (names of custom parameters must have at
  1110     // least 3 characters to prevent lookups for something like %D0%C8)
  1111     var catMan = null;
  1112     uri = uri.replace(/%(\w{3,})%/g, function parameterReplace(aMatch, aParam) {
  1113       if (!catMan) {
  1114         catMan = Cc["@mozilla.org/categorymanager;1"].
  1115                  getService(Ci.nsICategoryManager);
  1118       try {
  1119         var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, aParam);
  1120         var paramHandler = Cc[contractID].getService(Ci.nsIPropertyBag2);
  1121         return paramHandler.getPropertyAsAString(aParam);
  1123       catch(e) {
  1124         return aMatch;
  1126     });
  1128     // escape() does not properly encode + symbols in any embedded FVF strings.
  1129     return uri.replace(/\+/g, "%2B");
  1130   },
  1132   /**
  1133    * Performs a background update check by starting an update for all add-ons
  1134    * that can be updated.
  1135    */
  1136   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
  1137     if (!gStarted)
  1138       throw Components.Exception("AddonManager is not initialized",
  1139                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1141     let hotfixID = this.hotfixID;
  1143     let checkHotfix = hotfixID &&
  1144                       Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
  1145                       Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
  1147     if (!this.updateEnabled && !checkHotfix)
  1148       return;
  1150     Services.obs.notifyObservers(null, "addons-background-update-start", null);
  1152     // Start this from one to ensure the whole of this function completes before
  1153     // we can send the complete notification. Some parts can in some cases
  1154     // complete synchronously before later parts have a chance to increment
  1155     // pendingUpdates.
  1156     let pendingUpdates = 1;
  1158     function notifyComplete() {
  1159       if (--pendingUpdates == 0) {
  1160         Services.obs.notifyObservers(null,
  1161                                      "addons-background-update-complete",
  1162                                      null);
  1166     if (this.updateEnabled) {
  1167       let scope = {};
  1168       Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
  1169       scope.LightweightThemeManager.updateCurrentTheme();
  1171       pendingUpdates++;
  1172       this.getAllAddons(function getAddonsCallback(aAddons) {
  1173         // If there is a known hotfix then exclude it from the list of add-ons to update.
  1174         var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
  1176         // Repopulate repository cache first, to ensure compatibility overrides
  1177         // are up to date before checking for addon updates.
  1178         AddonRepository.backgroundUpdateCheck(
  1179                      ids, function BUC_backgroundUpdateCheckCallback() {
  1180           pendingUpdates += aAddons.length;
  1181           aAddons.forEach(function BUC_forEachCallback(aAddon) {
  1182             if (aAddon.id == hotfixID) {
  1183               notifyComplete();
  1184               return;
  1187             // Check all add-ons for updates so that any compatibility updates will
  1188             // be applied
  1189             aAddon.findUpdates({
  1190               onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
  1191                 // Start installing updates when the add-on can be updated and
  1192                 // background updates should be applied.
  1193                 if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
  1194                     AddonManager.shouldAutoUpdate(aAddon)) {
  1195                   aInstall.install();
  1197               },
  1199               onUpdateFinished: notifyComplete
  1200             }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
  1201           });
  1203           notifyComplete();
  1204         });
  1205       });
  1208     if (checkHotfix) {
  1209       var hotfixVersion = "";
  1210       try {
  1211         hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
  1213       catch (e) { }
  1215       let url = null;
  1216       if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
  1217         url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
  1218       else
  1219         url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
  1221       // Build the URI from a fake add-on data.
  1222       url = AddonManager.escapeAddonURI({
  1223         id: hotfixID,
  1224         version: hotfixVersion,
  1225         userDisabled: false,
  1226         appDisabled: false
  1227       }, url);
  1229       pendingUpdates++;
  1230       Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
  1231       AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
  1232         onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) {
  1233           let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates);
  1234           if (!update) {
  1235             notifyComplete();
  1236             return;
  1239           // If the available version isn't newer than the last installed
  1240           // version then ignore it.
  1241           if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
  1242             notifyComplete();
  1243             return;
  1246           logger.debug("Downloading hotfix version " + update.version);
  1247           AddonManager.getInstallForURL(update.updateURL,
  1248                                        function BUC_getInstallForURL(aInstall) {
  1249             aInstall.addListener({
  1250               onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
  1251                 try {
  1252                   if (!Services.prefs.getBoolPref(PREF_EM_CERT_CHECKATTRIBUTES))
  1253                     return;
  1255                 catch (e) {
  1256                   // By default don't do certificate checks.
  1257                   return;
  1260                 try {
  1261                   CertUtils.validateCert(aInstall.certificate,
  1262                                          CertUtils.readCertPrefs(PREF_EM_HOTFIX_CERTS));
  1264                 catch (e) {
  1265                   logger.warn("The hotfix add-on was not signed by the expected " +
  1266                        "certificate and so will not be installed.");
  1267                   aInstall.cancel();
  1269               },
  1271               onInstallEnded: function BUC_onInstallEnded(aInstall) {
  1272                 // Remember the last successfully installed version.
  1273                 Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
  1274                                            aInstall.version);
  1275               },
  1277               onInstallCancelled: function BUC_onInstallCancelled(aInstall) {
  1278                 // Revert to the previous version if the installation was
  1279                 // cancelled.
  1280                 Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
  1281                                            hotfixVersion);
  1283             });
  1285             aInstall.install();
  1287             notifyComplete();
  1288           }, "application/x-xpinstall", update.updateHash, null,
  1289              null, update.version);
  1290         },
  1292         onUpdateCheckError: notifyComplete
  1293       });
  1296     notifyComplete();
  1297   },
  1299   /**
  1300    * Adds a add-on to the list of detected changes for this startup. If
  1301    * addStartupChange is called multiple times for the same add-on in the same
  1302    * startup then only the most recent change will be remembered.
  1304    * @param  aType
  1305    *         The type of change as a string. Providers can define their own
  1306    *         types of changes or use the existing defined STARTUP_CHANGE_*
  1307    *         constants
  1308    * @param  aID
  1309    *         The ID of the add-on
  1310    */
  1311   addStartupChange: function AMI_addStartupChange(aType, aID) {
  1312     if (!aType || typeof aType != "string")
  1313       throw Components.Exception("aType must be a non-empty string",
  1314                                  Cr.NS_ERROR_INVALID_ARG);
  1316     if (!aID || typeof aID != "string")
  1317       throw Components.Exception("aID must be a non-empty string",
  1318                                  Cr.NS_ERROR_INVALID_ARG);
  1320     if (gStartupComplete)
  1321       return;
  1323     // Ensure that an ID is only listed in one type of change
  1324     for (let type in this.startupChanges)
  1325       this.removeStartupChange(type, aID);
  1327     if (!(aType in this.startupChanges))
  1328       this.startupChanges[aType] = [];
  1329     this.startupChanges[aType].push(aID);
  1330   },
  1332   /**
  1333    * Removes a startup change for an add-on.
  1335    * @param  aType
  1336    *         The type of change
  1337    * @param  aID
  1338    *         The ID of the add-on
  1339    */
  1340   removeStartupChange: function AMI_removeStartupChange(aType, aID) {
  1341     if (!aType || typeof aType != "string")
  1342       throw Components.Exception("aType must be a non-empty string",
  1343                                  Cr.NS_ERROR_INVALID_ARG);
  1345     if (!aID || typeof aID != "string")
  1346       throw Components.Exception("aID must be a non-empty string",
  1347                                  Cr.NS_ERROR_INVALID_ARG);
  1349     if (gStartupComplete)
  1350       return;
  1352     if (!(aType in this.startupChanges))
  1353       return;
  1355     this.startupChanges[aType] = this.startupChanges[aType].filter(
  1356                                  function filterItem(aItem) aItem != aID);
  1357   },
  1359   /**
  1360    * Calls all registered AddonManagerListeners with an event. Any parameters
  1361    * after the method parameter are passed to the listener.
  1363    * @param  aMethod
  1364    *         The method on the listeners to call
  1365    */
  1366   callManagerListeners: function AMI_callManagerListeners(aMethod, ...aArgs) {
  1367     if (!gStarted)
  1368       throw Components.Exception("AddonManager is not initialized",
  1369                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1371     if (!aMethod || typeof aMethod != "string")
  1372       throw Components.Exception("aMethod must be a non-empty string",
  1373                                  Cr.NS_ERROR_INVALID_ARG);
  1375     let managerListeners = this.managerListeners.slice(0);
  1376     for (let listener of managerListeners) {
  1377       try {
  1378         if (aMethod in listener)
  1379           listener[aMethod].apply(listener, aArgs);
  1381       catch (e) {
  1382         logger.warn("AddonManagerListener threw exception when calling " + aMethod, e);
  1385   },
  1387   /**
  1388    * Calls all registered InstallListeners with an event. Any parameters after
  1389    * the extraListeners parameter are passed to the listener.
  1391    * @param  aMethod
  1392    *         The method on the listeners to call
  1393    * @param  aExtraListeners
  1394    *         An optional array of extra InstallListeners to also call
  1395    * @return false if any of the listeners returned false, true otherwise
  1396    */
  1397   callInstallListeners: function AMI_callInstallListeners(aMethod,
  1398                                  aExtraListeners, ...aArgs) {
  1399     if (!gStarted)
  1400       throw Components.Exception("AddonManager is not initialized",
  1401                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1403     if (!aMethod || typeof aMethod != "string")
  1404       throw Components.Exception("aMethod must be a non-empty string",
  1405                                  Cr.NS_ERROR_INVALID_ARG);
  1407     if (aExtraListeners && !Array.isArray(aExtraListeners))
  1408       throw Components.Exception("aExtraListeners must be an array or null",
  1409                                  Cr.NS_ERROR_INVALID_ARG);
  1411     let result = true;
  1412     let listeners;
  1413     if (aExtraListeners)
  1414       listeners = aExtraListeners.concat(this.installListeners);
  1415     else
  1416       listeners = this.installListeners.slice(0);
  1418     for (let listener of listeners) {
  1419       try {
  1420         if (aMethod in listener) {
  1421           if (listener[aMethod].apply(listener, aArgs) === false)
  1422             result = false;
  1425       catch (e) {
  1426         logger.warn("InstallListener threw exception when calling " + aMethod, e);
  1429     return result;
  1430   },
  1432   /**
  1433    * Calls all registered AddonListeners with an event. Any parameters after
  1434    * the method parameter are passed to the listener.
  1436    * @param  aMethod
  1437    *         The method on the listeners to call
  1438    */
  1439   callAddonListeners: function AMI_callAddonListeners(aMethod, ...aArgs) {
  1440     if (!gStarted)
  1441       throw Components.Exception("AddonManager is not initialized",
  1442                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1444     if (!aMethod || typeof aMethod != "string")
  1445       throw Components.Exception("aMethod must be a non-empty string",
  1446                                  Cr.NS_ERROR_INVALID_ARG);
  1448     let addonListeners = this.addonListeners.slice(0);
  1449     for (let listener of addonListeners) {
  1450       try {
  1451         if (aMethod in listener)
  1452           listener[aMethod].apply(listener, aArgs);
  1454       catch (e) {
  1455         logger.warn("AddonListener threw exception when calling " + aMethod, e);
  1458   },
  1460   /**
  1461    * Notifies all providers that an add-on has been enabled when that type of
  1462    * add-on only supports a single add-on being enabled at a time. This allows
  1463    * the providers to disable theirs if necessary.
  1465    * @param  aID
  1466    *         The ID of the enabled add-on
  1467    * @param  aType
  1468    *         The type of the enabled add-on
  1469    * @param  aPendingRestart
  1470    *         A boolean indicating if the change will only take place the next
  1471    *         time the application is restarted
  1472    */
  1473   notifyAddonChanged: function AMI_notifyAddonChanged(aID, aType, aPendingRestart) {
  1474     if (!gStarted)
  1475       throw Components.Exception("AddonManager is not initialized",
  1476                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1478     if (aID && typeof aID != "string")
  1479       throw Components.Exception("aID must be a string or null",
  1480                                  Cr.NS_ERROR_INVALID_ARG);
  1482     if (!aType || typeof aType != "string")
  1483       throw Components.Exception("aType must be a non-empty string",
  1484                                  Cr.NS_ERROR_INVALID_ARG);
  1486     this.callProviders("addonChanged", aID, aType, aPendingRestart);
  1487   },
  1489   /**
  1490    * Notifies all providers they need to update the appDisabled property for
  1491    * their add-ons in response to an application change such as a blocklist
  1492    * update.
  1493    */
  1494   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
  1495     if (!gStarted)
  1496       throw Components.Exception("AddonManager is not initialized",
  1497                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1499     this.callProviders("updateAddonAppDisabledStates");
  1500   },
  1502   /**
  1503    * Notifies all providers that the repository has updated its data for
  1504    * installed add-ons.
  1506    * @param  aCallback
  1507    *         Function to call when operation is complete.
  1508    */
  1509   updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
  1510     if (!gStarted)
  1511       throw Components.Exception("AddonManager is not initialized",
  1512                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1514     if (typeof aCallback != "function")
  1515       throw Components.Exception("aCallback must be a function",
  1516                                  Cr.NS_ERROR_INVALID_ARG);
  1518     new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", {
  1519       nextObject: function updateAddonRepositoryData_nextObject(aCaller, aProvider) {
  1520         callProvider(aProvider,
  1521                      "updateAddonRepositoryData",
  1522                      null,
  1523                      aCaller.callNext.bind(aCaller));
  1524       },
  1525       noMoreObjects: function updateAddonRepositoryData_noMoreObjects(aCaller) {
  1526         safeCall(aCallback);
  1527         // only tests should care about this
  1528         Services.obs.notifyObservers(null, "TEST:addon-repository-data-updated", null);
  1530     });
  1531   },
  1533   /**
  1534    * Asynchronously gets an AddonInstall for a URL.
  1536    * @param  aUrl
  1537    *         The string represenation of the URL the add-on is located at
  1538    * @param  aCallback
  1539    *         A callback to pass the AddonInstall to
  1540    * @param  aMimetype
  1541    *         The mimetype of the add-on
  1542    * @param  aHash
  1543    *         An optional hash of the add-on
  1544    * @param  aName
  1545    *         An optional placeholder name while the add-on is being downloaded
  1546    * @param  aIcons
  1547    *         Optional placeholder icons while the add-on is being downloaded
  1548    * @param  aVersion
  1549    *         An optional placeholder version while the add-on is being downloaded
  1550    * @param  aLoadGroup
  1551    *         An optional nsILoadGroup to associate any network requests with
  1552    * @throws if the aUrl, aCallback or aMimetype arguments are not specified
  1553    */
  1554   getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype,
  1555                                                   aHash, aName, aIcons,
  1556                                                   aVersion, aLoadGroup) {
  1557     if (!gStarted)
  1558       throw Components.Exception("AddonManager is not initialized",
  1559                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1561     if (!aUrl || typeof aUrl != "string")
  1562       throw Components.Exception("aURL must be a non-empty string",
  1563                                  Cr.NS_ERROR_INVALID_ARG);
  1565     if (typeof aCallback != "function")
  1566       throw Components.Exception("aCallback must be a function",
  1567                                  Cr.NS_ERROR_INVALID_ARG);
  1569     if (!aMimetype || typeof aMimetype != "string")
  1570       throw Components.Exception("aMimetype must be a non-empty string",
  1571                                  Cr.NS_ERROR_INVALID_ARG);
  1573     if (aHash && typeof aHash != "string")
  1574       throw Components.Exception("aHash must be a string or null",
  1575                                  Cr.NS_ERROR_INVALID_ARG);
  1577     if (aName && typeof aName != "string")
  1578       throw Components.Exception("aName must be a string or null",
  1579                                  Cr.NS_ERROR_INVALID_ARG);
  1581     if (aIcons) {
  1582       if (typeof aIcons == "string")
  1583         aIcons = { "32": aIcons };
  1584       else if (typeof aIcons != "object")
  1585         throw Components.Exception("aIcons must be a string, an object or null",
  1586                                    Cr.NS_ERROR_INVALID_ARG);
  1587     } else {
  1588       aIcons = {};
  1591     if (aVersion && typeof aVersion != "string")
  1592       throw Components.Exception("aVersion must be a string or null",
  1593                                  Cr.NS_ERROR_INVALID_ARG);
  1595     if (aLoadGroup && (!(aLoadGroup instanceof Ci.nsILoadGroup)))
  1596       throw Components.Exception("aLoadGroup must be a nsILoadGroup or null",
  1597                                  Cr.NS_ERROR_INVALID_ARG);
  1599     let providers = this.providers.slice(0);
  1600     for (let provider of providers) {
  1601       if (callProvider(provider, "supportsMimetype", false, aMimetype)) {
  1602         callProvider(provider, "getInstallForURL", null,
  1603                      aUrl, aHash, aName, aIcons, aVersion, aLoadGroup,
  1604                      function  getInstallForURL_safeCall(aInstall) {
  1605           safeCall(aCallback, aInstall);
  1606         });
  1607         return;
  1610     safeCall(aCallback, null);
  1611   },
  1613   /**
  1614    * Asynchronously gets an AddonInstall for an nsIFile.
  1616    * @param  aFile
  1617    *         The nsIFile where the add-on is located
  1618    * @param  aCallback
  1619    *         A callback to pass the AddonInstall to
  1620    * @param  aMimetype
  1621    *         An optional mimetype hint for the add-on
  1622    * @throws if the aFile or aCallback arguments are not specified
  1623    */
  1624   getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) {
  1625     if (!gStarted)
  1626       throw Components.Exception("AddonManager is not initialized",
  1627                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1629     if (!(aFile instanceof Ci.nsIFile))
  1630       throw Components.Exception("aFile must be a nsIFile",
  1631                                  Cr.NS_ERROR_INVALID_ARG);
  1633     if (typeof aCallback != "function")
  1634       throw Components.Exception("aCallback must be a function",
  1635                                  Cr.NS_ERROR_INVALID_ARG);
  1637     if (aMimetype && typeof aMimetype != "string")
  1638       throw Components.Exception("aMimetype must be a string or null",
  1639                                  Cr.NS_ERROR_INVALID_ARG);
  1641     new AsyncObjectCaller(this.providers, "getInstallForFile", {
  1642       nextObject: function getInstallForFile_nextObject(aCaller, aProvider) {
  1643         callProvider(aProvider, "getInstallForFile", null, aFile,
  1644                      function getInstallForFile_safeCall(aInstall) {
  1645           if (aInstall)
  1646             safeCall(aCallback, aInstall);
  1647           else
  1648             aCaller.callNext();
  1649         });
  1650       },
  1652       noMoreObjects: function getInstallForFile_noMoreObjects(aCaller) {
  1653         safeCall(aCallback, null);
  1655     });
  1656   },
  1658   /**
  1659    * Asynchronously gets all current AddonInstalls optionally limiting to a list
  1660    * of types.
  1662    * @param  aTypes
  1663    *         An optional array of types to retrieve. Each type is a string name
  1664    * @param  aCallback
  1665    *         A callback which will be passed an array of AddonInstalls
  1666    * @throws If the aCallback argument is not specified
  1667    */
  1668   getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) {
  1669     if (!gStarted)
  1670       throw Components.Exception("AddonManager is not initialized",
  1671                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1673     if (aTypes && !Array.isArray(aTypes))
  1674       throw Components.Exception("aTypes must be an array or null",
  1675                                  Cr.NS_ERROR_INVALID_ARG);
  1677     if (typeof aCallback != "function")
  1678       throw Components.Exception("aCallback must be a function",
  1679                                  Cr.NS_ERROR_INVALID_ARG);
  1681     let installs = [];
  1683     new AsyncObjectCaller(this.providers, "getInstallsByTypes", {
  1684       nextObject: function getInstallsByTypes_nextObject(aCaller, aProvider) {
  1685         callProvider(aProvider, "getInstallsByTypes", null, aTypes,
  1686                      function getInstallsByTypes_safeCall(aProviderInstalls) {
  1687           installs = installs.concat(aProviderInstalls);
  1688           aCaller.callNext();
  1689         });
  1690       },
  1692       noMoreObjects: function getInstallsByTypes_noMoreObjects(aCaller) {
  1693         safeCall(aCallback, installs);
  1695     });
  1696   },
  1698   /**
  1699    * Asynchronously gets all current AddonInstalls.
  1701    * @param  aCallback
  1702    *         A callback which will be passed an array of AddonInstalls
  1703    */
  1704   getAllInstalls: function AMI_getAllInstalls(aCallback) {
  1705     if (!gStarted)
  1706       throw Components.Exception("AddonManager is not initialized",
  1707                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1709     this.getInstallsByTypes(null, aCallback);
  1710   },
  1712   /**
  1713    * Synchronously map a URI to the corresponding Addon ID.
  1715    * Mappable URIs are limited to in-application resources belonging to the
  1716    * add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
  1717    * but do not include URIs from meta data, such as the add-on homepage.
  1719    * @param  aURI
  1720    *         nsIURI to map to an addon id
  1721    * @return string containing the Addon ID or null
  1722    * @see    amIAddonManager.mapURIToAddonID
  1723    */
  1724   mapURIToAddonID: function AMI_mapURIToAddonID(aURI) {
  1725     if (!(aURI instanceof Ci.nsIURI)) {
  1726       throw Components.Exception("aURI is not a nsIURI",
  1727                                  Cr.NS_ERROR_INVALID_ARG);
  1729     // Try all providers
  1730     let providers = this.providers.slice(0);
  1731     for (let provider of providers) {
  1732       var id = callProvider(provider, "mapURIToAddonID", null, aURI);
  1733       if (id !== null) {
  1734         return id;
  1738     return null;
  1739   },
  1741   /**
  1742    * Checks whether installation is enabled for a particular mimetype.
  1744    * @param  aMimetype
  1745    *         The mimetype to check
  1746    * @return true if installation is enabled for the mimetype
  1747    */
  1748   isInstallEnabled: function AMI_isInstallEnabled(aMimetype) {
  1749     if (!gStarted)
  1750       throw Components.Exception("AddonManager is not initialized",
  1751                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1753     if (!aMimetype || typeof aMimetype != "string")
  1754       throw Components.Exception("aMimetype must be a non-empty string",
  1755                                  Cr.NS_ERROR_INVALID_ARG);
  1757     let providers = this.providers.slice(0);
  1758     for (let provider of providers) {
  1759       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
  1760           callProvider(provider, "isInstallEnabled"))
  1761         return true;
  1763     return false;
  1764   },
  1766   /**
  1767    * Checks whether a particular source is allowed to install add-ons of a
  1768    * given mimetype.
  1770    * @param  aMimetype
  1771    *         The mimetype of the add-on
  1772    * @param  aURI
  1773    *         The optional nsIURI of the source
  1774    * @return true if the source is allowed to install this mimetype
  1775    */
  1776   isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aURI) {
  1777     if (!gStarted)
  1778       throw Components.Exception("AddonManager is not initialized",
  1779                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1781     if (!aMimetype || typeof aMimetype != "string")
  1782       throw Components.Exception("aMimetype must be a non-empty string",
  1783                                  Cr.NS_ERROR_INVALID_ARG);
  1785     if (aURI && !(aURI instanceof Ci.nsIURI))
  1786       throw Components.Exception("aURI must be a nsIURI or null",
  1787                                  Cr.NS_ERROR_INVALID_ARG);
  1789     let providers = this.providers.slice(0);
  1790     for (let provider of providers) {
  1791       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
  1792           callProvider(provider, "isInstallAllowed", null, aURI))
  1793         return true;
  1795     return false;
  1796   },
  1798   /**
  1799    * Starts installation of an array of AddonInstalls notifying the registered
  1800    * web install listener of blocked or started installs.
  1802    * @param  aMimetype
  1803    *         The mimetype of add-ons being installed
  1804    * @param  aSource
  1805    *         The optional nsIDOMWindow that started the installs
  1806    * @param  aURI
  1807    *         The optional nsIURI that started the installs
  1808    * @param  aInstalls
  1809    *         The array of AddonInstalls to be installed
  1810    */
  1811   installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype,
  1812                                                                   aSource,
  1813                                                                   aURI,
  1814                                                                   aInstalls) {
  1815     if (!gStarted)
  1816       throw Components.Exception("AddonManager is not initialized",
  1817                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1819     if (!aMimetype || typeof aMimetype != "string")
  1820       throw Components.Exception("aMimetype must be a non-empty string",
  1821                                  Cr.NS_ERROR_INVALID_ARG);
  1823     if (aSource && !(aSource instanceof Ci.nsIDOMWindow))
  1824       throw Components.Exception("aSource must be a nsIDOMWindow or null",
  1825                                  Cr.NS_ERROR_INVALID_ARG);
  1827     if (aURI && !(aURI instanceof Ci.nsIURI))
  1828       throw Components.Exception("aURI must be a nsIURI or null",
  1829                                  Cr.NS_ERROR_INVALID_ARG);
  1831     if (!Array.isArray(aInstalls))
  1832       throw Components.Exception("aInstalls must be an array",
  1833                                  Cr.NS_ERROR_INVALID_ARG);
  1835     if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
  1836       logger.warn("No web installer available, cancelling all installs");
  1837       aInstalls.forEach(function(aInstall) {
  1838         aInstall.cancel();
  1839       });
  1840       return;
  1843     try {
  1844       let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
  1845                         getService(Ci.amIWebInstallListener);
  1847       if (!this.isInstallEnabled(aMimetype, aURI)) {
  1848         weblistener.onWebInstallDisabled(aSource, aURI, aInstalls,
  1849                                          aInstalls.length);
  1851       else if (!this.isInstallAllowed(aMimetype, aURI)) {
  1852         if (weblistener.onWebInstallBlocked(aSource, aURI, aInstalls,
  1853                                             aInstalls.length)) {
  1854           aInstalls.forEach(function(aInstall) {
  1855             aInstall.install();
  1856           });
  1859       else if (weblistener.onWebInstallRequested(aSource, aURI, aInstalls,
  1860                                                    aInstalls.length)) {
  1861         aInstalls.forEach(function(aInstall) {
  1862           aInstall.install();
  1863         });
  1866     catch (e) {
  1867       // In the event that the weblistener throws during instantiation or when
  1868       // calling onWebInstallBlocked or onWebInstallRequested all of the
  1869       // installs should get cancelled.
  1870       logger.warn("Failure calling web installer", e);
  1871       aInstalls.forEach(function(aInstall) {
  1872         aInstall.cancel();
  1873       });
  1875   },
  1877   /**
  1878    * Adds a new InstallListener if the listener is not already registered.
  1880    * @param  aListener
  1881    *         The InstallListener to add
  1882    */
  1883   addInstallListener: function AMI_addInstallListener(aListener) {
  1884     if (!aListener || typeof aListener != "object")
  1885       throw Components.Exception("aListener must be a InstallListener object",
  1886                                  Cr.NS_ERROR_INVALID_ARG);
  1888     if (!this.installListeners.some(function addInstallListener_matchListener(i) {
  1889       return i == aListener; }))
  1890       this.installListeners.push(aListener);
  1891   },
  1893   /**
  1894    * Removes an InstallListener if the listener is registered.
  1896    * @param  aListener
  1897    *         The InstallListener to remove
  1898    */
  1899   removeInstallListener: function AMI_removeInstallListener(aListener) {
  1900     if (!aListener || typeof aListener != "object")
  1901       throw Components.Exception("aListener must be a InstallListener object",
  1902                                  Cr.NS_ERROR_INVALID_ARG);
  1904     let pos = 0;
  1905     while (pos < this.installListeners.length) {
  1906       if (this.installListeners[pos] == aListener)
  1907         this.installListeners.splice(pos, 1);
  1908       else
  1909         pos++;
  1911   },
  1913   /**
  1914    * Asynchronously gets an add-on with a specific ID.
  1916    * @param  aID
  1917    *         The ID of the add-on to retrieve
  1918    * @param  aCallback
  1919    *         The callback to pass the retrieved add-on to
  1920    * @throws if the aID or aCallback arguments are not specified
  1921    */
  1922   getAddonByID: function AMI_getAddonByID(aID, aCallback) {
  1923     if (!gStarted)
  1924       throw Components.Exception("AddonManager is not initialized",
  1925                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1927     if (!aID || typeof aID != "string")
  1928       throw Components.Exception("aID must be a non-empty string",
  1929                                  Cr.NS_ERROR_INVALID_ARG);
  1931     if (typeof aCallback != "function")
  1932       throw Components.Exception("aCallback must be a function",
  1933                                  Cr.NS_ERROR_INVALID_ARG);
  1935     new AsyncObjectCaller(this.providers, "getAddonByID", {
  1936       nextObject: function getAddonByID_nextObject(aCaller, aProvider) {
  1937         callProvider(aProvider, "getAddonByID", null, aID,
  1938                     function getAddonByID_safeCall(aAddon) {
  1939           if (aAddon)
  1940             safeCall(aCallback, aAddon);
  1941           else
  1942             aCaller.callNext();
  1943         });
  1944       },
  1946       noMoreObjects: function getAddonByID_noMoreObjects(aCaller) {
  1947         safeCall(aCallback, null);
  1949     });
  1950   },
  1952   /**
  1953    * Asynchronously get an add-on with a specific Sync GUID.
  1955    * @param  aGUID
  1956    *         String GUID of add-on to retrieve
  1957    * @param  aCallback
  1958    *         The callback to pass the retrieved add-on to.
  1959    * @throws if the aGUID or aCallback arguments are not specified
  1960    */
  1961   getAddonBySyncGUID: function AMI_getAddonBySyncGUID(aGUID, aCallback) {
  1962     if (!gStarted)
  1963       throw Components.Exception("AddonManager is not initialized",
  1964                                  Cr.NS_ERROR_NOT_INITIALIZED);
  1966     if (!aGUID || typeof aGUID != "string")
  1967       throw Components.Exception("aGUID must be a non-empty string",
  1968                                  Cr.NS_ERROR_INVALID_ARG);
  1970     if (typeof aCallback != "function")
  1971       throw Components.Exception("aCallback must be a function",
  1972                                  Cr.NS_ERROR_INVALID_ARG);
  1974     new AsyncObjectCaller(this.providers, "getAddonBySyncGUID", {
  1975       nextObject: function getAddonBySyncGUID_nextObject(aCaller, aProvider) {
  1976         callProvider(aProvider, "getAddonBySyncGUID", null, aGUID,
  1977                     function getAddonBySyncGUID_safeCall(aAddon) {
  1978           if (aAddon) {
  1979             safeCall(aCallback, aAddon);
  1980           } else {
  1981             aCaller.callNext();
  1983         });
  1984       },
  1986       noMoreObjects: function getAddonBySyncGUID_noMoreObjects(aCaller) {
  1987         safeCall(aCallback, null);
  1989     });
  1990   },
  1992   /**
  1993    * Asynchronously gets an array of add-ons.
  1995    * @param  aIDs
  1996    *         The array of IDs to retrieve
  1997    * @param  aCallback
  1998    *         The callback to pass an array of Addons to
  1999    * @throws if the aID or aCallback arguments are not specified
  2000    */
  2001   getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) {
  2002     if (!gStarted)
  2003       throw Components.Exception("AddonManager is not initialized",
  2004                                  Cr.NS_ERROR_NOT_INITIALIZED);
  2006     if (!Array.isArray(aIDs))
  2007       throw Components.Exception("aIDs must be an array",
  2008                                  Cr.NS_ERROR_INVALID_ARG);
  2010     if (typeof aCallback != "function")
  2011       throw Components.Exception("aCallback must be a function",
  2012                                  Cr.NS_ERROR_INVALID_ARG);
  2014     let addons = [];
  2016     new AsyncObjectCaller(aIDs, null, {
  2017       nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
  2018         AddonManagerInternal.getAddonByID(aID,
  2019                              function getAddonsByIDs_getAddonByID(aAddon) {
  2020           addons.push(aAddon);
  2021           aCaller.callNext();
  2022         });
  2023       },
  2025       noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) {
  2026         safeCall(aCallback, addons);
  2028     });
  2029   },
  2031   /**
  2032    * Asynchronously gets add-ons of specific types.
  2034    * @param  aTypes
  2035    *         An optional array of types to retrieve. Each type is a string name
  2036    * @param  aCallback
  2037    *         The callback to pass an array of Addons to.
  2038    * @throws if the aCallback argument is not specified
  2039    */
  2040   getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) {
  2041     if (!gStarted)
  2042       throw Components.Exception("AddonManager is not initialized",
  2043                                  Cr.NS_ERROR_NOT_INITIALIZED);
  2045     if (aTypes && !Array.isArray(aTypes))
  2046       throw Components.Exception("aTypes must be an array or null",
  2047                                  Cr.NS_ERROR_INVALID_ARG);
  2049     if (typeof aCallback != "function")
  2050       throw Components.Exception("aCallback must be a function",
  2051                                  Cr.NS_ERROR_INVALID_ARG);
  2053     let addons = [];
  2055     new AsyncObjectCaller(this.providers, "getAddonsByTypes", {
  2056       nextObject: function getAddonsByTypes_nextObject(aCaller, aProvider) {
  2057         callProvider(aProvider, "getAddonsByTypes", null, aTypes,
  2058                      function getAddonsByTypes_concatAddons(aProviderAddons) {
  2059           addons = addons.concat(aProviderAddons);
  2060           aCaller.callNext();
  2061         });
  2062       },
  2064       noMoreObjects: function getAddonsByTypes_noMoreObjects(aCaller) {
  2065         safeCall(aCallback, addons);
  2067     });
  2068   },
  2070   /**
  2071    * Asynchronously gets all installed add-ons.
  2073    * @param  aCallback
  2074    *         A callback which will be passed an array of Addons
  2075    */
  2076   getAllAddons: function AMI_getAllAddons(aCallback) {
  2077     if (!gStarted)
  2078       throw Components.Exception("AddonManager is not initialized",
  2079                                  Cr.NS_ERROR_NOT_INITIALIZED);
  2081     if (typeof aCallback != "function")
  2082       throw Components.Exception("aCallback must be a function",
  2083                                  Cr.NS_ERROR_INVALID_ARG);
  2085     this.getAddonsByTypes(null, aCallback);
  2086   },
  2088   /**
  2089    * Asynchronously gets add-ons that have operations waiting for an application
  2090    * restart to complete.
  2092    * @param  aTypes
  2093    *         An optional array of types to retrieve. Each type is a string name
  2094    * @param  aCallback
  2095    *         The callback to pass the array of Addons to
  2096    * @throws if the aCallback argument is not specified
  2097    */
  2098   getAddonsWithOperationsByTypes:
  2099   function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
  2100     if (!gStarted)
  2101       throw Components.Exception("AddonManager is not initialized",
  2102                                  Cr.NS_ERROR_NOT_INITIALIZED);
  2104     if (aTypes && !Array.isArray(aTypes))
  2105       throw Components.Exception("aTypes must be an array or null",
  2106                                  Cr.NS_ERROR_INVALID_ARG);
  2108     if (typeof aCallback != "function")
  2109       throw Components.Exception("aCallback must be a function",
  2110                                  Cr.NS_ERROR_INVALID_ARG);
  2112     let addons = [];
  2114     new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", {
  2115       nextObject: function getAddonsWithOperationsByTypes_nextObject
  2116                            (aCaller, aProvider) {
  2117         callProvider(aProvider, "getAddonsWithOperationsByTypes", null, aTypes,
  2118                      function getAddonsWithOperationsByTypes_concatAddons
  2119                               (aProviderAddons) {
  2120           addons = addons.concat(aProviderAddons);
  2121           aCaller.callNext();
  2122         });
  2123       },
  2125       noMoreObjects: function getAddonsWithOperationsByTypes_noMoreObjects(caller) {
  2126         safeCall(aCallback, addons);
  2128     });
  2129   },
  2131   /**
  2132    * Adds a new AddonManagerListener if the listener is not already registered.
  2134    * @param  aListener
  2135    *         The listener to add
  2136    */
  2137   addManagerListener: function AMI_addManagerListener(aListener) {
  2138     if (!aListener || typeof aListener != "object")
  2139       throw Components.Exception("aListener must be an AddonManagerListener object",
  2140                                  Cr.NS_ERROR_INVALID_ARG);
  2142     if (!this.managerListeners.some(function addManagerListener_matchListener(i) {
  2143       return i == aListener; }))
  2144       this.managerListeners.push(aListener);
  2145   },
  2147   /**
  2148    * Removes an AddonManagerListener if the listener is registered.
  2150    * @param  aListener
  2151    *         The listener to remove
  2152    */
  2153   removeManagerListener: function AMI_removeManagerListener(aListener) {
  2154     if (!aListener || typeof aListener != "object")
  2155       throw Components.Exception("aListener must be an AddonManagerListener object",
  2156                                  Cr.NS_ERROR_INVALID_ARG);
  2158     let pos = 0;
  2159     while (pos < this.managerListeners.length) {
  2160       if (this.managerListeners[pos] == aListener)
  2161         this.managerListeners.splice(pos, 1);
  2162       else
  2163         pos++;
  2165   },
  2167   /**
  2168    * Adds a new AddonListener if the listener is not already registered.
  2170    * @param  aListener
  2171    *         The AddonListener to add
  2172    */
  2173   addAddonListener: function AMI_addAddonListener(aListener) {
  2174     if (!aListener || typeof aListener != "object")
  2175       throw Components.Exception("aListener must be an AddonListener object",
  2176                                  Cr.NS_ERROR_INVALID_ARG);
  2178     if (!this.addonListeners.some(function addAddonListener_matchListener(i) {
  2179       return i == aListener; }))
  2180       this.addonListeners.push(aListener);
  2181   },
  2183   /**
  2184    * Removes an AddonListener if the listener is registered.
  2186    * @param  aListener
  2187    *         The AddonListener to remove
  2188    */
  2189   removeAddonListener: function AMI_removeAddonListener(aListener) {
  2190     if (!aListener || typeof aListener != "object")
  2191       throw Components.Exception("aListener must be an AddonListener object",
  2192                                  Cr.NS_ERROR_INVALID_ARG);
  2194     let pos = 0;
  2195     while (pos < this.addonListeners.length) {
  2196       if (this.addonListeners[pos] == aListener)
  2197         this.addonListeners.splice(pos, 1);
  2198       else
  2199         pos++;
  2201   },
  2203   /**
  2204    * Adds a new TypeListener if the listener is not already registered.
  2206    * @param  aListener
  2207    *         The TypeListener to add
  2208    */
  2209   addTypeListener: function AMI_addTypeListener(aListener) {
  2210     if (!aListener || typeof aListener != "object")
  2211       throw Components.Exception("aListener must be a TypeListener object",
  2212                                  Cr.NS_ERROR_INVALID_ARG);
  2214     if (!this.typeListeners.some(function addTypeListener_matchListener(i) {
  2215       return i == aListener; }))
  2216       this.typeListeners.push(aListener);
  2217   },
  2219   /**
  2220    * Removes an TypeListener if the listener is registered.
  2222    * @param  aListener
  2223    *         The TypeListener to remove
  2224    */
  2225   removeTypeListener: function AMI_removeTypeListener(aListener) {
  2226     if (!aListener || typeof aListener != "object")
  2227       throw Components.Exception("aListener must be a TypeListener object",
  2228                                  Cr.NS_ERROR_INVALID_ARG);
  2230     let pos = 0;
  2231     while (pos < this.typeListeners.length) {
  2232       if (this.typeListeners[pos] == aListener)
  2233         this.typeListeners.splice(pos, 1);
  2234       else
  2235         pos++;
  2237   },
  2239   get addonTypes() {
  2240     return this.typesProxy;
  2241   },
  2243   get autoUpdateDefault() {
  2244     return gAutoUpdateDefault;
  2245   },
  2247   set autoUpdateDefault(aValue) {
  2248     aValue = !!aValue;
  2249     if (aValue != gAutoUpdateDefault)
  2250       Services.prefs.setBoolPref(PREF_EM_AUTOUPDATE_DEFAULT, aValue);
  2251     return aValue;
  2252   },
  2254   get checkCompatibility() {
  2255     return gCheckCompatibility;
  2256   },
  2258   set checkCompatibility(aValue) {
  2259     aValue = !!aValue;
  2260     if (aValue != gCheckCompatibility) {
  2261       if (!aValue)
  2262         Services.prefs.setBoolPref(PREF_EM_CHECK_COMPATIBILITY, false);
  2263       else
  2264         Services.prefs.clearUserPref(PREF_EM_CHECK_COMPATIBILITY);
  2266     return aValue;
  2267   },
  2269   get strictCompatibility() {
  2270     return gStrictCompatibility;
  2271   },
  2273   set strictCompatibility(aValue) {
  2274     aValue = !!aValue;
  2275     if (aValue != gStrictCompatibility)
  2276       Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, aValue);
  2277     return aValue;
  2278   },
  2280   get checkUpdateSecurityDefault() {
  2281     return gCheckUpdateSecurityDefault;
  2282   },
  2284   get checkUpdateSecurity() {
  2285     return gCheckUpdateSecurity;
  2286   },
  2288   set checkUpdateSecurity(aValue) {
  2289     aValue = !!aValue;
  2290     if (aValue != gCheckUpdateSecurity) {
  2291       if (aValue != gCheckUpdateSecurityDefault)
  2292         Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, aValue);
  2293       else
  2294         Services.prefs.clearUserPref(PREF_EM_CHECK_UPDATE_SECURITY);
  2296     return aValue;
  2297   },
  2299   get updateEnabled() {
  2300     return gUpdateEnabled;
  2301   },
  2303   set updateEnabled(aValue) {
  2304     aValue = !!aValue;
  2305     if (aValue != gUpdateEnabled)
  2306       Services.prefs.setBoolPref(PREF_EM_UPDATE_ENABLED, aValue);
  2307     return aValue;
  2308   },
  2310   get hotfixID() {
  2311     return gHotfixID;
  2312   },
  2313 };
  2315 /**
  2316  * Should not be used outside of core Mozilla code. This is a private API for
  2317  * the startup and platform integration code to use. Refer to the methods on
  2318  * AddonManagerInternal for documentation however note that these methods are
  2319  * subject to change at any time.
  2320  */
  2321 this.AddonManagerPrivate = {
  2322   startup: function AMP_startup() {
  2323     AddonManagerInternal.startup();
  2324   },
  2326   registerProvider: function AMP_registerProvider(aProvider, aTypes) {
  2327     AddonManagerInternal.registerProvider(aProvider, aTypes);
  2328   },
  2330   unregisterProvider: function AMP_unregisterProvider(aProvider) {
  2331     AddonManagerInternal.unregisterProvider(aProvider);
  2332   },
  2334   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
  2335     AddonManagerInternal.backgroundUpdateCheck();
  2336   },
  2338   addStartupChange: function AMP_addStartupChange(aType, aID) {
  2339     AddonManagerInternal.addStartupChange(aType, aID);
  2340   },
  2342   removeStartupChange: function AMP_removeStartupChange(aType, aID) {
  2343     AddonManagerInternal.removeStartupChange(aType, aID);
  2344   },
  2346   notifyAddonChanged: function AMP_notifyAddonChanged(aID, aType, aPendingRestart) {
  2347     AddonManagerInternal.notifyAddonChanged(aID, aType, aPendingRestart);
  2348   },
  2350   updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() {
  2351     AddonManagerInternal.updateAddonAppDisabledStates();
  2352   },
  2354   updateAddonRepositoryData: function AMP_updateAddonRepositoryData(aCallback) {
  2355     AddonManagerInternal.updateAddonRepositoryData(aCallback);
  2356   },
  2358   callInstallListeners: function AMP_callInstallListeners(...aArgs) {
  2359     return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal,
  2360                                                            aArgs);
  2361   },
  2363   callAddonListeners: function AMP_callAddonListeners(...aArgs) {
  2364     AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);
  2365   },
  2367   AddonAuthor: AddonAuthor,
  2369   AddonScreenshot: AddonScreenshot,
  2371   AddonCompatibilityOverride: AddonCompatibilityOverride,
  2373   AddonType: AddonType,
  2375   recordTimestamp: function AMP_recordTimestamp(name, value) {
  2376     AddonManagerInternal.recordTimestamp(name, value);
  2377   },
  2379   _simpleMeasures: {},
  2380   recordSimpleMeasure: function AMP_recordSimpleMeasure(name, value) {
  2381     this._simpleMeasures[name] = value;
  2382   },
  2384   recordException: function AMP_recordException(aModule, aContext, aException) {
  2385     let report = {
  2386       module: aModule,
  2387       context: aContext
  2388     };
  2390     if (typeof aException == "number") {
  2391       report.message = Components.Exception("", aException).name;
  2393     else {
  2394       report.message = aException.toString();
  2395       if (aException.fileName) {
  2396         report.file = aException.fileName;
  2397         report.line = aException.lineNumber;
  2401     this._simpleMeasures.exception = report;
  2402   },
  2404   getSimpleMeasures: function AMP_getSimpleMeasures() {
  2405     return this._simpleMeasures;
  2406   },
  2408   getTelemetryDetails: function AMP_getTelemetryDetails() {
  2409     return AddonManagerInternal.telemetryDetails;
  2410   },
  2412   setTelemetryDetails: function AMP_setTelemetryDetails(aProvider, aDetails) {
  2413     AddonManagerInternal.telemetryDetails[aProvider] = aDetails;
  2414   },
  2416   // Start a timer, record a simple measure of the time interval when
  2417   // timer.done() is called
  2418   simpleTimer: function(aName) {
  2419     let startTime = Date.now();
  2420     return {
  2421       done: () => this.recordSimpleMeasure(aName, Date.now() - startTime)
  2422     };
  2423   },
  2425   /**
  2426    * Helper to call update listeners when no update is available.
  2428    * This can be used as an implementation for Addon.findUpdates() when
  2429    * no update mechanism is available.
  2430    */
  2431   callNoUpdateListeners: function (addon, listener, reason, appVersion, platformVersion) {
  2432     if ("onNoCompatibilityUpdateAvailable" in listener) {
  2433       safeCall(listener.onNoCompatibilityUpdateAvailable.bind(listener), addon);
  2435     if ("onNoUpdateAvailable" in listener) {
  2436       safeCall(listener.onNoUpdateAvailable.bind(listener), addon);
  2438     if ("onUpdateFinished" in listener) {
  2439       safeCall(listener.onUpdateFinished.bind(listener), addon);
  2441   },
  2442 };
  2444 /**
  2445  * This is the public API that UI and developers should be calling. All methods
  2446  * just forward to AddonManagerInternal.
  2447  */
  2448 this.AddonManager = {
  2449   // Constants for the AddonInstall.state property
  2450   // The install is available for download.
  2451   STATE_AVAILABLE: 0,
  2452   // The install is being downloaded.
  2453   STATE_DOWNLOADING: 1,
  2454   // The install is checking for compatibility information.
  2455   STATE_CHECKING: 2,
  2456   // The install is downloaded and ready to install.
  2457   STATE_DOWNLOADED: 3,
  2458   // The download failed.
  2459   STATE_DOWNLOAD_FAILED: 4,
  2460   // The add-on is being installed.
  2461   STATE_INSTALLING: 5,
  2462   // The add-on has been installed.
  2463   STATE_INSTALLED: 6,
  2464   // The install failed.
  2465   STATE_INSTALL_FAILED: 7,
  2466   // The install has been cancelled.
  2467   STATE_CANCELLED: 8,
  2469   // Constants representing different types of errors while downloading an
  2470   // add-on.
  2471   // The download failed due to network problems.
  2472   ERROR_NETWORK_FAILURE: -1,
  2473   // The downloaded file did not match the provided hash.
  2474   ERROR_INCORRECT_HASH: -2,
  2475   // The downloaded file seems to be corrupted in some way.
  2476   ERROR_CORRUPT_FILE: -3,
  2477   // An error occured trying to write to the filesystem.
  2478   ERROR_FILE_ACCESS: -4,
  2480   // These must be kept in sync with AddonUpdateChecker.
  2481   // No error was encountered.
  2482   UPDATE_STATUS_NO_ERROR: 0,
  2483   // The update check timed out
  2484   UPDATE_STATUS_TIMEOUT: -1,
  2485   // There was an error while downloading the update information.
  2486   UPDATE_STATUS_DOWNLOAD_ERROR: -2,
  2487   // The update information was malformed in some way.
  2488   UPDATE_STATUS_PARSE_ERROR: -3,
  2489   // The update information was not in any known format.
  2490   UPDATE_STATUS_UNKNOWN_FORMAT: -4,
  2491   // The update information was not correctly signed or there was an SSL error.
  2492   UPDATE_STATUS_SECURITY_ERROR: -5,
  2493   // The update was cancelled.
  2494   UPDATE_STATUS_CANCELLED: -6,
  2496   // Constants to indicate why an update check is being performed
  2497   // Update check has been requested by the user.
  2498   UPDATE_WHEN_USER_REQUESTED: 1,
  2499   // Update check is necessary to see if the Addon is compatibile with a new
  2500   // version of the application.
  2501   UPDATE_WHEN_NEW_APP_DETECTED: 2,
  2502   // Update check is necessary because a new application has been installed.
  2503   UPDATE_WHEN_NEW_APP_INSTALLED: 3,
  2504   // Update check is a regular background update check.
  2505   UPDATE_WHEN_PERIODIC_UPDATE: 16,
  2506   // Update check is needed to check an Addon that is being installed.
  2507   UPDATE_WHEN_ADDON_INSTALLED: 17,
  2509   // Constants for operations in Addon.pendingOperations
  2510   // Indicates that the Addon has no pending operations.
  2511   PENDING_NONE: 0,
  2512   // Indicates that the Addon will be enabled after the application restarts.
  2513   PENDING_ENABLE: 1,
  2514   // Indicates that the Addon will be disabled after the application restarts.
  2515   PENDING_DISABLE: 2,
  2516   // Indicates that the Addon will be uninstalled after the application restarts.
  2517   PENDING_UNINSTALL: 4,
  2518   // Indicates that the Addon will be installed after the application restarts.
  2519   PENDING_INSTALL: 8,
  2520   PENDING_UPGRADE: 16,
  2522   // Constants for operations in Addon.operationsRequiringRestart
  2523   // Indicates that restart isn't required for any operation.
  2524   OP_NEEDS_RESTART_NONE: 0,
  2525   // Indicates that restart is required for enabling the addon.
  2526   OP_NEEDS_RESTART_ENABLE: 1,
  2527   // Indicates that restart is required for disabling the addon.
  2528   OP_NEEDS_RESTART_DISABLE: 2,
  2529   // Indicates that restart is required for uninstalling the addon.
  2530   OP_NEEDS_RESTART_UNINSTALL: 4,
  2531   // Indicates that restart is required for installing the addon.
  2532   OP_NEEDS_RESTART_INSTALL: 8,
  2534   // Constants for permissions in Addon.permissions.
  2535   // Indicates that the Addon can be uninstalled.
  2536   PERM_CAN_UNINSTALL: 1,
  2537   // Indicates that the Addon can be enabled by the user.
  2538   PERM_CAN_ENABLE: 2,
  2539   // Indicates that the Addon can be disabled by the user.
  2540   PERM_CAN_DISABLE: 4,
  2541   // Indicates that the Addon can be upgraded.
  2542   PERM_CAN_UPGRADE: 8,
  2543   // Indicates that the Addon can be set to be optionally enabled
  2544   // on a case-by-case basis.
  2545   PERM_CAN_ASK_TO_ACTIVATE: 16,
  2547   // General descriptions of where items are installed.
  2548   // Installed in this profile.
  2549   SCOPE_PROFILE: 1,
  2550   // Installed for all of this user's profiles.
  2551   SCOPE_USER: 2,
  2552   // Installed and owned by the application.
  2553   SCOPE_APPLICATION: 4,
  2554   // Installed for all users of the computer.
  2555   SCOPE_SYSTEM: 8,
  2556   // The combination of all scopes.
  2557   SCOPE_ALL: 15,
  2559   // 1-15 are different built-in views for the add-on type
  2560   VIEW_TYPE_LIST: "list",
  2562   TYPE_UI_HIDE_EMPTY: 16,
  2563   // Indicates that this add-on type supports the ask-to-activate state.
  2564   // That is, add-ons of this type can be set to be optionally enabled
  2565   // on a case-by-case basis.
  2566   TYPE_SUPPORTS_ASK_TO_ACTIVATE: 32,
  2568   // Constants for Addon.applyBackgroundUpdates.
  2569   // Indicates that the Addon should not update automatically.
  2570   AUTOUPDATE_DISABLE: 0,
  2571   // Indicates that the Addon should update automatically only if
  2572   // that's the global default.
  2573   AUTOUPDATE_DEFAULT: 1,
  2574   // Indicates that the Addon should update automatically.
  2575   AUTOUPDATE_ENABLE: 2,
  2577   // Constants for how Addon options should be shown.
  2578   // Options will be opened in a new window
  2579   OPTIONS_TYPE_DIALOG: 1,
  2580   // Options will be displayed within the AM detail view
  2581   OPTIONS_TYPE_INLINE: 2,
  2582   // Options will be displayed in a new tab, if possible
  2583   OPTIONS_TYPE_TAB: 3,
  2584   // Same as OPTIONS_TYPE_INLINE, but no Preferences button will be shown.
  2585   // Used to indicate that only non-interactive information will be shown.
  2586   OPTIONS_TYPE_INLINE_INFO: 4,
  2588   // Constants for displayed or hidden options notifications
  2589   // Options notification will be displayed
  2590   OPTIONS_NOTIFICATION_DISPLAYED: "addon-options-displayed",
  2591   // Options notification will be hidden
  2592   OPTIONS_NOTIFICATION_HIDDEN: "addon-options-hidden",
  2594   // Constants for getStartupChanges, addStartupChange and removeStartupChange
  2595   // Add-ons that were detected as installed during startup. Doesn't include
  2596   // add-ons that were pending installation the last time the application ran.
  2597   STARTUP_CHANGE_INSTALLED: "installed",
  2598   // Add-ons that were detected as changed during startup. This includes an
  2599   // add-on moving to a different location, changing version or just having
  2600   // been detected as possibly changed.
  2601   STARTUP_CHANGE_CHANGED: "changed",
  2602   // Add-ons that were detected as uninstalled during startup. Doesn't include
  2603   // add-ons that were pending uninstallation the last time the application ran.
  2604   STARTUP_CHANGE_UNINSTALLED: "uninstalled",
  2605   // Add-ons that were detected as disabled during startup, normally because of
  2606   // an application change making an add-on incompatible. Doesn't include
  2607   // add-ons that were pending being disabled the last time the application ran.
  2608   STARTUP_CHANGE_DISABLED: "disabled",
  2609   // Add-ons that were detected as enabled during startup, normally because of
  2610   // an application change making an add-on compatible. Doesn't include
  2611   // add-ons that were pending being enabled the last time the application ran.
  2612   STARTUP_CHANGE_ENABLED: "enabled",
  2614   // Constants for the Addon.userDisabled property
  2615   // Indicates that the userDisabled state of this add-on is currently
  2616   // ask-to-activate. That is, it can be conditionally enabled on a
  2617   // case-by-case basis.
  2618   STATE_ASK_TO_ACTIVATE: "askToActivate",
  2620 #ifdef MOZ_EM_DEBUG
  2621   get __AddonManagerInternal__() {
  2622     return AddonManagerInternal;
  2623   },
  2624 #endif
  2626   getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
  2627                                                  aHash, aName, aIcons,
  2628                                                  aVersion, aLoadGroup) {
  2629     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
  2630                                           aName, aIcons, aVersion, aLoadGroup);
  2631   },
  2633   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
  2634     AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
  2635   },
  2637   /**
  2638    * Gets an array of add-on IDs that changed during the most recent startup.
  2640    * @param  aType
  2641    *         The type of startup change to get
  2642    * @return An array of add-on IDs
  2643    */
  2644   getStartupChanges: function AM_getStartupChanges(aType) {
  2645     if (!(aType in AddonManagerInternal.startupChanges))
  2646       return [];
  2647     return AddonManagerInternal.startupChanges[aType].slice(0);
  2648   },
  2650   getAddonByID: function AM_getAddonByID(aID, aCallback) {
  2651     AddonManagerInternal.getAddonByID(aID, aCallback);
  2652   },
  2654   getAddonBySyncGUID: function AM_getAddonBySyncGUID(aGUID, aCallback) {
  2655     AddonManagerInternal.getAddonBySyncGUID(aGUID, aCallback);
  2656   },
  2658   getAddonsByIDs: function AM_getAddonsByIDs(aIDs, aCallback) {
  2659     AddonManagerInternal.getAddonsByIDs(aIDs, aCallback);
  2660   },
  2662   getAddonsWithOperationsByTypes:
  2663   function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) {
  2664     AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback);
  2665   },
  2667   getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) {
  2668     AddonManagerInternal.getAddonsByTypes(aTypes, aCallback);
  2669   },
  2671   getAllAddons: function AM_getAllAddons(aCallback) {
  2672     AddonManagerInternal.getAllAddons(aCallback);
  2673   },
  2675   getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) {
  2676     AddonManagerInternal.getInstallsByTypes(aTypes, aCallback);
  2677   },
  2679   getAllInstalls: function AM_getAllInstalls(aCallback) {
  2680     AddonManagerInternal.getAllInstalls(aCallback);
  2681   },
  2683   mapURIToAddonID: function AM_mapURIToAddonID(aURI) {
  2684     return AddonManagerInternal.mapURIToAddonID(aURI);
  2685   },
  2687   isInstallEnabled: function AM_isInstallEnabled(aType) {
  2688     return AddonManagerInternal.isInstallEnabled(aType);
  2689   },
  2691   isInstallAllowed: function AM_isInstallAllowed(aType, aUri) {
  2692     return AddonManagerInternal.isInstallAllowed(aType, aUri);
  2693   },
  2695   installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aSource,
  2696                                                                  aUri, aInstalls) {
  2697     AddonManagerInternal.installAddonsFromWebpage(aType, aSource, aUri, aInstalls);
  2698   },
  2700   addManagerListener: function AM_addManagerListener(aListener) {
  2701     AddonManagerInternal.addManagerListener(aListener);
  2702   },
  2704   removeManagerListener: function AM_removeManagerListener(aListener) {
  2705     AddonManagerInternal.removeManagerListener(aListener);
  2706   },
  2708   addInstallListener: function AM_addInstallListener(aListener) {
  2709     AddonManagerInternal.addInstallListener(aListener);
  2710   },
  2712   removeInstallListener: function AM_removeInstallListener(aListener) {
  2713     AddonManagerInternal.removeInstallListener(aListener);
  2714   },
  2716   addAddonListener: function AM_addAddonListener(aListener) {
  2717     AddonManagerInternal.addAddonListener(aListener);
  2718   },
  2720   removeAddonListener: function AM_removeAddonListener(aListener) {
  2721     AddonManagerInternal.removeAddonListener(aListener);
  2722   },
  2724   addTypeListener: function AM_addTypeListener(aListener) {
  2725     AddonManagerInternal.addTypeListener(aListener);
  2726   },
  2728   removeTypeListener: function AM_removeTypeListener(aListener) {
  2729     AddonManagerInternal.removeTypeListener(aListener);
  2730   },
  2732   get addonTypes() {
  2733     return AddonManagerInternal.addonTypes;
  2734   },
  2736   /**
  2737    * Determines whether an Addon should auto-update or not.
  2739    * @param  aAddon
  2740    *         The Addon representing the add-on
  2741    * @return true if the addon should auto-update, false otherwise.
  2742    */
  2743   shouldAutoUpdate: function AM_shouldAutoUpdate(aAddon) {
  2744     if (!aAddon || typeof aAddon != "object")
  2745       throw Components.Exception("aAddon must be specified",
  2746                                  Cr.NS_ERROR_INVALID_ARG);
  2748     if (!("applyBackgroundUpdates" in aAddon))
  2749       return false;
  2750     if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)
  2751       return true;
  2752     if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE)
  2753       return false;
  2754     return this.autoUpdateDefault;
  2755   },
  2757   get checkCompatibility() {
  2758     return AddonManagerInternal.checkCompatibility;
  2759   },
  2761   set checkCompatibility(aValue) {
  2762     AddonManagerInternal.checkCompatibility = aValue;
  2763   },
  2765   get strictCompatibility() {
  2766     return AddonManagerInternal.strictCompatibility;
  2767   },
  2769   set strictCompatibility(aValue) {
  2770     AddonManagerInternal.strictCompatibility = aValue;
  2771   },
  2773   get checkUpdateSecurityDefault() {
  2774     return AddonManagerInternal.checkUpdateSecurityDefault;
  2775   },
  2777   get checkUpdateSecurity() {
  2778     return AddonManagerInternal.checkUpdateSecurity;
  2779   },
  2781   set checkUpdateSecurity(aValue) {
  2782     AddonManagerInternal.checkUpdateSecurity = aValue;
  2783   },
  2785   get updateEnabled() {
  2786     return AddonManagerInternal.updateEnabled;
  2787   },
  2789   set updateEnabled(aValue) {
  2790     AddonManagerInternal.updateEnabled = aValue;
  2791   },
  2793   get autoUpdateDefault() {
  2794     return AddonManagerInternal.autoUpdateDefault;
  2795   },
  2797   set autoUpdateDefault(aValue) {
  2798     AddonManagerInternal.autoUpdateDefault = aValue;
  2799   },
  2801   get hotfixID() {
  2802     return AddonManagerInternal.hotfixID;
  2803   },
  2805   escapeAddonURI: function AM_escapeAddonURI(aAddon, aUri, aAppVersion) {
  2806     return AddonManagerInternal.escapeAddonURI(aAddon, aUri, aAppVersion);
  2808 };
  2810 // load the timestamps module into AddonManagerInternal
  2811 Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", AddonManagerInternal);
  2812 Object.freeze(AddonManagerInternal);
  2813 Object.freeze(AddonManagerPrivate);
  2814 Object.freeze(AddonManager);

mercurial