Bug 404024: Add AMO integration pane. r=robstrong
authordtownsend@oxymoronical.com
Mon, 28 Jan 2008 09:11:48 -0800
changeset 10827 033a35620a04b28e0df5c8f21a325269bcc60795
parent 10826 f8ec442314c816971efd7c386d76b1f2d2c3bcbf
child 10828 85d832500dcae5899bd757fd438ac19a9e2377cc
push idunknown
push userunknown
push dateunknown
reviewersrobstrong
bugs404024
milestone1.9b3pre
Bug 404024: Add AMO integration pane. r=robstrong
browser/app/profile/firefox.js
toolkit/mozapps/extensions/content/eula.js
toolkit/mozapps/extensions/content/eula.xul
toolkit/mozapps/extensions/content/extensions.css
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/content/extensions.xul
toolkit/mozapps/extensions/jar.mn
toolkit/mozapps/extensions/public/Makefile.in
toolkit/mozapps/extensions/public/nsIAddonRepository.idl
toolkit/mozapps/extensions/src/Makefile.in
toolkit/mozapps/extensions/src/nsAddonRepository.js
toolkit/mozapps/extensions/src/nsExtensionManager.js.in
toolkit/themes/gnomestripe/mozapps/extensions/extensions.css
toolkit/themes/pinstripe/mozapps/extensions/eula.css
toolkit/themes/pinstripe/mozapps/extensions/extensionIcons.png
toolkit/themes/pinstripe/mozapps/extensions/extensions.css
toolkit/themes/pinstripe/mozapps/extensions/ratings.png
toolkit/themes/pinstripe/mozapps/extensions/searchIcons.png
toolkit/themes/pinstripe/mozapps/jar.mn
toolkit/themes/winstripe/mozapps/extensions/eula.css
toolkit/themes/winstripe/mozapps/extensions/extensionIcons.png
toolkit/themes/winstripe/mozapps/extensions/extensions.css
toolkit/themes/winstripe/mozapps/extensions/ratings.png
toolkit/themes/winstripe/mozapps/extensions/searchIcons.png
toolkit/themes/winstripe/mozapps/extensions/viewButtons.png
toolkit/themes/winstripe/mozapps/jar.mn
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -63,16 +63,25 @@ pref("xpinstall.dialog.progress.type.chr
 // their extensions are being updated and thus reregistered every time the app is
 // started.
 pref("extensions.ignoreMTimeChanges", false);
 // Enables some extra Extension System Logging (can reduce performance)
 pref("extensions.logging.enabled", false);
 // Hides the install button in the add-ons mgr
 pref("extensions.hideInstallButton", true);
 
+// Preferences for the Get Add-ons pane
+pref("extensions.getAddons.showPane", true);
+pref("extensions.getAddons.browseAddons", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%");
+pref("extensions.getAddons.maxResults", 5);
+pref("extensions.getAddons.recommended.browseURL", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%/recommended");
+pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/list/featured/all/10");
+pref("extensions.getAddons.search.browseURL", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%/search?q=%TERMS%");
+pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/search/%TERMS%");
+
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/1/%APP_ID%/%APP_VERSION%/");
 pref("extensions.blocklist.detailsURL", "http://%LOCALE%.www.mozilla.com/%LOCALE%/blocklist/");
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/firefox/%VERSION%/dictionaries/");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/eula.js
@@ -0,0 +1,43 @@
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# ***** 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 *****
+
+function Startup() {
+  var bundle = document.getElementById("extensionsStrings");
+  var label = document.createTextNode(bundle.getFormattedString("eulaHeader", [window.arguments[0].name]));
+  document.getElementById("heading").appendChild(label);
+  document.getElementById("eula").appendChild(document.createTextNode(window.arguments[0].text));
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/eula.xul
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# ***** 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 *****
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/extensions/eula.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+%extensionsDTD;
+]>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="&eula.title;" width="&eula.width;" height="&eula.height;"
+        buttons="accept,cancel" buttonlabelaccept="&eula.accept;"
+        ondialogaccept="window.arguments[0].accepted = true"
+        onload="Startup();">
+
+  <script type="application/javascript" src="chrome://mozapps/content/extensions/eula.js"/>
+  
+  <stringbundleset id="extensionsSet">
+    <stringbundle id="extensionsStrings" src="chrome://mozapps/locale/extensions/extensions.properties"/>
+  </stringbundleset>
+
+  <label id="heading"/>
+
+  <vbox id="scrollbox" flex="1">
+    <description id="eula"/>
+  </vbox>
+
+</dialog>
--- a/toolkit/mozapps/extensions/content/extensions.css
+++ b/toolkit/mozapps/extensions/content/extensions.css
@@ -5,16 +5,56 @@ richlistbox#extensionsView {
 richlistitem {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon");
 }
 
 richlistitem[selected="true"] {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-selected");
 }
 
+richlistitem[typeName="searchResult"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#searchresult");
+}
+
+richlistitem[typeName="searchResult"][selected="true"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#searchresult-selected");
+}
+
+vbox[typeName="status"][type="search-failure"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-search-failure");
+}
+
+vbox[typeName="status"][type="recommended-failure"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-recommended-failure");
+}
+
+vbox[typeName="status"][type="header-recommended"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-header-recommended");
+}
+
+vbox[typeName="status"][type="footer-recommended"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-recommended");
+}
+
+vbox[typeName="status"][type="footer-search"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-search");
+}
+
+vbox[typeName="status"][type="footer-search"][count="0"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-search-empty");
+}
+
+vbox[typeName="status"][type="retrieve-search"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-retrieve-search");
+}
+
+vbox[typeName="status"][type="retrieve-recommended"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-retrieve-recommended");
+}
+
 richlistitem[typeName="update"] {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#update-found");
 }
 
 richlistitem[updateStatus] {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#update-checking");
 }
 
@@ -94,16 +134,20 @@ richlistitem[opType="needs-enable"] hbox
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-needs-enable");
 }
 
 richlistitem[opType="needs-disable"] hbox.addon-optype,
 richlistitem[opType="needs-disable"] hbox.addon-description {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-needs-disable");
 }
 
+#searchbox {
+  -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#search-textbox");
+}
+
 #viewGroup radio {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#viewbutton");
   -moz-box-orient: vertical;
   -moz-box-align: center;
   -moz-appearance: none;
 }
 
 /* Hide / Display buttons depending on the state of the add-on */
@@ -143,8 +187,41 @@ richlistitem[type="4"] .disableShow,
 richlistitem[type="4"] .disableHide {
   display: none;
 }
 
 richlistitem:not([plugin]) .pluginIcon ,
 richlistitem[plugin] .addonIcon:not(.pluginIcon) {
   display: none;
 }
+
+richlistitem[action] .addonInstallButton {
+  display: none;
+}
+
+.searchResultInstalling, .searchResultFailed, .searchResultInstalled {
+  display: none;
+}
+
+richlistitem[action="installing"] .searchResultInstalling {
+  display: -moz-box;
+}
+
+richlistitem[action="failed"] .searchResultFailed,
+richlistitem[action="failed"] .addonInstallButton {
+  display: -moz-box;
+}
+
+richlistitem[action="installed"] .searchResultInstalled {
+  display: -moz-box;
+}
+
+richlistitem .addonType {
+  display: none;
+}
+
+richlistitem[addonType="2"] .addonTypeExtension {
+  display: -moz-box;
+}
+
+richlistitem[addonType="4"] .addonTypeTheme {
+  display: -moz-box;
+}
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -62,28 +62,38 @@ var gCheckUpdateSecurity = true;
 var gUpdatesOnly      = false;
 var gAppID            = "";
 var gPref             = null;
 var gPriorityCount    = 0;
 var gInstallCount     = 0;
 var gPendingActions   = false;
 var gPlugins          = null;
 var gPluginsDS        = null;
+var gSearchDS         = null;
+var gAddonRepository  = null;
+var gShowGetAddonsPane = false;
+var gRetrievedResults = false;
+var gRecommendedAddons = null;
+var gRDF              = null;
+var gPendingInstalls  = [];
 
 const PREF_EM_CHECK_COMPATIBILITY           = "extensions.checkCompatibility";
 const PREF_EM_CHECK_UPDATE_SECURITY         = "extensions.checkUpdateSecurity";
 const PREF_EXTENSIONS_GETMORETHEMESURL      = "extensions.getMoreThemesURL";
 const PREF_EXTENSIONS_GETMOREEXTENSIONSURL  = "extensions.getMoreExtensionsURL";
 const PREF_EXTENSIONS_GETMOREPLUGINSURL     = "extensions.getMorePluginsURL";
 const PREF_EXTENSIONS_DSS_ENABLED           = "extensions.dss.enabled";
 const PREF_EXTENSIONS_DSS_SWITCHPENDING     = "extensions.dss.switchPending";
 const PREF_EXTENSIONS_HIDE_INSTALL_BTN      = "extensions.hideInstallButton";
 const PREF_EM_LAST_SELECTED_SKIN            = "extensions.lastSelectedSkin";
 const PREF_GENERAL_SKINS_SELECTEDSKIN       = "general.skins.selectedSkin";
 const PREF_UPDATE_NOTIFYUSER                = "extensions.update.notifyUser";
+const PREF_GETADDONS_SHOWPANE               = "extensions.getAddons.showPane";
+const PREF_GETADDONS_REPOSITORY             = "extensions.getAddons.repository";
+const PREF_GETADDONS_MAXRESULTS             = "extensions.getAddons.maxResults";
 
 const URI_GENERIC_ICON_XPINSTALL      = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
 const URI_GENERIC_ICON_THEME          = "chrome://mozapps/skin/extensions/themeGeneric.png";
 
 const RDFURI_ITEM_ROOT    = "urn:mozilla:item:root";
 const PREFIX_ITEM_URI     = "urn:mozilla:item:";
 const PREFIX_NS_EM        = "http://www.mozilla.org/2004/em-rdf#";
 const kXULNSURI           = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@@ -103,16 +113,24 @@ function setElementDisabledByID(aID, aDo
   if (element) {
     if (aDoDisable)
       element.setAttribute("disabled", "true");
     else
       element.removeAttribute("disabled");
   }
 }
 
+function isSafeURI(aURI) {
+  try {
+    var uri = makeURI(aURI);
+    var scheme = uri.scheme;
+  } catch (ex) {}
+  return (uri && (scheme == "http" || scheme == "https" || scheme == "ftp"));
+}
+
 function getBrandShortName() {
   var brandStrings = document.getElementById("brandStrings");
   return brandStrings.getString("brandShortName");
 }
 
 function getExtensionString(key, strings) {
   if (strings)
     return gExtensionStrings.getFormattedString(key, strings);
@@ -153,43 +171,37 @@ function showMessage(aIconURL, aMessage,
     gPriorityCount = 0;
   addonsMsg.appendNotification(aMessage, aMessage, aIconURL,
                                addonsMsg.PRIORITY_WARNING_LOW + gPriorityCount,
                                buttons).hideclose = !aShowCloseButton;
 }
 
 // dynamically creates a template
 var AddonsViewBuilder = {
-  _bindingList: null,
-  _actionList: null,
-
   // if aActionList is null aBindingList will be used to generate actions
-  updateView: function(aRulesList, aURI, aBindingList, aActionList)
+  updateView: function(aRulesList, aURIList, aBindingList, aActionList)
   {
-    this._bindingList = aBindingList;
-    this._actionList = aActionList ? aActionList : aBindingList;
+    var actionList = aActionList ? aActionList : aBindingList;
 
     this.clearChildren(gExtensionsView);
     var template = document.createElementNS(kXULNSURI, "template");
     gExtensionsView.appendChild(template);
     for (var i = 0; i < aRulesList.length; ++i)
-      template.appendChild(this.createRule(aRulesList[i], aURI));
+      template.appendChild(this.createRule(aRulesList[i], aURIList[i], aBindingList[i], actionList[i]));
 
-    this._bindingList = null;
-    this._actionList = null;
     gExtensionsView.builder.rebuild();
   },
 
   clearChildren: function(aEl)
   {
     while (aEl.hasChildNodes())
       aEl.removeChild(aEl.lastChild);
   },
 
-  createRule: function(aTriplesList, aURI)
+  createRule: function(aTriplesList, aURI, aBindingList, aActionList)
   {
     var rule = document.createElementNS(kXULNSURI, "rule");
     var conditions = document.createElementNS(kXULNSURI, "conditions");
     rule.appendChild(conditions);
 
     var content = document.createElementNS(kXULNSURI, "content");
     content.setAttribute("uri", "?uri");
     conditions.appendChild(content);
@@ -197,26 +209,26 @@ var AddonsViewBuilder = {
     var member = this.createMember("?uri", "?" + aURI);
     conditions.appendChild(member);
 
     for (var i = 0; i < aTriplesList.length; ++i)
       conditions.appendChild(this.createTriple("?" + aURI, PREFIX_NS_EM + aTriplesList[i][0], aTriplesList[i][1], aTriplesList[i][2]));
 
     var bindings = document.createElementNS(kXULNSURI, "bindings");
     rule.appendChild(bindings);
-    for (i = 0; i < this._bindingList.length; ++i)
-      bindings.appendChild(this.createBinding(this._bindingList[i], aURI));
+    for (i = 0; i < aBindingList.length; ++i)
+      bindings.appendChild(this.createBinding(aBindingList[i], aURI));
 
     var action = document.createElementNS(kXULNSURI, "action");
     rule.appendChild(action);
     var extension = document.createElementNS(kXULNSURI, aURI);
     action.appendChild(extension);
     extension.setAttribute("uri", "?" + aURI);
-    for (i = 0; i < this._actionList.length; ++i)
-      extension.setAttribute(this._actionList[i][0], this._actionList[i][1]);
+    for (i = 0; i < aActionList.length; ++i)
+      extension.setAttribute(aActionList[i][0], aActionList[i][1]);
 
     return rule;
   },
 
   createTriple: function(aSubject, aPredicate, aObject, aParseType)
   {
     var triple = document.createElementNS(kXULNSURI, "triple");
     triple.setAttribute("subject", aSubject);
@@ -251,56 +263,83 @@ function showView(aView) {
     return;
 
   updateLastSelected(aView);
   gView = aView;
 
   // Using disabled to represent add-on state in regards to the EM causes evil
   // focus behavior when used as an element attribute when the element isn't
   // really disabled.
-  var bindingList = [ ["aboutURL", "?aboutURL"],
-                      ["addonID", "?addonID"],
-                      ["availableUpdateURL", "?availableUpdateURL"],
-                      ["availableUpdateVersion", "?availableUpdateVersion"],
-                      ["blocklisted", "?blocklisted"],
-                      ["compatible", "?compatible"],
-                      ["description", "?description"],
-                      ["downloadURL", "?downloadURL"],
-                      ["isDisabled", "?isDisabled"],
-                      ["hidden", "?hidden"],
-                      ["homepageURL", "?homepageURL"],
-                      ["iconURL", "?iconURL"],
-                      ["internalName", "?internalName"],
-                      ["locked", "?locked"],
-                      ["name", "?name"],
-                      ["optionsURL", "?optionsURL"],
-                      ["opType", "?opType"],
-                      ["plugin", "?plugin"],
-                      ["previewImage", "?previewImage"],
-                      ["satisfiesDependencies", "?satisfiesDependencies"],
-                      ["providesUpdatesSecurely", "?providesUpdatesSecurely"],
-                      ["type", "?type"],
-                      ["updateable", "?updateable"],
-                      ["updateURL", "?updateURL"],
-                      ["version", "?version"] ];
+  var bindingList = [ [ ["aboutURL", "?aboutURL"],
+                        ["addonID", "?addonID"],
+                        ["availableUpdateURL", "?availableUpdateURL"],
+                        ["availableUpdateVersion", "?availableUpdateVersion"],
+                        ["blocklisted", "?blocklisted"],
+                        ["compatible", "?compatible"],
+                        ["description", "?description"],
+                        ["downloadURL", "?downloadURL"],
+                        ["isDisabled", "?isDisabled"],
+                        ["hidden", "?hidden"],
+                        ["homepageURL", "?homepageURL"],
+                        ["iconURL", "?iconURL"],
+                        ["internalName", "?internalName"],
+                        ["locked", "?locked"],
+                        ["name", "?name"],
+                        ["optionsURL", "?optionsURL"],
+                        ["opType", "?opType"],
+                        ["plugin", "?plugin"],
+                        ["previewImage", "?previewImage"],
+                        ["satisfiesDependencies", "?satisfiesDependencies"],
+                        ["providesUpdatesSecurely", "?providesUpdatesSecurely"],
+                        ["type", "?type"],
+                        ["updateable", "?updateable"],
+                        ["updateURL", "?updateURL"],
+                        ["version", "?version"] ] ];
+  var displays = [ "richlistitem" ];
 
   var prefURL;
   var showInstallFile = true;
   try {
     showInstallFile = !gPref.getBoolPref(PREF_EXTENSIONS_HIDE_INSTALL_BTN);
   }
   catch (e) { }
   var showCheckUpdatesAll = true;
   var showInstallUpdatesAll = false;
   var showSkip = false;
   var showContinue = false;
   switch (aView) {
+    case "search":
+      var bindingList = [ [ ["action", "?action"],
+                            ["addonID", "?addonID"],
+                            ["description", "?description"],
+                            ["eula", "?eula"],
+                            ["homepageURL", "?homepageURL"],
+                            ["iconURL", "?iconURL"],
+                            ["name", "?name"],
+                            ["previewImage", "?previewImage"],
+                            ["rating", "?rating"],
+                            ["addonType", "?addonType"],
+                            ["thumbnailURL", "?thumbnailURL"],
+                            ["version", "?version"],
+                            ["xpiHash", "?xpiHash"],
+                            ["xpiURL", "?xpiURL"],
+                            ["typeName", "searchResult"] ],
+                          [ ["type", "?type"],
+                            ["typeName", "status"],
+                            ["count", "?count"],
+                            ["link", "?link" ] ] ];
+      var types = [ [ ["searchResult", "true", null] ],
+                    [ ["statusMessage", "true", null] ] ];
+      var displays = [ "richlistitem", "vbox" ];
+      showCheckUpdatesAll = false;
+      document.getElementById("searchbox").disabled = isOffline("offlineSearchMsg");
+      break;
     case "extensions":
       prefURL = PREF_EXTENSIONS_GETMOREEXTENSIONSURL;
-      var types = [ [ ["type", nsIUpdateItem.TYPE_EXTENSION, "Integer"] ] ];
+      types = [ [ ["type", nsIUpdateItem.TYPE_EXTENSION, "Integer"] ] ];
       break;
     case "themes":
       prefURL = PREF_EXTENSIONS_GETMORETHEMESURL;
       types = [ [ ["type", nsIUpdateItem.TYPE_THEME, "Integer"] ] ];
       break;
     case "locales":
       types = [ [ ["type", nsIUpdateItem.TYPE_LOCALE, "Integer"] ] ];
       break;
@@ -310,78 +349,78 @@ function showView(aView) {
       break;
     case "updates":
       document.getElementById("updates-view").hidden = false;
       showInstallFile = false;
       showCheckUpdatesAll = false;
       showInstallUpdatesAll = true;
       if (gUpdatesOnly)
         showSkip = true;
-      bindingList = [ ["aboutURL", "?aboutURL"],
-                      ["availableUpdateURL", "?availableUpdateURL"],
-                      ["availableUpdateVersion", "?availableUpdateVersion"],
-                      ["availableUpdateInfo", "?availableUpdateInfo"],
-                      ["blocklisted", "?blocklisted"],
-                      ["homepageURL", "?homepageURL"],
-                      ["iconURL", "?iconURL"],
-                      ["internalName", "?internalName"],
-                      ["locked", "?locked"],
-                      ["name", "?name"],
-                      ["opType", "?opType"],
-                      ["previewImage", "?previewImage"],
-                      ["satisfiesDependencies", "?satisfiesDependencies"],
-                      ["providesUpdatesSecurely", "?providesUpdatesSecurely"],
-                      ["type", "?type"],
-                      ["updateURL", "?updateURL"],
-                      ["version", "?version"],
-                      ["typeName", "update"] ];
+      bindingList = [ [ ["aboutURL", "?aboutURL"],
+                        ["availableUpdateURL", "?availableUpdateURL"],
+                        ["availableUpdateVersion", "?availableUpdateVersion"],
+                        ["availableUpdateInfo", "?availableUpdateInfo"],
+                        ["blocklisted", "?blocklisted"],
+                        ["homepageURL", "?homepageURL"],
+                        ["iconURL", "?iconURL"],
+                        ["internalName", "?internalName"],
+                        ["locked", "?locked"],
+                        ["name", "?name"],
+                        ["opType", "?opType"],
+                        ["previewImage", "?previewImage"],
+                        ["satisfiesDependencies", "?satisfiesDependencies"],
+                        ["providesUpdatesSecurely", "?providesUpdatesSecurely"],
+                        ["type", "?type"],
+                        ["updateURL", "?updateURL"],
+                        ["version", "?version"],
+                        ["typeName", "update"] ] ];
       types = [ [ ["availableUpdateVersion", "?availableUpdateVersion", null],
                   ["updateable", "true", null] ] ];
       break;
     case "installs":
       document.getElementById("installs-view").hidden = false;
       showInstallFile = false;
       showCheckUpdatesAll = false;
       showInstallUpdatesAll = false;
       if (gUpdatesOnly)
         showContinue = true;
-      bindingList = [ ["aboutURL", "?aboutURL"],
-                      ["addonID", "?addonID"],
-                      ["availableUpdateURL", "?availableUpdateURL"],
-                      ["availableUpdateVersion", "?availableUpdateVersion"],
-                      ["blocklisted", "?blocklisted"],
-                      ["compatible", "?compatible"],
-                      ["description", "?description"],
-                      ["downloadURL", "?downloadURL"],
-                      ["incompatibleUpdate", "?incompatibleUpdate"],
-                      ["isDisabled", "?isDisabled"],
-                      ["hidden", "?hidden"],
-                      ["homepageURL", "?homepageURL"],
-                      ["iconURL", "?iconURL"],
-                      ["internalName", "?internalName"],
-                      ["locked", "?locked"],
-                      ["name", "?name"],
-                      ["optionsURL", "?optionsURL"],
-                      ["opType", "?opType"],
-                      ["previewImage", "?previewImage"],
-                      ["progress", "?progress"],
-                      ["state", "?state"],
-                      ["type", "?type"],
-                      ["updateable", "?updateable"],
-                      ["updateURL", "?updateURL"],
-                      ["version", "?version"],
-                      ["newVersion", "?newVersion"],
-                      ["typeName", "install"] ];
+      bindingList = [ [ ["aboutURL", "?aboutURL"],
+                        ["addonID", "?addonID"],
+                        ["availableUpdateURL", "?availableUpdateURL"],
+                        ["availableUpdateVersion", "?availableUpdateVersion"],
+                        ["blocklisted", "?blocklisted"],
+                        ["compatible", "?compatible"],
+                        ["description", "?description"],
+                        ["downloadURL", "?downloadURL"],
+                        ["incompatibleUpdate", "?incompatibleUpdate"],
+                        ["isDisabled", "?isDisabled"],
+                        ["hidden", "?hidden"],
+                        ["homepageURL", "?homepageURL"],
+                        ["iconURL", "?iconURL"],
+                        ["internalName", "?internalName"],
+                        ["locked", "?locked"],
+                        ["name", "?name"],
+                        ["optionsURL", "?optionsURL"],
+                        ["opType", "?opType"],
+                        ["previewImage", "?previewImage"],
+                        ["progress", "?progress"],
+                        ["state", "?state"],
+                        ["type", "?type"],
+                        ["updateable", "?updateable"],
+                        ["updateURL", "?updateURL"],
+                        ["version", "?version"],
+                        ["newVersion", "?newVersion"],
+                        ["typeName", "install"] ] ];
       types = [ [ ["state", "?state", null] ] ];
       break;
   }
 
   var showGetMore = false;
   var getMore = document.getElementById("getMore");
-  if (prefURL) {
+  if (prefURL && !gShowGetAddonsPane) {
     try {
       getMore.setAttribute("value", getMore.getAttribute("value" + aView));
       var getMoreURL = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                                  .getService(Components.interfaces.nsIURLFormatter)
                                  .formatURLPref(prefURL);
       getMore.setAttribute("getMoreURL", getMoreURL);
       showGetMore = getMoreURL == "about:blank" ? false : true;
     }
@@ -404,18 +443,19 @@ function showView(aView) {
   document.getElementById("checkUpdatesAllButton").hidden = !showCheckUpdatesAll;
   document.getElementById("installUpdatesAllButton").hidden = !showInstallUpdatesAll;
   document.getElementById("skipDialogButton").hidden = !showSkip;
   document.getElementById("continueDialogButton").hidden = !showContinue;
   document.getElementById("themePreviewArea").hidden = !isThemes;
   document.getElementById("themeSplitter").hidden = !isThemes;
   document.getElementById("showUpdateInfoButton").hidden = aView != "updates";
   document.getElementById("hideUpdateInfoButton").hidden = true;
+  document.getElementById("searchPanel").hidden = aView != "search";
 
-  AddonsViewBuilder.updateView(types, "richlistitem", bindingList, null);
+  AddonsViewBuilder.updateView(types, displays, bindingList, null);
 
   if (aView == "updates" || aView == "installs")
     gExtensionsView.selectedItem = gExtensionsView.children[0];
 
   if (showSkip) {
     var button = document.getElementById("installUpdatesAllButton");
     button.setAttribute("default", "true");
     window.setTimeout(function () { button.focus(); }, 0);
@@ -519,45 +559,299 @@ function setRestartMessage(aItem)
       item.setAttribute("description", item.getAttribute("oldDescription"));
       item.removeAttribute("oldDescription");
     }
   }
   aItem.setAttribute("oldDescription", aItem.getAttribute("description"));
   aItem.setAttribute("description", restartMessage);
 }
 
+// Removes any assertions in the datasource about a given resource
+function cleanResource(ds, resource) {
+  // Remove outward arcs
+  var arcs = ds.ArcLabelsOut(resource);
+  while (arcs.hasMoreElements()) {
+    var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
+    var targets = ds.GetTargets(resource, arc, true);
+    while (targets.hasMoreElements()) {
+      var value = targets.getNext().QueryInterface(Components.interfaces.nsIRDFNode);
+      if (value)
+        ds.Unassert(resource, arc, value);
+    }
+  }
+}
+
+// Wipes the datasource clean of assertions
+function cleanDataSource(ds, rootctr) {
+  // Remove old entries from the list
+  var nodes = rootctr.GetElements();
+  while (nodes.hasMoreElements()) {
+    var node = nodes.getNext()
+                    .QueryInterface(Components.interfaces.nsIRDFResource);
+    rootctr.RemoveElement(node, false);
+    cleanResource(ds, node);
+  }
+}
+
+// Displays the search status message
+function displaySearchThrobber(aKey) {
+  var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"]
+                        .getService(Components.interfaces.nsIRDFContainerUtils);
+  var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT));
+
+  cleanDataSource(gSearchDS, rootctr);
+
+  var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header");
+  rootctr.AppendElement(labelNode);
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "statusMessage"),
+                   gRDF.GetLiteral("true"),
+                   true);
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "type"),
+                   gRDF.GetLiteral(aKey),
+                   true);
+}
+
+// Clears the search box and updates the result list
+function resetSearch() {
+  var searchbox = document.getElementById("searchbox");
+  searchbox.value = "";
+  retrieveRepositoryAddons("");
+}
+
+// Searches for results
+function retrieveRepositoryAddons(aTerms) {
+  if (gAddonRepository.isSearching)
+    gAddonRepository.cancelSearch();
+  if (aTerms) {
+    displaySearchThrobber("retrieve-search");
+    gAddonRepository.searchAddons(aTerms,
+                                  gPref.getIntPref(PREF_GETADDONS_MAXRESULTS),
+                                  AddonSearchResults);
+  }
+  else {
+    if (gRecommendedAddons) {
+      displaySearchResults(gRecommendedAddons, -1, true);
+    }
+    else {
+      displaySearchThrobber("retrieve-recommended");
+      gAddonRepository.retrieveRecommendedAddons(gPref.getIntPref(PREF_GETADDONS_MAXRESULTS),
+                                                 RecommendedSearchResults);
+    }
+  }
+  gRetrievedResults = true;
+}
+
+// Puts search results into the search datasource
+function displaySearchResults(addons, count, isRecommended) {
+  var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"]
+                        .getService(Components.interfaces.nsIRDFContainerUtils);
+  var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT));
+
+  gSearchDS.beginUpdateBatch();
+
+  cleanDataSource(gSearchDS, rootctr);
+
+  if (isRecommended) {
+    var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header");
+    rootctr.AppendElement(labelNode);
+    gSearchDS.Assert(labelNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "statusMessage"),
+                     gRDF.GetLiteral("true"),
+                     true);
+    gSearchDS.Assert(labelNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "type"),
+                     gRDF.GetLiteral("header-recommended"),
+                     true);
+
+    // Case insensitive sort
+    function compare(a, b) {
+      if (a.name.toLowerCase() < b.name.toLowerCase())
+        return -1;
+      if (a.name.toLowerCase() > b.name.toLowerCase())
+        return 1;
+      return 0;
+    }
+    addons.sort(compare);
+  }
+
+  var urlproperties = [ "iconURL", "homepageURL", "thumbnailURL", "xpiURL" ];
+  var properties = [ "name", "eula", "iconURL", "homepageURL", "thumbnailURL", "xpiURL", "xpiHash" ];
+  for (var i = 0; i < addons.length; i++) {
+    var addon = addons[i];
+    // Strip out any items with potentially unsafe urls
+    var unsafe = false;
+    for (var j = 0; j < urlproperties.length; j++) {
+      if (!isSafeURI(addon[urlproperties[j]])) {
+        unsafe = true;
+        break;
+      }
+    }
+    if (unsafe)
+      continue;
+
+    var resultNode = gRDF.GetResource("urn:mozilla:addons:search:" + addon.xpiURL);
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "addonID"),
+                     gRDF.GetLiteral(addon.id),
+                     true);
+    // Use the short summary for our "description"
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "description"),
+                     gRDF.GetLiteral(addon.summary),
+                     true);
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "addonType"),
+                     gRDF.GetIntLiteral(addon.type),
+                     true);
+    if (addon.rating >= 0) {
+      gSearchDS.Assert(resultNode,
+                       gRDF.GetResource(PREFIX_NS_EM + "rating"),
+                       gRDF.GetIntLiteral(addon.rating),
+                       gRDF.GetIntLiteral(3),
+                       true);
+    }
+
+    for (var j = 0; j < properties.length; j++) {
+      gSearchDS.Assert(resultNode,
+                       gRDF.GetResource(PREFIX_NS_EM + properties[j]),
+                       gRDF.GetLiteral(addon[properties[j]]),
+                       true);
+    }
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "searchResult"),
+                     gRDF.GetLiteral("true"),
+                     true);
+    rootctr.AppendElement(resultNode);
+  }
+
+  labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:footer");
+  rootctr.AppendElement(labelNode);
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "statusMessage"),
+                   gRDF.GetLiteral("true"),
+                   true);
+  if (isRecommended) {
+    gSearchDS.Assert(labelNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "type"),
+                     gRDF.GetLiteral("footer-recommended"),
+                     true);
+    var url = gAddonRepository.getRecommendedURL();
+  }
+  else {
+    gSearchDS.Assert(labelNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "type"),
+                     gRDF.GetLiteral("footer-search"),
+                     true);
+    gSearchDS.Assert(labelNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "count"),
+                     gRDF.GetIntLiteral(count),
+                     true);
+    var searchbox = document.getElementById("searchbox");
+    // The value attribute will be the persisted value of the last search run
+    url = gAddonRepository.getSearchURL(searchbox.getAttribute("value"));
+  }
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "link"),
+                   gRDF.GetLiteral(url),
+                   true);
+
+  gSearchDS.endUpdateBatch();
+}
+
+// Displays the search failure status message
+function displaySearchFailure(isRecommended) {
+  var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"]
+                        .getService(Components.interfaces.nsIRDFContainerUtils);
+  var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT));
+
+  cleanDataSource(gSearchDS, rootctr);
+
+  var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header");
+  rootctr.AppendElement(labelNode);
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "statusMessage"),
+                   gRDF.GetLiteral("true"),
+                   true);
+  gSearchDS.Assert(labelNode,
+                   gRDF.GetResource(PREFIX_NS_EM + "type"),
+                   gRDF.GetLiteral(isRecommended ? "recommended-failure" : "search-failure"),
+                   true);
+}
+
+// nsIAddonSearchResultsCallback for the recommended search
+var RecommendedSearchResults = {
+  searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
+    gRecommendedAddons = aAddons;
+    displaySearchResults(aAddons, aTotalResults, true);
+  },
+
+  searchFailed: function() {
+    displaySearchFailure(true);
+  }
+}
+
+// nsIAddonSearchResultsCallback for a standard search
+var AddonSearchResults = {
+  searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
+    displaySearchResults(aAddons, aTotalResults, false);
+  },
+
+  searchFailed: function() {
+    displaySearchFailure(false);
+  }
+}
+
+// Initialises the search repository and the results datasource
+function initSearchDS() {
+  var repository = "@mozilla.org/extensions/addon-repository;1";
+  try {
+    var repo = gPref.getCharPref(PREF_GETADDONS_REPOSITORY);
+    if (repo in Components.classes)
+      repository = repo;
+  } catch (e) { }
+  gAddonRepository = Components.classes[repository]
+                               .createInstance(Components.interfaces.nsIAddonRepository);
+  var browseAddons = document.getElementById("browseAddons");
+  var homepage = gAddonRepository.homepageURL;
+  if (homepage)
+    browseAddons.setAttribute("homepageURL", homepage);
+  else
+    browseAddons.hidden = true;
+  gSearchDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
+                        .createInstance(Components.interfaces.nsIRDFDataSource);
+  gExtensionsView.database.AddDataSource(gSearchDS);
+  var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                            .getService(nsIIOService);
+  if (!ioService.offline)
+    retrieveRepositoryAddons(document.getElementById("searchbox").value);
+}
+
 function initPluginsDS()
 {
   gPluginsDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
                          .createInstance(Components.interfaces.nsIRDFDataSource);
   rebuildPluginsDS();
 }
 
 function rebuildPluginsDS()
 {
   var phs = Components.classes["@mozilla.org/plugin/host;1"]
                       .getService(Components.interfaces.nsIPluginHost);
   var plugins = phs.getPluginTags({ });
-  var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
-                      .getService(Components.interfaces.nsIRDFService);
   var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"]
                         .getService(Components.interfaces.nsIRDFContainerUtils);
-  var rootctr = rdfCU.MakeSeq(gPluginsDS, rdf.GetResource(RDFURI_ITEM_ROOT));
+  var rootctr = rdfCU.MakeSeq(gPluginsDS, gRDF.GetResource(RDFURI_ITEM_ROOT));
   gPlugins = { };
   
   // Running in a batch stops the template builder from running
   gPluginsDS.beginUpdateBatch();
 
-  // Remove old entries from the list
-  var nodes = rootctr.GetElements();
-  while (nodes.hasMoreElements()) {
-    var node = nodes.getNext()
-                    .QueryInterface(Components.interfaces.nsIRDFResource);
-    rootctr.RemoveElement(node, false);
-  }
+  cleanDataSource(gPluginsDS, rootctr);
 
   // Case insensitive sort
   function compare(a, b) {
     if (a.name.toLowerCase() < b.name.toLowerCase())
       return -1;
     if (a.name.toLowerCase() > b.name.toLowerCase())
       return 1;
     return 0;
@@ -587,69 +881,67 @@ function rebuildPluginsDS()
                                plugins     : [] };
     }
     gPlugins[name][desc].plugins.push(plugin);
   }
 
   for (var pluginName in gPlugins) {
     for (var pluginDesc in gPlugins[pluginName]) {
       plugin = gPlugins[pluginName][pluginDesc];
-      var pluginNode = rdf.GetResource(PREFIX_ITEM_URI + plugin.filename);
+      var pluginNode = gRDF.GetResource(PREFIX_ITEM_URI + plugin.filename);
       rootctr.AppendElement(pluginNode);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "name"),
-                        rdf.GetLiteral(pluginName),
+                        gRDF.GetResource(PREFIX_NS_EM + "name"),
+                        gRDF.GetLiteral(pluginName),
                         true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "addonID"),
-                        rdf.GetLiteral(plugin.filename),
+                        gRDF.GetResource(PREFIX_NS_EM + "addonID"),
+                        gRDF.GetLiteral(plugin.filename),
                         true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "description"),
-                        rdf.GetLiteral(pluginDesc),
+                        gRDF.GetResource(PREFIX_NS_EM + "description"),
+                        gRDF.GetLiteral(pluginDesc),
                         true);
       if (plugin.homepageURL)
         gPluginsDS.Assert(pluginNode,
-                          rdf.GetResource(PREFIX_NS_EM + "homepageURL"),
-                          rdf.GetLiteral(plugin.homepageURL),
+                          gRDF.GetResource(PREFIX_NS_EM + "homepageURL"),
+                          gRDF.GetLiteral(plugin.homepageURL),
                           true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "isDisabled"),
-                        rdf.GetLiteral(plugin.disabled ? "true" : "false"),
+                        gRDF.GetResource(PREFIX_NS_EM + "isDisabled"),
+                        gRDF.GetLiteral(plugin.disabled ? "true" : "false"),
                         true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "blocklisted"),
-                        rdf.GetLiteral(plugin.blocklisted ? "true" : "false"),
+                        gRDF.GetResource(PREFIX_NS_EM + "blocklisted"),
+                        gRDF.GetLiteral(plugin.blocklisted ? "true" : "false"),
                         true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "compatible"),
-                        rdf.GetLiteral("true"),
+                        gRDF.GetResource(PREFIX_NS_EM + "compatible"),
+                        gRDF.GetLiteral("true"),
                         true);
       gPluginsDS.Assert(pluginNode,
-                        rdf.GetResource(PREFIX_NS_EM + "plugin"),
-                        rdf.GetLiteral("true"),
+                        gRDF.GetResource(PREFIX_NS_EM + "plugin"),
+                        gRDF.GetLiteral("true"),
                         true);
     }
   }
   
   gPluginsDS.endUpdateBatch();
 }
 
 function togglePluginDisabled(aName, aDesc)
 {
   var plugin = gPlugins[aName][aDesc];
   plugin.disabled = !plugin.disabled;
   for (var i = 0; i < plugin.plugins.length; ++i)
     plugin.plugins[i].disabled = plugin.disabled;
-  var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
-                      .getService(Components.interfaces.nsIRDFService);
-  gPluginsDS.Change(rdf.GetResource(PREFIX_ITEM_URI + plugin.filename),
-                    rdf.GetResource(PREFIX_NS_EM + "isDisabled"),
-                    rdf.GetLiteral(plugin.disabled ? "false" : "true"),
-                    rdf.GetLiteral(plugin.disabled ? "true" : "false"));
+  gPluginsDS.Change(gRDF.GetResource(PREFIX_ITEM_URI + plugin.filename),
+                    gRDF.GetResource(PREFIX_NS_EM + "isDisabled"),
+                    gRDF.GetLiteral(plugin.disabled ? "false" : "true"),
+                    gRDF.GetLiteral(plugin.disabled ? "true" : "false"));
   gExtensionsViewController.onCommandUpdate();
   gExtensionsView.selectedItem.focus();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Startup, Shutdown
 function Startup()
 {
@@ -676,26 +968,37 @@ function Startup()
   try {
     gCheckCompat = gPref.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
   } catch(e) { }
 
   try {
     gCheckUpdateSecurity = gPref.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY);
   } catch(e) { }
 
+  try {
+    gShowGetAddonsPane = gPref.getBoolPref(PREF_GETADDONS_SHOWPANE);
+  } catch(e) { }
+
   // Sort on startup and anytime an add-on is installed or upgraded.
   gExtensionManager.sortTypeByProperty(nsIUpdateItem.TYPE_ADDON, "name", true);
   // Extension Command Updating is handled by a command controller.
   gExtensionsView.controllers.appendController(gExtensionsViewController);
   gExtensionsView.addEventListener("select", onAddonSelect, false);
 
+  gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
+                   .getService(Components.interfaces.nsIRDFService);
+
   initPluginsDS();
   gExtensionsView.database.AddDataSource(gPluginsDS);
+  if (gShowGetAddonsPane)
+    initSearchDS();
   gExtensionsView.database.AddDataSource(gExtensionManager.datasource);
   gExtensionsView.setAttribute("ref", RDFURI_ITEM_ROOT);
+
+  document.getElementById("search-view").hidden = !gShowGetAddonsPane;
   updateOptionalViews();
 
   var viewGroup = document.getElementById("viewGroup");
 
   gExtensionsView.focus();
   gExtensionsViewController.onCommandUpdate();
 
   // Now look and see if we're being opened by XPInstall
@@ -739,17 +1042,16 @@ function Startup()
                 getExtensionString("safeModeMsg"),
                 null, null, true, null);
   }
 
   if ("arguments" in window) {
     try {
       var params = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock);
       var manager = window.arguments[1].QueryInterface(Components.interfaces.nsIObserver);
-      showView("installs");
       gDownloadManager.addDownloads(params, manager);
     }
     catch (e) {
       if (window.arguments[0] == "updates-only") {
         gUpdatesOnly = true;
 #ifdef MOZ_PHOENIX
         // If we are Firefox when updating on startup don't display context
         // menuitems that can open a browser window.
@@ -763,32 +1065,37 @@ function Startup()
                     null, null, true, null);
         document.title = getExtensionString("newUpdateWindowTitle", [getBrandShortName()]);
       }
       else
         showView(window.arguments[0]);
     }
   }
   else if (viewGroup.hasAttribute("last-selected") &&
+           document.getElementById(viewGroup.getAttribute("last-selected") + "-view") &&
            !document.getElementById(viewGroup.getAttribute("last-selected") + "-view").hidden)
     showView(viewGroup.getAttribute("last-selected"));
   else
     showView("extensions");
 
   if (gExtensionsView.selectedItem)
     gExtensionsView.scrollBoxObject.scrollToElement(gExtensionsView.selectedItem);
 
   gPref.setBoolPref(PREF_UPDATE_NOTIFYUSER, false);
 
   if (gUpdatesOnly && gExtensionsView.children.length == 0)
     window.close();
 }
 
 function Shutdown()
 {
+  if (gAddonRepository && gAddonRepository.isSearching)
+    gAddonRepository.cancelSearch();
+
+  gRDF = null;
   gPref = null;
   gExtensionsView.removeEventListener("select", onAddonSelect, false);
   gExtensionsView.database.RemoveDataSource(gExtensionManager.datasource);
 
   gExtensionManager.removeUpdateListenerAt(gObserverIndex);
 
   var os = Components.classes["@mozilla.org/observer-service;1"]
                      .getService(Components.interfaces.nsIObserverService);
@@ -825,31 +1132,31 @@ XPInstallDownloadManager.prototype = {
   _statusFormatKBMB : null,
   _statusFormatKBKB : null,
   _statusFormatMBMB : null,
 
   observe: function (aSubject, aTopic, aData)
   {
     switch (aTopic) {
       case "xpinstall-download-started":
-        showView("installs");
         var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray);
         var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer);
         paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock);
         var manager = params.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsInterfacePointer);
         manager = manager.data.QueryInterface(Components.interfaces.nsIObserver);
         this.addDownloads(paramBlock, manager);
         break;
     }
   },
 
   addDownloads: function (aParams, aManager)
   {
     var numXPInstallItems = aParams.GetInt(1);
     var items = [];
+    var switchPane = false;
     for (var i = 0; i < numXPInstallItems;) {
       var displayName = aParams.GetString(i++);
       var url = aParams.GetString(i++);
       var iconURL = aParams.GetString(i++);
       var uri = Components.classes["@mozilla.org/network/io-service;1"]
                           .getService(nsIIOService).newURI(url, null, null);
       var isTheme = uri.QueryInterface(nsIURL).fileExtension.toLowerCase() == "jar";
       if (!iconURL) {
@@ -860,21 +1167,32 @@ XPInstallDownloadManager.prototype = {
       var type = isTheme ? nsIUpdateItem.TYPE_THEME : nsIUpdateItem.TYPE_EXTENSION;
       var item = Components.classes["@mozilla.org/updates/item;1"]
                            .createInstance(Components.interfaces.nsIUpdateItem);
       item.init(url, " ", "app-profile", "", "", displayName, url, "", iconURL, "", "", type, "");
       items.push(item);
 
       // Advance the enumerator
       var certName = aParams.GetString(i++);
+
+      // Check whether the install was triggered from the Get Add-ons pane.
+      var pos = gPendingInstalls.indexOf(url);
+      if (pos >= 0)
+        gPendingInstalls.splice(pos, 1);
+      else
+        switchPane = true;
     }
 
     gExtensionManager.addDownloads(items, items.length, aManager);
     updateOptionalViews();
     updateGlobalCommands();
+    // Only switch to the installs pane if there was an not started by the
+    // Get Add-ons pane
+    if (switchPane)
+      showView("installs");
   },
 
   getElementForAddon: function(aAddon)
   {
     var element = document.getElementById(PREFIX_ITEM_URI + aAddon.id);
     if (aAddon.id == aAddon.xpiURL)
       element = document.getElementById(aAddon.xpiURL);
     return element;
@@ -1164,21 +1482,17 @@ function onAddonSelect(aEvent)
       }
     }
     else if (gView == "updates") {
       UpdateInfoLoader.cancelLoad();
       if (!gExtensionsView.selectedItem) {
         previewImageDeck.selectedIndex = 3;
       }
       else {
-        try {
-          var uri = makeURI(gExtensionsView.selectedItem.getAttribute("availableUpdateInfo"));
-          var scheme = uri.scheme;
-        } catch (ex) {}
-        if (uri && (scheme == "http" || scheme == "https"))
+        if (isSafeURI(gExtensionsView.selectedItem.getAttribute("availableUpdateInfo")))
           UpdateInfoLoader.loadInfo(uri.spec);
         else
           previewImageDeck.selectedIndex = 4;
       }
     }
   }
 }
 
@@ -1547,16 +1861,21 @@ const gAddonsMsgObserver = {
         if (child.hasAttribute("updateStatus"))
           child.removeAttribute("updateStatus");
       }
       break;
     case "addons-go-online":
       var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                                 .getService(nsIIOService);
       ioService.offline = false;
+      // If no results have been retrieved start pulling some
+      if (!gRetrievedResults)
+        retrieveRepositoryAddons(document.getElementById("searchbox").value);
+      if (gView == "search")
+        document.getElementById("searchbox").disabled = false;
       break;
     case "addons-message-dismiss":
       break;
     case "addons-restart-app":
       restartApp();
       break;
     }
     if (gExtensionsView.selectedItem)
@@ -1856,16 +2175,48 @@ function confirmOperation(aName, aTitle,
   for (var i = 0; i < aDependantItems.length; ++i)
     names.push(aDependantItems[i].name + " " + aDependantItems[i].version);
 
   window.openDialog("chrome://mozapps/content/extensions/list.xul", "",
                     "titlebar,modal,centerscreen", names, params);
   return params.result == "accept";
 }
 
+function installCallback(item, status) {
+  var resultNode = gRDF.GetResource(item.id);
+
+  // Strip out old status
+  gSearchDS.Unassert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "action"),
+                     gRDF.GetLiteral("installing"),
+                     true);
+
+  if (status == -210) {
+    // User cancelled
+    var pos = gPendingInstalls.indexOf(item.getAttribute("xpiURL"));
+    if (pos >= 0)
+      gPendingInstalls.splice(pos, 1);
+    return;
+  }
+  if (status < 0) {
+    // Some other failure
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "action"),
+                     gRDF.GetLiteral("failed"),
+                     true);
+  }
+  else {
+    // Success
+    gSearchDS.Assert(resultNode,
+                     gRDF.GetResource(PREFIX_NS_EM + "action"),
+                     gRDF.GetLiteral("installed"),
+                     true);
+  }
+}
+
 var gExtensionsViewController = {
   supportsCommand: function (aCommand)
   {
     var commandNode = document.getElementById(aCommand);
     return commandNode && (commandNode.parentNode == document.getElementById("extensionsCommands"));
   },
 
   isCommandEnabled: function (aCommand)
@@ -1876,16 +2227,18 @@ var gExtensionsViewController = {
 
     if (selectedItem.hasAttribute("downloadURL") &&
         selectedItem.getAttribute("downloadURL") != "") {
       if (aCommand == "cmd_uninstall")
         return true;
       return false;
     }
     switch (aCommand) {
+    case "cmd_installSearchResult":
+      return true;
     case "cmd_useTheme":
       return selectedItem.type == nsIUpdateItem.TYPE_THEME &&
              !selectedItem.isDisabled &&
              selectedItem.opType != OP_NEEDS_UNINSTALL &&
              gCurrentTheme != selectedItem.getAttribute("internalName");
     case "cmd_options":
       return selectedItem.type == nsIUpdateItem.TYPE_EXTENSION &&
              !selectedItem.isDisabled &&
@@ -1964,16 +2317,51 @@ var gExtensionsViewController = {
   {
     if (this.isCommandEnabled(command.id))
       command.removeAttribute("disabled");
     else
       command.setAttribute("disabled", "true");
   },
 
   commands: {
+    cmd_installSearchResult: function (aSelectedItem)
+    {
+      if (!isXPInstallEnabled())
+        return;
+
+      if (aSelectedItem.hasAttribute("eula")) {
+        var eula = {
+          name: aSelectedItem.getAttribute("name"),
+          text: aSelectedItem.getAttribute("eula"),
+          accepted: false
+        };
+        window.openDialog("chrome://mozapps/content/extensions/eula.xul", "_blank",
+                          "chrome,dialog,modal,centerscreen,resizable=no", eula);
+        if (!eula.accepted)
+          return;
+      }
+
+      var details = {
+        URL: aSelectedItem.getAttribute("xpiURL"),
+        Hash: aSelectedItem.getAttribute("xpiHash"),
+        IconURL: aSelectedItem.getAttribute("iconURL"),
+        toString: function () { return this.URL; }
+      };
+      var params = [];
+      params[aSelectedItem.getAttribute("name")] = details;
+
+      gSearchDS.Assert(gRDF.GetResource(aSelectedItem.id),
+                       gRDF.GetResource(PREFIX_NS_EM + "action"),
+                       gRDF.GetLiteral("installing"),
+                       true);
+      // Remember that we don't want to change panes for this install
+      gPendingInstalls.push(details.URL);
+      InstallTrigger.install(params, function(url, status) { installCallback(aSelectedItem, status); });
+    },
+
     cmd_close: function (aSelectedItem)
     {
       closeWindow(true);
     },
 
     cmd_useTheme: function (aSelectedItem)
     {
       gCurrentTheme = aSelectedItem.getAttribute("internalName");
@@ -2030,21 +2418,17 @@ var gExtensionsViewController = {
       openDialog(optionsURL, "", features);
     },
 
     cmd_homepage: function (aSelectedItem)
     {
       if (!aSelectedItem) return;
       var homepageURL = aSelectedItem.getAttribute("homepageURL");
       // only allow http(s) homepages
-      try {
-        var uri = makeURI(homepageURL);
-        var scheme = uri.scheme;
-      } catch (ex) {}
-      if (uri && (scheme == "http" || scheme == "https"))
+      if (isSafeURI(homepageURL))
         openURL(uri.spec);
     },
 
     cmd_about: function (aSelectedItem)
     {
       if (!aSelectedItem) return;
       var aboutURL = aSelectedItem.getAttribute("aboutURL");
       if (aboutURL != "")
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -380,16 +380,260 @@
           if (opType == "needs-uninstall" && cmd == "cmd_uninstall" ||
               opType == "needs-disable" && cmd == "cmd_disable" && !this.isDisabled ||
               opType == "needs-enable" && cmd == "cmd_disable" && this.isDisabled)
             this.parentNode.ensureElementIsVisible(this);
         ]]>
       </handler>
     </handlers>
   </binding>
+  
+  <binding id="searchresult" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <resources>
+      <stylesheet src="chrome://mozapps/skin/extensions/extensions.css"/>
+    </resources>
+
+    <content>
+      <xul:hbox flex="1">
+        <xul:vbox class="addon-icon" xbl:inherits="iconURL"/>
+        <xul:vbox flex="1" class="addonTextBox">
+          <xul:hbox align="center">
+            <xul:hbox anonid="addonNameVersion" class="addon-name-version" xbl:inherits="name, version"/>
+            <xul:spacer flex="1"/>
+            <xul:image class="addonRating" xbl:inherits="rating"/>
+          </xul:hbox>
+          <xul:hbox anonid="addonDescription" class="addon-description" xbl:inherits="description, opType"/>
+        </xul:vbox>
+      </xul:hbox>
+    </content>
+
+    <implementation extends="nsIAccessibleProvider">
+      <constructor>
+        if (!this.hasAttribute("thumbnailURL"))
+          return this._hideImage();
+
+        if (this.hasAttribute("thumbwidth"))
+          return this._displayImage();
+
+        var image = new Image();
+        var id = this.id;
+        image.onload = function() {
+          document.getElementById(id)._imageLoaded(image);
+        };
+        image.onerror = function() {
+          document.getElementById(id)._hideImage();
+        };
+        image.src = this.getAttribute("thumbnailURL");
+      </constructor>
+
+      <field name="_maxSize">125</field>
+
+      <method name="_displayImage">
+        <body>
+        </body>
+      </method>
+
+      <method name="_hideImage">
+        <body>
+        </body>
+      </method>
+
+      <method name="_imageLoaded">
+        <parameter name="image"/>
+        <body>
+        <![CDATA[
+          if ((this._maxSize >= image.width) &&
+              (this._maxSize >= image.height)) {
+            var width = image.width;
+            var height = image.height;
+          }
+          else if (image.width > image.height) {
+            width = this._maxSize;
+            height = (this._maxSize / image.width) * image.height;
+          }
+          else {
+            height = this._maxSize;
+            width = (this._maxSize / image.height) * image.width;
+          }
+
+          this.setAttribute("thumbwidth", width);
+          this.setAttribute("thumbheight", height);
+          this._displayImage();
+        ]]>
+        </body>
+      </method>
+    </implementation>
+  </binding>
+  
+  <binding id="searchresult-selected" extends="chrome://mozapps/content/extensions/extensions.xml#searchresult">
+    <content>
+      <xul:hbox flex="1">
+        <xul:vbox class="addon-icon" xbl:inherits="iconURL"/>
+        <xul:vbox flex="1" class="addonTextBox">
+          <xul:hbox align="center">
+            <xul:hbox anonid="addonNameVersion" class="addon-name-version" xbl:inherits="name, version"/>
+            <xul:spacer flex="1"/>
+            <xul:image class="addonRating" xbl:inherits="rating"/>
+          </xul:hbox>
+          <xul:hbox flex="1" align="stretch" class="addon-search-details">
+            <xul:vbox pack="start">
+              <xul:deck class="addonThumbnailContainer" selectedIndex="0">
+                <xul:vbox flex="1" align="center" pack="center">
+                  <xul:image class="addonThrobber"/>
+                </xul:vbox>
+                <xul:vbox flex="1" align="center" pack="center">
+                  <xul:image class="addonThumbnail"/>
+                </xul:vbox>
+                <xul:vbox flex="1" align="center" pack="center">
+                  <xul:label class="addonMissingThumbnail" value="&missingThumbnail.label;"/>
+                </xul:vbox>
+              </xul:deck>
+            </xul:vbox>
+            <xul:vbox flex="1">
+              <xul:label anonid="addonDescriptionWrap" class="descriptionWrap"
+                         xbl:inherits="xbl:text=description"/>
+              <xul:label class="addonLearnMore text-link" xbl:inherits="homepageURL"
+                         value="&searchResultHomepage.value;"
+                         onclick="openURL(this.getAttribute('homepageURL'));"/>
+              <xul:spacer flex="1"/>
+              <xul:hbox anonid="selectedButtons" class="selectedButtons">
+                <xul:hbox align="center" class="addonType addonTypeExtension">
+                  <xul:image/>
+                  <xul:label value="&addonTypeExtension.label;"/>
+                </xul:hbox>
+                <xul:hbox align="center" class="addonType addonTypeTheme">
+                  <xul:image/>
+                  <xul:label value="&addonTypeTheme.label;"/>
+                </xul:hbox>
+                <xul:spacer flex="1"/>
+                <xul:hbox align="center" class="searchResultInstalling">
+                  <xul:image class="addonThrobber"/>
+                  <xul:label value="&searchResultInstalling.label;"/>
+                </xul:hbox>
+                <xul:hbox align="center" class="searchResultFailed">
+                  <xul:label value="&searchResultFailed.label;"/>
+                </xul:hbox>
+                <xul:hbox align="center" class="searchResultInstalled">
+                  <xul:label value="&searchResultInstalled.label;"/>
+                </xul:hbox>
+                <xul:button class="addonInstallButton"
+                            label="&cmd.installSearchResult.label;"
+                            accesskey="&cmd.installSearchResult.accesskey;"
+                            tooltiptext="&cmd.installSearchResult.tooltip;"
+                            command="cmd_installSearchResult"/>
+              </xul:hbox>
+            </xul:vbox>
+          </xul:hbox>
+        </xul:vbox>
+      </xul:hbox>
+    </content>
+
+    <implementation implements="nsIAccessibleProvider">
+      <field name="_thumbnailContainer">
+        document.getAnonymousElementByAttribute(this, "class", "addonThumbnailContainer");
+      </field>
+      <field name="_thumbnail">
+        document.getAnonymousElementByAttribute(this, "class", "addonThumbnail");
+      </field>
+      
+      <method name="_displayImage">
+        <body>
+          this._thumbnail.style.width = this.getAttribute("thumbwidth") + "px";
+          this._thumbnail.style.height = this.getAttribute("thumbheight") + "px";
+          this._thumbnail.src = this.getAttribute("thumbnailURL");
+          this._thumbnailContainer.selectedIndex = 1;
+        </body>
+      </method>
+
+      <method name="_hideImage">
+        <body>
+          this._thumbnailContainer.selectedIndex = 2;
+        </body>
+      </method>
+    </implementation>
+  </binding>
+
+  <binding id="status-search-failure">
+    <content align="center">
+      <xul:hbox align="center" pack="center">
+        <xul:image class="addonFailure"/>
+        <xul:label value="&searchFailed.label;"/>
+      </xul:hbox>
+      <xul:button command="cmd_resetSearch" label="&cancelSearch.button;"/>
+    </content>
+  </binding>
+
+  <binding id="status-recommended-failure">
+    <content>
+      <xul:hbox align="center" pack="center">
+        <xul:image class="addonFailure"/>
+        <xul:label value="&searchFailed.label;"/>
+      </xul:hbox>
+    </content>
+  </binding>
+
+  <binding id="status-header-recommended">
+    <content>
+      <xul:label value="&recommendedHeader.label;"/>
+    </content>
+  </binding>
+
+  <binding id="status-footer-recommended">
+    <content align="end">
+      <xul:label class="text-link" xbl:inherits="searchURL=link" value="&recommendedResults.label;"
+                 onclick="openURL(this.getAttribute('homepageURL'));"/>
+    </content>
+  </binding>
+
+  <binding id="status-footer-search">
+    <content align="stretch">
+      <xul:hbox align="center">
+        <xul:label class="text-link" xbl:inherits="searchURL=link" anonid="searchLink"
+                   onclick="openURL(this.getAttribute('searchURL'));"/>
+        <xul:spacer flex="1"/>
+        <xul:button command="cmd_resetSearch" label="&resetSearch.label;"/>
+      </xul:hbox>
+    </content>
+
+    <implementation>
+      <constructor>
+        var strings = [this.getAttribute("count")];
+        var text = document.getElementById("extensionsStrings")
+                           .getFormattedString("searchResults", strings);
+        var label = document.getAnonymousElementByAttribute(this, "anonid", "searchLink");
+        label.value = text;
+      </constructor>
+    </implementation>
+  </binding>
+
+  <binding id="status-footer-search-empty">
+    <content align="center">
+      <xul:label value="&searchEmpty.label;"/>
+      <xul:button command="cmd_resetSearch" label="&searchEmpty.button;"/>
+    </content>
+  </binding>
+
+  <binding id="status-retrieve-search">
+    <content align="center">
+      <xul:hbox align="center" pack="center">
+        <xul:image class="addonThrobber"/>
+        <xul:label value="&searchThrobber.label;"/>
+      </xul:hbox>
+      <xul:button command="cmd_resetSearch" label="&cancelSearch.button;"/>
+    </content>
+  </binding>
+
+  <binding id="status-retrieve-recommended">
+    <content>
+      <xul:hbox align="center" pack="center">
+        <xul:image class="addonThrobber"/>
+        <xul:label value="&recommendedThrobber.label;"/>
+      </xul:hbox>
+    </content>
+  </binding>
 
   <binding id="addon-icon">
     <content>
       <xul:stack class="addonIconStack">
         <xul:vbox pack="start" align="start">
           <xul:image class="addonIcon" xbl:inherits="src=iconURL"/>
         </xul:vbox>
         <xul:vbox pack="start" align="start">
@@ -594,16 +838,114 @@
           <xul:hbox class="addon-name-version" xbl:inherits="name, version"/>
           <xul:progressmeter class="extension-item-progress" xbl:inherits="value=progress"/>
           <xul:label class="extension-item-status" xbl:inherits="value=status" value="&installWaiting.label;"/>
         </xul:vbox>
       </xul:hbox>
     </content>  
   </binding>
 
+  <binding id="search-textbox" extends="xul:box">
+    <resources>
+      <stylesheet src="chrome://global/skin/textbox.css"/>
+    </resources>
+
+    <content align="center">
+      <xul:image class="searchbox-image" xbl:inherits="src=image"/>
+      <xul:textbox class="plain"
+                   xbl:inherits="emptyText,onfocus,onblur,spellcheck,value,maxlength,disabled,size,readonly,tabindex,accesskey"/>
+      <xul:button class="searchbox-search" xbl:inherits="disabled" oncommand="this.parentNode.startSearch()"/>
+      <xul:button class="searchbox-cancel" xbl:inherits="disabled" hidden="true" oncommand="this.parentNode.clearSearch()"/>
+    </content>
+
+    <implementation>
+      <field name="textbox">
+        document.getAnonymousNodes(this)[1];
+      </field>
+
+      <field name="_searchButton">
+        document.getAnonymousElementByAttribute(this, "class", "searchbox-search");
+      </field>
+
+      <field name="_cancelButton">
+        document.getAnonymousElementByAttribute(this, "class", "searchbox-cancel");
+      </field>
+
+      <property name="value" onget="return this.textbox.value"
+                             onset="this.textbox.value = val"/>
+
+      <property name="disabled" onget="return this.textbox.disabled">
+        <setter>
+          if (val)
+            this.setAttribute("disabled", "true");
+          else
+            this.removeAttribute("disabled");
+        </setter>
+      </property>
+
+      <constructor>
+        if (this.value) {
+          this._cancelButton.hidden = false;
+          this._searchButton.hidden = true;
+        }
+      </constructor>
+
+      <method name="startSearch">
+        <body>
+          if (this.value) {
+            this._cancelButton.hidden = false;
+            this._searchButton.hidden = true;
+          }
+          this.setAttribute("value", this.value);
+        </body>
+      </method>
+
+      <method name="clearSearch">
+        <body>
+          this.value = "";
+          this.setAttribute("value", "");
+          this._cancelButton.hidden = true;
+          this._searchButton.hidden = false;
+        </body>
+      </method>
+
+      <method name="_dispatchCommandEvent">
+        <parameter name="aEvent"/>
+        <body>
+          var event = document.createEvent("commandevent");
+          event.initCommandEvent("command", true, true, window, null,
+                                 false, false, false, false, aEvent);
+          this.dispatchEvent(event);
+        </body>
+      </method>
+    </implementation>
+    
+    <handlers>
+      <handler event="keypress" keycode="VK_ENTER">
+        if (event.originalTarget == this.textbox.inputField) {
+          this.startSearch();
+          this._dispatchCommandEvent(event);
+        }
+      </handler>
+
+      <handler event="keypress" keycode="VK_RETURN">
+        if (event.originalTarget == this.textbox.inputField) {
+          this.startSearch();
+          this._dispatchCommandEvent(event);
+        }
+      </handler>
+
+      <handler event="input">
+        this._cancelButton.hidden = true;
+        this._searchButton.hidden = false;
+      </handler>
+
+    </handlers>
+  </binding>
+
 <!-- based on preferences.xml paneButton -->
   <binding id="viewbutton" extends="chrome://global/content/bindings/radio.xml#radio">
     <resources>
       <stylesheet src="chrome://mozapps/skin/extensions/extensions.css"/>
     </resources>
     <content>
       <xul:image class="viewButtonIcon" xbl:inherits="src"/>
       <xul:label class="viewButtonLabel" xbl:inherits="value=label"/>
--- a/toolkit/mozapps/extensions/content/extensions.xul
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -99,26 +99,28 @@
     <command id="cmd_cancelInstall"/>
     <command id="cmd_cancelUpgrade"/>
     <command id="cmd_checkUpdate"/>
     <command id="cmd_includeUpdate"/>
     <command id="cmd_installUpdate"/>
     <command id="cmd_enable"/>
     <command id="cmd_disable"/>
     <command id="cmd_useTheme"/>
+    <command id="cmd_installSearchResult"/>
   </commandset>
   
   <commandset id="globalCommands">
     <command id="cmd_installFile" oncommand="installWithFilePicker();"/>
     <command id="cmd_checkUpdatesAll" oncommand="checkUpdatesAll();"/>
     <command id="cmd_installUpdatesAll" oncommand="installUpdatesAll();"/>
     <command id="cmd_continue" oncommand="closeEM();" disabled="true"/>
     <command id="cmd_close" oncommand="closeEM();"/>
     <command id="cmd_showUpdateInfo" oncommand="showUpdateInfo();"/>
     <command id="cmd_hideUpdateInfo" oncommand="hideUpdateInfo();"/>
+    <command id="cmd_resetSearch" oncommand="resetSearch();"/>
   </commandset>
 
   <vbox id="addonContextMenuPalette" hidden="true">
     <menuitem id="menuitem_useTheme" default="true" command="cmd_useTheme"
               label="&cmd.useTheme.label;" accesskey="&cmd.useTheme.accesskey;"/>
     <menuitem id="menuitem_options" default="true" command="cmd_options"
 #ifdef XP_WIN
               label="&cmd.options.label;" accesskey="&cmd.options.accesskey;"/>
@@ -151,16 +153,17 @@
               type="checkbox"/>
   </vbox>
 
   <popup id="addonContextMenu" onpopupshowing="return buildContextMenu(event);"/>
 
   <stack id="topStackBar">
     <radiogroup id="viewGroup" xhtml:role="listbox" persist="last-selected"
                 class="viewSelector chromeclass-toolbar" orient="horizontal">
+      <radio id="search-view" label="&search.label;" oncommand="showView('search');" persist="last-selected"/>
       <radio id="extensions-view" label="&extensions.label;" oncommand="showView('extensions');" persist="last-selected"/>
       <radio id="themes-view" label="&themes.label;" oncommand="showView('themes');" persist="last-selected"/>
       <radio id="locales-view" label="&locales.label;" oncommand="showView('locales');" persist="last-selected"/>
       <radio id="plugins-view" label="&plugins.label;" oncommand="showView('plugins');" persist="last-selected"/>
       <radio id="updates-view" label="&update.label;" oncommand="showView('updates');"/>
       <radio id="installs-view" label="&install.label;" oncommand="showView('installs');" hidden="true"/>
     </radiogroup>
     <vbox id="progressBox" hidden="true" class="viewSelector" flex="1">
@@ -169,56 +172,67 @@
         <image class="addonThrobber"/>
         <label id="progressStatus" value="&progressStatus.label;"/>
       </hbox>
       <progressmeter id="addonsProgress" class="extension-item-progress" flex="1"/>
       <spacer flex="1"/>
     </vbox>
   </stack>
   <notificationbox id="addonsMsg" flex="1">
-    <hbox id="extensionsBox" flex="1">
-      <richlistbox id="extensionsView" flex="1" 
-                   datasources="rdf:null" context="addonContextMenu"
-                   ondragenter="nsDragAndDrop.dragEnter(event, gExtensionsDNDObserver);"
-                   ondragover="nsDragAndDrop.dragOver(event, gExtensionsDNDObserver);"
-                   ondragdrop="nsDragAndDrop.drop(event, gExtensionsDNDObserver);"
-                   ondblclick="onViewDoubleClick(event);"/>
-
-      <splitter id="themeSplitter" hidden="true" collapse="after" persist="state"/>
-
-      <vbox id="themePreviewArea" hidden="true" width="220" persist="width">
-        <deck id="previewImageDeck" flex="1">
-          <vbox id="noThemeSelected" pack="center" align="center">
-            <label class="previewText">&previewNoThemeSelected.label;</label>
-          </vbox>
-          <vbox id="noPreviewImage" pack="center" align="center">
-            <label class="previewText">&previewNoPreviewImage.label;</label>
-          </vbox>
-          <vbox id="previewImageContainer" align="center" pack="center">
-            <description>
-              <image id="previewImage"/>
-            </description>
-          </vbox>
-          <vbox id="infoNoAddonSelected" align="center" pack="center">
-            <label class="previewText">&infoNoAddonSelected.label;</label>
-          </vbox>
-          <vbox id="infoNoUpdateInfo" align="center" pack="center">
-            <label class="previewText">&infoNoUpdateInfo.label;</label>
-          </vbox>
-          <vbox id="infoLoadingInfo" align="center" pack="center">
-            <image class="addonThrobber"/>
-          </vbox>
-          <vbox id="infoUpdateInfoError" align="center" pack="center">
-            <label class="previewText">&infoUpdateInfoError.label;</label>
-          </vbox>
-          <vbox id="infoDisplay">
-          </vbox>
-        </deck>
-      </vbox>
-    </hbox>
+    <vbox id="extensionsBox" flex="1">
+      <hbox id="searchPanel" align="center">
+        <textbox id="searchbox" emptyText="&searchAddons.label;"
+                 oncommand="retrieveRepositoryAddons(this.value);"
+                 persist="value"/>
+        <spacer flex="1"/>
+        <label id="browseAddons" class="text-link" value="&browseAddons.label;"
+               onclick="openURL(this.getAttribute('homepageURL'));"/>
+      </hbox>
+      
+      <hbox flex="1">
+        <richlistbox id="extensionsView" flex="1" 
+                     datasources="rdf:null" context="addonContextMenu"
+                     ondragenter="nsDragAndDrop.dragEnter(event, gExtensionsDNDObserver);"
+                     ondragover="nsDragAndDrop.dragOver(event, gExtensionsDNDObserver);"
+                     ondragdrop="nsDragAndDrop.drop(event, gExtensionsDNDObserver);"
+                     ondblclick="onViewDoubleClick(event);"/>
+  
+        <splitter id="themeSplitter" hidden="true" collapse="after" persist="state"/>
+  
+        <vbox id="themePreviewArea" hidden="true" width="220" persist="width">
+          <deck id="previewImageDeck" flex="1">
+            <vbox id="noThemeSelected" pack="center" align="center">
+              <label class="previewText">&previewNoThemeSelected.label;</label>
+            </vbox>
+            <vbox id="noPreviewImage" pack="center" align="center">
+              <label class="previewText">&previewNoPreviewImage.label;</label>
+            </vbox>
+            <vbox id="previewImageContainer" align="center" pack="center">
+              <description>
+                <image id="previewImage"/>
+              </description>
+            </vbox>
+            <vbox id="infoNoAddonSelected" align="center" pack="center">
+              <label class="previewText">&infoNoAddonSelected.label;</label>
+            </vbox>
+            <vbox id="infoNoUpdateInfo" align="center" pack="center">
+              <label class="previewText">&infoNoUpdateInfo.label;</label>
+            </vbox>
+            <vbox id="infoLoadingInfo" align="center" pack="center">
+              <image class="addonThrobber"/>
+            </vbox>
+            <vbox id="infoUpdateInfoError" align="center" pack="center">
+              <label class="previewText">&infoUpdateInfoError.label;</label>
+            </vbox>
+            <vbox id="infoDisplay">
+            </vbox>
+          </deck>
+        </vbox>
+      </hbox>
+    </vbox>
   </notificationbox>
   <vbox>
     <hbox id="commandBarBottom" align="center">
       <button id="installFileButton" label="&cmd.installLocalFile.label;"
               accesskey="&cmd.installLocalFile.accesskey;"
               tooltiptextaddons="&cmd.installFileAddon.tooltip;"
               tooltiptextthemes="&cmd.installFileTheme.tooltip;"
               command="cmd_installFile"/>
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -6,9 +6,10 @@ toolkit.jar:
   content/mozapps/extensions/updateinfo.xsl                     (content/updateinfo.xsl)
   content/mozapps/extensions/extensions.css                     (content/extensions.css)
 * content/mozapps/extensions/about.xul                          (content/about.xul)
 * content/mozapps/extensions/about.js                           (content/about.js)
 * content/mozapps/extensions/list.xul                           (content/list.xul)
 * content/mozapps/extensions/list.js                            (content/list.js)
 * content/mozapps/extensions/update.xul                         (content/update.xul)
 * content/mozapps/extensions/update.js                          (content/update.js)
-
+* content/mozapps/extensions/eula.xul                           (content/eula.xul)
+* content/mozapps/extensions/eula.js                            (content/eula.js)
--- a/toolkit/mozapps/extensions/public/Makefile.in
+++ b/toolkit/mozapps/extensions/public/Makefile.in
@@ -39,12 +39,16 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE        = extensions
 XPIDL_MODULE  = extensions
 
-XPIDLSRCS = nsIExtensionManager.idl nsIBlocklistService.idl
+XPIDLSRCS = \
+  nsIExtensionManager.idl \
+  nsIBlocklistService.idl \
+  nsIAddonRepository.idl \
+  $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/public/nsIAddonRepository.idl
@@ -0,0 +1,198 @@
+/* -*- 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(a549a714-2ada-4bb9-8a47-be26e73d49a5)]
+interface nsIAddonSearchResult : nsISupports
+{
+  /**
+   * 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-10 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 occured 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/src/Makefile.in
+++ b/toolkit/mozapps/extensions/src/Makefile.in
@@ -40,16 +40,20 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = extensions
 
 EXTRA_COMPONENTS = nsExtensionManager.js
-EXTRA_PP_COMPONENTS = nsBlocklistService.js
+EXTRA_PP_COMPONENTS = \
+  nsBlocklistService.js \
+  nsAddonRepository.js \
+  $(NULL)
+
 GARBAGE += nsExtensionManager.js
 
 include $(topsrcdir)/config/rules.mk
 
 nsExtensionManager.js: nsExtensionManager.js.in
 	$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@ 
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/src/nsAddonRepository.js
@@ -0,0 +1,367 @@
+/* -*- 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");
+
+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"
+
+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
+  _addons: [],
+
+  // 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,
+
+  // How many times in succession has a search yielded no new results. Stops
+  // us trying to find more results indefinately
+  _emptyCount: null,
+
+  // Callback object to notify on completion
+  _callback: null,
+
+  // The uri to pull results from
+  _retrieveURI: 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;
+    }
+  },
+
+  retrieveRecommendedAddons: function(aMaxResults, aCallback) {
+    if (this._searching)
+      return;
+
+    this._searching = true;
+    this._addons = [];
+    this._callback = aCallback;
+    this._recommended = true;
+    this._emptyCount = 0;
+    this._maxResults = aMaxResults;
+
+    var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
+                         .getService(Components.interfaces.nsIURLFormatter);
+
+    this._retrieveURI = urlf.formatURLPref(PREF_GETADDONS_GETRECOMMENDED);
+    this._loadList();
+  },
+
+  searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
+    if (this._searching)
+      return;
+
+    this._searching = true;
+    this._addons = [];
+    this._callback = aCallback;
+    this._recommended = false;
+    this._emptyCount = 0;
+    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);
+
+    this._retrieveURI = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
+    this._retrieveURI = this._retrieveURI.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
+    this._retrieveURI = urlf.formatURL(this._retrieveURI);
+    this._loadList();
+  },
+
+  // Posts results to the callback
+  _reportSuccess: function(aCount) {
+    this._searching = false;
+    this._request = null;
+    this._callback.searchSucceeded(this._addons, this._addons.length,
+                                   this._recommended ? -1 : aCount);
+  },
+
+  // Notifies the callback of a failure
+  _reportFailure: function(aEvent) {
+    this._searching = false;
+    this._request = null;
+    this._callback.searchFailed();
+  },
+
+  // Parses an add-on entry from an <addon> element
+  _parseAddon: function(element) {
+    var em = Cc["@mozilla.org/extensions/manager;1"].
+             getService(Ci.nsIExtensionManager);
+    var app = Cc["@mozilla.org/xre/app-info;1"].
+              getService(Ci.nsIXULAppInfo).
+              QueryInterface(Ci.nsIXULRuntime);
+
+    var guid = element.getElementsByTagName("guid");
+    if (guid.length != 1)
+      return;
+
+    // Ignore add-ons already seen in the results
+    for (var i = 0; i < this._addons.length; i++)
+      if (this._addons[i].id == guid[0].textContent)
+        return;
+
+    // Ignore installed add-ons
+    if (em.getItemForID(guid[0].textContent) != null)
+      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
+    var compatible = false;
+    var os = element.getElementsByTagName("compatible_os");
+    var i = 0;
+    while (i < os.length && !compatible) {
+      if (os[i].textContent == "ALL" || os[i].textContent == app.OS) {
+        compatible = true;
+        break;
+      }
+      i++;
+    }
+    if (!compatible)
+      return;
+
+    // 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);
+    var apps = tags[0].getElementsByTagName("name");
+    var i = 0;
+    while (i < apps.length) {
+      if (apps[i].textContent.toLowerCase() == app.name.toLowerCase()) {
+        var minversion = apps[i].parentNode.getElementsByTagName("min_version")[0].textContent;
+        var maxversion = apps[i].parentNode.getElementsByTagName("max_version")[0].textContent;
+        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();
+    addon.id = guid[0].textContent;
+    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":
+            addon[node.localName] = node.textContent;
+            break;
+          case "rating":
+            if (node.textContent.length > 0)
+              addon.rating = parseInt(node.textContent);
+            break;
+          case "thumbnail":
+            addon.thumbnailURL = node.textContent;
+            break;
+          case "icon":
+            addon.iconURL = node.textContent;
+            break;
+          case "learnmore":
+            addon.homepageURL = node.textContent;
+            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.nsIUpdateItem.TYPE_THEME;
+            else
+              addon.type = Ci.nsIUpdateItem.TYPE_EXTENSION;
+            break;
+          case "install":
+            addon.xpiURL = node.textContent;
+            if (node.hasAttribute("hash"))
+              addon.xpiHash = node.getAttribute("hash");
+            break;
+        }
+      }
+      node = node.nextSibling;
+    }
+
+    this._addons.push(addon);
+  },
+
+  // 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");
+    var oldcount = this._addons.length;
+    for (var i = 0; i < elements.length; i++) {
+      this._parseAddon(elements[i]);
+
+      var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                            .getService(Components.interfaces.nsIPrefBranch);
+      if (this._addons.length == this._maxResults) {
+        this._reportSuccess(elements.length);
+        return;
+      }
+    }
+
+    // We didn't find any new add-ons this pass
+    if (oldcount == this._addons.length)
+      this._emptyCount++;
+    else
+      this._emptyCount = 0;
+
+    /* We should keep trying to find new recommended add-ons, but bail if we
+       don't get a new result for a few passes. */
+    if (this._recommended && this._emptyCount < 5)
+      this._loadList();
+    else
+      this._reportSuccess(elements.length);
+  },
+
+  // Performs a new request for results
+  _loadList: function() {
+    this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+                    createInstance(Ci.nsIXMLHttpRequest);
+    this._request.open("GET", this._retrieveURI, 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.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/src/nsExtensionManager.js.in
+++ b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
@@ -6210,23 +6210,21 @@ RDFItemUpdater.prototype = {
                                  Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
       return;
     }
 
     LOG("RDFItemUpdater:checkForUpdates sending a request to server for: " +
         uri.spec + ", item = " + aItem.objectSource);
 
     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                  createInstance(Ci.nsIXMLHttpRequest).
-                  QueryInterface(Ci.nsIJSXMLHttpRequest);
+                  createInstance(Ci.nsIXMLHttpRequest);
     request.open("GET", uri.spec, true);
     request.channel.notificationCallbacks = new BadCertHandler();
     request.overrideMimeType("text/xml");
     request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
-    request.QueryInterface(Ci.nsIJSXMLHttpRequest);
 
     var self = this;
     request.onerror     = function(event) { self.onXMLError(event, aItem);    };
     request.onload      = function(event) { self.onXMLLoad(event, aItem);     };
     request.send(null);
   },
 
   onXMLLoad: function(aEvent, aItem) {
--- a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css
@@ -259,16 +259,141 @@ richlistitem[loading="true"] .updateBadg
   font-weight: normal;
   border: none;
 }
 
 richlistitem[opType="needs-uninstall"] .notifyBadge {
   display: none;
 }
 
+.addon-search-details {
+  margin-top: 5px;
+  margin-bottom: 5px;
+  -moz-margin-start: 6px;
+  -moz-margin-end: 0;
+}
+
+.addonThumbnailContainer {
+  background: window;
+  padding: 5px;
+  border: 2px solid ActiveBorder;
+  width: 135px;
+  min-height: 104px;
+  -moz-margin-end: 5px;
+}
+
+.addonMissingThumbnail {
+  color: GrayText;
+}
+
+.addonFailure {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png");
+  -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+.addonRating {
+  display: none;
+}
+
+.addonLearnMore {
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
+.addonRating[rating] {
+  display: -moz-box;
+  width: 68px;
+  height: 12px;
+  list-style-image: url("chrome://mozapps/skin/extensions/ratings.png");
+}
+
+.addonRating[rating="0"] {
+  -moz-image-region: rect(0px 68px 12px 0px);
+}
+
+.addonRating[rating="1"], .addonRating[rating="2"] {
+  -moz-image-region: rect(12px 68px 24px 0px);
+}
+
+.addonRating[rating="3"], .addonRating[rating="4"] {
+  -moz-image-region: rect(24px 68px 36px 0px);
+}
+
+.addonRating[rating="5"], .addonRating[rating="6"] {
+  -moz-image-region: rect(36px 68px 48px 0px);
+}
+
+.addonRating[rating="7"], .addonRating[rating="8"] {
+  -moz-image-region: rect(48px 68px 60px 0px);
+}
+
+.addonRating[rating="9"], .addonRating[rating="10"] {
+  -moz-image-region: rect(60px 68px 72px 0px);
+}
+
+.addonType image {
+  -moz-margin-start: 6px;
+  list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png");
+  width: 16px;
+  height: 16px;
+}
+
+.addonTypeExtension image {
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+.addonTypeTheme image {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+vbox[typeName="status"][type="search-failure"],
+vbox[typeName="status"][type="recommended-failure"],
+vbox[typeName="status"][type="retrieve-search"],
+vbox[typeName="status"][type="retrieve-recommended"] {
+  margin-top: 2em;
+}
+
+vbox[typeName="status"][type="footer-recommended"],
+vbox[typeName="status"][type="footer-search"] {
+  margin-top: 1em;
+}
+
+vbox[typeName="status"][type="header-recommended"] {
+  font-size: 150%;
+  background: -moz-dialog;
+}
+
+#searchbox {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 19px 19px 0px);
+}
+
+.searchbox-search, .searchbox-cancel {
+  -moz-appearance: none;
+  cursor: pointer;
+  margin: 0;
+  border: 0;
+  padding: 0;
+  width: 19px;
+  height: 19px;
+  min-width: 19px;
+}
+
+.searchbox-search {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 38px 19px 19px);
+}
+
+.searchbox-cancel {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 57px 19px 38px);
+}
+
 #progressBox {
   padding: 5px 5px 5px 5px;
 }
 
 #progressBox > hbox {
   -moz-box-align: center;
 }
 
@@ -334,16 +459,23 @@ radio#updates-view:hover, radio#updates-
 
 radio#installs-view {
   -moz-image-region: rect(0px, 192px, 32px, 160px)
 }
 radio#installs-view:hover, radio#installs-view[selected="true"] {
   -moz-image-region: rect(32px, 192px, 64px, 160px)
 }
 
+radio#search-view {
+  -moz-image-region: rect(0px, 224px, 32px, 192px)
+}
+radio#search-view:hover, radio#search-view[selected="true"] {
+  -moz-image-region: rect(32px, 224px, 64px, 192px)
+}
+
 /* Update view checkbox */
 .includeUpdate {
   -moz-user-focus: none;
 }
 
 richlistitem[selected="true"] .includeUpdate {
   -moz-user-focus: normal;
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/pinstripe/mozapps/extensions/eula.css
@@ -0,0 +1,16 @@
+#heading {
+  font-size: 120%;
+}
+
+#scrollbox {
+  overflow-y: auto;
+  background-color: window;
+  border: 1px solid ActiveBorder;
+  margin: 1em 0 1em 0;
+  padding: 5px;
+}
+
+#eula {
+  font-family: monospace;
+  white-space: -moz-pre-wrap;
+}
new file mode 100644
index 0000000000000000000000000000000000000000..ca7f72ed45241fff11c0a8d47f69104738cb840b
GIT binary patch
literal 1375
zc$@)W1)%zgP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp)
z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3=|9$0g2bOSpWb8@kvBMR7l6g
zm3wHGRUF4Z=Xsv@Hs7hc^*Y_0wJcFbnxZnJ?4m_w*aaCyW>%I-p(wS)O3V}^D-F>K
zu`IigTA7(v=wj2_nx&b}VY=z2TQ2AJZhJ4!b1wa{`DXqR6hQ|LoWt)Nz8}87-|rmw
zpNF_`vtrM*;sN1;-9bC#cPtyrvXEM0w4tW;!1blqepL2vM2>BJ>F&=LKh-#=n#zk~
z)?aBvUb?T3FF!nxfxQQjpI$&-xPa_n1WP!@|3-Yv(z~YAm+#z=%o{!RlaD{J|JDJ3
zm3tn3dPru`vdmD1Fo0Ap)y-93Jvsi(oBxUM#<$;iz)1JOk->wbzgf3kj9s#ku2`Jk
zTiY7i+M}}`ojG%N3Sj;x_e}8F_5>jWAhM;Alcx;5VW8BCs)qfhEgo9|bj3Wk?3Ft=
z{cRdHu3vL+MrQVs!YhVGbW;_lHax>?M@o2n*?fw;UkK&qQr!~OH8qDO&z=2PnFG+$
z6`OEXzuwPZo_!@?<Jb;oyIK$khF)@+&_>U<Fzh(;6F}MY4Yw{3!rHt3{%t9k?OQiR
zVjUgphmRb6-RUMt=@YNBs-%G*YaODITk$j`7R@H^4>Dp)rFE?C<g36)2jJ|vP7>Jx
zcGOg>Vf_Z!+3CH(blal=Z8VmEwyv&TC2!uaGc(Pd)E-kQ2e)tC6wylUzUj6p1&-sR
zuKo~@OcEU4_5~wmt!3&h#Uv!eOIMSW;KiU%M&H4yB22o{-B;Io=y-Gc@K1j_*wLP7
zBO&7?wTr8gc*-T3jMCl}pVvRjnk2QPGvTFd?~Jyu9Y0}W!NU*DV*0d)7!YozC{)8S
zHHyNCCEel@zP}8n_MJ^-M+HJy0>A<Idfwipz*2zX<zr-X^bD@2v27cH!IctEORnti
z7YR?2RFb%qDS+&p$c;XqAB;gNg=*YK`IZVsy;y-~9O4oZ5|Rp(0ZTwM<|b1B7Y?H#
z=EhMPP+&}V3Z&Kup-Cu(HU`X}4Wx}R0PEga$ElNbbbPr6-{hqP`xVle@bK{ASLKX!
z3@dZ?lPltEZ`%BR3gGI+qZj37IHmP1YEDMj@^vO$EDJ0I8jYtw2+iKw#L1!g{`_!I
z2rwyt*0U`ut1Bz!Ml!=z;^<3M9<s@rbY0z!kKcMPJu@;fXYjajMd8QD3HczjkMGLA
zZA$N(voHnVO1L!Q1VRC^CM#?cmy+6M6*WRAZBY4r$ja_*iEzOF{MXa6VNk?g<|$G|
zF!jC}&u#l`^Qyz;A0A0}cZEE48;z$=H>8C#u8GIp!3ib!EwGw&JHQX+I~$XpzP<F(
zlD5C3KxxS6WnqM&_LOYsI1?XsV8zcd!02UT-bxEtGl1W#7FI16ua2)X(0F0$I;!6-
z3}FQ;c3E=JEroXB@Uho=T9B1Dn1IjXMD1_7?)cHomJ^)w>=V=4&fiM`_#J_u+t{K4
zU;aT@2h6<s#W8mjMw|!xqzU|C#{zm)&fT-);@Vlg;nVzJ*jl7~xn;Rdcu2hM%xWhP
zbWR?q@aj(--`0BW+^V%Nzp$@I;=EKhK*-1UyMB(pB7`t>M@(ACst5c&i>AYk@@1ur
z74VrVpcfG6(T$$xuQ<Uspo4HKuzUZ4C{PTXfBO~S0eL_YNB}Xw760L{Dtg`(yJuP#
ho$2W-RL|M~<`2~8HIHbijvxR4002ovPDHLkV1fnqrO^NY
--- a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css
@@ -215,16 +215,146 @@ richlistitem[loading="true"] .updateBadg
 .attention {
   -moz-box-pack: end;
 }
 
 richlistitem[opType="needs-uninstall"] .notifyBadge {
   display: none;
 }
 
+.addon-search-details {
+  margin-top: 5px;
+  margin-bottom: 5px;
+  -moz-margin-start: 6px;
+  -moz-margin-end: 0;
+}
+
+.addonThumbnailContainer {
+  background: window;
+  padding: 5px;
+  border: 2px solid ActiveBorder;
+  width: 135px;
+  min-height: 104px;
+  -moz-margin-end: 5px;
+}
+
+.addonMissingThumbnail {
+  color: GrayText;
+}
+
+.addonFailure {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png");
+  -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+.addonRating {
+  display: none;
+}
+
+.addonLearnMore {
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
+.addonRating[rating] {
+  display: -moz-box;
+  width: 68px;
+  height: 12px;
+  list-style-image: url("chrome://mozapps/skin/extensions/ratings.png");
+}
+
+.addonRating[rating="0"] {
+  -moz-image-region: rect(0px 68px 12px 0px);
+}
+
+.addonRating[rating="1"], .addonRating[rating="2"] {
+  -moz-image-region: rect(12px 68px 24px 0px);
+}
+
+.addonRating[rating="3"], .addonRating[rating="4"] {
+  -moz-image-region: rect(24px 68px 36px 0px);
+}
+
+.addonRating[rating="5"], .addonRating[rating="6"] {
+  -moz-image-region: rect(36px 68px 48px 0px);
+}
+
+.addonRating[rating="7"], .addonRating[rating="8"] {
+  -moz-image-region: rect(48px 68px 60px 0px);
+}
+
+.addonRating[rating="9"], .addonRating[rating="10"] {
+  -moz-image-region: rect(60px 68px 72px 0px);
+}
+
+.addonType image {
+  -moz-margin-start: 6px;
+  list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png");
+  width: 16px;
+  height: 16px;
+}
+
+.addonTypeExtension image {
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+.addonTypeTheme image {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+vbox[typeName="status"][type="search-failure"],
+vbox[typeName="status"][type="recommended-failure"],
+vbox[typeName="status"][type="retrieve-search"],
+vbox[typeName="status"][type="retrieve-recommended"] {
+  margin-top: 2em;
+}
+
+vbox[typeName="status"][type="footer-recommended"],
+vbox[typeName="status"][type="footer-search"] {
+  margin-top: 1em;
+}
+
+vbox[typeName="status"][type="header-recommended"] {
+  font-size: 150%;
+  background: -moz-dialog;
+}
+
+#searchPanel {
+  border-bottom: 1px solid #878787;
+}
+
+#searchbox {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 19px 19px 0px);
+}
+
+.searchbox-search, .searchbox-cancel {
+  -moz-appearance: none;
+  cursor: pointer;
+  margin: 0;
+  border: 0;
+  padding: 0;
+  width: 19px;
+  height: 19px;
+  min-width: 19px;
+  min-height: 19px;
+}
+
+.searchbox-search {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 38px 19px 19px);
+}
+
+.searchbox-cancel {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 57px 19px 38px);
+}
+
 #progressBox {
   padding: 5px 5px 5px 5px;
 }
 
 #progressBox > hbox {
   -moz-box-align: center;
 }
 
new file mode 100644
index 0000000000000000000000000000000000000000..986f999c78f12f8e71ec20755252d8df1f6d447f
GIT binary patch
literal 568
zc%17D@N?(olHy`uVBq!ia0vp^E<o(T!3-oT_!s{MQjEnx?oJHr&dIz4a$*B~LR|m<
z|9|GpnNOcSeV)kh_3PI=ckcZ8^XJ;NYnLuv`uX$cn>TOn-@pI#>C-=t_y2u$9tgf~
z3HbNpJrF#4^yuZwmtU6|{QY?U*R}aSb|w5gQU7(m!j~BWzwfO7b-w?{!GgoWVLl*R
zOM?7@862M7NCR>>3p^r=85p>QL70(Y)*K0-AbW|YuPgg?9uaO%{q;(R_<?#tJY5_^
zA`YLud^+!tfk10wj5-S!ujlQ8*H8b-Pwq*acroM54<D8}tyASS<6LUGkJd!z&2g;0
zZ`mBy?jOIWmwTbyc`oi-<vZ3!CQl4o8<amSW69c;+3ri;``E9MDg1lx_9{+Mxh-w;
zw#Qd2`G4<8OVY8g$0gs$)TM2>d^CRk-2Ottvd1?cmHm5^d-z`Ba@*zBeS1%zoA>U;
zoHD+WlQ%P$cP}sNSbP4Q!~6OFYRi6|{25dx;BknL$@7Mzf}u5sN+Nrs%3FmAOJrFl
zZD@87LP**k=@SgP2f>r_j&o?{ALn@4C#$@~x~ylyCp0XV@7^Ag0Kq9+7CUQg0OE~M
uya5$gZITsR0Y)8QaswExfTOvYF$_<(*-vRdB6S%U<P4s!elF{r5}E)fj|-gu
new file mode 100644
index 0000000000000000000000000000000000000000..68863e2bed5b3c00d8ddb44907a7c32d5244cade
GIT binary patch
literal 1540
zc$@(Q2K)JmP)<h;3K|Lk000e1NJLTq0021v000vR0ssI2rA;z|00001b5ch_0Itp)
z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3?B>$8Y|zIr~m*3mPtfGR7l6g
zmQ83>R}{z3{d(ghF=Na)$#llpq|nqXMp2@mSb~D?x{zSojTEJnmLlD%?M7^=1wl)t
z3yEYQG+i|iA#Q34L6I`H2}L`t&4&{;nbf2+m1dmx?m4H68)J;mEF!dL_1?WR@BZ$&
z|MR~`Ff+VBFf#)K0}%lLF)f}IAQl3|{OgL>(k}u^OpekKm*>j&?%%J9M&t3g5JE^H
zg<Sjq5`baFxFM|H=e{k@JwU|9+LvE%ePfx7I#6P&JgW5d^<BDj$(>R%nLK##U~_Y`
zqm)W&rG*d@BuEgjLRPut-M5cQb2G`B4awT3slW2eoj$MwMT<Q>J$-$BQp&crwuXj=
z$;rvnr%&hedB5TZL4X#6AYf)9f)`NS(!6?l6%!)|kAg?BXdIS@Wk!n=6BAdiT!}`b
zCr+G5r_)4q@#4k){{G(H-b05DX|0)A8!fERMiY^gav4QEZ}tgm@6gx8L`X=4h|Hj+
znV22x{^1L2Eh4_R?~|pUCUU7A85se9j*gCWIt>6)%FfQt{{H^4v9WTw?0KFmyZJiP
z;R48Jv)8U&d+^{vG#YJcYTCble>55mXC<bF>Ly}xG9+dONY<?<CT9Be&M%=0phd^w
z(4UMWGBYuk%Vjq%m2|;jA~^fy+1Z)dAP4~9;lqcyT<+AVQ^$@S^E@v^uZZNZX8xJX
z&-|XB$xY1sHdz{<EZzYSXl>|`F)$*gQYmI07#J8G9cAVq2zq*Y03e-8RVo$V_kG_-
zi*5r<OIfd8z3Lfn_wL;_H8pNz)22=B?d@mIoUu=@s~0knpZOzdYSvV*E6x4Ij7Ui2
z6C(lDR@a^U=0j_(MLhY=`6r4cX68g9v1`|^!NI}f$B#ERH_y(_7K=rtRQtB}APAIF
zN-1lt)*2C|dP<Jg+JS+Axw*N9hK3_Yj&xn<>gec*)yBHJyOmP6Zr#dcGD}(C+Wrv$
zTp0MQHo68WAQwyoR47g)6RC#yOAPem(ACAYHGdZ{@7=psDK$7a=u#`C+_rsNbyc<R
z`&w%TKtyN5OC(S(mtFF**{l%agZDp}o}NB`{=Dz|LWn}4@LV52w8Vr+#7IoUM1-gi
ziJa`L#ZaS_QtsTjGn2^_3Wa<=fBpLP@v-rZuWfWYA)J@>vv*`<RRj?MKnT(DN=va=
zjKyMgb#;Y8K}uOwRTaY8HSj4~td7(Y5t1b!a?DKitCQn*N2mXs3apRx6zjtM>FP#n
zy>{)|s;a6RH*QQ#O_fTeL?YqJP6#od*QY~CwR-jHQmJ&{%>%L8*tv7(GMUWb!-u=N
zx=N)|JRT3ztHlk8R|vT9_ucjNuM)U2phEEuGfz$5`~2PRrTG*90;H5mDItZB(imf{
zW#+cFw&CI7TrQVPCWVyRnE95#{cAC+Qk|WhmoH!L{jS&deP+&Pv$t>GZftBUl}fFx
ztsy=Tphcju>6`U6jd!Q>!@2Cww|=~HZ!BJuLPQ(p15!vSq+>POL_E(JV>~YsiMWCo
z86Ay8BAzkwapI+v&stVXOUvfXn=6&dLZ9IKez{yedh}?xzuF*RqV@4sB>Z!>@bTMU
z0l=}YLoX##7A;Z;>*Av~#vmdh3L&g9#+ZHk_60%U=(W~bYxn!mUSP+L9j&dcLqkL3
z<Kyvl@pLM^Wy_XlTbu>YvNuPDk<eNTEFN#bUjJ!ONmCt(g|dPGr4%!3tv%0kMW&R}
zT5sLDRS2P!(#Ck6=bUT4H4KaT`uaV4_AK8Ih!yv*#KgqTjQqg2fenJdK8}UYdY)&D
zahq`ojzl8=b^j_rfCR}B06gQlPQXb5ADfhvQV8Kh;%0^XKYDE;1TaV?-NAa`p+q4B
qGrL-JRyBY6QfRHElq+st0pMSe+h-J1q4hNY0000<MNUMnLSTXjZ|)QT
--- a/toolkit/themes/pinstripe/mozapps/jar.mn
+++ b/toolkit/themes/pinstripe/mozapps/jar.mn
@@ -8,20 +8,24 @@ classic.jar:
   skin/classic/mozapps/downloads/unknownContentType.css           (downloads/unknownContentType.css)
   skin/classic/mozapps/extensions/extensionItem.png               (extensions/extensionItem.png)
   skin/classic/mozapps/extensions/itemDisabledFader.png           (extensions/itemDisabledFader.png)
   skin/classic/mozapps/extensions/itemEnabledFader.png            (extensions/itemEnabledFader.png)
   skin/classic/mozapps/extensions/notifyBadges.png                (extensions/notifyBadges.png)
   skin/classic/mozapps/extensions/question.png                    (extensions/question.png)
   skin/classic/mozapps/extensions/themeGeneric.png                (extensions/themeGeneric.png)
   skin/classic/mozapps/extensions/viewButtons.png                 (extensions/viewButtons.png)
+  skin/classic/mozapps/extensions/ratings.png                     (extensions/ratings.png)
+  skin/classic/mozapps/extensions/extensionIcons.png              (extensions/extensionIcons.png)
+  skin/classic/mozapps/extensions/searchIcons.png                 (extensions/searchIcons.png)
   skin/classic/mozapps/extensions/about.css                       (extensions/about.css)
   skin/classic/mozapps/extensions/extensions.css                  (extensions/extensions.css)
   skin/classic/mozapps/extensions/extensions.xml                  (extensions/extensions.xml)
   skin/classic/mozapps/extensions/update.css                      (extensions/update.css)
+  skin/classic/mozapps/extensions/eula.css                        (extensions/eula.css)
   skin/classic/mozapps/plugins/missingPlugin.css                  (plugins/missingPlugin.css)
   skin/classic/mozapps/plugins/pluginGeneric.png                  (plugins/pluginGeneric.png)
   skin/classic/mozapps/profile/profileicon.png                    (profile/profileicon.png)
   skin/classic/mozapps/profile/profileSelection.css               (profile/profileSelection.css)
   skin/classic/mozapps/shared/itemFader.png                       (shared/itemFader.png)
   skin/classic/mozapps/shared/itemSelected.png                    (shared/itemSelected.png)
   skin/classic/mozapps/shared/viewFader.png                       (shared/viewFader.png)
   skin/classic/mozapps/shared/richview.css                        (shared/richview.css)
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/winstripe/mozapps/extensions/eula.css
@@ -0,0 +1,16 @@
+#heading {
+  font-size: 120%;
+}
+
+#scrollbox {
+  overflow-y: auto;
+  background-color: window;
+  border: 1px solid ActiveBorder;
+  margin: 1em 0 1em 0;
+  padding: 5px;
+}
+
+#eula {
+  font-family: monospace;
+  white-space: -moz-pre-wrap;
+}
new file mode 100644
index 0000000000000000000000000000000000000000..ca7f72ed45241fff11c0a8d47f69104738cb840b
GIT binary patch
literal 1375
zc$@)W1)%zgP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp)
z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3=|9$0g2bOSpWb8@kvBMR7l6g
zm3wHGRUF4Z=Xsv@Hs7hc^*Y_0wJcFbnxZnJ?4m_w*aaCyW>%I-p(wS)O3V}^D-F>K
zu`IigTA7(v=wj2_nx&b}VY=z2TQ2AJZhJ4!b1wa{`DXqR6hQ|LoWt)Nz8}87-|rmw
zpNF_`vtrM*;sN1;-9bC#cPtyrvXEM0w4tW;!1blqepL2vM2>BJ>F&=LKh-#=n#zk~
z)?aBvUb?T3FF!nxfxQQjpI$&-xPa_n1WP!@|3-Yv(z~YAm+#z=%o{!RlaD{J|JDJ3
zm3tn3dPru`vdmD1Fo0Ap)y-93Jvsi(oBxUM#<$;iz)1JOk->wbzgf3kj9s#ku2`Jk
zTiY7i+M}}`ojG%N3Sj;x_e}8F_5>jWAhM;Alcx;5VW8BCs)qfhEgo9|bj3Wk?3Ft=
z{cRdHu3vL+MrQVs!YhVGbW;_lHax>?M@o2n*?fw;UkK&qQr!~OH8qDO&z=2PnFG+$
z6`OEXzuwPZo_!@?<Jb;oyIK$khF)@+&_>U<Fzh(;6F}MY4Yw{3!rHt3{%t9k?OQiR
zVjUgphmRb6-RUMt=@YNBs-%G*YaODITk$j`7R@H^4>Dp)rFE?C<g36)2jJ|vP7>Jx
zcGOg>Vf_Z!+3CH(blal=Z8VmEwyv&TC2!uaGc(Pd)E-kQ2e)tC6wylUzUj6p1&-sR
zuKo~@OcEU4_5~wmt!3&h#Uv!eOIMSW;KiU%M&H4yB22o{-B;Io=y-Gc@K1j_*wLP7
zBO&7?wTr8gc*-T3jMCl}pVvRjnk2QPGvTFd?~Jyu9Y0}W!NU*DV*0d)7!YozC{)8S
zHHyNCCEel@zP}8n_MJ^-M+HJy0>A<Idfwipz*2zX<zr-X^bD@2v27cH!IctEORnti
z7YR?2RFb%qDS+&p$c;XqAB;gNg=*YK`IZVsy;y-~9O4oZ5|Rp(0ZTwM<|b1B7Y?H#
z=EhMPP+&}V3Z&Kup-Cu(HU`X}4Wx}R0PEga$ElNbbbPr6-{hqP`xVle@bK{ASLKX!
z3@dZ?lPltEZ`%BR3gGI+qZj37IHmP1YEDMj@^vO$EDJ0I8jYtw2+iKw#L1!g{`_!I
z2rwyt*0U`ut1Bz!Ml!=z;^<3M9<s@rbY0z!kKcMPJu@;fXYjajMd8QD3HczjkMGLA
zZA$N(voHnVO1L!Q1VRC^CM#?cmy+6M6*WRAZBY4r$ja_*iEzOF{MXa6VNk?g<|$G|
zF!jC}&u#l`^Qyz;A0A0}cZEE48;z$=H>8C#u8GIp!3ib!EwGw&JHQX+I~$XpzP<F(
zlD5C3KxxS6WnqM&_LOYsI1?XsV8zcd!02UT-bxEtGl1W#7FI16ua2)X(0F0$I;!6-
z3}FQ;c3E=JEroXB@Uho=T9B1Dn1IjXMD1_7?)cHomJ^)w>=V=4&fiM`_#J_u+t{K4
zU;aT@2h6<s#W8mjMw|!xqzU|C#{zm)&fT-);@Vlg;nVzJ*jl7~xn;Rdcu2hM%xWhP
zbWR?q@aj(--`0BW+^V%Nzp$@I;=EKhK*-1UyMB(pB7`t>M@(ACst5c&i>AYk@@1ur
z74VrVpcfG6(T$$xuQ<Uspo4HKuzUZ4C{PTXfBO~S0eL_YNB}Xw760L{Dtg`(yJuP#
ho$2W-RL|M~<`2~8HIHbijvxR4002ovPDHLkV1fnqrO^NY
--- a/toolkit/themes/winstripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/winstripe/mozapps/extensions/extensions.css
@@ -258,16 +258,148 @@ richlistitem[loading="true"] .updateBadg
   font-weight: normal;
   border: none;
 }
 
 richlistitem[opType="needs-uninstall"] .notifyBadge {
   display: none;
 }
 
+.addon-search-details {
+  margin-top: 5px;
+  margin-bottom: 5px;
+  -moz-margin-start: 6px;
+  -moz-margin-end: 0;
+}
+
+.addonThumbnailContainer {
+  background: window;
+  padding: 5px;
+  border: 2px solid ActiveBorder;
+  width: 135px;
+  min-height: 104px;
+  -moz-margin-end: 5px;
+}
+
+.addonMissingThumbnail {
+  color: GrayText;
+}
+
+.addonFailure {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png");
+  -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+.addonRating {
+  display: none;
+}
+
+.addonLearnMore {
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
+.addonRating[rating] {
+  display: -moz-box;
+  width: 68px;
+  height: 12px;
+  list-style-image: url("chrome://mozapps/skin/extensions/ratings.png");
+}
+
+.addonRating[rating="0"] {
+  -moz-image-region: rect(0px 68px 12px 0px);
+}
+
+.addonRating[rating="1"], .addonRating[rating="2"] {
+  -moz-image-region: rect(12px 68px 24px 0px);
+}
+
+.addonRating[rating="3"], .addonRating[rating="4"] {
+  -moz-image-region: rect(24px 68px 36px 0px);
+}
+
+.addonRating[rating="5"], .addonRating[rating="6"] {
+  -moz-image-region: rect(36px 68px 48px 0px);
+}
+
+.addonRating[rating="7"], .addonRating[rating="8"] {
+  -moz-image-region: rect(48px 68px 60px 0px);
+}
+
+.addonRating[rating="9"], .addonRating[rating="10"] {
+  -moz-image-region: rect(60px 68px 72px 0px);
+}
+
+.addonType image {
+  -moz-margin-start: 6px;
+  list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png");
+  width: 16px;
+  height: 16px;
+}
+
+.addonTypeExtension image {
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+.addonTypeTheme image {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+vbox[typeName="status"][type="search-failure"],
+vbox[typeName="status"][type="recommended-failure"],
+vbox[typeName="status"][type="retrieve-search"],
+vbox[typeName="status"][type="retrieve-recommended"] {
+  margin-top: 2em;
+}
+
+vbox[typeName="status"][type="footer-recommended"],
+vbox[typeName="status"][type="footer-search"] {
+  margin-top: 1em;
+}
+
+vbox[typeName="status"][type="header-recommended"] {
+  font-size: 150%;
+  background: -moz-dialog;
+}
+
+#searchbox {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 19px 19px 0px);
+  padding: 0;
+}
+
+.searchbox-search, .searchbox-cancel {
+  -moz-appearance: none;
+  cursor: pointer;
+  margin: 0;
+  border: 0;
+  padding: 0;
+  width: 19px;
+  height: 19px;
+  min-width: 19px;
+}
+
+.searchbox-search .button-box,
+.searchbox-cancel .button-box {
+  border: 0px;
+  padding: 0px;
+}
+
+.searchbox-search {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 38px 19px 19px);
+}
+
+.searchbox-cancel {
+  list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png);
+  -moz-image-region: rect(0px 57px 19px 38px);
+}
+
 #progressBox {
   padding: 5px 5px 5px 5px;
 }
 
 #progressBox > hbox {
   -moz-box-align: center;
 }
 
@@ -340,16 +472,23 @@ radio#updates-view:hover, radio#updates-
 
 radio#installs-view {
   -moz-image-region: rect(0px, 192px, 32px, 160px)
 }
 radio#installs-view:hover, radio#installs-view[selected="true"] {
   -moz-image-region: rect(32px, 192px, 64px, 160px)
 }
 
+radio#search-view {
+  -moz-image-region: rect(0px, 224px, 32px, 192px)
+}
+radio#search-view:hover, radio#search-view[selected="true"] {
+  -moz-image-region: rect(32px, 224px, 64px, 192px)
+}
+
 /* Update view checkbox */
 .includeUpdate {
   -moz-user-focus: none;
 }
 
 richlistitem[selected="true"] .includeUpdate {
   -moz-user-focus: normal;
 }
new file mode 100644
index 0000000000000000000000000000000000000000..986f999c78f12f8e71ec20755252d8df1f6d447f
GIT binary patch
literal 568
zc%17D@N?(olHy`uVBq!ia0vp^E<o(T!3-oT_!s{MQjEnx?oJHr&dIz4a$*B~LR|m<
z|9|GpnNOcSeV)kh_3PI=ckcZ8^XJ;NYnLuv`uX$cn>TOn-@pI#>C-=t_y2u$9tgf~
z3HbNpJrF#4^yuZwmtU6|{QY?U*R}aSb|w5gQU7(m!j~BWzwfO7b-w?{!GgoWVLl*R
zOM?7@862M7NCR>>3p^r=85p>QL70(Y)*K0-AbW|YuPgg?9uaO%{q;(R_<?#tJY5_^
zA`YLud^+!tfk10wj5-S!ujlQ8*H8b-Pwq*acroM54<D8}tyASS<6LUGkJd!z&2g;0
zZ`mBy?jOIWmwTbyc`oi-<vZ3!CQl4o8<amSW69c;+3ri;``E9MDg1lx_9{+Mxh-w;
zw#Qd2`G4<8OVY8g$0gs$)TM2>d^CRk-2Ottvd1?cmHm5^d-z`Ba@*zBeS1%zoA>U;
zoHD+WlQ%P$cP}sNSbP4Q!~6OFYRi6|{25dx;BknL$@7Mzf}u5sN+Nrs%3FmAOJrFl
zZD@87LP**k=@SgP2f>r_j&o?{ALn@4C#$@~x~ylyCp0XV@7^Ag0Kq9+7CUQg0OE~M
uya5$gZITsR0Y)8QaswExfTOvYF$_<(*-vRdB6S%U<P4s!elF{r5}E)fj|-gu
new file mode 100644
index 0000000000000000000000000000000000000000..68863e2bed5b3c00d8ddb44907a7c32d5244cade
GIT binary patch
literal 1540
zc$@(Q2K)JmP)<h;3K|Lk000e1NJLTq0021v000vR0ssI2rA;z|00001b5ch_0Itp)
z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3?B>$8Y|zIr~m*3mPtfGR7l6g
zmQ83>R}{z3{d(ghF=Na)$#llpq|nqXMp2@mSb~D?x{zSojTEJnmLlD%?M7^=1wl)t
z3yEYQG+i|iA#Q34L6I`H2}L`t&4&{;nbf2+m1dmx?m4H68)J;mEF!dL_1?WR@BZ$&
z|MR~`Ff+VBFf#)K0}%lLF)f}IAQl3|{OgL>(k}u^OpekKm*>j&?%%J9M&t3g5JE^H
zg<Sjq5`baFxFM|H=e{k@JwU|9+LvE%ePfx7I#6P&JgW5d^<BDj$(>R%nLK##U~_Y`
zqm)W&rG*d@BuEgjLRPut-M5cQb2G`B4awT3slW2eoj$MwMT<Q>J$-$BQp&crwuXj=
z$;rvnr%&hedB5TZL4X#6AYf)9f)`NS(!6?l6%!)|kAg?BXdIS@Wk!n=6BAdiT!}`b
zCr+G5r_)4q@#4k){{G(H-b05DX|0)A8!fERMiY^gav4QEZ}tgm@6gx8L`X=4h|Hj+
znV22x{^1L2Eh4_R?~|pUCUU7A85se9j*gCWIt>6)%FfQt{{H^4v9WTw?0KFmyZJiP
z;R48Jv)8U&d+^{vG#YJcYTCble>55mXC<bF>Ly}xG9+dONY<?<CT9Be&M%=0phd^w
z(4UMWGBYuk%Vjq%m2|;jA~^fy+1Z)dAP4~9;lqcyT<+AVQ^$@S^E@v^uZZNZX8xJX
z&-|XB$xY1sHdz{<EZzYSXl>|`F)$*gQYmI07#J8G9cAVq2zq*Y03e-8RVo$V_kG_-
zi*5r<OIfd8z3Lfn_wL;_H8pNz)22=B?d@mIoUu=@s~0knpZOzdYSvV*E6x4Ij7Ui2
z6C(lDR@a^U=0j_(MLhY=`6r4cX68g9v1`|^!NI}f$B#ERH_y(_7K=rtRQtB}APAIF
zN-1lt)*2C|dP<Jg+JS+Axw*N9hK3_Yj&xn<>gec*)yBHJyOmP6Zr#dcGD}(C+Wrv$
zTp0MQHo68WAQwyoR47g)6RC#yOAPem(ACAYHGdZ{@7=psDK$7a=u#`C+_rsNbyc<R
z`&w%TKtyN5OC(S(mtFF**{l%agZDp}o}NB`{=Dz|LWn}4@LV52w8Vr+#7IoUM1-gi
ziJa`L#ZaS_QtsTjGn2^_3Wa<=fBpLP@v-rZuWfWYA)J@>vv*`<RRj?MKnT(DN=va=
zjKyMgb#;Y8K}uOwRTaY8HSj4~td7(Y5t1b!a?DKitCQn*N2mXs3apRx6zjtM>FP#n
zy>{)|s;a6RH*QQ#O_fTeL?YqJP6#od*QY~CwR-jHQmJ&{%>%L8*tv7(GMUWb!-u=N
zx=N)|JRT3ztHlk8R|vT9_ucjNuM)U2phEEuGfz$5`~2PRrTG*90;H5mDItZB(imf{
zW#+cFw&CI7TrQVPCWVyRnE95#{cAC+Qk|WhmoH!L{jS&deP+&Pv$t>GZftBUl}fFx
ztsy=Tphcju>6`U6jd!Q>!@2Cww|=~HZ!BJuLPQ(p15!vSq+>POL_E(JV>~YsiMWCo
z86Ay8BAzkwapI+v&stVXOUvfXn=6&dLZ9IKez{yedh}?xzuF*RqV@4sB>Z!>@bTMU
z0l=}YLoX##7A;Z;>*Av~#vmdh3L&g9#+ZHk_60%U=(W~bYxn!mUSP+L9j&dcLqkL3
z<Kyvl@pLM^Wy_XlTbu>YvNuPDk<eNTEFN#bUjJ!ONmCt(g|dPGr4%!3tv%0kMW&R}
zT5sLDRS2P!(#Ck6=bUT4H4KaT`uaV4_AK8Ih!yv*#KgqTjQqg2fenJdK8}UYdY)&D
zahq`ojzl8=b^j_rfCR}B06gQlPQXb5ADfhvQV8Kh;%0^XKYDE;1TaV?-NAa`p+q4B
qGrL-JRyBY6QfRHElq+st0pMSe+h-J1q4hNY0000<MNUMnLSTXjZ|)QT
index 8bed36fc4608e2d72a230611dda759b5c7d4f26e..9ee4d9a68f093ee08a3d7c614a4144683e78a703
GIT binary patch
literal 24552
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
XLlv?%rcXIw00000NkvXXu0mjf)1!5a
--- a/toolkit/themes/winstripe/mozapps/jar.mn
+++ b/toolkit/themes/winstripe/mozapps/jar.mn
@@ -10,16 +10,20 @@ classic.jar:
         skin/classic/mozapps/extensions/extensions.css                (extensions/extensions.css)
         skin/classic/mozapps/extensions/itemDisabledFader.png         (extensions/itemDisabledFader.png)
         skin/classic/mozapps/extensions/itemEnabledFader.png          (extensions/itemEnabledFader.png)
         skin/classic/mozapps/extensions/notifyBadges.png              (extensions/notifyBadges.png)
         skin/classic/mozapps/extensions/question.png                  (extensions/question.png)
         skin/classic/mozapps/extensions/update.css                    (extensions/update.css)
         skin/classic/mozapps/extensions/themeGeneric.png              (extensions/themeGeneric.png)
         skin/classic/mozapps/extensions/viewButtons.png               (extensions/viewButtons.png)
+        skin/classic/mozapps/extensions/ratings.png                   (extensions/ratings.png)
+        skin/classic/mozapps/extensions/extensionIcons.png            (extensions/extensionIcons.png)
+        skin/classic/mozapps/extensions/searchIcons.png               (extensions/searchIcons.png)
+        skin/classic/mozapps/extensions/eula.css                      (extensions/eula.css)
         skin/classic/mozapps/plugins/missingPlugin.css                (plugins/missingPlugin.css)
         skin/classic/mozapps/plugins/pluginGeneric.png                (plugins/pluginGeneric.png)
         skin/classic/mozapps/plugins/pluginInstallerWizard.css        (plugins/pluginInstallerWizard.css)
         skin/classic/mozapps/profile/profileicon.png                  (profile/profileicon.png)
         skin/classic/mozapps/profile/profileSelection.css             (profile/profileSelection.css)
         skin/classic/mozapps/shared/itemFader.png                     (shared/itemFader.png)
         skin/classic/mozapps/shared/itemSelected.png                  (shared/itemSelected.png)
         skin/classic/mozapps/shared/richview.css                      (shared/richview.css)