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