toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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, "&amp;");
    82     aString = aString.replace(/</g, "&lt;");
    83     aString = aString.replace(/>/g, "&gt;");
    84     return aString.replace(/"/g, "&quot;");
    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 };

mercurial