Bug 568728: Move nsAddonRepository.js to a JSM. r=dtownsend
authorBen Parr <bparr@mozilla.com>
Thu, 17 Jun 2010 15:47:51 -0700
changeset 43746 8b2a10fda8d8d0e0e8582c2f4a68d20c70a98b61
parent 43745 14383ec3611b1085e42e7502037136ccd4cd0d91
child 43747 de6a2611ca220e17d639f31e2f12efe34a1ee563
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdtownsend
bugs568728
milestone1.9.3a6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 568728: Move nsAddonRepository.js to a JSM. r=dtownsend
browser/installer/package-manifest.in
toolkit/mozapps/extensions/AddonRepository.jsm
toolkit/mozapps/extensions/Makefile.in
toolkit/mozapps/extensions/nsAddonRepository.js
toolkit/mozapps/extensions/nsIAddonRepository.idl
toolkit/mozapps/extensions/test/xpcshell/test_bug404024.js
toolkit/mozapps/extensions/test/xpcshell/test_bug417606.js
toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -260,17 +260,16 @@
 @BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/FeedConverter.js
 @BINPATH@/components/FeedWriter.js
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
-@BINPATH@/components/nsAddonRepository.js
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.js
 @BINPATH@/components/nsMicrosummaryService.js
 @BINPATH@/components/nsPlacesTransactionsService.js
 @BINPATH@/components/nsPrivateBrowsingService.js
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
rename from toolkit/mozapps/extensions/nsAddonRepository.js
rename to toolkit/mozapps/extensions/AddonRepository.jsm
--- a/toolkit/mozapps/extensions/nsAddonRepository.js
+++ b/toolkit/mozapps/extensions/AddonRepository.jsm
@@ -1,9 +1,8 @@
-/* -*- 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/
@@ -39,203 +38,359 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
+var EXPORTED_SYMBOLS = [ "AddonRepository" ];
+
 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";
 
 const XMLURI_PARSE_ERROR  = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
 
 const API_VERSION = "1.2";
 
-function AddonSearchResult() {
+function AddonSearchResult(aId) {
+  this.id = aId;
+  this.screenshots = [];
 }
 
 AddonSearchResult.prototype = {
+  /**
+   * The ID of the add-on
+   */
   id: null,
+
+  /**
+   * The name of the add-on
+   */
   name: null,
+
+  /**
+   * The version of the add-on
+   */
   version: null,
-  summary: null,
+
+  /**
+   * A short description of the add-on
+   */
   description: null,
-  rating: null,
+
+  /**
+   * 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
+   */
   iconURL: null,
-  thumbnailURL: null,
+
+  /**
+   * An array of screenshot urls for the add-on
+   */
+  screenshots: null,
+
+  /**
+   * The homepage for the add-on
+   */
   homepageURL: null,
-  eula: null,
+
+  /**
+   * The add-on type (e.g. "extension" or "theme")
+   */
   type: null,
-  xpiURL: null,
-  xpiHash: null,
+
+  /**
+   * 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,
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult])
+  /**
+   * True if the user wants this add-on to be disabled
+   */
+  userDisabled: false,
+
+  /**
+   * 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
 }
 
-function AddonRepository() {
-}
-
-AddonRepository.prototype = {
+/**
+ * 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 = {
   // The current set of results
-  _addons: null,
+  _results: null,
 
   // 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
+  /*
+   * 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.
+   */
   _callback: null,
 
   // Maximum number of results to return
   _maxResults: null,
 
+  /**
+   * The homepage for visiting this repository. This may be null or an empty
+   * string.
+   */
   get homepageURL() {
-    return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
-                     .getService(Components.interfaces.nsIURLFormatter)
-                     .formatURLPref(PREF_GETADDONS_BROWSEADDONS);
+    return Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+           getService(Ci.nsIURLFormatter).
+           formatURLPref(PREF_GETADDONS_BROWSEADDONS);
   },
 
+  /**
+   * Returns whether this instance is currently performing a search. New
+   * searches will not be performed while this is the case.
+   */
   get isSearching() {
     return this._searching;
   },
 
+  /**
+   * The url that can be visited to see recommended add-ons in this repository.
+   */
   getRecommendedURL: function() {
-    var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
-                         .getService(Components.interfaces.nsIURLFormatter);
+    var urlf = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+               getService(Ci.nsIURLFormatter);
 
     return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED);
   },
 
+  /**
+   * Retrieves the url that can be visited to see search results for the given
+   * terms.
+   *
+   * @param  aSearchTerms  search terms used to search the repository
+   */
   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 prefs = Cc["@mozilla.org/preferences-service;1"].
+                getService(Ci.nsIPrefBranch);
+    var urlf = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+               getService(Ci.nsIURLFormatter);
 
     var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
     url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
     return urlf.formatURL(url);
   },
 
+  /**
+   * Cancels the search in progress. If there is no search in progress this
+   * does nothing.
+   */
   cancelSearch: function() {
     this._searching = false;
     if (this._request) {
       this._request.abort();
       this._request = null;
     }
     this._callback = null;
-    this._addons = null;
+    this._results = null;
   },
 
+  /**
+   * 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
+   */
   retrieveRecommendedAddons: function(aMaxResults, aCallback) {
     if (this._searching)
       return;
 
     this._searching = true;
-    this._addons = [];
+    this._results = [];
     this._callback = aCallback;
     this._recommended = true;
     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);
+    var prefs = Cc["@mozilla.org/preferences-service;1"].
+                getService(Ci.nsIPrefBranch);
+    var urlf = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+               getService(Ci.nsIURLFormatter);
 
     var uri = prefs.getCharPref(PREF_GETADDONS_GETRECOMMENDED);
     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
     uri = urlf.formatURL(uri);
     this._loadList(uri);
   },
 
+  /**
+   * 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
+   */
   searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
     if (this._searching)
       return;
 
     this._searching = true;
-    this._addons = [];
+    this._results = [];
     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);
+    var prefs = Cc["@mozilla.org/preferences-service;1"].
+                getService(Ci.nsIPrefBranch);
+    var urlf = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+               getService(Ci.nsIURLFormatter);
 
     var uri = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
     // We double encode due to bug 427155
     uri = uri.replace(/%TERMS%/g, encodeURIComponent(encodeURIComponent(aSearchTerms)));
     uri = urlf.formatURL(uri);
     this._loadList(uri);
   },
 
   // Posts results to the callback
-  _reportSuccess: function(aCount) {
+  _reportSuccess: function(aTotalResults) {
     this._searching = false;
     this._request = null;
     // The callback may want to trigger a new search so clear references early
-    var addons = this._addons;
+    var addons = [result.addon for each(result in this._results)];
     var callback = this._callback;
     this._callback = null;
-    this._addons = null;
-    callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aCount);
+    this._results = null;
+    callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aTotalResults);
   },
 
   // Notifies the callback of a failure
-  _reportFailure: function(aEvent) {
+  _reportFailure: function() {
     this._searching = false;
     this._request = null;
     // The callback may want to trigger a new search so clear references early
     var callback = this._callback;
     this._callback = null;
-    this._addons = null;
+    this._results = null;
     callback.searchFailed();
   },
 
   // Parses an add-on entry from an <addon> element
-  _parseAddon: function(element, known_ids) {
+  _parseAddon: function(aElement, aSkip) {
     var app = Cc["@mozilla.org/xre/app-info;1"].
               getService(Ci.nsIXULAppInfo).
               QueryInterface(Ci.nsIXULRuntime);
 
-    var guidList = element.getElementsByTagName("guid");
+    var guidList = aElement.getElementsByTagName("guid");
     if (guidList.length != 1)
       return;
 
     var guid = guidList[0].textContent.trim();
 
     // Ignore add-ons already seen in the results
-    for (var i = 0; i < this._addons.length; i++)
-      if (this._addons[i].id == guid)
+    for (var i = 0; i < this._results.length; i++)
+      if (this._results[i].addon.id == guid)
         return;
 
     // Ignore installed add-ons
-    if (known_ids.indexOf(guid) != -1)
+    if (aSkip.ids.indexOf(guid) != -1)
       return;
 
     // Ignore sandboxed add-ons
-    var status = element.getElementsByTagName("status");
+    var status = aElement.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
-    var osList = element.getElementsByTagName("compatible_os");
+    var osList = aElement.getElementsByTagName("compatible_os");
     // Only the version 0 schema included compatible_os if it isn't there then
     // we will see os compatibility on the install elements.
     if (osList.length > 0) {
       var compatible = false;
       var i = 0;
       while (i < osList.length && !compatible) {
         var os = osList[i].textContent.trim();
         if (os == "ALL" || os == app.OS) {
@@ -245,17 +400,17 @@ AddonRepository.prototype = {
         i++;
       }
       if (!compatible)
         return;
     }
 
     // Ignore add-ons not compatible with this Application
     compatible = false;
-    var tags = element.getElementsByTagName("compatible_applications");
+    var tags = aElement.getElementsByTagName("compatible_applications");
     if (tags.length != 1)
       return;
     var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
              getService(Ci.nsIVersionComparator);
     var apps = tags[0].getElementsByTagName("appID");
     var i = 0;
     while (i < apps.length) {
       if (apps[i].textContent.trim() == app.ID) {
@@ -268,125 +423,154 @@ AddonRepository.prototype = {
         compatible = true;
         break;
       }
       i++;
     }
     if (!compatible)
       return;
 
-    var addon = new AddonSearchResult();
-    addon.id = guid;
-    addon.rating = -1;
-    var node = element.firstChild;
+    var addon = new AddonSearchResult(guid);
+    var result = {
+      addon: addon,
+      xpiURL: null,
+      xpiHash: null
+    };
+    var node = aElement.firstChild;
     while (node) {
       if (node instanceof Ci.nsIDOMElement) {
         switch (node.localName) {
           case "name":
           case "version":
+            addon[node.localName] = node.textContent.trim();
+            break;
           case "summary":
+            addon.description = node.textContent.trim();
+            break;
           case "description":
-          case "eula":
-            addon[node.localName] = node.textContent.trim();
+            addon.fullDescription = node.textContent.trim();
             break;
           case "rating":
             if (node.textContent.length > 0) {
               var rating = parseInt(node.textContent);
               if (rating >= 0)
                 addon.rating = Math.min(5, rating);
             }
             break;
           case "thumbnail":
-            addon.thumbnailURL = node.textContent.trim();
+            addon.screenshots.push(node.textContent.trim());
             break;
           case "icon":
             addon.iconURL = node.textContent.trim();
             break;
           case "learnmore":
             addon.homepageURL = node.textContent.trim();
             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)
-              addon.type = Ci.nsIAddonSearchResult.TYPE_THEME;
-            else
-              addon.type = Ci.nsIAddonSearchResult.TYPE_EXTENSION;
+            addon.type = (node.getAttribute("id") == 2) ? "theme" : "extension";
             break;
           case "install":
             // 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;
             }
-            addon.xpiURL = node.textContent.trim();
-            if (node.hasAttribute("hash"))
-              addon.xpiHash = node.getAttribute("hash");
+            result.xpiURL = node.textContent.trim();
+
+            // Ignore add-on installs
+            if (aSkip.sourceURLs.indexOf(result.xpiURL) != -1)
+              return;
+
+            result.xpiHash = node.hasAttribute("hash") ? node.getAttribute("hash") : null;
             break;
         }
       }
       node = node.nextSibling;
     }
 
     // Add only if there was an xpi compatible with this os
-    if (addon.xpiURL)
-      this._addons.push(addon);
+    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);
+    });
   },
 
   // 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;
     }
 
+    var elements = responseXML.documentElement.getElementsByTagName("addon");
+    if (responseXML.documentElement.hasAttribute("total_results"))
+      var totalResults = responseXML.documentElement.getAttribute("total_results");
+    else
+      var totalResults = elements.length;
+
     var self = this;
-    AddonManager.getAllAddons(function(addons) {
-      var known_ids = [a.id for each(a in addons)];
+    var skip = {ids: null, sourceURLs: null};
 
-      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;
-        }
-      }
+    AddonManager.getAllAddons(function(aAddons) {
+      skip.ids  = [a.id for each (a in aAddons)];
+      if (skip.sourceURLs)
+        self._parseAddons(elements, totalResults, skip);
+    });
 
-      if (responseXML.documentElement.hasAttribute("total_results"))
-        self._reportSuccess(responseXML.documentElement.getAttribute("total_results"));
-      else
-        self._reportSuccess(elements.length);
+    AddonManager.getAllInstalls(function(aInstalls) {
+      skip.sourceURLs = [];
+      aInstalls.forEach(function(aInstall) {
+        if (aInstall.state != AddonManager.STATE_AVAILABLE)
+          skip.sourceURLs.push(aInstall.sourceURL);
+      });
+
+      if (skip.ids)
+        self._parseAddons(elements, totalResults, skip);
     });
   },
 
   // Performs a new request for results
   _loadList: function(aURI) {
     this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                     createInstance(Ci.nsIXMLHttpRequest);
     this._request.open("GET", aURI, true);
     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.onerror = function(aEvent) { self._reportFailure(); };
+    this._request.onload = function(aEvent) { self._listLoaded(aEvent); };
     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]);
-}
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -48,33 +48,32 @@ MODULE_NAME    = AddonsModule
 GRE_MODULE     = 1
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 
 XPIDLSRCS = \
   amIInstallTrigger.idl \
   amIWebInstallListener.idl \
   amIWebInstaller.idl \
-  nsIAddonRepository.idl \
   $(NULL)
 
 CPPSRCS		= \
   amInstallTrigger.cpp \
   $(NULL)
 
 EXTRA_PP_COMPONENTS = \
-  nsAddonRepository.js \
   nsBlocklistService.js \
   addonManager.js \
   amContentHandler.js \
   amWebInstallListener.js \
   $(NULL)
 
 EXTRA_PP_JS_MODULES = \
   AddonManager.jsm \
+  AddonRepository.jsm \
   XPIProvider.jsm \
   PluginProvider.jsm \
   AddonUpdateChecker.jsm \
   AddonLogging.jsm \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   LightweightThemeManager.jsm \
deleted file mode 100644
--- a/toolkit/mozapps/extensions/nsIAddonRepository.idl
+++ /dev/null
@@ -1,204 +0,0 @@
-/* -*- Mode: IDL; 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 ***** */
-
-
-#include "nsISupports.idl"
-
-[scriptable, uuid(f81ed0bc-ee98-4edd-bf2d-751b47bf665d)]
-interface nsIAddonSearchResult : nsISupports
-{
-  /**
-   * Values for the type attribute
-   */
-  const unsigned long TYPE_EXTENSION   = 0x02;
-  const unsigned long TYPE_THEME       = 0x04;
-
-  /**
-   * The ID of the add-on
-   */
-  readonly attribute AString id;
-
-  /**
-   * The name of the add-on
-   */
-  readonly attribute AString name;
-
-  /**
-   * The version of the add-on
-   */
-  readonly attribute AString version;
-
-  /**
-   * A short summary of the add-on
-   */
-  readonly attribute AString summary;
-
-  /**
-   * The full description of the add-on
-   */
-  readonly attribute AString description;
-
-  /**
-   * The rating of the add-on, 0-5 or -1 if unrated.
-   */
-  readonly attribute long rating;
-
-  /**
-   * The url of the add-ons icon or empty if there is no icon.
-   */
-  readonly attribute AString iconURL;
-
-  /**
-   * The url of a thumbnail for the add-on
-   */
-  readonly attribute AString thumbnailURL;
-
-  /**
-   * The homepage for the add-on
-   */
-  readonly attribute AString homepageURL;
-
-  /**
-   * A EULA that must be accepted before install.
-   */
-  readonly attribute AString eula;
-
-  /**
-   * The add-on type (see nsIUpdateItem).
-   */
-  readonly attribute unsigned long type;
-
-  /**
-   * The url of the xpi for this add-on
-   */
-  readonly attribute AString xpiURL;
-
-  /**
-   * The hash for the xpi.
-   */
-  readonly attribute AString xpiHash;
-};
-
-[scriptable, uuid(a6f70917-dd30-4eb6-8b3d-453204f96f33)]
-interface nsIAddonSearchResultsCallback : nsISupports
-{
-  /**
-   * 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
-   */
-  void searchSucceeded([array, size_is(aAddonCount)] in nsIAddonSearchResult aAddons,
-                       in unsigned long aAddonCount,
-                       in unsigned long aTotalResults);
-
-  /**
-   * Called when an error occurred when performing a search.
-   */
-  void searchFailed();
-};
-
-/**
- * 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.
- */
-[scriptable, uuid(c4d2ac29-6edc-43cd-8dc8-e4cf213aa1be)]
-interface nsIAddonRepository : nsISupports
-{
-  /**
-   * The homepage for visiting this repository. This may be null or an empty
-   * string.
-   */
-  readonly attribute AString homepageURL;
-
-  /**
-   * Returns whether this instance is currently performing a search. New
-   * searches will not be performed while this is the case.
-   */
-  readonly attribute boolean isSearching;
-
-  /**
-   * The url that can be visited to see recommended add-ons in this repository.
-   */
-  AString getRecommendedURL();
-
-  /**
-   * Retrieves the url that can be visited to see search results for the given
-   * terms.
-   *
-   * @param  aSearchTerms  search terms used to search the repository
-   */
-  AString getSearchURL(in AString aSearchTerms);
-
-  /**
-   * 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
-   */
-  void retrieveRecommendedAddons(in unsigned long aMaxResults,
-                                 in nsIAddonSearchResultsCallback aCallback);
-
-  /**
-   * 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
-   */
-  void searchAddons(in AString aSearchTerms, in unsigned long aMaxResults,
-                    in nsIAddonSearchResultsCallback aCallback);
-
-  /**
-   * Cancels the search in progress. If there is no search in progress this
-   * does nothing.
-   */
-  void cancelSearch();
-};
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug404024.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug404024.js
@@ -31,16 +31,18 @@
  * 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 *****
  */
 
+Components.utils.import("resource://gre/modules/AddonRepository.jsm");
+
 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";
 
 const BROWSE      = "http://localhost:4444/";
 const RECOMMENDED = "http://localhost:4444/recommended.html";
@@ -50,93 +52,103 @@ var BROWSE_SEARCH_URLS = [
   ["test",                              SEARCH + "test" ],
   ["SEARCH",                            SEARCH + "SEARCH" ],
   ["test search",                       SEARCH + "test%20search" ],
   ["odd=search:with&weird\"characters", SEARCH + "odd%3Dsearch%3Awith%26weird%22characters" ]
 ];
 
 do_load_httpd_js();
 var server;
-var addonRepo;
 
 var RESULTS = [
 {
-  id:           "test5@tests.mozilla.org",
-  name:         "PASS",
-  version:      "1.0",
-  summary:      "This should work fine",
-  description:  "Test description",
-  rating:       -1,
-  iconURL:      null,
-  thumbnailURL: null,
-  homepageURL:  "https://addons.mozilla.org/addon/5992",
-  eula:         null,
-  type:         Ci.nsIAddonSearchResult.TYPE_EXTENSION,
-  xpiURL:       "http://localhost:4444/test.xpi",
-  xpiHash:      "sha1:c26f0b0d62e5dcddcda95074d3f3fedb9bbc26e3"
+  id:               "test5@tests.mozilla.org",
+  name:             "PASS",
+  version:          "1.0",
+  description:      "This should work fine",
+  fullDescription:  "Test description",
+  rating:           -1,
+  iconURL:          null,
+  screenshots:      [],
+  homepageURL:      "https://addons.mozilla.org/addon/5992",
+  type:             "extension",
+  sourceURL:        "http://localhost:4444/test.xpi"
 },
 {
-  id:           "test6@tests.mozilla.org",
-  name:         "PASS",
-  version:      "1.0",
-  summary:      "Specific OS should work fine",
-  description:  null,
-  rating:       4,
-  iconURL:      "http://localhost:4444/test_bug404024/icon.png",
-  thumbnailURL: "http://localhost:4444/test_bug404024/thumbnail.png",
-  homepageURL:  null,
-  eula:         "EULA should be confirmed",
-  type:         Ci.nsIAddonSearchResult.TYPE_THEME,
-  xpiURL:       "http://localhost:4444/test.xpi",
-  xpiHash:      null
+  id:               "test6@tests.mozilla.org",
+  name:             "PASS",
+  version:          "1.0",
+  description:      "Specific OS should work fine",
+  fullDescription:  null,
+  rating:           4,
+  iconURL:          "http://localhost:4444/test_bug404024/icon.png",
+  screenshots:      ["http://localhost:4444/test_bug404024/thumbnail.png"],
+  homepageURL:      null,
+  type:             "theme",
+  sourceURL:        "http://localhost:4444/test.xpi"
 }
 ];
 
 function checkResults(addons, length) {
   do_check_eq(addons.length, RESULTS.length);
   do_check_eq(addons.length, length);
 
   for (var i = 0; i < addons.length; i++) {
     if (addons[i].name == "FAIL")
       do_throw(addons[i].id + " - " + addons[i].summary);
     if (addons[i].name != "PASS")
       do_throw(addons[i].id + " - " + "invalid add-on name " + addons[i].name);
   }
 
   for (var i = 0; i < addons.length; i++) {
+    do_check_neq(addons[i]["install"], null);
+
     for (var p in RESULTS[i]) {
+      if (p == "screenshots") {
+        do_check_eq(addons[i][p].length, RESULTS[i][p].length);
+        for (var j = 0; j < addons[i][p].length; j++)
+          do_check_eq(addons[i][p][j], addons[i][p][j]);
+
+        continue;
+      }
+
+      if (p == "sourceURL") {
+        do_check_eq(addons[i]["install"][p], RESULTS[i][p]);
+        continue;
+      }
+
       if (addons[i][p] != RESULTS[i][p])
         do_throw("Failed on property " + p + " on add-on " + addons[i].id +
                  addons[i][p] + " == " + RESULTS[i][p]);
     }
   }
 }
 
 var RecommendedCallback = {
   searchSucceeded: function(addons, length, total) {
     // Search is complete
-    do_check_false(addonRepo.isSearching);
+    do_check_false(AddonRepository.isSearching);
     checkResults(addons, length);
 
     // "search" for the same results
-    addonRepo.searchAddons("bug404024", 10, SearchCallback);
+    AddonRepository.searchAddons("bug404024", 10, SearchCallback);
     // Should be searching now and any attempt to retrieve again should be ignored
-    do_check_true(addonRepo.isSearching);
-    addonRepo.searchAddons("test search", 10, FailCallback);
+    do_check_true(AddonRepository.isSearching);
+    AddonRepository.searchAddons("test search", 10, FailCallback);
   },
 
   searchFailed: function() {
     server.stop(do_test_finished);
     do_throw("Recommended results failed");
   }
 };
 
 var SearchCallback = {
   searchSucceeded: function(addons, length, total) {
-    do_check_false(addonRepo.isSearching);
+    do_check_false(AddonRepository.isSearching);
     checkResults(addons, length);
 
     server.stop(do_test_finished);
   },
 
   searchFailed: function() {
     server.stop(do_test_finished);
     do_throw("Search results failed");
@@ -172,34 +184,31 @@ function run_test()
 
     // Point the addons repository to the test server
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSEADDONS, BROWSE);
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSERECOMMENDED, RECOMMENDED);
     Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, "http://localhost:4444/test_bug404024.xml");
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS, SEARCH + "%TERMS%");
     Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, "http://localhost:4444/test_%TERMS%.xml");
 
-    addonRepo = Components.classes["@mozilla.org/extensions/addon-repository;1"]
-                          .getService(Components.interfaces.nsIAddonRepository);
-
-    do_check_neq(addonRepo, null);
+    do_check_neq(AddonRepository, null);
     // Check the homepage and recommended urls
-    do_check_eq(addonRepo.homepageURL, BROWSE);
-    do_check_eq(addonRepo.getRecommendedURL(), RECOMMENDED);
+    do_check_eq(AddonRepository.homepageURL, BROWSE);
+    do_check_eq(AddonRepository.getRecommendedURL(), RECOMMENDED);
 
     // Check that search urls are correct
     for (var i = 0; i < BROWSE_SEARCH_URLS.length; i++) {
-      var url = addonRepo.getSearchURL(BROWSE_SEARCH_URLS[i][0]);
+      var url = AddonRepository.getSearchURL(BROWSE_SEARCH_URLS[i][0]);
       if (url != BROWSE_SEARCH_URLS[i][1])
         do_throw("BROWSE_SEARCH_URL[" + i + "] returned " + url);
     }
 
     // This should fail because we cancel it immediately.
-    addonRepo.retrieveRecommendedAddons(10, FailCallback);
-    addonRepo.cancelSearch();
+    AddonRepository.retrieveRecommendedAddons(10, FailCallback);
+    AddonRepository.cancelSearch();
     // Pull some results.
-    addonRepo.retrieveRecommendedAddons(10, RecommendedCallback);
+    AddonRepository.retrieveRecommendedAddons(10, RecommendedCallback);
     // Should be searching now and any attempt to retrieve again should be ignored
-    do_check_true(addonRepo.isSearching);
-    addonRepo.retrieveRecommendedAddons(10, FailCallback);
+    do_check_true(AddonRepository.isSearching);
+    AddonRepository.retrieveRecommendedAddons(10, FailCallback);
   });
 }
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug417606.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug417606.js
@@ -31,16 +31,18 @@
  * 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 *****
  */
 
+Components.utils.import("resource://gre/modules/AddonRepository.jsm");
+
 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";
 
 const BROWSE      = "http://localhost:4444/";
 const RECOMMENDED = "http://localhost:4444/recommended.html";
@@ -50,92 +52,102 @@ var BROWSE_SEARCH_URLS = [
   ["test",                              SEARCH + "test" ],
   ["SEARCH",                            SEARCH + "SEARCH" ],
   ["test search",                       SEARCH + "test%20search" ],
   ["odd=search:with&weird\"characters", SEARCH + "odd%3Dsearch%3Awith%26weird%22characters" ]
 ];
 
 do_load_httpd_js();
 var server;
-var addonRepo;
 
 var RESULTS = [
 {
-  id:           "test5@tests.mozilla.org",
-  name:         "PASS",
-  version:      "1.0",
-  summary:      "This should work fine",
-  description:  "Test description",
-  rating:       -1,
-  iconURL:      null,
-  thumbnailURL: null,
-  homepageURL:  "https://addons.mozilla.org/addon/5992",
-  eula:         null,
-  type:         Ci.nsIAddonSearchResult.TYPE_EXTENSION,
-  xpiURL:       "http://localhost:4444/test.xpi",
-  xpiHash:      "sha1:c26f0b0d62e5dcddcda95074d3f3fedb9bbc26e3"
+  id:               "test5@tests.mozilla.org",
+  name:             "PASS",
+  version:          "1.0",
+  description:      "This should work fine",
+  fullDescription:  "Test description",
+  rating:           -1,
+  iconURL:          null,
+  screenshots:      [],
+  homepageURL:      "https://addons.mozilla.org/addon/5992",
+  type:             "extension",
+  sourceURL:        "http://localhost:4444/test.xpi"
 },
 {
-  id:           "test6@tests.mozilla.org",
-  name:         "PASS",
-  version:      "1.0",
-  summary:      "Specific OS should work fine",
-  description:  null,
-  rating:       4,
-  iconURL:      "http://localhost:4444/test_bug404024/icon.png",
-  thumbnailURL: "http://localhost:4444/test_bug404024/thumbnail.png",
-  homepageURL:  null,
-  eula:         "EULA should be confirmed",
-  type:         Ci.nsIAddonSearchResult.TYPE_THEME,
-  xpiURL:       "http://localhost:4444/XPCShell.xpi",
-  xpiHash:      null
+  id:               "test6@tests.mozilla.org",
+  name:             "PASS",
+  version:          "1.0",
+  description:      "Specific OS should work fine",
+  fullDescription:  null,
+  rating:           4,
+  iconURL:          "http://localhost:4444/test_bug404024/icon.png",
+  screenshots:      ["http://localhost:4444/test_bug404024/thumbnail.png"],
+  homepageURL:      null,
+  type:             "theme",
+  sourceURL:        "http://localhost:4444/XPCShell.xpi"
 }
 ];
 
 function checkResults(addons) {
   do_check_eq(addons.length, RESULTS.length);
   
   for (var i = 0; i < addons.length; i++) {
     if (addons[i].name == "FAIL")
       do_throw(addons[i].id + " - " + addons[i].summary);
     if (addons[i].name != "PASS")
       do_throw(addons[i].id + " - " + "invalid add-on name " + addons[i].name);
   }
 
   for (var i = 0; i < addons.length; i++) {
+    do_check_neq(addons[i]["install"], null);
+
     for (var p in RESULTS[i]) {
+      if (p == "screenshots") {
+        do_check_eq(addons[i][p].length, RESULTS[i][p].length);
+        for (var j = 0; j < addons[i][p].length; j++)
+          do_check_eq(addons[i][p][j], addons[i][p][j]);
+
+        continue;
+      }
+
+      if (p == "sourceURL") {
+        do_check_eq(addons[i]["install"][p], RESULTS[i][p]);
+        continue;
+      }
+
       if (addons[i][p] != RESULTS[i][p])
         do_throw("Failed on property " + p + " on add-on " + addons[i].id +
                  addons[i][p] + " == " + RESULTS[i][p]);
     }
   }
 }
 
 var RecommendedCallback = {
   searchSucceeded: function(addons, length, total) {
     // Search is complete
-    do_check_false(addonRepo.isSearching);
+    do_check_false(AddonRepository.isSearching);
     checkResults(addons);
 
     // "search" for the same results
-    addonRepo.searchAddons("bug417606", 10, SearchCallback);
+    AddonRepository.searchAddons("bug417606", 10, SearchCallback);
     // Should be searching now and any attempt to retrieve again should be ignored
-    do_check_true(addonRepo.isSearching);
-    addonRepo.searchAddons("test search", 10, FailCallback);
+    do_check_true(AddonRepository.isSearching);
+    AddonRepository.searchAddons("test search", 10, FailCallback);
   },
 
   searchFailed: function() {
     server.stop(do_test_finished);
     do_throw("Recommended results failed");
   }
 };
 
 var SearchCallback = {
   searchSucceeded: function(addons, length, total) {
-    do_check_false(addonRepo.isSearching);
+    do_check_false(AddonRepository.isSearching);
     do_check_eq(total, 100);
     checkResults(addons);
 
     server.stop(do_test_finished);
   },
 
   searchFailed: function() {
     server.stop(do_test_finished);
@@ -172,34 +184,31 @@ function run_test()
 
     // Point the addons repository to the test server
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSEADDONS, BROWSE);
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSERECOMMENDED, RECOMMENDED);
     Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, "http://localhost:4444/test_bug417606.xml");
     Services.prefs.setCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS, SEARCH + "%TERMS%");
     Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, "http://localhost:4444/test_%TERMS%.xml");
 
-    addonRepo = Components.classes["@mozilla.org/extensions/addon-repository;1"]
-                          .getService(Components.interfaces.nsIAddonRepository);
-
-    do_check_neq(addonRepo, null);
+    do_check_neq(AddonRepository, null);
     // Check the homepage and recommended urls
-    do_check_eq(addonRepo.homepageURL, BROWSE);
-    do_check_eq(addonRepo.getRecommendedURL(), RECOMMENDED);
+    do_check_eq(AddonRepository.homepageURL, BROWSE);
+    do_check_eq(AddonRepository.getRecommendedURL(), RECOMMENDED);
 
     // Check that search urls are correct
     for (var i = 0; i < BROWSE_SEARCH_URLS.length; i++) {
-      var url = addonRepo.getSearchURL(BROWSE_SEARCH_URLS[i][0]);
+      var url = AddonRepository.getSearchURL(BROWSE_SEARCH_URLS[i][0]);
       if (url != BROWSE_SEARCH_URLS[i][1])
         do_throw("BROWSE_SEARCH_URL[" + i + "] returned " + url);
     }
 
     // This should fail because we cancel it immediately.
-    addonRepo.retrieveRecommendedAddons(10, FailCallback);
-    addonRepo.cancelSearch();
+    AddonRepository.retrieveRecommendedAddons(10, FailCallback);
+    AddonRepository.cancelSearch();
     // Pull some results.
-    addonRepo.retrieveRecommendedAddons(10, RecommendedCallback);
+    AddonRepository.retrieveRecommendedAddons(10, RecommendedCallback);
     // Should be searching now and any attempt to retrieve again should be ignored
-    do_check_true(addonRepo.isSearching);
-    addonRepo.retrieveRecommendedAddons(10, FailCallback);
+    do_check_true(AddonRepository.isSearching);
+    AddonRepository.retrieveRecommendedAddons(10, FailCallback);
   });
 }
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug424262.js
@@ -30,23 +30,22 @@
  * 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 *****
  */
+Components.utils.import("resource://gre/modules/AddonRepository.jsm");
 
 const PREF_GETADDONS_GETRECOMMENDED      = "extensions.getAddons.recommended.url";
 
 do_load_httpd_js();
 var server;
-var addonRepo;
-
 var RESULTS = [
   -1,
   -1,
   0,
   2,
   4,
   5,
   5,
@@ -80,18 +79,15 @@ function run_test()
 
   server = new nsHttpServer();
   server.registerDirectory("/", do_get_file("data"));
   server.start(4444);
 
   // Point the addons repository to the test server
   Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, "http://localhost:4444/test_bug424262.xml");
   
-  addonRepo = Components.classes["@mozilla.org/extensions/addon-repository;1"]
-                        .getService(Components.interfaces.nsIAddonRepository);
-
-  do_check_neq(addonRepo, null);
+  do_check_neq(AddonRepository, null);
 
   do_test_pending();
   // Pull some results.
-  addonRepo.retrieveRecommendedAddons(RESULTS.length, RecommendedCallback);
+  AddonRepository.retrieveRecommendedAddons(RESULTS.length, RecommendedCallback);
 }