toolkit/mozapps/extensions/AddonManager.jsm

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

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

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

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

mercurial