Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6 * The AddonUpdateChecker is responsible for retrieving the update information
7 * from an add-on's remote update manifest.
8 */
10 "use strict";
12 const Cc = Components.classes;
13 const Ci = Components.interfaces;
14 const Cu = Components.utils;
16 this.EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ];
18 const TIMEOUT = 60 * 1000;
19 const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
20 const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
21 const PREFIX_ITEM = "urn:mozilla:item:";
22 const PREFIX_EXTENSION = "urn:mozilla:extension:";
23 const PREFIX_THEME = "urn:mozilla:theme:";
24 const TOOLKIT_ID = "toolkit@mozilla.org"
25 const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
27 const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
29 Components.utils.import("resource://gre/modules/Services.jsm");
30 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
32 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
33 "resource://gre/modules/AddonManager.jsm");
34 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
35 "resource://gre/modules/addons/AddonRepository.jsm");
37 // Shared code for suppressing bad cert dialogs.
38 XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
39 let certUtils = {};
40 Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
41 return certUtils;
42 });
44 var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
45 getService(Ci.nsIRDFService);
47 Cu.import("resource://gre/modules/Log.jsm");
48 const LOGGER_ID = "addons.update-checker";
50 // Create a new logger for use by the Addons Update Checker
51 // (Requires AddonManager.jsm)
52 let logger = Log.repository.getLogger(LOGGER_ID);
54 /**
55 * A serialisation method for RDF data that produces an identical string
56 * for matching RDF graphs.
57 * The serialisation is not complete, only assertions stemming from a given
58 * resource are included, multiple references to the same resource are not
59 * permitted, and the RDF prolog and epilog are not included.
60 * RDF Blob and Date literals are not supported.
61 */
62 function RDFSerializer() {
63 this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
64 getService(Ci.nsIRDFContainerUtils);
65 this.resources = [];
66 }
68 RDFSerializer.prototype = {
69 INDENT: " ", // The indent used for pretty-printing
70 resources: null, // Array of the resources that have been found
72 /**
73 * Escapes characters from a string that should not appear in XML.
74 *
75 * @param aString
76 * The string to be escaped
77 * @return a string with all characters invalid in XML character data
78 * converted to entity references.
79 */
80 escapeEntities: function RDFS_escapeEntities(aString) {
81 aString = aString.replace(/&/g, "&");
82 aString = aString.replace(/</g, "<");
83 aString = aString.replace(/>/g, ">");
84 return aString.replace(/"/g, """);
85 },
87 /**
88 * Serializes all the elements of an RDF container.
89 *
90 * @param aDs
91 * The RDF datasource
92 * @param aContainer
93 * The RDF container to output the child elements of
94 * @param aIndent
95 * The current level of indent for pretty-printing
96 * @return a string containing the serialized elements.
97 */
98 serializeContainerItems: function RDFS_serializeContainerItems(aDs, aContainer,
99 aIndent) {
100 var result = "";
101 var items = aContainer.GetElements();
102 while (items.hasMoreElements()) {
103 var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
104 result += aIndent + "<RDF:li>\n"
105 result += this.serializeResource(aDs, item, aIndent + this.INDENT);
106 result += aIndent + "</RDF:li>\n"
107 }
108 return result;
109 },
111 /**
112 * Serializes all em:* (see EM_NS) properties of an RDF resource except for
113 * the em:signature property. As this serialization is to be compared against
114 * the manifest signature it cannot contain the em:signature property itself.
115 *
116 * @param aDs
117 * The RDF datasource
118 * @param aResource
119 * The RDF resource that contains the properties to serialize
120 * @param aIndent
121 * The current level of indent for pretty-printing
122 * @return a string containing the serialized properties.
123 * @throws if the resource contains a property that cannot be serialized
124 */
125 serializeResourceProperties: function RDFS_serializeResourceProperties(aDs,
126 aResource,
127 aIndent) {
128 var result = "";
129 var items = [];
130 var arcs = aDs.ArcLabelsOut(aResource);
131 while (arcs.hasMoreElements()) {
132 var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
133 if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM)
134 continue;
135 var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length);
136 if (prop == "signature")
137 continue;
139 var targets = aDs.GetTargets(aResource, arc, true);
140 while (targets.hasMoreElements()) {
141 var target = targets.getNext();
142 if (target instanceof Ci.nsIRDFResource) {
143 var item = aIndent + "<em:" + prop + ">\n";
144 item += this.serializeResource(aDs, target, aIndent + this.INDENT);
145 item += aIndent + "</em:" + prop + ">\n";
146 items.push(item);
147 }
148 else if (target instanceof Ci.nsIRDFLiteral) {
149 items.push(aIndent + "<em:" + prop + ">" +
150 this.escapeEntities(target.Value) + "</em:" + prop + ">\n");
151 }
152 else if (target instanceof Ci.nsIRDFInt) {
153 items.push(aIndent + "<em:" + prop + " NC:parseType=\"Integer\">" +
154 target.Value + "</em:" + prop + ">\n");
155 }
156 else {
157 throw Components.Exception("Cannot serialize unknown literal type");
158 }
159 }
160 }
161 items.sort();
162 result += items.join("");
163 return result;
164 },
166 /**
167 * Recursively serializes an RDF resource and all resources it links to.
168 * This will only output EM_NS properties and will ignore any em:signature
169 * property.
170 *
171 * @param aDs
172 * The RDF datasource
173 * @param aResource
174 * The RDF resource to serialize
175 * @param aIndent
176 * The current level of indent for pretty-printing. If undefined no
177 * indent will be added
178 * @return a string containing the serialized resource.
179 * @throws if the RDF data contains multiple references to the same resource.
180 */
181 serializeResource: function RDFS_serializeResource(aDs, aResource, aIndent) {
182 if (this.resources.indexOf(aResource) != -1 ) {
183 // We cannot output multiple references to the same resource.
184 throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
185 }
186 if (aIndent === undefined)
187 aIndent = "";
189 this.resources.push(aResource);
190 var container = null;
191 var type = "Description";
192 if (this.cUtils.IsSeq(aDs, aResource)) {
193 type = "Seq";
194 container = this.cUtils.MakeSeq(aDs, aResource);
195 }
196 else if (this.cUtils.IsAlt(aDs, aResource)) {
197 type = "Alt";
198 container = this.cUtils.MakeAlt(aDs, aResource);
199 }
200 else if (this.cUtils.IsBag(aDs, aResource)) {
201 type = "Bag";
202 container = this.cUtils.MakeBag(aDs, aResource);
203 }
205 var result = aIndent + "<RDF:" + type;
206 if (!gRDF.IsAnonymousResource(aResource))
207 result += " about=\"" + this.escapeEntities(aResource.ValueUTF8) + "\"";
208 result += ">\n";
210 if (container)
211 result += this.serializeContainerItems(aDs, container, aIndent + this.INDENT);
213 result += this.serializeResourceProperties(aDs, aResource, aIndent + this.INDENT);
215 result += aIndent + "</RDF:" + type + ">\n";
216 return result;
217 }
218 }
220 /**
221 * Parses an RDF style update manifest into an array of update objects.
222 *
223 * @param aId
224 * The ID of the add-on being checked for updates
225 * @param aUpdateKey
226 * An optional update key for the add-on
227 * @param aRequest
228 * The XMLHttpRequest that has retrieved the update manifest
229 * @return an array of update objects
230 * @throws if the update manifest is invalid in any way
231 */
232 function parseRDFManifest(aId, aUpdateKey, aRequest) {
233 function EM_R(aProp) {
234 return gRDF.GetResource(PREFIX_NS_EM + aProp);
235 }
237 function getValue(aLiteral) {
238 if (aLiteral instanceof Ci.nsIRDFLiteral)
239 return aLiteral.Value;
240 if (aLiteral instanceof Ci.nsIRDFResource)
241 return aLiteral.Value;
242 if (aLiteral instanceof Ci.nsIRDFInt)
243 return aLiteral.Value;
244 return null;
245 }
247 function getProperty(aDs, aSource, aProperty) {
248 return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true));
249 }
251 function getRequiredProperty(aDs, aSource, aProperty) {
252 let value = getProperty(aDs, aSource, aProperty);
253 if (!value)
254 throw Components.Exception("Update manifest is missing a required " + aProperty + " property.");
255 return value;
256 }
258 let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
259 createInstance(Ci.nsIRDFXMLParser);
260 let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
261 createInstance(Ci.nsIRDFDataSource);
262 rdfParser.parseString(ds, aRequest.channel.URI, aRequest.responseText);
264 // Differentiating between add-on types is deprecated
265 let extensionRes = gRDF.GetResource(PREFIX_EXTENSION + aId);
266 let themeRes = gRDF.GetResource(PREFIX_THEME + aId);
267 let itemRes = gRDF.GetResource(PREFIX_ITEM + aId);
268 let addonRes = ds.ArcLabelsOut(extensionRes).hasMoreElements() ? extensionRes
269 : ds.ArcLabelsOut(themeRes).hasMoreElements() ? themeRes
270 : itemRes;
272 // If we have an update key then the update manifest must be signed
273 if (aUpdateKey) {
274 let signature = getProperty(ds, addonRes, "signature");
275 if (!signature)
276 throw Components.Exception("Update manifest for " + aId + " does not contain a required signature");
277 let serializer = new RDFSerializer();
278 let updateString = null;
280 try {
281 updateString = serializer.serializeResource(ds, addonRes);
282 }
283 catch (e) {
284 throw Components.Exception("Failed to generate signed string for " + aId + ". Serializer threw " + e,
285 e.result);
286 }
288 let result = false;
290 try {
291 let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"].
292 getService(Ci.nsIDataSignatureVerifier);
293 result = verifier.verifyData(updateString, signature, aUpdateKey);
294 }
295 catch (e) {
296 throw Components.Exception("The signature or updateKey for " + aId + " is malformed." +
297 "Verifier threw " + e, e.result);
298 }
300 if (!result)
301 throw Components.Exception("The signature for " + aId + " was not created by the add-on's updateKey");
302 }
304 let updates = ds.GetTarget(addonRes, EM_R("updates"), true);
306 // A missing updates property doesn't count as a failure, just as no avialable
307 // update information
308 if (!updates) {
309 logger.warn("Update manifest for " + aId + " did not contain an updates property");
310 return [];
311 }
313 if (!(updates instanceof Ci.nsIRDFResource))
314 throw Components.Exception("Missing updates property for " + addonRes.Value);
316 let cu = Cc["@mozilla.org/rdf/container-utils;1"].
317 getService(Ci.nsIRDFContainerUtils);
318 if (!cu.IsContainer(ds, updates))
319 throw Components.Exception("Updates property was not an RDF container");
321 let results = [];
322 let ctr = Cc["@mozilla.org/rdf/container;1"].
323 createInstance(Ci.nsIRDFContainer);
324 ctr.Init(ds, updates);
325 let items = ctr.GetElements();
326 while (items.hasMoreElements()) {
327 let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
328 let version = getProperty(ds, item, "version");
329 if (!version) {
330 logger.warn("Update manifest is missing a required version property.");
331 continue;
332 }
334 logger.debug("Found an update entry for " + aId + " version " + version);
336 let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true);
337 while (targetApps.hasMoreElements()) {
338 let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
340 let appEntry = {};
341 try {
342 appEntry.id = getRequiredProperty(ds, targetApp, "id");
343 appEntry.minVersion = getRequiredProperty(ds, targetApp, "minVersion");
344 appEntry.maxVersion = getRequiredProperty(ds, targetApp, "maxVersion");
345 }
346 catch (e) {
347 logger.warn(e);
348 continue;
349 }
351 let result = {
352 id: aId,
353 version: version,
354 updateURL: getProperty(ds, targetApp, "updateLink"),
355 updateHash: getProperty(ds, targetApp, "updateHash"),
356 updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
357 strictCompatibility: getProperty(ds, targetApp, "strictCompatibility") == "true",
358 targetApplications: [appEntry]
359 };
361 if (result.updateURL && AddonManager.checkUpdateSecurity &&
362 result.updateURL.substring(0, 6) != "https:" &&
363 (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
364 logger.warn("updateLink " + result.updateURL + " is not secure and is not verified" +
365 " by a strong enough hash (needs to be sha1 or stronger).");
366 delete result.updateURL;
367 delete result.updateHash;
368 }
369 results.push(result);
370 }
371 }
372 return results;
373 }
375 /**
376 * Starts downloading an update manifest and then passes it to an appropriate
377 * parser to convert to an array of update objects
378 *
379 * @param aId
380 * The ID of the add-on being checked for updates
381 * @param aUpdateKey
382 * An optional update key for the add-on
383 * @param aUrl
384 * The URL of the update manifest
385 * @param aObserver
386 * An observer to pass results to
387 */
388 function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
389 this.id = aId;
390 this.updateKey = aUpdateKey;
391 this.observer = aObserver;
392 this.url = aUrl;
394 let requireBuiltIn = true;
395 try {
396 requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
397 }
398 catch (e) {
399 }
401 logger.debug("Requesting " + aUrl);
402 try {
403 this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
404 createInstance(Ci.nsIXMLHttpRequest);
405 this.request.open("GET", this.url, true);
406 this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
407 this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
408 // Prevent the request from writing to cache.
409 this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
410 this.request.overrideMimeType("text/xml");
411 this.request.timeout = TIMEOUT;
412 var self = this;
413 this.request.addEventListener("load", function loadEventListener(event) { self.onLoad() }, false);
414 this.request.addEventListener("error", function errorEventListener(event) { self.onError() }, false);
415 this.request.addEventListener("timeout", function timeoutEventListener(event) { self.onTimeout() }, false);
416 this.request.send(null);
417 }
418 catch (e) {
419 logger.error("Failed to request update manifest", e);
420 }
421 }
423 UpdateParser.prototype = {
424 id: null,
425 updateKey: null,
426 observer: null,
427 request: null,
428 url: null,
430 /**
431 * Called when the manifest has been successfully loaded.
432 */
433 onLoad: function UP_onLoad() {
434 let request = this.request;
435 this.request = null;
437 let requireBuiltIn = true;
438 try {
439 requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
440 }
441 catch (e) {
442 }
444 try {
445 CertUtils.checkCert(request.channel, !requireBuiltIn);
446 }
447 catch (e) {
448 this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
449 return;
450 }
452 if (!Components.isSuccessCode(request.status)) {
453 logger.warn("Request failed: " + this.url + " - " + request.status);
454 this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
455 return;
456 }
458 let channel = request.channel;
459 if (channel instanceof Ci.nsIHttpChannel && !channel.requestSucceeded) {
460 logger.warn("Request failed: " + this.url + " - " + channel.responseStatus +
461 ": " + channel.responseStatusText);
462 this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
463 return;
464 }
466 let xml = request.responseXML;
467 if (!xml || xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR) {
468 logger.warn("Update manifest was not valid XML");
469 this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
470 return;
471 }
473 // We currently only know about RDF update manifests
474 if (xml.documentElement.namespaceURI == PREFIX_NS_RDF) {
475 let results = null;
477 try {
478 results = parseRDFManifest(this.id, this.updateKey, request);
479 }
480 catch (e) {
481 logger.warn(e);
482 this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
483 return;
484 }
485 if ("onUpdateCheckComplete" in this.observer) {
486 try {
487 this.observer.onUpdateCheckComplete(results);
488 }
489 catch (e) {
490 logger.warn("onUpdateCheckComplete notification failed", e);
491 }
492 }
493 return;
494 }
496 logger.warn("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI);
497 this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
498 },
500 /**
501 * Called when the request times out
502 */
503 onTimeout: function() {
504 this.request = null;
505 logger.warn("Request for " + this.url + " timed out");
506 this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
507 },
509 /**
510 * Called when the manifest failed to load.
511 */
512 onError: function UP_onError() {
513 if (!Components.isSuccessCode(this.request.status)) {
514 logger.warn("Request failed: " + this.url + " - " + this.request.status);
515 }
516 else if (this.request.channel instanceof Ci.nsIHttpChannel) {
517 try {
518 if (this.request.channel.requestSucceeded) {
519 logger.warn("Request failed: " + this.url + " - " +
520 this.request.channel.responseStatus + ": " +
521 this.request.channel.responseStatusText);
522 }
523 }
524 catch (e) {
525 logger.warn("HTTP Request failed for an unknown reason");
526 }
527 }
528 else {
529 logger.warn("Request failed for an unknown reason");
530 }
532 this.request = null;
534 this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
535 },
537 /**
538 * Helper method to notify the observer that an error occured.
539 */
540 notifyError: function UP_notifyError(aStatus) {
541 if ("onUpdateCheckError" in this.observer) {
542 try {
543 this.observer.onUpdateCheckError(aStatus);
544 }
545 catch (e) {
546 logger.warn("onUpdateCheckError notification failed", e);
547 }
548 }
549 },
551 /**
552 * Called to cancel an in-progress update check.
553 */
554 cancel: function UP_cancel() {
555 this.request.abort();
556 this.request = null;
557 this.notifyError(AddonUpdateChecker.ERROR_CANCELLED);
558 }
559 };
561 /**
562 * Tests if an update matches a version of the application or platform
563 *
564 * @param aUpdate
565 * The available update
566 * @param aAppVersion
567 * The application version to use
568 * @param aPlatformVersion
569 * The platform version to use
570 * @param aIgnoreMaxVersion
571 * Ignore maxVersion when testing if an update matches. Optional.
572 * @param aIgnoreStrictCompat
573 * Ignore strictCompatibility when testing if an update matches. Optional.
574 * @param aCompatOverrides
575 * AddonCompatibilityOverride objects to match against. Optional.
576 * @return true if the update is compatible with the application/platform
577 */
578 function matchesVersions(aUpdate, aAppVersion, aPlatformVersion,
579 aIgnoreMaxVersion, aIgnoreStrictCompat,
580 aCompatOverrides) {
581 if (aCompatOverrides) {
582 let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
583 aCompatOverrides,
584 aAppVersion,
585 aPlatformVersion);
586 if (override && override.type == "incompatible")
587 return false;
588 }
590 if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
591 aIgnoreMaxVersion = false;
593 let result = false;
594 for (let app of aUpdate.targetApplications) {
595 if (app.id == Services.appinfo.ID) {
596 return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
597 (aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
598 }
599 if (app.id == TOOLKIT_ID) {
600 result = (Services.vc.compare(aPlatformVersion, app.minVersion) >= 0) &&
601 (aIgnoreMaxVersion || (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0));
602 }
603 }
604 return result;
605 }
607 this.AddonUpdateChecker = {
608 // These must be kept in sync with AddonManager
609 // The update check timed out
610 ERROR_TIMEOUT: -1,
611 // There was an error while downloading the update information.
612 ERROR_DOWNLOAD_ERROR: -2,
613 // The update information was malformed in some way.
614 ERROR_PARSE_ERROR: -3,
615 // The update information was not in any known format.
616 ERROR_UNKNOWN_FORMAT: -4,
617 // The update information was not correctly signed or there was an SSL error.
618 ERROR_SECURITY_ERROR: -5,
619 // The update was cancelled
620 ERROR_CANCELLED: -6,
622 /**
623 * Retrieves the best matching compatibility update for the application from
624 * a list of available update objects.
625 *
626 * @param aUpdates
627 * An array of update objects
628 * @param aVersion
629 * The version of the add-on to get new compatibility information for
630 * @param aIgnoreCompatibility
631 * An optional parameter to get the first compatibility update that
632 * is compatible with any version of the application or toolkit
633 * @param aAppVersion
634 * The version of the application or null to use the current version
635 * @param aPlatformVersion
636 * The version of the platform or null to use the current version
637 * @param aIgnoreMaxVersion
638 * Ignore maxVersion when testing if an update matches. Optional.
639 * @param aIgnoreStrictCompat
640 * Ignore strictCompatibility when testing if an update matches. Optional.
641 * @return an update object if one matches or null if not
642 */
643 getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion,
644 aIgnoreCompatibility,
645 aAppVersion,
646 aPlatformVersion,
647 aIgnoreMaxVersion,
648 aIgnoreStrictCompat) {
649 if (!aAppVersion)
650 aAppVersion = Services.appinfo.version;
651 if (!aPlatformVersion)
652 aPlatformVersion = Services.appinfo.platformVersion;
654 for (let update of aUpdates) {
655 if (Services.vc.compare(update.version, aVersion) == 0) {
656 if (aIgnoreCompatibility) {
657 for (let targetApp of update.targetApplications) {
658 let id = targetApp.id;
659 if (id == Services.appinfo.ID || id == TOOLKIT_ID)
660 return update;
661 }
662 }
663 else if (matchesVersions(update, aAppVersion, aPlatformVersion,
664 aIgnoreMaxVersion, aIgnoreStrictCompat)) {
665 return update;
666 }
667 }
668 }
669 return null;
670 },
672 /**
673 * Returns the newest available update from a list of update objects.
674 *
675 * @param aUpdates
676 * An array of update objects
677 * @param aAppVersion
678 * The version of the application or null to use the current version
679 * @param aPlatformVersion
680 * The version of the platform or null to use the current version
681 * @param aIgnoreMaxVersion
682 * When determining compatible updates, ignore maxVersion. Optional.
683 * @param aIgnoreStrictCompat
684 * When determining compatible updates, ignore strictCompatibility. Optional.
685 * @param aCompatOverrides
686 * Array of AddonCompatibilityOverride to take into account. Optional.
687 * @return an update object if one matches or null if not
688 */
689 getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates,
690 aAppVersion,
691 aPlatformVersion,
692 aIgnoreMaxVersion,
693 aIgnoreStrictCompat,
694 aCompatOverrides) {
695 if (!aAppVersion)
696 aAppVersion = Services.appinfo.version;
697 if (!aPlatformVersion)
698 aPlatformVersion = Services.appinfo.platformVersion;
700 let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
701 getService(Ci.nsIBlocklistService);
703 let newest = null;
704 for (let update of aUpdates) {
705 if (!update.updateURL)
706 continue;
707 let state = blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
708 if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
709 continue;
710 if ((newest == null || (Services.vc.compare(newest.version, update.version) < 0)) &&
711 matchesVersions(update, aAppVersion, aPlatformVersion,
712 aIgnoreMaxVersion, aIgnoreStrictCompat,
713 aCompatOverrides)) {
714 newest = update;
715 }
716 }
717 return newest;
718 },
720 /**
721 * Starts an update check.
722 *
723 * @param aId
724 * The ID of the add-on being checked for updates
725 * @param aUpdateKey
726 * An optional update key for the add-on
727 * @param aUrl
728 * The URL of the add-on's update manifest
729 * @param aObserver
730 * An observer to notify of results
731 * @return UpdateParser so that the caller can use UpdateParser.cancel() to shut
732 * down in-progress update requests
733 */
734 checkForUpdates: function AUC_checkForUpdates(aId, aUpdateKey, aUrl,
735 aObserver) {
736 return new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
737 }
738 };