[JAEGER] Merge from tracemonkey.
[mozilla-central.git] / toolkit / mozapps / extensions / AddonManager.jsm
blob80680d14852611cdfedd7cbe42baa23336b16c13
1 /*
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is the Extension Manager.
17 # The Initial Developer of the Original Code is
18 # the Mozilla Foundation.
19 # Portions created by the Initial Developer are Copyright (C) 2009
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 #   Dave Townsend <dtownsend@oxymoronical.com>
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
40 const Cc = Components.classes;
41 const Ci = Components.interfaces;
42 const Cr = Components.results;
44 const PREF_EM_UPDATE_ENABLED = "extensions.update.enabled";
46 Components.utils.import("resource://gre/modules/Services.jsm");
48 var EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
50 const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
52 // A list of providers to load by default
53 const DEFAULT_PROVIDERS = [
54   "resource://gre/modules/XPIProvider.jsm",
55   "resource://gre/modules/LightweightThemeManager.jsm"
58 ["LOG", "WARN", "ERROR"].forEach(function(aName) {
59   this.__defineGetter__(aName, function() {
60     Components.utils.import("resource://gre/modules/AddonLogging.jsm");
62     LogManager.getLogger("addons.manager", this);
63     return this[aName];
64   });
65 }, this);
67 /**
68  * Calls a callback method consuming any thrown exception. Any parameters after
69  * the callback parameter will be passed to the callback.
70  *
71  * @param  aCallback
72  *         The callback method to call
73  */
74 function safeCall(aCallback) {
75   var args = Array.slice(arguments, 1);
77   try {
78     aCallback.apply(null, args);
79   }
80   catch (e) {
81     WARN("Exception calling callback: " + e);
82   }
85 /**
86  * Calls a method on a provider if it exists and consumes any thrown exception.
87  * Any parameters after the dflt parameter are passed to the provider's method.
88  *
89  * @param  aProvider
90  *         The provider to call
91  * @param  aMethod
92  *         The method name to call
93  * @param  aDefault
94  *         A default return value if the provider does not implement the named
95  *         method or throws an error.
96  * @return the return value from the provider or dflt if the provider does not
97  *         implement method or throws an error
98  */
99 function callProvider(aProvider, aMethod, aDefault) {
100   if (!(aMethod in aProvider))
101     return aDefault;
103   var args = Array.slice(arguments, 3);
105   try {
106     return aProvider[aMethod].apply(aProvider, args);
107   } catch (e) {
108     ERROR("Exception calling provider." + aMethod + ": " + e);
109     return aDefault;
110   }
114  * A helper class to repeatedly call a listener with each object in an array
115  * optionally checking whether the object has a method in it.
117  * @param  aObjects
118  *         The array of objects to iterate through
119  * @param  aMethod
120  *         An optional method name, if not null any objects without this method
121  *         will not be passed to the listener
122  * @param  aListener
123  *         A listener implementing nextObject and noMoreObjects methods. The
124  *         former will be called with the AsyncObjectCaller as the first
125  *         parameter and the object as the second. noMoreObjects will be passed
126  *         just the AsyncObjectCaller
127  */
128 function AsyncObjectCaller(aObjects, aMethod, aListener) {
129   this.objects = aObjects.slice(0);
130   this.method = aMethod;
131   this.listener = aListener;
133   this.callNext();
136 AsyncObjectCaller.prototype = {
137   objects: null,
138   method: null,
139   listener: null,
141   /**
142    * Passes the next object to the listener or calls noMoreObjects if there
143    * are none left.
144    */
145   callNext: function AOC_callNext() {
146     if (this.objects.length == 0) {
147       this.listener.noMoreObjects(this);
148       return;
149     }
151     let object = this.objects.shift();
152     if (!this.method || this.method in object)
153       this.listener.nextObject(this, object);
154     else
155       this.callNext();
156   }
160  * This is the real manager, kept here rather than in AddonManager to keep its
161  * contents hidden from API users.
162  */
163 var AddonManagerInternal = {
164   installListeners: null,
165   addonListeners: null,
166   providers: [],
167   started: false,
169   /**
170    * Initializes the AddonManager, loading any known providers and initializing
171    * them. 
172    */
173   startup: function AMI_startup() {
174     if (this.started)
175       return;
177     this.installListeners = [];
178     this.addonListeners = [];
180     let appChanged = true;
182     try {
183       appChanged = Services.appinfo.version !=
184                    Services.prefs.getCharPref("extensions.lastAppVersion");
185     }
186     catch (e) { }
188     if (appChanged) {
189       LOG("Application has been upgraded");
190       Services.prefs.setCharPref("extensions.lastAppVersion",
191                                  Services.appinfo.version);
192     }
194     // Ensure all default providers have had a chance to register themselves
195     DEFAULT_PROVIDERS.forEach(function(url) {
196       try {
197         Components.utils.import(url, {});
198       }
199       catch (e) {
200         ERROR("Exception loading default provider \"" + url + "\": " + e);
201       }
202     });
204     // Load any providers registered in the category manager
205     let catman = Cc["@mozilla.org/categorymanager;1"].
206                  getService(Ci.nsICategoryManager);
207     let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
208     while (entries.hasMoreElements()) {
209       let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
210       let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
212       try {
213         Components.utils.import(url, {});
214       }
215       catch (e) {
216         ERROR("Exception loading provider " + entry + " from category \"" +
217               url + "\": " + e);
218       }
219     }
221     let needsRestart = false;
222     this.providers.forEach(function(provider) {
223       callProvider(provider, "startup");
224       if (callProvider(provider, "checkForChanges", false, appChanged))
225         needsRestart = true;
226     });
227     this.started = true;
229     // Flag to the platform that a restart is necessary
230     if (needsRestart) {
231       let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
232                        getService(Ci.nsIAppStartup2);
233       appStartup.needsRestart = needsRestart;
234     }
235   },
237   /**
238    * Registers a new AddonProvider.
239    *
240    * @param  aProvider
241    *         The provider to register
242    */
243   registerProvider: function AMI_registerProvider(aProvider) {
244     this.providers.push(aProvider);
246     // If we're registering after startup call this provider's startup.
247     if (this.started)
248       callProvider(aProvider, "startup");
249   },
251   /**
252    * Unregisters an AddonProvider.
253    *
254    * @param  aProvider
255    *         The provider to unregister
256    */
257   unregisterProvider: function AMI_unregisterProvider(aProvider) {
258     this.providers = this.providers.filter(function(p) {
259       return p != aProvider;
260     });
262     // If we're unregistering after startup call this provider's shutdown.
263     if (this.started)
264       callProvider(aProvider, "shutdown");
265   },
267   /**
268    * Shuts down the addon manager and all registered providers, this must clean
269    * up everything in order for automated tests to fake restarts.
270    */
271   shutdown: function AM_shutdown() {
272     this.providers.forEach(function(provider) {
273       callProvider(provider, "shutdown");
274     });
276     this.installListeners = null;
277     this.addonListeners = null;
278     this.started = false;
279   },
281   /**
282    * Performs a background update check by starting an update for all add-ons
283    * that can be updated.
284    */
285   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
286     if (!Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED))
287       return;
289     let scope = {};
290     Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
291     scope.LightweightThemeManager.updateCurrentTheme();
293     this.getAllAddons(function getAddonsCallback(aAddons) {
294       aAddons.forEach(function BUC_forEachCallback(aAddon) {
295         // Check all add-ons for updates so that any compatibility updates will
296         // be applied
297         aAddon.findUpdates({
298           onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
299             // Start installing updates when the add-on can be updated and
300             // background updates should be applied.
301             if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
302                 aAddon.applyBackgroundUpdates) {
303               aInstall.install();
304             }
305           }
306         }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
307       });
308     });
309   },
311   /**
312    * Calls all registered InstallListeners with an event. Any parameters after
313    * the extraListeners parameter are passed to the listener.
314    *
315    * @param  aMethod
316    *         The method on the listeners to call
317    * @param  aExtraListeners
318    *         An array of extra InstallListeners to also call
319    * @return false if any of the listeners returned false, true otherwise
320    */
321   callInstallListeners: function AMI_callInstallListeners(aMethod, aExtraListeners) {
322     let result = true;
323     let listeners = this.installListeners;
324     if (aExtraListeners)
325       listeners = aExtraListeners.concat(listeners);
326     let args = Array.slice(arguments, 2);
328     listeners.forEach(function(listener) {
329       try {
330         if (aMethod in listener) {
331           if (listener[aMethod].apply(listener, args) === false)
332             result = false;
333         }
334       }
335       catch (e) {
336         WARN("InstallListener threw exception when calling " + aMethod + ": " + e);
337       }
338     });
339     return result;
340   },
342   /**
343    * Calls all registered AddonListeners with an event. Any parameters after
344    * the method parameter are passed to the listener.
345    *
346    * @param  aMethod
347    *         The method on the listeners to call
348    */
349   callAddonListeners: function AMI_callAddonListeners(aMethod) {
350     var args = Array.slice(arguments, 1);
351     this.addonListeners.forEach(function(listener) {
352       try {
353         if (aMethod in listener)
354           listener[aMethod].apply(listener, args);
355       }
356       catch (e) {
357         WARN("AddonListener threw exception when calling " + aMethod + ": " + e);
358       }
359     });
360   },
362   /**
363    * Notifies all providers that an add-on has been enabled when that type of
364    * add-on only supports a single add-on being enabled at a time. This allows
365    * the providers to disable theirs if necessary.
366    *
367    * @param  aId
368    *         The id of the enabled add-on
369    * @param  aType
370    *         The type of the enabled add-on
371    * @param  aPendingRestart
372    *         A boolean indicating if the change will only take place the next
373    *         time the application is restarted
374    */
375   notifyAddonChanged: function AMI_notifyAddonChanged(aId, aType, aPendingRestart) {
376     this.providers.forEach(function(provider) {
377       callProvider(provider, "addonChanged", null, aId, aType, aPendingRestart);
378     });
379   },
381   /**
382    * Notifies all providers they need to update the appDisabled property for
383    * their add-ons in response to an application change such as a blocklist
384    * update.
385    */
386   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
387     this.providers.forEach(function(provider) {
388       callProvider(provider, "updateAddonAppDisabledStates");
389     });
390   },
392   /**
393    * Asynchronously gets an AddonInstall for a URL.
394    *
395    * @param  aUrl
396    *         The url the add-on is located at
397    * @param  aCallback
398    *         A callback to pass the AddonInstall to
399    * @param  aMimetype
400    *         The mimetype of the add-on
401    * @param  aHash
402    *         An optional hash of the add-on
403    * @param  aName
404    *         An optional placeholder name while the add-on is being downloaded
405    * @param  aIconURL
406    *         An optional placeholder icon URL while the add-on is being downloaded
407    * @param  aVersion
408    *         An optional placeholder version while the add-on is being downloaded
409    * @param  aLoadGroup
410    *         An optional nsILoadGroup to associate any network requests with
411    * @throws if the aUrl, aCallback or aMimetype arguments are not specified
412    */
413   getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype,
414                                                   aHash, aName, aIconURL,
415                                                   aVersion, aLoadGroup) {
416     if (!aUrl || !aMimetype || !aCallback)
417       throw new TypeError("Invalid arguments");
419     for (let i = 0; i < this.providers.length; i++) {
420       if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype)) {
421         callProvider(this.providers[i], "getInstallForURL", null,
422                      aUrl, aHash, aName, aIconURL, aVersion, aLoadGroup,
423                      function(aInstall) {
424           safeCall(aCallback, aInstall);
425         });
426         return;
427       }
428     }
429     safeCall(aCallback, null);
430   },
432   /**
433    * Asynchronously gets an AddonInstall for an nsIFile.
434    *
435    * @param  aFile
436    *         the nsIFile where the add-on is located
437    * @param  aCallback
438    *         A callback to pass the AddonInstall to
439    * @param  aMimetype
440    *         An optional mimetype hint for the add-on
441    * @throws if the aFile or aCallback arguments are not specified
442    */
443   getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) {
444     if (!aFile || !aCallback)
445       throw Cr.NS_ERROR_INVALID_ARG;
447     new AsyncObjectCaller(this.providers, "getInstallForFile", {
448       nextObject: function(aCaller, aProvider) {
449         callProvider(aProvider, "getInstallForFile", null, aFile,
450                      function(aInstall) {
451           if (aInstall)
452             safeCall(aCallback, aInstall);
453           else
454             aCaller.callNext();
455         });
456       },
458       noMoreObjects: function(aCaller) {
459         safeCall(aCallback, null);
460       }
461     });
462   },
464   /**
465    * Asynchronously gets all current AddonInstalls optionally limiting to a list
466    * of types.
467    *
468    * @param  aTypes
469    *         An optional array of types to retrieve. Each type is a string name
470    * @param  aCallback
471    *         A callback which will be passed an array of AddonInstalls
472    * @throws if the aCallback argument is not specified
473    */
474   getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) {
475     if (!aCallback)
476       throw Cr.NS_ERROR_INVALID_ARG;
478     let installs = [];
480     new AsyncObjectCaller(this.providers, "getInstallsByTypes", {
481       nextObject: function(aCaller, aProvider) {
482         callProvider(aProvider, "getInstallsByTypes", null, aTypes,
483                      function(aProviderInstalls) {
484           installs = installs.concat(aProviderInstalls);
485           aCaller.callNext();
486         });
487       },
489       noMoreObjects: function(aCaller) {
490         safeCall(aCallback, installs);
491       }
492     });
493   },
495   /**
496    * Asynchronously gets all current AddonInstalls.
497    *
498    * @param  aCallback
499    *         A callback which will be passed an array of AddonInstalls
500    */
501   getAllInstalls: function AMI_getAllInstalls(aCallback) {
502     this.getInstallsByTypes(null, aCallback);
503   },
505   /**
506    * Checks whether installation is enabled for a particular mimetype.
507    *
508    * @param  aMimetype
509    *         The mimetype to check
510    * @return true if installation is enabled for the mimetype
511    */
512   isInstallEnabled: function AMI_isInstallEnabled(aMimetype) {
513     for (let i = 0; i < this.providers.length; i++) {
514       if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype) &&
515           callProvider(this.providers[i], "isInstallEnabled"))
516         return true;
517     }
518     return false;
519   },
521   /**
522    * Checks whether a particular source is allowed to install add-ons of a
523    * given mimetype.
524    *
525    * @param  aMimetype
526    *         The mimetype of the add-on
527    * @param  aURI
528    *         The uri of the source, may be null
529    * @return true if the source is allowed to install this mimetype
530    */
531   isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aURI) {
532     for (let i = 0; i < this.providers.length; i++) {
533       if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype) &&
534           callProvider(this.providers[i], "isInstallAllowed", null, aURI))
535         return true;
536     }
537     return false;
538   },
540   /**
541    * Starts installation of an array of AddonInstalls notifying the registered
542    * web install listener of blocked or started installs.
543    *
544    * @param  aMimetype
545    *         The mimetype of add-ons being installed
546    * @param  aSource
547    *         The nsIDOMWindowInternal that started the installs
548    * @param  aURI
549    *         the nsIURI that started the installs
550    * @param  aInstalls
551    *         The array of AddonInstalls to be installed
552    */
553   installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype,
554                                                                   aSource,
555                                                                   aURI,
556                                                                   aInstalls) {
557     if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
558       WARN("No web installer available, cancelling all installs");
559       aInstalls.forEach(function(aInstall) {
560         aInstall.cancel();
561       });
562       return;
563     }
565     try {
566       let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
567                         getService(Ci.amIWebInstallListener);
569       if (!this.isInstallAllowed(aMimetype, aURI)) {
570         if (weblistener.onWebInstallBlocked(aSource, aURI, aInstalls,
571                                             aInstalls.length)) {
572           aInstalls.forEach(function(aInstall) {
573             aInstall.install();
574           });
575         }
576       }
577       else if (weblistener.onWebInstallRequested(aSource, aURI, aInstalls,
578                                                    aInstalls.length)) {
579         aInstalls.forEach(function(aInstall) {
580           aInstall.install();
581         });
582       }
583     }
584     catch (e) {
585       // In the event that the weblistener throws during instatiation or when
586       // calling onWebInstallBlocked or onWebInstallRequested all of the
587       // installs should get cancelled.
588       WARN("Failure calling web installer: " + e);
589       aInstalls.forEach(function(aInstall) {
590         aInstall.cancel();
591       });
592     }
593   },
595   /**
596    * Adds a new InstallListener if the listener is not already registered.
597    *
598    * @param  aListener
599    *         The InstallListener to add
600    */
601   addInstallListener: function AMI_addInstallListener(aListener) {
602     if (!this.installListeners.some(function(i) { return i == aListener; }))
603       this.installListeners.push(aListener);
604   },
606   /**
607    * Removes an InstallListener if the listener is registered.
608    *
609    * @param  aListener
610    *         The InstallListener to remove
611    */
612   removeInstallListener: function AMI_removeInstallListener(aListener) {
613     this.installListeners = this.installListeners.filter(function(i) {
614       return i != aListener;
615     });
616   },
618   /**
619    * Asynchronously gets an add-on with a specific ID.
620    *
621    * @param  aId
622    *         The ID of the add-on to retrieve
623    * @param  aCallback
624    *         The callback to pass the retrieved add-on to
625    * @throws if the aId or aCallback arguments are not specified
626    */
627   getAddonByID: function AMI_getAddonByID(aId, aCallback) {
628     if (!aId || !aCallback)
629       throw Cr.NS_ERROR_INVALID_ARG;
631     new AsyncObjectCaller(this.providers, "getAddonByID", {
632       nextObject: function(aCaller, aProvider) {
633         callProvider(aProvider, "getAddonByID", null, aId, function(aAddon) {
634           if (aAddon)
635             safeCall(aCallback, aAddon);
636           else
637             aCaller.callNext();
638         });
639       },
641       noMoreObjects: function(aCaller) {
642         safeCall(aCallback, null);
643       }
644     });
645   },
647   /**
648    * Asynchronously gets an array of add-ons.
649    *
650    * @param  aIds
651    *         The array of IDs to retrieve
652    * @param  aCallback
653    *         The callback to pass an array of Addons to
654    * @throws if the aId or aCallback arguments are not specified
655    */
656   getAddonsByIDs: function AMI_getAddonsByIDs(aIds, aCallback) {
657     if (!aIds || !aCallback)
658       throw Cr.NS_ERROR_INVALID_ARG;
660     let addons = [];
662     new AsyncObjectCaller(aIds, null, {
663       nextObject: function(aCaller, aId) {
664         AddonManagerInternal.getAddonByID(aId, function(aAddon) {
665           addons.push(aAddon);
666           aCaller.callNext();
667         });
668       },
670       noMoreObjects: function(aCaller) {
671         safeCall(aCallback, addons);
672       }
673     });
674   },
676   /**
677    * Asynchronously gets add-ons of specific types.
678    *
679    * @param  aTypes
680    *         An optional array of types to retrieve. Each type is a string name
681    * @param  aCallback
682    *         The callback to pass an array of Addons to.
683    * @throws if the aCallback argument is not specified
684    */
685   getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) {
686     if (!aCallback)
687       throw Cr.NS_ERROR_INVALID_ARG;
689     let addons = [];
691     new AsyncObjectCaller(this.providers, "getAddonsByTypes", {
692       nextObject: function(aCaller, aProvider) {
693         callProvider(aProvider, "getAddonsByTypes", null, aTypes,
694                      function(aProviderAddons) {
695           addons = addons.concat(aProviderAddons);
696           aCaller.callNext();
697         });
698       },
700       noMoreObjects: function(aCaller) {
701         safeCall(aCallback, addons);
702       }
703     });
704   },
706   /**
707    * Asynchronously gets all installed add-ons.
708    *
709    * @param  aCallback
710    *         A callback which will be passed an array of Addons
711    */
712   getAllAddons: function AMI_getAllAddons(aCallback) {
713     this.getAddonsByTypes(null, aCallback);
714   },
716   /**
717    * Asynchronously gets add-ons that have operations waiting for an application
718    * restart to complete.
719    *
720    * @param  aTypes
721    *         An optional array of types to retrieve. Each type is a string name
722    * @param  aCallback
723    *         The callback to pass the array of Addons to
724    * @throws if the aCallback argument is not specified
725    */
726   getAddonsWithOperationsByTypes:
727   function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
728     if (!aCallback)
729       throw Cr.NS_ERROR_INVALID_ARG;
731     let addons = [];
733     new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", {
734       nextObject: function(aCaller, aProvider) {
735         callProvider(aProvider, "getAddonsWithOperationsByTypes", null, aTypes,
736                      function(aProviderAddons) {
737           addons = addons.concat(aProviderAddons);
738           aCaller.callNext();
739         });
740       },
742       noMoreObjects: function(caller) {
743         safeCall(aCallback, addons);
744       }
745     });
746   },
748   /**
749    * Adds a new AddonListener if the listener is not already registered.
750    *
751    * @param  aListener
752    *         The listener to add
753    */
754   addAddonListener: function AMI_addAddonListener(aListener) {
755     if (!this.addonListeners.some(function(i) { return i == aListener; }))
756       this.addonListeners.push(aListener);
757   },
759   /**
760    * Removes an AddonListener if the listener is registered.
761    *
762    * @param  aListener
763    *         The listener to remove
764    */
765   removeAddonListener: function AMI_removeAddonListener(aListener) {
766     this.addonListeners = this.addonListeners.filter(function(i) {
767       return i != aListener;
768     });
769   }
773  * Should not be used outside of core Mozilla code. This is a private API for
774  * the startup and platform integration code to use. Refer to the methods on
775  * AddonManagerInternal for documentation however note that these methods are
776  * subject to change at any time.
777  */
778 var AddonManagerPrivate = {
779   startup: function AMP_startup() {
780     AddonManagerInternal.startup();
781   },
783   registerProvider: function AMP_registerProvider(aProvider) {
784     AddonManagerInternal.registerProvider(aProvider);
785   },
787   unregisterProvider: function AMP_unregisterProvider(aProvider) {
788     AddonManagerInternal.unregisterProvider(aProvider);
789   },
791   shutdown: function AMP_shutdown() {
792     AddonManagerInternal.shutdown();
793   },
795   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
796     AddonManagerInternal.backgroundUpdateCheck();
797   },
799   notifyAddonChanged: function AMP_notifyAddonChanged(aId, aType, aPendingRestart) {
800     AddonManagerInternal.notifyAddonChanged(aId, aType, aPendingRestart);
801   },
803   updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() {
804     AddonManagerInternal.updateAddonAppDisabledStates();
805   },
807   callInstallListeners: function AMP_callInstallListeners(aMethod) {
808     return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal,
809                                                            arguments);
810   },
812   callAddonListeners: function AMP_callAddonListeners(aMethod) {
813     AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, arguments);
814   }
818  * This is the public API that UI and developers should be calling. All methods
819  * just forward to AddonManagerInternal.
820  */
821 var AddonManager = {
822   // Constants for the AddonInstall.state property
823   // The install is available for download.
824   STATE_AVAILABLE: 0,
825   // The install is being downloaded.
826   STATE_DOWNLOADING: 1,
827   // The install is checking for compatibility information.
828   STATE_CHECKING: 2,
829   // The install is downloaded and ready to install.
830   STATE_DOWNLOADED: 3,
831   // The download failed.
832   STATE_DOWNLOAD_FAILED: 4,
833   // The add-on is being installed.
834   STATE_INSTALLING: 5,
835   // The add-on has been installed.
836   STATE_INSTALLED: 6,
837   // The install failed.
838   STATE_INSTALL_FAILED: 7,
839   // The install has been cancelled.
840   STATE_CANCELLED: 8,
842   // Constants representing different types of errors while downloading an
843   // add-on.
844   // The download failed due to network problems.
845   ERROR_NETWORK_FAILURE: -1,
846   // The downloaded file did not match the provided hash.
847   ERROR_INCORRECT_HASH: -2,
848   // The downloaded file seems to be corrupted in some way.
849   ERROR_CORRUPT_FILE: -3,
850   // An error occured trying to write to the filesystem.
851   ERROR_FILE_ACCESS: -4,
853   // These must be kept in sync with AddonUpdateChecker.
854   // No error was encountered.
855   UPDATE_STATUS_NO_ERROR: 0,
856   // The update check timed out
857   UPDATE_STATUS_TIMEOUT: -1,
858   // There was an error while downloading the update information.
859   UPDATE_STATUS_DOWNLOAD_ERROR: -2,
860   // The update information was malformed in some way.
861   UPDATE_STATUS_PARSE_ERROR: -3,
862   // The update information was not in any known format.
863   UPDATE_STATUS_UNKNOWN_FORMAT: -4,
864   // The update information was not correctly signed or there was an SSL error.
865   UPDATE_STATUS_SECURITY_ERROR: -5,
867   // Constants to indicate why an update check is being performed
868   // Update check has been requested by the user.
869   UPDATE_WHEN_USER_REQUESTED: 1,
870   // Update check is necessary to see if the Addon is compatibile with a new
871   // version of the application.
872   UPDATE_WHEN_NEW_APP_DETECTED: 2,
873   // Update check is necessary because a new application has been installed.
874   UPDATE_WHEN_NEW_APP_INSTALLED: 3,
875   // Update check is a regular background update check.
876   UPDATE_WHEN_PERIODIC_UPDATE: 16,
877   // Update check is needed to check an Addon that is being installed.
878   UPDATE_WHEN_ADDON_INSTALLED: 17,
880   // Constants for operations in Addon.pendingOperations
881   // Indicates that the Addon has no pending operations.
882   PENDING_NONE: 0,
883   // Indicates that the Addon will be enabled after the application restarts.
884   PENDING_ENABLE: 1,
885   // Indicates that the Addon will be disabled after the application restarts.
886   PENDING_DISABLE: 2,
887   // Indicates that the Addon will be uninstalled after the application restarts.
888   PENDING_UNINSTALL: 4,
889   // Indicates that the Addon will be installed after the application restarts.
890   PENDING_INSTALL: 8,
891   PENDING_UPGRADE: 16,
893   // Constants for permissions in Addon.permissions.
894   // Indicates that the Addon can be uninstalled.
895   PERM_CAN_UNINSTALL: 1,
896   // Indicates that the Addon can be enabled by the user.
897   PERM_CAN_ENABLE: 2,
898   // Indicates that the Addon can be disabled by the user.
899   PERM_CAN_DISABLE: 4,
900   // Indicates that the Addon can be upgraded.
901   PERM_CAN_UPGRADE: 8,
903   // General descriptions of where items are installed.
904   // Installed in this profile.
905   SCOPE_PROFILE: 1,
906   // Installed for all of this user's profiles.
907   SCOPE_USER: 2,
908   // Installed and owned by the application.
909   SCOPE_APPLICATION: 4,
910   // Installed for all users of the computer.
911   SCOPE_SYSTEM: 8,
912   // The combination of all scopes.
913   SCOPE_ALL: 15,
915   getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
916                                                  aHash, aName, aIconURL,
917                                                  aVersion, aLoadGroup) {
918     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
919                                           aName, aIconURL, aVersion, aLoadGroup);
920   },
922   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
923     AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
924   },
926   getAddonByID: function AM_getAddonByID(aId, aCallback) {
927     AddonManagerInternal.getAddonByID(aId, aCallback);
928   },
930   getAddonsByIDs: function AM_getAddonsByIDs(aIds, aCallback) {
931     AddonManagerInternal.getAddonsByIDs(aIds, aCallback);
932   },
934   getAddonsWithOperationsByTypes:
935   function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) {
936     AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback);
937   },
939   getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) {
940     AddonManagerInternal.getAddonsByTypes(aTypes, aCallback);
941   },
943   getAllAddons: function AM_getAllAddons(aCallback) {
944     AddonManagerInternal.getAllAddons(aCallback);
945   },
947   getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) {
948     AddonManagerInternal.getInstallsByTypes(aTypes, aCallback);
949   },
951   getAllInstalls: function AM_getAllInstalls(aCallback) {
952     AddonManagerInternal.getAllInstalls(aCallback);
953   },
955   isInstallEnabled: function AM_isInstallEnabled(aType) {
956     return AddonManagerInternal.isInstallEnabled(aType);
957   },
959   isInstallAllowed: function AM_isInstallAllowed(aType, aUri) {
960     return AddonManagerInternal.isInstallAllowed(aType, aUri);
961   },
963   installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aSource,
964                                                                  aUri, aInstalls) {
965     AddonManagerInternal.installAddonsFromWebpage(aType, aSource, aUri, aInstalls);
966   },
968   addInstallListener: function AM_addInstallListener(aListener) {
969     AddonManagerInternal.addInstallListener(aListener);
970   },
972   removeInstallListener: function AM_removeInstallListener(aListener) {
973     AddonManagerInternal.removeInstallListener(aListener);
974   },
976   addAddonListener: function AM_addAddonListener(aListener) {
977     AddonManagerInternal.addAddonListener(aListener);
978   },
980   removeAddonListener: function AM_removeAddonListener(aListener) {
981     AddonManagerInternal.removeAddonListener(aListener);
982   }