Bug 594795 - Add a 'What's an Add-on' block to the Add-ons Manager [r=vingtetun]
authorMark Finkle <mfinkle@mozilla.com>
Wed, 29 Sep 2010 15:54:48 -0400
changeset 66734 f74e51a1e66f98434b99363cfc3bba234b97a848
parent 66733 fec34d1ef66aa367e0c2f8d206220628f9198a21
child 66735 4c614114c223ff4449bfcae3c8770860dbd9a693
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvingtetun
bugs594795
Bug 594795 - Add a 'What's an Add-on' block to the Add-ons Manager [r=vingtetun]
mobile/chrome/content/bindings/extensions.xml
mobile/chrome/content/browser.css
mobile/chrome/content/extensions.js
mobile/locales/en-US/chrome/browser.properties
mobile/themes/core/browser.css
mobile/themes/core/platform.css
--- a/mobile/chrome/content/bindings/extensions.xml
+++ b/mobile/chrome/content/bindings/extensions.xml
@@ -236,9 +236,30 @@
         <xul:image class="addon-showmore-image" xbl:inherits="src=image"/>
         <xul:label class="title" xbl:inherits="value=label"/>
         <xul:spacer flex="1"/>
         <xul:button label="&addonShowPage.label;" oncommand="ExtensionsView.showMoreResults(document.getBindingParent(this));"/>
       </xul:hbox>
     </content>
   </binding>
 
+  <binding id="extension-search-banner" extends="chrome://browser/content/bindings.xml#richlistitem">
+    <content orient="vertical" nohighlight="true">
+      <xul:hbox align="start">
+        <xul:image class="addon-banner-image" xbl:inherits="src=image"/>
+        <xul:vbox flex="1">
+          <xul:hbox align="center">
+            <xul:label class="title" xbl:inherits="value=label" crop="end" flex="1"/>
+          </xul:hbox>
+          <xul:vbox>
+            <xul:label class="normal-black hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
+            <xul:description class="normal-black show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
+          </xul:vbox>
+        </xul:vbox>
+      </xul:hbox>
+      <xul:hbox class="show-on-select">
+        <xul:spacer flex="1"/>
+        <xul:button xbl:inherits="label=button,hidden=hidebutton,oncommand=onbuttoncommand"/>
+      </xul:hbox>
+    </content>
+  </binding>
+
 </bindings>
--- a/mobile/chrome/content/browser.css
+++ b/mobile/chrome/content/browser.css
@@ -153,16 +153,20 @@ richlistitem[typeName="search"] hbox.add
 richlistitem[typeName="message"] {
   -moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-message");
 }
 
 richlistitem[typeName="showmore"] {
   -moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-search-showmore");
 }
 
+richlistitem[typeName="banner"] {
+  -moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-search-banner");
+}
+
 richlistitem[typeName="download"][state="-1"] {
   -moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-not-started");
 }
 
 richlistitem[typeName="download"] {
   -moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-downloading");
 }
 
--- a/mobile/chrome/content/extensions.js
+++ b/mobile/chrome/content/extensions.js
@@ -487,102 +487,143 @@ var ExtensionsView = {
       else {
         this.displaySectionMessage("repo", strings.getString("addonsSearchStart.label"),
                                   strings.getString("addonsSearchStart.button"), false);
         AddonRepository.retrieveRecommendedAddons(Services.prefs.getIntPref(PREF_GETADDONS_MAXRESULTS), RecommendedSearchResults);
       }
     }
   },
 
-  displaySearchResults: function ev_displaySearchResults(aAddons, aTotalResults, aIsRecommended, aSelectFirstResult) {
-    this.clearSection("repo");
-
-    let strings = Elements.browserBundle;
-    if (aAddons.length == 0) {
-      let msg = aIsRecommended ? strings.getString("addonsSearchNone.recommended") :
-                                 strings.getString("addonsSearchNone.search");
-      let button = aIsRecommended ? strings.getString("addonsSearchNone.button") :
-                                    strings.getString("addonsSearchSuccess2.button");
-      let item = this.displaySectionMessage("repo", msg, button, true);
-
-      if (aSelectFirstResult)
-        this._list.scrollBoxObject.scrollToElement(item);
-      return;
-    }
-
-    if (aIsRecommended) {
-      // Locale sensitive sort
-      function compare(a, b) {
-        return String.localeCompare(a.name, b.name);
-      }
-      aAddons.sort(compare);
-    }
-
+  appendSearchResults: function(aAddons, aShowRating) {
     var urlproperties = [ "iconURL", "homepageURL" ];
     var properties = [ "name", "iconURL", "homepageURL", "screenshots" ];
     var foundItem = false;
     for (let i = 0; i < aAddons.length; i++) {
       let addon = aAddons[i];
 
+      // Check for a duplicate add-on, already in the search results
+      // (can happen when blending the recommended and browsed lists)
+      let element = ExtensionsView.getElementForAddon(addon.install.sourceURI.spec);
+      if (element)
+        continue;
+
       // Check for any items with potentially unsafe urls
       if (urlproperties.some(function (p) !this._isSafeURI(addon[p]), this))
         continue;
-      if (addon.screenshots &&
-          addon.screenshots.some(function (aScreenshot) !this._isSafeURI(aScreenshot), this))
+      if (addon.screenshots && addon.screenshots.some(function (aScreenshot) !this._isSafeURI(aScreenshot), this))
         continue;
 
       // Convert the numeric type to a string
       let types = {"2":"extension", "4":"theme", "8":"locale"};
       addon.type = types[addon.type];
 
       let listitem = this._createItem(addon, "search");
       listitem.setAttribute("description", addon.description);
       listitem.setAttribute("homepageURL", addon.homepageURL);
       listitem.install = addon.install;
       listitem.setAttribute("sourceURL", addon.install.sourceURI.spec);
-      if (!aIsRecommended)
+      if (aShowRating)
         listitem.setAttribute("rating", addon.averageRating);
 
       let item = this._list.appendChild(listitem);
+    }
+  },
 
-      if (aSelectFirstResult && !foundItem) {
-        foundItem = true;
-        this._list.selectItem(item);
+  displayRecommendedResults: function ev_displaySearchResults(aRecommendedAddons, aBrowseAddons) {
+    this.clearSection("repo");
+
+    let strings = Elements.browserBundle;
+    let brandBundle = document.getElementById("bundle_brand");
+    let brandShortName = brandBundle.getString("brandShortName");
+
+    let whatare = document.createElement("richlistitem");
+    whatare.setAttribute("typeName", "banner");
+    whatare.setAttribute("label", strings.getString("addonsWhatAre.label"));
+
+    let desc = strings.getString("addonsWhatAre.description");
+    desc = desc.replace(/#1/g, brandShortName);
+    whatare.setAttribute("description", desc);
+
+    whatare.setAttribute("button", strings.getString("addonsWhatAre.button"));
+    whatare.setAttribute("onbuttoncommand", "BrowserUI.newTab('http://ebay.com');");
+    this._list.appendChild(whatare);
+
+    if (aRecommendedAddons.length == 0 && aBrowseAddons.length == 0) {
+      let msg = strings.getString("addonsSearchNone.recommended");
+      let button = strings.getString("addonsSearchNone.button");
+      let item = this.displaySectionMessage("repo", msg, button, true);
+
+      this._list.scrollBoxObject.scrollToElement(item);
+      return;
+    }
+
+    // Locale sensitive sort
+    function nameCompare(a, b) {
+      return String.localeCompare(a.name, b.name);
+    }
+    aRecommendedAddons.sort(nameCompare);
+
+    // Rating sort
+    function ratingCompare(a, b) {
+      return a.averageRating < b.averageRating;
+    }
+    aBrowseAddons.sort(ratingCompare);
+
+    this.appendSearchResults(aRecommendedAddons, false);
+    this.appendSearchResults(aBrowseAddons, true);
+
+    let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
+
+    let showmore = document.createElement("richlistitem");
+    showmore.setAttribute("typeName", "showmore");
+    showmore.setAttribute("label", strings.getString("addonsBrowseAll.label"));
+
+    let url = formatter.formatURLPref("extensions.getAddons.browseAddons");
+    showmore.setAttribute("url", url);
+    this._list.appendChild(showmore);
+  },
+
+  displaySearchResults: function ev_displaySearchResults(aAddons, aTotalResults, aSelectFirstResult) {
+    this.clearSection("repo");
+
+    let strings = Elements.browserBundle;
+    if (aAddons.length == 0) {
+      let msg = strings.getString("addonsSearchNone.search");
+      let button = strings.getString("addonsSearchSuccess2.button");
+      let item = this.displaySectionMessage("repo", msg, button, true);
+
+      if (aSelectFirstResult)
         this._list.scrollBoxObject.scrollToElement(item);
-      }
+      return;
+    }
+
+    let firstItem = this.appendSearchResults(aAddons, true);
+    if (aSelectFirstResult) {
+      this._list.selectItem(firstItem);
+      this._list.scrollBoxObject.scrollToElement(firstItem);
     }
 
     let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
 
-    if (!aIsRecommended) {
-      if (aTotalResults > aAddons.length) {
-        let showmore = document.createElement("richlistitem");
-        showmore.setAttribute("typeName", "showmore");
-
-        let labelBase = strings.getString("addonsSearchMore.label");
-        let label = PluralForm.get(aTotalResults, labelBase).replace("#1", aTotalResults);
-        showmore.setAttribute("label", label);
-
-        let url = Services.prefs.getCharPref("extensions.getAddons.search.browseURL");
-        url = url.replace(/%TERMS%/g, encodeURIComponent(this.searchBox.value));
-        url = formatter.formatURL(url);
-        showmore.setAttribute("url", url);
-        this._list.appendChild(showmore);
-      }
-
-      this.displaySectionMessage("repo", null, strings.getString("addonsSearchSuccess2.button"), true);
-    } else {
+    if (aTotalResults > aAddons.length) {
       let showmore = document.createElement("richlistitem");
       showmore.setAttribute("typeName", "showmore");
-      showmore.setAttribute("label", strings.getString("addonsBrowseAll.label"));
 
-      let url = formatter.formatURLPref("extensions.getAddons.browseAddons");
+      let labelBase = strings.getString("addonsSearchMore.label");
+      let label = PluralForm.get(aTotalResults, labelBase).replace("#1", aTotalResults);
+      showmore.setAttribute("label", label);
+
+      let url = Services.prefs.getCharPref("extensions.getAddons.search.browseURL");
+      url = url.replace(/%TERMS%/g, encodeURIComponent(this.searchBox.value));
+      url = formatter.formatURL(url);
       showmore.setAttribute("url", url);
       this._list.appendChild(showmore);
     }
+
+    this.displaySectionMessage("repo", null, strings.getString("addonsSearchSuccess2.button"), true);
   },
 
   showPage: function ev_showPage(aItem) {
     let uri = aItem.getAttribute("homepageURL");
     if (uri)
       BrowserUI.newTab(uri);
   },
 
@@ -687,17 +728,27 @@ function searchFailed() {
 
 ///////////////////////////////////////////////////////////////////////////////
 // callback for the recommended search
 var RecommendedSearchResults = {
   cache: null,
 
   searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
     this.cache = aAddons;
-    ExtensionsView.displaySearchResults(aAddons, aTotalResults, true);
+    AddonRepository.searchAddons(" ", Services.prefs.getIntPref(PREF_GETADDONS_MAXRESULTS), BrowseSearchResults);
+  },
+
+  searchFailed: searchFailed
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// callback for the browse search
+var BrowseSearchResults = {
+  searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
+    ExtensionsView.displayRecommendedResults(RecommendedSearchResults.cache, aAddons);
   },
 
   searchFailed: searchFailed
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // callback for a standard search
 var AddonSearchResults = {
--- a/mobile/locales/en-US/chrome/browser.properties
+++ b/mobile/locales/en-US/chrome/browser.properties
@@ -16,16 +16,23 @@ addonsBrowseAll.description=addons.mozil
 # #1 total number of add-ons that match the search terms
 addonsSearchMore.label=Show result;Show all #1 results
 
 # LOCALIZATION NOTE (addonsSearchMore.description): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of search results currently visible
 addonsSearchMore.description=If this result isn't what you're looking for, try this;If these #1 results aren't what you're looking for, try this
 
+addonsWhatAre.label=What are Add-ons?
+addonsWhatAre.button=Learn More
+
+# LOCALIZATION NOTE (addonsWhatAre.description):
+# #1 is the application name
+addonsWhatAre.description=Add-ons are applications that let you personalize #1 with extra functionality or style. You can make #1 your own.
+
 addonsSearchEngine.description=Integrated Search
 
 addonsConfirmInstall.title=Installing Add-on
 addonsConfirmInstall.install=Install
 
 addonType.2=Extension
 addonType.4=Theme
 addonType.8=Locale
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -536,16 +536,22 @@ toolbarbutton.page-button {
 }
 
 .addon-showmore-image {
   width: 32px;
   height: 32px;
   list-style-image: url("chrome://browser/skin/images/addons-32.png");
 }
 
+.addon-banner-image {
+  width: 32px;
+  height: 32px;
+  list-style-image: url("chrome://browser/skin/images/addons-32.png");
+}
+
 richlistitem[isDisabled="true"] .title {
   color: gray;
 }
 
 richlistitem[isDisabled="true"] .normal {
   color: lightgray;
 }
 
--- a/mobile/themes/core/platform.css
+++ b/mobile/themes/core/platform.css
@@ -545,16 +545,22 @@ richlistitem description.title {
 
 richlistitem label.normal,
 richlistitem description.normal {
   color: gray;
   font-size: 18px !important;
   white-space: pre-wrap;
 }
 
+richlistitem label.normal-black,
+richlistitem description.normal-black {
+  font-size: 18px !important;
+  white-space: pre-wrap;
+}
+
 richlistitem label.normal-bold,
 richlistitem description.normal-bold {
   font-weight: bold;
   font-size: 18px !important;
   white-space: pre-wrap;
 }
 
 richlistitem[selected="true"] {