2008-01-28 09:11:48 -08:00
|
|
|
/*
|
|
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
#
|
|
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
# the License. You may obtain a copy of the License at
|
|
|
|
# http://www.mozilla.org/MPL/
|
|
|
|
#
|
|
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
# for the specific language governing rights and limitations under the
|
|
|
|
# License.
|
|
|
|
#
|
|
|
|
# The Original Code is the Extension Manager.
|
|
|
|
#
|
|
|
|
# The Initial Developer of the Original Code is mozilla.org
|
|
|
|
# Portions created by the Initial Developer are Copyright (C) 2008
|
|
|
|
# the Initial Developer. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s):
|
|
|
|
# Dave Townsend <dtownsend@oxymoronical.com>
|
|
|
|
#
|
|
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
# the provisions above, a recipient may use your version of this file under
|
|
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
#
|
|
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
2010-07-19 16:01:23 -07:00
|
|
|
const Cu = Components.utils;
|
2008-01-28 09:11:48 -08:00
|
|
|
|
2010-07-19 16:01:23 -07:00
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
2010-04-29 10:41:24 -07:00
|
|
|
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
2008-01-28 09:11:48 -08:00
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
var EXPORTED_SYMBOLS = [ "AddonRepository" ];
|
|
|
|
|
2008-01-28 09:11:48 -08:00
|
|
|
const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
|
|
|
|
const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
|
|
|
|
const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
|
|
|
|
const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
|
|
|
|
const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
|
|
|
|
|
2008-03-06 18:05:22 -08:00
|
|
|
const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
|
2008-01-28 09:11:48 -08:00
|
|
|
|
2008-10-18 07:03:55 -07:00
|
|
|
const API_VERSION = "1.2";
|
2008-03-07 03:16:48 -08:00
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
function AddonSearchResult(aId) {
|
|
|
|
this.id = aId;
|
|
|
|
this.screenshots = [];
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
AddonSearchResult.prototype = {
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* The ID of the add-on
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
id: null,
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The name of the add-on
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
name: null,
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The version of the add-on
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
version: null,
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A short description of the add-on
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
description: null,
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The full description of the add-on
|
|
|
|
*/
|
|
|
|
fullDescription: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The rating of the add-on, 0-5 or -1 if unrated
|
|
|
|
*/
|
|
|
|
rating: -1,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The url of the add-ons icon or empty if there is no icon
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
iconURL: null,
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of screenshot urls for the add-on
|
|
|
|
*/
|
|
|
|
screenshots: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The homepage for the add-on
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
homepageURL: null,
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* The add-on type (e.g. "extension" or "theme")
|
|
|
|
*/
|
|
|
|
type: null,
|
2008-01-28 09:11:48 -08:00
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* AddonInstall object generated from the add-on XPI url
|
|
|
|
*/
|
|
|
|
install: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True or false depending on whether the add-on is compatible with the
|
|
|
|
* current version and platform of the application
|
|
|
|
*/
|
|
|
|
isCompatible: true,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the add-on has a secure means of updating
|
|
|
|
*/
|
|
|
|
providesUpdatesSecurely: true,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current blocklist state of the add-on
|
|
|
|
*/
|
|
|
|
blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if this add-on cannot be used in the application based on version
|
|
|
|
* compatibility, dependencies and blocklisting
|
|
|
|
*/
|
|
|
|
appDisabled: false,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the user wants this add-on to be disabled
|
|
|
|
*/
|
|
|
|
userDisabled: false,
|
|
|
|
|
2010-07-02 07:48:41 -07:00
|
|
|
/**
|
|
|
|
* The size of the add-on's files in bytes. For an add-on that have not yet
|
|
|
|
* been downloaded this may be an estimated value.
|
|
|
|
*/
|
|
|
|
size: null,
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Indicates what scope the add-on is installed in, per profile, user,
|
|
|
|
* system or application
|
|
|
|
*/
|
|
|
|
scope: AddonManager.SCOPE_PROFILE,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the add-on is currently functional
|
|
|
|
*/
|
|
|
|
isActive: true,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The creator of the add-on
|
|
|
|
*/
|
|
|
|
creator: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A bitfield holding all of the current operations that are waiting to be
|
|
|
|
* performed for this add-on
|
|
|
|
*/
|
|
|
|
pendingOperations: AddonManager.PENDING_NONE,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A bitfield holding all the the operations that can be performed on
|
|
|
|
* this add-on
|
|
|
|
*/
|
|
|
|
permissions: 0
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* The add-on repository is a source of add-ons that can be installed. It can
|
|
|
|
* be searched in two ways. One returns a list of add-ons that come highly
|
|
|
|
* recommended, this list should change frequently. The other way is to
|
|
|
|
* search for specific search terms entered by the user. Searches are
|
|
|
|
* asynchronous and results should be passed to the provided callback object
|
|
|
|
* when complete. The results passed to the callback should only include add-ons
|
|
|
|
* that are compatible with the current application and are not already
|
|
|
|
* installed. Searches are always asynchronous and should be passed to the
|
|
|
|
* callback object provided.
|
|
|
|
*/
|
|
|
|
var AddonRepository = {
|
2008-01-28 09:11:48 -08:00
|
|
|
// The current set of results
|
2010-06-17 15:47:51 -07:00
|
|
|
_results: null,
|
2008-01-28 09:11:48 -08:00
|
|
|
|
|
|
|
// Whether we are currently searching or not
|
|
|
|
_searching: false,
|
|
|
|
|
|
|
|
// Is this a search for recommended add-ons
|
|
|
|
_recommended: false,
|
|
|
|
|
|
|
|
// XHR associated with the current request
|
|
|
|
_request: null,
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/*
|
|
|
|
* Addon search results callback object that contains two functions
|
|
|
|
*
|
|
|
|
* searchSucceeded - Called when a search has suceeded.
|
|
|
|
*
|
|
|
|
* @param aAddons an array of the add-on results. In the case of
|
|
|
|
* searching for specific terms the ordering of results
|
|
|
|
* may be determined by the search provider.
|
|
|
|
* @param aAddonCount The length of aAddons
|
|
|
|
* @param aTotalResults The total results actually available in the
|
|
|
|
* repository
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* searchFailed - Called when an error occurred when performing a search.
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
_callback: null,
|
|
|
|
|
|
|
|
// Maximum number of results to return
|
|
|
|
_maxResults: null,
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* The homepage for visiting this repository. This may be null or an empty
|
|
|
|
* string.
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
get homepageURL() {
|
2010-07-19 16:01:23 -07:00
|
|
|
var url = this._formatURLPref(PREF_GETADDONS_BROWSEADDONS, {});
|
|
|
|
return (url != null) ? url : "about:blank";
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Returns whether this instance is currently performing a search. New
|
|
|
|
* searches will not be performed while this is the case.
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
get isSearching() {
|
|
|
|
return this._searching;
|
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* The url that can be visited to see recommended add-ons in this repository.
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
getRecommendedURL: function() {
|
2010-07-19 16:01:23 -07:00
|
|
|
var url = this._formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED, {});
|
|
|
|
return (url != null) ? url : "about:blank";
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Retrieves the url that can be visited to see search results for the given
|
|
|
|
* terms.
|
|
|
|
*
|
|
|
|
* @param aSearchTerms search terms used to search the repository
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
getSearchURL: function(aSearchTerms) {
|
2010-07-19 16:01:23 -07:00
|
|
|
var url = this._formatURLPref(PREF_GETADDONS_BROWSESEARCHRESULTS, {
|
|
|
|
TERMS : encodeURIComponent(aSearchTerms)
|
|
|
|
});
|
|
|
|
return (url != null) ? url : "about:blank";
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Cancels the search in progress. If there is no search in progress this
|
|
|
|
* does nothing.
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
cancelSearch: function() {
|
|
|
|
this._searching = false;
|
|
|
|
if (this._request) {
|
|
|
|
this._request.abort();
|
|
|
|
this._request = null;
|
|
|
|
}
|
2008-04-17 01:27:18 -07:00
|
|
|
this._callback = null;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._results = null;
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Begins a search for recommended add-ons in this repository. Results will
|
|
|
|
* be passed to the given callback.
|
|
|
|
*
|
|
|
|
* @param aMaxResults the maximum number of results to return
|
|
|
|
* @param aCallback the callback to pass results to
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
retrieveRecommendedAddons: function(aMaxResults, aCallback) {
|
|
|
|
if (this._searching)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._searching = true;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._results = [];
|
2008-01-28 09:11:48 -08:00
|
|
|
this._callback = aCallback;
|
|
|
|
this._recommended = true;
|
|
|
|
this._maxResults = aMaxResults;
|
|
|
|
|
2010-07-19 16:01:23 -07:00
|
|
|
var url = this._formatURLPref(PREF_GETADDONS_GETRECOMMENDED, {
|
|
|
|
API_VERSION : API_VERSION,
|
|
|
|
|
|
|
|
// Get twice as many results to account for potential filtering
|
|
|
|
MAX_RESULTS : 2 * aMaxResults
|
|
|
|
});
|
2008-01-28 09:11:48 -08:00
|
|
|
|
2010-07-19 16:01:23 -07:00
|
|
|
(url != null) ? this._loadList(url) : this.reportFailure();
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
/**
|
|
|
|
* Begins a search for add-ons in this repository. Results will be passed to
|
|
|
|
* the given callback.
|
|
|
|
*
|
|
|
|
* @param aSearchTerms the terms to search for
|
|
|
|
* @param aMaxResults the maximum number of results to return
|
|
|
|
* @param aCallback the callback to pass results to
|
|
|
|
*/
|
2008-01-28 09:11:48 -08:00
|
|
|
searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
|
|
|
|
if (this._searching)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._searching = true;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._results = [];
|
2008-01-28 09:11:48 -08:00
|
|
|
this._callback = aCallback;
|
|
|
|
this._recommended = false;
|
|
|
|
this._maxResults = aMaxResults;
|
|
|
|
|
2010-07-19 16:01:23 -07:00
|
|
|
// Get twice as many results to account for potential filtering
|
|
|
|
var url = this._formatURLPref(PREF_GETADDONS_GETSEARCHRESULTS, {
|
|
|
|
API_VERSION : API_VERSION,
|
|
|
|
|
|
|
|
// Get twice as many results to account for potential filtering
|
|
|
|
MAX_RESULTS : 2 * aMaxResults,
|
|
|
|
|
|
|
|
// We double encode due to bug 427155
|
|
|
|
TERMS : encodeURIComponent(encodeURIComponent(aSearchTerms))
|
|
|
|
});
|
|
|
|
|
|
|
|
(url != null) ? this._loadList(url) : this.reportFailure();
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Posts results to the callback
|
2010-06-17 15:47:51 -07:00
|
|
|
_reportSuccess: function(aTotalResults) {
|
2008-01-28 09:11:48 -08:00
|
|
|
this._searching = false;
|
|
|
|
this._request = null;
|
2008-04-17 06:18:46 -07:00
|
|
|
// The callback may want to trigger a new search so clear references early
|
2010-06-17 15:47:51 -07:00
|
|
|
var addons = [result.addon for each(result in this._results)];
|
2008-04-17 06:18:46 -07:00
|
|
|
var callback = this._callback;
|
2008-04-17 01:27:18 -07:00
|
|
|
this._callback = null;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._results = null;
|
|
|
|
callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aTotalResults);
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Notifies the callback of a failure
|
2010-06-17 15:47:51 -07:00
|
|
|
_reportFailure: function() {
|
2008-01-28 09:11:48 -08:00
|
|
|
this._searching = false;
|
|
|
|
this._request = null;
|
2008-04-17 06:18:46 -07:00
|
|
|
// The callback may want to trigger a new search so clear references early
|
|
|
|
var callback = this._callback;
|
2008-04-17 01:27:18 -07:00
|
|
|
this._callback = null;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._results = null;
|
2008-04-17 06:18:46 -07:00
|
|
|
callback.searchFailed();
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Parses an add-on entry from an <addon> element
|
2010-06-17 15:47:51 -07:00
|
|
|
_parseAddon: function(aElement, aSkip) {
|
|
|
|
var guidList = aElement.getElementsByTagName("guid");
|
2010-05-18 21:05:44 -07:00
|
|
|
if (guidList.length != 1)
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
|
2010-05-18 21:05:44 -07:00
|
|
|
var guid = guidList[0].textContent.trim();
|
|
|
|
|
2008-01-28 09:11:48 -08:00
|
|
|
// Ignore add-ons already seen in the results
|
2010-06-17 15:47:51 -07:00
|
|
|
for (var i = 0; i < this._results.length; i++)
|
|
|
|
if (this._results[i].addon.id == guid)
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore installed add-ons
|
2010-06-17 15:47:51 -07:00
|
|
|
if (aSkip.ids.indexOf(guid) != -1)
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore sandboxed add-ons
|
2010-06-17 15:47:51 -07:00
|
|
|
var status = aElement.getElementsByTagName("status");
|
2008-01-28 09:11:48 -08:00
|
|
|
// The status element has a unique id for each status type. 4 is Public.
|
|
|
|
if (status.length != 1 || status[0].getAttribute("id") != 4)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore add-ons not compatible with this OS
|
2010-06-17 15:47:51 -07:00
|
|
|
var osList = aElement.getElementsByTagName("compatible_os");
|
2008-03-07 03:16:48 -08:00
|
|
|
// Only the version 0 schema included compatible_os if it isn't there then
|
|
|
|
// we will see os compatibility on the install elements.
|
2010-05-18 21:05:44 -07:00
|
|
|
if (osList.length > 0) {
|
2008-03-07 03:16:48 -08:00
|
|
|
var compatible = false;
|
|
|
|
var i = 0;
|
2010-05-18 21:05:44 -07:00
|
|
|
while (i < osList.length && !compatible) {
|
|
|
|
var os = osList[i].textContent.trim();
|
2010-07-19 16:01:23 -07:00
|
|
|
if (os == "ALL" || os == Services.appinfo.OS) {
|
2008-03-07 03:16:48 -08:00
|
|
|
compatible = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
2008-03-07 03:16:48 -08:00
|
|
|
if (!compatible)
|
|
|
|
return;
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore add-ons not compatible with this Application
|
|
|
|
compatible = false;
|
2010-06-17 15:47:51 -07:00
|
|
|
var tags = aElement.getElementsByTagName("compatible_applications");
|
2008-01-28 09:11:48 -08:00
|
|
|
if (tags.length != 1)
|
|
|
|
return;
|
2010-07-19 16:01:23 -07:00
|
|
|
|
2008-04-09 12:47:49 -07:00
|
|
|
var apps = tags[0].getElementsByTagName("appID");
|
2008-01-28 09:11:48 -08:00
|
|
|
var i = 0;
|
|
|
|
while (i < apps.length) {
|
2010-07-19 16:01:23 -07:00
|
|
|
if (apps[i].textContent.trim() == Services.appinfo.ID) {
|
2010-05-18 21:05:44 -07:00
|
|
|
var parent = apps[i].parentNode;
|
|
|
|
var minversion = parent.getElementsByTagName("min_version")[0].textContent.trim();
|
|
|
|
var maxversion = parent.getElementsByTagName("max_version")[0].textContent.trim();
|
2010-07-19 16:01:23 -07:00
|
|
|
if ((Services.vc.compare(minversion, Services.appinfo.version) > 0) ||
|
|
|
|
(Services.vc.compare(Services.appinfo.version, maxversion) > 0))
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
compatible = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (!compatible)
|
|
|
|
return;
|
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
var addon = new AddonSearchResult(guid);
|
|
|
|
var result = {
|
|
|
|
addon: addon,
|
|
|
|
xpiURL: null,
|
|
|
|
xpiHash: null
|
|
|
|
};
|
|
|
|
var node = aElement.firstChild;
|
2008-01-28 09:11:48 -08:00
|
|
|
while (node) {
|
|
|
|
if (node instanceof Ci.nsIDOMElement) {
|
|
|
|
switch (node.localName) {
|
|
|
|
case "name":
|
|
|
|
case "version":
|
2010-06-17 15:47:51 -07:00
|
|
|
addon[node.localName] = node.textContent.trim();
|
|
|
|
break;
|
2008-01-28 09:11:48 -08:00
|
|
|
case "summary":
|
2010-06-17 15:47:51 -07:00
|
|
|
addon.description = node.textContent.trim();
|
|
|
|
break;
|
2008-01-28 09:11:48 -08:00
|
|
|
case "description":
|
2010-06-17 15:47:51 -07:00
|
|
|
addon.fullDescription = node.textContent.trim();
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "rating":
|
2008-03-21 09:09:44 -07:00
|
|
|
if (node.textContent.length > 0) {
|
|
|
|
var rating = parseInt(node.textContent);
|
|
|
|
if (rating >= 0)
|
|
|
|
addon.rating = Math.min(5, rating);
|
|
|
|
}
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "thumbnail":
|
2010-06-17 15:47:51 -07:00
|
|
|
addon.screenshots.push(node.textContent.trim());
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "icon":
|
2010-05-18 21:05:44 -07:00
|
|
|
addon.iconURL = node.textContent.trim();
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "learnmore":
|
2010-05-18 21:05:44 -07:00
|
|
|
addon.homepageURL = node.textContent.trim();
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "type":
|
|
|
|
// The type element has an id attribute that is the id from AMO's
|
|
|
|
// database. This doesn't match our type values to perform a mapping
|
2010-06-17 15:47:51 -07:00
|
|
|
addon.type = (node.getAttribute("id") == 2) ? "theme" : "extension";
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
case "install":
|
2008-03-07 03:16:48 -08:00
|
|
|
// No os attribute means the xpi is compatible with any os
|
|
|
|
if (node.hasAttribute("os")) {
|
|
|
|
var os = node.getAttribute("os").toLowerCase();
|
|
|
|
// If the os is not ALL and not the current OS then ignore this xpi
|
2010-07-19 16:01:23 -07:00
|
|
|
if (os != "all" && os != Services.appinfo.OS.toLowerCase())
|
2008-03-07 03:16:48 -08:00
|
|
|
break;
|
|
|
|
}
|
2010-06-17 15:47:51 -07:00
|
|
|
result.xpiURL = node.textContent.trim();
|
2010-07-02 07:48:41 -07:00
|
|
|
if (node.hasAttribute("size"))
|
|
|
|
addon.size = node.getAttribute("size");
|
2010-06-17 15:47:51 -07:00
|
|
|
|
|
|
|
// Ignore add-on installs
|
2010-07-01 20:36:50 -07:00
|
|
|
if (aSkip.sourceURIs.indexOf(result.xpiURL) != -1)
|
2010-06-17 15:47:51 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
result.xpiHash = node.hasAttribute("hash") ? node.getAttribute("hash") : null;
|
2008-01-28 09:11:48 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = node.nextSibling;
|
|
|
|
}
|
|
|
|
|
2008-03-07 03:16:48 -08:00
|
|
|
// Add only if there was an xpi compatible with this os
|
2010-06-17 15:47:51 -07:00
|
|
|
if (result.xpiURL)
|
|
|
|
this._results.push(result);
|
|
|
|
},
|
|
|
|
|
|
|
|
_parseAddons: function(aElements, aTotalResults, aSkip) {
|
|
|
|
for (var i = 0; i < aElements.length && this._results.length < this._maxResults; i++)
|
|
|
|
this._parseAddon(aElements[i], aSkip);
|
|
|
|
|
|
|
|
var pendingResults = this._results.length;
|
|
|
|
if (pendingResults == 0) {
|
|
|
|
this._reportSuccess(aTotalResults);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this._results.forEach(function(aResult) {
|
|
|
|
var addon = aResult.addon;
|
|
|
|
var callback = function(aInstall) {
|
|
|
|
addon.install = aInstall;
|
|
|
|
pendingResults--;
|
|
|
|
if (pendingResults == 0)
|
|
|
|
self._reportSuccess(aTotalResults);
|
|
|
|
}
|
|
|
|
|
|
|
|
AddonManager.getInstallForURL(aResult.xpiURL, callback,
|
|
|
|
"application/x-xpinstall", aResult.xpiHash,
|
|
|
|
addon.name, addon.iconURL, addon.version);
|
|
|
|
});
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Called when a single request has completed, parses out any add-ons and
|
|
|
|
// either notifies the callback or does a new request for more results
|
|
|
|
_listLoaded: function(aEvent) {
|
|
|
|
var request = aEvent.target;
|
|
|
|
var responseXML = request.responseXML;
|
|
|
|
|
|
|
|
if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
|
|
|
|
(request.status != 200 && request.status != 0)) {
|
|
|
|
this._reportFailure();
|
|
|
|
return;
|
|
|
|
}
|
2010-04-29 10:41:24 -07:00
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
var elements = responseXML.documentElement.getElementsByTagName("addon");
|
|
|
|
if (responseXML.documentElement.hasAttribute("total_results"))
|
|
|
|
var totalResults = responseXML.documentElement.getAttribute("total_results");
|
|
|
|
else
|
|
|
|
var totalResults = elements.length;
|
|
|
|
|
2010-04-29 10:41:24 -07:00
|
|
|
var self = this;
|
2010-07-01 20:36:50 -07:00
|
|
|
var skip = {ids: null, sourceURIs: null};
|
2008-03-07 03:16:48 -08:00
|
|
|
|
2010-06-17 15:47:51 -07:00
|
|
|
AddonManager.getAllAddons(function(aAddons) {
|
|
|
|
skip.ids = [a.id for each (a in aAddons)];
|
2010-07-01 20:36:50 -07:00
|
|
|
if (skip.sourceURIs)
|
2010-06-17 15:47:51 -07:00
|
|
|
self._parseAddons(elements, totalResults, skip);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddonManager.getAllInstalls(function(aInstalls) {
|
2010-07-01 20:36:50 -07:00
|
|
|
skip.sourceURIs = [];
|
2010-06-17 15:47:51 -07:00
|
|
|
aInstalls.forEach(function(aInstall) {
|
|
|
|
if (aInstall.state != AddonManager.STATE_AVAILABLE)
|
2010-07-01 20:36:50 -07:00
|
|
|
skip.sourceURIs.push(aInstall.sourceURI.spec);
|
2010-06-17 15:47:51 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
if (skip.ids)
|
|
|
|
self._parseAddons(elements, totalResults, skip);
|
2010-04-29 10:41:24 -07:00
|
|
|
});
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Performs a new request for results
|
2008-03-06 18:05:22 -08:00
|
|
|
_loadList: function(aURI) {
|
2008-01-28 09:11:48 -08:00
|
|
|
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
|
|
|
createInstance(Ci.nsIXMLHttpRequest);
|
2008-03-06 18:05:22 -08:00
|
|
|
this._request.open("GET", aURI, true);
|
2008-01-28 09:11:48 -08:00
|
|
|
this._request.overrideMimeType("text/xml");
|
|
|
|
|
|
|
|
var self = this;
|
2010-06-17 15:47:51 -07:00
|
|
|
this._request.onerror = function(aEvent) { self._reportFailure(); };
|
|
|
|
this._request.onload = function(aEvent) { self._listLoaded(aEvent); };
|
2008-01-28 09:11:48 -08:00
|
|
|
this._request.send(null);
|
2010-07-19 16:01:23 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// Create url from pref, returning null if pref does not exist
|
|
|
|
_formatURLPref: function(aPref, aSubstitutions) {
|
|
|
|
var url = null;
|
|
|
|
try {
|
|
|
|
url = Services.prefs.getCharPref(aPref);
|
|
|
|
} catch(e) {
|
|
|
|
Cu.reportError("_formatURLPref: Couldn't get pref: " + aPref);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
url = url.replace(/%([A-Z_]+)%/g, function(aMatch, aKey) {
|
|
|
|
return (aKey in aSubstitutions) ? aSubstitutions[aKey] : aMatch;
|
|
|
|
});
|
|
|
|
|
|
|
|
return Services.urlFormatter.formatURL(url);
|
2010-06-17 15:47:51 -07:00
|
|
|
}
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
|
|
|
|