Sat, 03 Jan 2015 20:18:00 +0100
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;
1006 }
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;
1013 }
1015 this.callManagerListeners("onCheckUpdateSecurityChanged");
1017 if (gCheckUpdateSecurity != oldValue)
1018 this.updateAddonAppDisabledStates();
1020 break;
1021 }
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;
1028 }
1030 this.callManagerListeners("onUpdateModeChanged");
1031 break;
1032 }
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;
1039 }
1041 this.callManagerListeners("onUpdateModeChanged");
1042 break;
1043 }
1044 case PREF_EM_HOTFIX_ID: {
1045 try {
1046 gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
1047 } catch(e) {
1048 gHotfixID = null;
1049 }
1050 break;
1051 }
1052 }
1053 },
1055 /**
1056 * Replaces %...% strings in an addon url (update and updateInfo) with
1057 * appropriate values.
1058 *
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)
1068 {
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;
1095 }
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);
1116 }
1118 try {
1119 var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, aParam);
1120 var paramHandler = Cc[contractID].getService(Ci.nsIPropertyBag2);
1121 return paramHandler.getPropertyAsAString(aParam);
1122 }
1123 catch(e) {
1124 return aMatch;
1125 }
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);
1163 }
1164 }
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;
1185 }
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();
1196 }
1197 },
1199 onUpdateFinished: notifyComplete
1200 }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
1201 });
1203 notifyComplete();
1204 });
1205 });
1206 }
1208 if (checkHotfix) {
1209 var hotfixVersion = "";
1210 try {
1211 hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
1212 }
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;
1237 }
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;
1244 }
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;
1254 }
1255 catch (e) {
1256 // By default don't do certificate checks.
1257 return;
1258 }
1260 try {
1261 CertUtils.validateCert(aInstall.certificate,
1262 CertUtils.readCertPrefs(PREF_EM_HOTFIX_CERTS));
1263 }
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();
1268 }
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);
1282 }
1283 });
1285 aInstall.install();
1287 notifyComplete();
1288 }, "application/x-xpinstall", update.updateHash, null,
1289 null, update.version);
1290 },
1292 onUpdateCheckError: notifyComplete
1293 });
1294 }
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.
1303 *
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.
1334 *
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.
1362 *
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);
1380 }
1381 catch (e) {
1382 logger.warn("AddonManagerListener threw exception when calling " + aMethod, e);
1383 }
1384 }
1385 },
1387 /**
1388 * Calls all registered InstallListeners with an event. Any parameters after
1389 * the extraListeners parameter are passed to the listener.
1390 *
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;
1423 }
1424 }
1425 catch (e) {
1426 logger.warn("InstallListener threw exception when calling " + aMethod, e);
1427 }
1428 }
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.
1435 *
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);
1453 }
1454 catch (e) {
1455 logger.warn("AddonListener threw exception when calling " + aMethod, e);
1456 }
1457 }
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.
1464 *
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.
1505 *
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);
1529 }
1530 });
1531 },
1533 /**
1534 * Asynchronously gets an AddonInstall for a URL.
1535 *
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 = {};
1589 }
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;
1608 }
1609 }
1610 safeCall(aCallback, null);
1611 },
1613 /**
1614 * Asynchronously gets an AddonInstall for an nsIFile.
1615 *
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);
1654 }
1655 });
1656 },
1658 /**
1659 * Asynchronously gets all current AddonInstalls optionally limiting to a list
1660 * of types.
1661 *
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);
1694 }
1695 });
1696 },
1698 /**
1699 * Asynchronously gets all current AddonInstalls.
1700 *
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.
1714 *
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.
1718 *
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);
1728 }
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;
1735 }
1736 }
1738 return null;
1739 },
1741 /**
1742 * Checks whether installation is enabled for a particular mimetype.
1743 *
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;
1762 }
1763 return false;
1764 },
1766 /**
1767 * Checks whether a particular source is allowed to install add-ons of a
1768 * given mimetype.
1769 *
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;
1794 }
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.
1801 *
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;
1841 }
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);
1850 }
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 });
1857 }
1858 }
1859 else if (weblistener.onWebInstallRequested(aSource, aURI, aInstalls,
1860 aInstalls.length)) {
1861 aInstalls.forEach(function(aInstall) {
1862 aInstall.install();
1863 });
1864 }
1865 }
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 });
1874 }
1875 },
1877 /**
1878 * Adds a new InstallListener if the listener is not already registered.
1879 *
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.
1895 *
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++;
1910 }
1911 },
1913 /**
1914 * Asynchronously gets an add-on with a specific ID.
1915 *
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);
1948 }
1949 });
1950 },
1952 /**
1953 * Asynchronously get an add-on with a specific Sync GUID.
1954 *
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();
1982 }
1983 });
1984 },
1986 noMoreObjects: function getAddonBySyncGUID_noMoreObjects(aCaller) {
1987 safeCall(aCallback, null);
1988 }
1989 });
1990 },
1992 /**
1993 * Asynchronously gets an array of add-ons.
1994 *
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);
2027 }
2028 });
2029 },
2031 /**
2032 * Asynchronously gets add-ons of specific types.
2033 *
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);
2066 }
2067 });
2068 },
2070 /**
2071 * Asynchronously gets all installed add-ons.
2072 *
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.
2091 *
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);
2127 }
2128 });
2129 },
2131 /**
2132 * Adds a new AddonManagerListener if the listener is not already registered.
2133 *
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.
2149 *
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++;
2164 }
2165 },
2167 /**
2168 * Adds a new AddonListener if the listener is not already registered.
2169 *
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.
2185 *
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++;
2200 }
2201 },
2203 /**
2204 * Adds a new TypeListener if the listener is not already registered.
2205 *
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.
2221 *
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++;
2236 }
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);
2265 }
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);
2295 }
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;
2392 }
2393 else {
2394 report.message = aException.toString();
2395 if (aException.fileName) {
2396 report.file = aException.fileName;
2397 report.line = aException.lineNumber;
2398 }
2399 }
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.
2427 *
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);
2434 }
2435 if ("onNoUpdateAvailable" in listener) {
2436 safeCall(listener.onNoUpdateAvailable.bind(listener), addon);
2437 }
2438 if ("onUpdateFinished" in listener) {
2439 safeCall(listener.onUpdateFinished.bind(listener), addon);
2440 }
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.
2639 *
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.
2738 *
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);
2807 }
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);