2008-01-28 09:11:48 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/*
|
|
|
|
# ***** 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;
|
|
|
|
const Cr = Components.results;
|
|
|
|
|
|
|
|
Components.utils.import("resource://gre/modules/XPCOMUtils.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
|
|
|
|
|
|
|
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
|
|
|
|
2008-01-28 09:11:48 -08:00
|
|
|
function AddonSearchResult() {
|
|
|
|
}
|
|
|
|
|
|
|
|
AddonSearchResult.prototype = {
|
|
|
|
id: null,
|
|
|
|
name: null,
|
|
|
|
version: null,
|
|
|
|
summary: null,
|
|
|
|
description: null,
|
|
|
|
rating: null,
|
|
|
|
iconURL: null,
|
|
|
|
thumbnailURL: null,
|
|
|
|
homepageURL: null,
|
|
|
|
eula: null,
|
|
|
|
type: null,
|
|
|
|
xpiURL: null,
|
|
|
|
xpiHash: null,
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult])
|
|
|
|
}
|
|
|
|
|
|
|
|
function AddonRepository() {
|
|
|
|
}
|
|
|
|
|
|
|
|
AddonRepository.prototype = {
|
|
|
|
// The current set of results
|
2008-04-17 01:27:18 -07:00
|
|
|
_addons: 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,
|
|
|
|
|
|
|
|
// Callback object to notify on completion
|
|
|
|
_callback: null,
|
|
|
|
|
|
|
|
// Maximum number of results to return
|
|
|
|
_maxResults: null,
|
|
|
|
|
|
|
|
get homepageURL() {
|
|
|
|
return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
|
|
.getService(Components.interfaces.nsIURLFormatter)
|
|
|
|
.formatURLPref(PREF_GETADDONS_BROWSEADDONS);
|
|
|
|
},
|
|
|
|
|
|
|
|
get isSearching() {
|
|
|
|
return this._searching;
|
|
|
|
},
|
|
|
|
|
|
|
|
getRecommendedURL: function() {
|
|
|
|
var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
|
|
.getService(Components.interfaces.nsIURLFormatter);
|
|
|
|
|
|
|
|
return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSearchURL: function(aSearchTerms) {
|
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
|
|
.getService(Components.interfaces.nsIURLFormatter);
|
|
|
|
|
|
|
|
var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
|
|
|
|
url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
|
|
|
|
return urlf.formatURL(url);
|
|
|
|
},
|
|
|
|
|
|
|
|
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;
|
|
|
|
this._addons = null;
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
retrieveRecommendedAddons: function(aMaxResults, aCallback) {
|
|
|
|
if (this._searching)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._searching = true;
|
|
|
|
this._addons = [];
|
|
|
|
this._callback = aCallback;
|
|
|
|
this._recommended = true;
|
|
|
|
this._maxResults = aMaxResults;
|
|
|
|
|
2008-03-07 03:16:48 -08:00
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
2008-01-28 09:11:48 -08:00
|
|
|
var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
|
|
.getService(Components.interfaces.nsIURLFormatter);
|
|
|
|
|
2008-03-07 03:16:48 -08:00
|
|
|
var uri = prefs.getCharPref(PREF_GETADDONS_GETRECOMMENDED);
|
|
|
|
uri = uri.replace(/%API_VERSION%/g, API_VERSION);
|
|
|
|
uri = urlf.formatURL(uri);
|
2008-03-06 18:05:22 -08:00
|
|
|
this._loadList(uri);
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
|
|
|
|
if (this._searching)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._searching = true;
|
|
|
|
this._addons = [];
|
|
|
|
this._callback = aCallback;
|
|
|
|
this._recommended = false;
|
|
|
|
this._maxResults = aMaxResults;
|
|
|
|
|
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
|
|
.getService(Components.interfaces.nsIURLFormatter);
|
|
|
|
|
2008-03-06 18:05:22 -08:00
|
|
|
var uri = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
|
2008-03-07 03:16:48 -08:00
|
|
|
uri = uri.replace(/%API_VERSION%/g, API_VERSION);
|
2008-10-18 07:03:55 -07:00
|
|
|
// We double encode due to bug 427155
|
|
|
|
uri = uri.replace(/%TERMS%/g, encodeURIComponent(encodeURIComponent(aSearchTerms)));
|
2008-03-06 18:05:22 -08:00
|
|
|
uri = urlf.formatURL(uri);
|
|
|
|
this._loadList(uri);
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Posts results to the callback
|
|
|
|
_reportSuccess: function(aCount) {
|
|
|
|
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 addons = this._addons;
|
|
|
|
var callback = this._callback;
|
2008-04-17 01:27:18 -07:00
|
|
|
this._callback = null;
|
|
|
|
this._addons = null;
|
2008-04-17 06:18:46 -07:00
|
|
|
callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aCount);
|
2008-01-28 09:11:48 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// Notifies the callback of a failure
|
|
|
|
_reportFailure: function(aEvent) {
|
|
|
|
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;
|
|
|
|
this._addons = 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-04-29 10:41:24 -07:00
|
|
|
_parseAddon: function(element, known_ids) {
|
2008-01-28 09:11:48 -08:00
|
|
|
var app = Cc["@mozilla.org/xre/app-info;1"].
|
|
|
|
getService(Ci.nsIXULAppInfo).
|
|
|
|
QueryInterface(Ci.nsIXULRuntime);
|
|
|
|
|
2010-05-18 21:05:44 -07:00
|
|
|
var guidList = element.getElementsByTagName("guid");
|
|
|
|
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
|
|
|
|
for (var i = 0; i < this._addons.length; i++)
|
2010-05-18 21:05:44 -07:00
|
|
|
if (this._addons[i].id == guid)
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore installed add-ons
|
2010-05-18 21:05:44 -07:00
|
|
|
if (known_ids.indexOf(guid) != -1)
|
2008-01-28 09:11:48 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore sandboxed add-ons
|
|
|
|
var status = element.getElementsByTagName("status");
|
|
|
|
// 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-05-18 21:05:44 -07:00
|
|
|
var osList = element.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();
|
|
|
|
if (os == "ALL" || os == app.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;
|
|
|
|
var tags = element.getElementsByTagName("compatible_applications");
|
|
|
|
if (tags.length != 1)
|
|
|
|
return;
|
|
|
|
var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
|
|
|
|
getService(Ci.nsIVersionComparator);
|
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-05-18 21:05:44 -07:00
|
|
|
if (apps[i].textContent.trim() == app.ID) {
|
|
|
|
var parent = apps[i].parentNode;
|
|
|
|
var minversion = parent.getElementsByTagName("min_version")[0].textContent.trim();
|
|
|
|
var maxversion = parent.getElementsByTagName("max_version")[0].textContent.trim();
|
2008-01-28 09:11:48 -08:00
|
|
|
if ((vc.compare(minversion, app.version) > 0) ||
|
|
|
|
(vc.compare(app.version, maxversion) > 0))
|
|
|
|
return;
|
|
|
|
compatible = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (!compatible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var addon = new AddonSearchResult();
|
2010-05-18 21:05:44 -07:00
|
|
|
addon.id = guid;
|
2008-01-28 09:11:48 -08:00
|
|
|
addon.rating = -1;
|
|
|
|
var node = element.firstChild;
|
|
|
|
while (node) {
|
|
|
|
if (node instanceof Ci.nsIDOMElement) {
|
|
|
|
switch (node.localName) {
|
|
|
|
case "name":
|
|
|
|
case "version":
|
|
|
|
case "summary":
|
|
|
|
case "description":
|
|
|
|
case "eula":
|
2010-05-18 21:05:44 -07:00
|
|
|
addon[node.localName] = 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-05-18 21:05:44 -07:00
|
|
|
addon.thumbnailURL = 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
|
|
|
|
if (node.getAttribute("id") == 2)
|
2010-04-29 10:41:24 -07:00
|
|
|
addon.type = Ci.nsIAddonSearchResult.TYPE_THEME;
|
2008-01-28 09:11:48 -08:00
|
|
|
else
|
2010-04-29 10:41:24 -07:00
|
|
|
addon.type = Ci.nsIAddonSearchResult.TYPE_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
|
|
|
|
if (os != "all" && os != app.OS.toLowerCase())
|
|
|
|
break;
|
|
|
|
}
|
2010-05-18 21:05:44 -07:00
|
|
|
addon.xpiURL = node.textContent.trim();
|
2008-01-28 09:11:48 -08:00
|
|
|
if (node.hasAttribute("hash"))
|
|
|
|
addon.xpiHash = node.getAttribute("hash");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = node.nextSibling;
|
|
|
|
}
|
|
|
|
|
2008-03-07 03:16:48 -08:00
|
|
|
// Add only if there was an xpi compatible with this os
|
|
|
|
if (addon.xpiURL)
|
|
|
|
this._addons.push(addon);
|
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
|
|
|
|
|
|
|
var self = this;
|
|
|
|
AddonManager.getAllAddons(function(addons) {
|
|
|
|
var known_ids = [a.id for each(a in addons)];
|
|
|
|
|
|
|
|
var elements = responseXML.documentElement.getElementsByTagName("addon");
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
|
|
self._parseAddon(elements[i], known_ids);
|
|
|
|
|
|
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
if (self._addons.length == self._maxResults) {
|
|
|
|
self._reportSuccess(elements.length);
|
|
|
|
return;
|
|
|
|
}
|
2008-01-28 09:11:48 -08:00
|
|
|
}
|
2008-03-07 03:16:48 -08:00
|
|
|
|
2010-04-29 10:41:24 -07:00
|
|
|
if (responseXML.documentElement.hasAttribute("total_results"))
|
|
|
|
self._reportSuccess(responseXML.documentElement.getAttribute("total_results"));
|
|
|
|
else
|
|
|
|
self._reportSuccess(elements.length);
|
|
|
|
});
|
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;
|
|
|
|
this._request.onerror = function(event) { self._reportFailure(event); };
|
|
|
|
this._request.onload = function(event) { self._listLoaded(event); };
|
|
|
|
this._request.send(null);
|
|
|
|
},
|
|
|
|
|
|
|
|
classDescription: "Addon Repository",
|
|
|
|
contractID: "@mozilla.org/extensions/addon-repository;1",
|
|
|
|
classID: Components.ID("{8eaaf524-7d6d-4f7d-ae8b-9277b324008d}"),
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonRepository])
|
|
|
|
}
|
|
|
|
|
|
|
|
function NSGetModule(aCompMgr, aFileSpec) {
|
|
|
|
return XPCOMUtils.generateModule([AddonRepository]);
|
|
|
|
}
|