PageRenderTime 25ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/org.mozilla.xulrunner.win32.win32.x86/xulrunner/components/nsAddonRepository.js

https://github.com/aptana/xulrunner
JavaScript | 349 lines | 268 code | 49 blank | 32 comment | 53 complexity | 3829dab42d7eaae62e9c07ceca14ca87 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 38 "e:\builds\moz2_slave\xulrunner_win32_build\build\toolkit\mozapps\extensions\src\nsAddonRepository.js"
  4. */
  5. const Cc = Components.classes;
  6. const Ci = Components.interfaces;
  7. const Cr = Components.results;
  8. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  9. const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
  10. const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
  11. const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
  12. const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
  13. const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
  14. const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
  15. const API_VERSION = "1.2";
  16. function AddonSearchResult() {
  17. }
  18. AddonSearchResult.prototype = {
  19. id: null,
  20. name: null,
  21. version: null,
  22. summary: null,
  23. description: null,
  24. rating: null,
  25. iconURL: null,
  26. thumbnailURL: null,
  27. homepageURL: null,
  28. eula: null,
  29. type: null,
  30. xpiURL: null,
  31. xpiHash: null,
  32. QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult])
  33. }
  34. function AddonRepository() {
  35. }
  36. AddonRepository.prototype = {
  37. // The current set of results
  38. _addons: null,
  39. // Whether we are currently searching or not
  40. _searching: false,
  41. // Is this a search for recommended add-ons
  42. _recommended: false,
  43. // XHR associated with the current request
  44. _request: null,
  45. // Callback object to notify on completion
  46. _callback: null,
  47. // Maximum number of results to return
  48. _maxResults: null,
  49. get homepageURL() {
  50. return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  51. .getService(Components.interfaces.nsIURLFormatter)
  52. .formatURLPref(PREF_GETADDONS_BROWSEADDONS);
  53. },
  54. get isSearching() {
  55. return this._searching;
  56. },
  57. getRecommendedURL: function() {
  58. var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  59. .getService(Components.interfaces.nsIURLFormatter);
  60. return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED);
  61. },
  62. getSearchURL: function(aSearchTerms) {
  63. var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  64. .getService(Components.interfaces.nsIPrefBranch);
  65. var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  66. .getService(Components.interfaces.nsIURLFormatter);
  67. var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
  68. url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
  69. return urlf.formatURL(url);
  70. },
  71. cancelSearch: function() {
  72. this._searching = false;
  73. if (this._request) {
  74. this._request.abort();
  75. this._request = null;
  76. }
  77. this._callback = null;
  78. this._addons = null;
  79. },
  80. retrieveRecommendedAddons: function(aMaxResults, aCallback) {
  81. if (this._searching)
  82. return;
  83. this._searching = true;
  84. this._addons = [];
  85. this._callback = aCallback;
  86. this._recommended = true;
  87. this._maxResults = aMaxResults;
  88. var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  89. .getService(Components.interfaces.nsIPrefBranch);
  90. var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  91. .getService(Components.interfaces.nsIURLFormatter);
  92. var uri = prefs.getCharPref(PREF_GETADDONS_GETRECOMMENDED);
  93. uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  94. uri = urlf.formatURL(uri);
  95. this._loadList(uri);
  96. },
  97. searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
  98. if (this._searching)
  99. return;
  100. this._searching = true;
  101. this._addons = [];
  102. this._callback = aCallback;
  103. this._recommended = false;
  104. this._maxResults = aMaxResults;
  105. var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  106. .getService(Components.interfaces.nsIPrefBranch);
  107. var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  108. .getService(Components.interfaces.nsIURLFormatter);
  109. var uri = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
  110. uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  111. // We double encode due to bug 427155
  112. uri = uri.replace(/%TERMS%/g, encodeURIComponent(encodeURIComponent(aSearchTerms)));
  113. uri = urlf.formatURL(uri);
  114. this._loadList(uri);
  115. },
  116. // Posts results to the callback
  117. _reportSuccess: function(aCount) {
  118. this._searching = false;
  119. this._request = null;
  120. // The callback may want to trigger a new search so clear references early
  121. var addons = this._addons;
  122. var callback = this._callback;
  123. this._callback = null;
  124. this._addons = null;
  125. callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aCount);
  126. },
  127. // Notifies the callback of a failure
  128. _reportFailure: function(aEvent) {
  129. this._searching = false;
  130. this._request = null;
  131. // The callback may want to trigger a new search so clear references early
  132. var callback = this._callback;
  133. this._callback = null;
  134. this._addons = null;
  135. callback.searchFailed();
  136. },
  137. // Parses an add-on entry from an <addon> element
  138. _parseAddon: function(element) {
  139. var em = Cc["@mozilla.org/extensions/manager;1"].
  140. getService(Ci.nsIExtensionManager);
  141. var app = Cc["@mozilla.org/xre/app-info;1"].
  142. getService(Ci.nsIXULAppInfo).
  143. QueryInterface(Ci.nsIXULRuntime);
  144. var guid = element.getElementsByTagName("guid");
  145. if (guid.length != 1)
  146. return;
  147. // Ignore add-ons already seen in the results
  148. for (var i = 0; i < this._addons.length; i++)
  149. if (this._addons[i].id == guid[0].textContent)
  150. return;
  151. // Ignore installed add-ons
  152. if (em.getItemForID(guid[0].textContent) != null)
  153. return;
  154. // Ignore sandboxed add-ons
  155. var status = element.getElementsByTagName("status");
  156. // The status element has a unique id for each status type. 4 is Public.
  157. if (status.length != 1 || status[0].getAttribute("id") != 4)
  158. return;
  159. // Ignore add-ons not compatible with this OS
  160. var os = element.getElementsByTagName("compatible_os");
  161. // Only the version 0 schema included compatible_os if it isn't there then
  162. // we will see os compatibility on the install elements.
  163. if (os.length > 0) {
  164. var compatible = false;
  165. var i = 0;
  166. while (i < os.length && !compatible) {
  167. if (os[i].textContent == "ALL" || os[i].textContent == app.OS) {
  168. compatible = true;
  169. break;
  170. }
  171. i++;
  172. }
  173. if (!compatible)
  174. return;
  175. }
  176. // Ignore add-ons not compatible with this Application
  177. compatible = false;
  178. var tags = element.getElementsByTagName("compatible_applications");
  179. if (tags.length != 1)
  180. return;
  181. var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  182. getService(Ci.nsIVersionComparator);
  183. var apps = tags[0].getElementsByTagName("appID");
  184. var i = 0;
  185. while (i < apps.length) {
  186. if (apps[i].textContent == app.ID) {
  187. var minversion = apps[i].parentNode.getElementsByTagName("min_version")[0].textContent;
  188. var maxversion = apps[i].parentNode.getElementsByTagName("max_version")[0].textContent;
  189. if ((vc.compare(minversion, app.version) > 0) ||
  190. (vc.compare(app.version, maxversion) > 0))
  191. return;
  192. compatible = true;
  193. break;
  194. }
  195. i++;
  196. }
  197. if (!compatible)
  198. return;
  199. var addon = new AddonSearchResult();
  200. addon.id = guid[0].textContent;
  201. addon.rating = -1;
  202. var node = element.firstChild;
  203. while (node) {
  204. if (node instanceof Ci.nsIDOMElement) {
  205. switch (node.localName) {
  206. case "name":
  207. case "version":
  208. case "summary":
  209. case "description":
  210. case "eula":
  211. addon[node.localName] = node.textContent;
  212. break;
  213. case "rating":
  214. if (node.textContent.length > 0) {
  215. var rating = parseInt(node.textContent);
  216. if (rating >= 0)
  217. addon.rating = Math.min(5, rating);
  218. }
  219. break;
  220. case "thumbnail":
  221. addon.thumbnailURL = node.textContent;
  222. break;
  223. case "icon":
  224. addon.iconURL = node.textContent;
  225. break;
  226. case "learnmore":
  227. addon.homepageURL = node.textContent;
  228. break;
  229. case "type":
  230. // The type element has an id attribute that is the id from AMO's
  231. // database. This doesn't match our type values to perform a mapping
  232. if (node.getAttribute("id") == 2)
  233. addon.type = Ci.nsIUpdateItem.TYPE_THEME;
  234. else
  235. addon.type = Ci.nsIUpdateItem.TYPE_EXTENSION;
  236. break;
  237. case "install":
  238. // No os attribute means the xpi is compatible with any os
  239. if (node.hasAttribute("os")) {
  240. var os = node.getAttribute("os").toLowerCase();
  241. // If the os is not ALL and not the current OS then ignore this xpi
  242. if (os != "all" && os != app.OS.toLowerCase())
  243. break;
  244. }
  245. addon.xpiURL = node.textContent;
  246. if (node.hasAttribute("hash"))
  247. addon.xpiHash = node.getAttribute("hash");
  248. break;
  249. }
  250. }
  251. node = node.nextSibling;
  252. }
  253. // Add only if there was an xpi compatible with this os
  254. if (addon.xpiURL)
  255. this._addons.push(addon);
  256. },
  257. // Called when a single request has completed, parses out any add-ons and
  258. // either notifies the callback or does a new request for more results
  259. _listLoaded: function(aEvent) {
  260. var request = aEvent.target;
  261. var responseXML = request.responseXML;
  262. if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  263. (request.status != 200 && request.status != 0)) {
  264. this._reportFailure();
  265. return;
  266. }
  267. var elements = responseXML.documentElement.getElementsByTagName("addon");
  268. for (var i = 0; i < elements.length; i++) {
  269. this._parseAddon(elements[i]);
  270. var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  271. .getService(Components.interfaces.nsIPrefBranch);
  272. if (this._addons.length == this._maxResults) {
  273. this._reportSuccess(elements.length);
  274. return;
  275. }
  276. }
  277. if (responseXML.documentElement.hasAttribute("total_results"))
  278. this._reportSuccess(responseXML.documentElement.getAttribute("total_results"));
  279. else
  280. this._reportSuccess(elements.length);
  281. },
  282. // Performs a new request for results
  283. _loadList: function(aURI) {
  284. this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  285. createInstance(Ci.nsIXMLHttpRequest);
  286. this._request.open("GET", aURI, true);
  287. this._request.overrideMimeType("text/xml");
  288. var self = this;
  289. this._request.onerror = function(event) { self._reportFailure(event); };
  290. this._request.onload = function(event) { self._listLoaded(event); };
  291. this._request.send(null);
  292. },
  293. classDescription: "Addon Repository",
  294. contractID: "@mozilla.org/extensions/addon-repository;1",
  295. classID: Components.ID("{8eaaf524-7d6d-4f7d-ae8b-9277b324008d}"),
  296. QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonRepository])
  297. }
  298. function NSGetModule(aCompMgr, aFileSpec) {
  299. return XPCOMUtils.generateModule([AddonRepository]);
  300. }