Home | History | Annotate | Download | only in webstore
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/search/search.h"
     17 #include "chrome/browser/ui/app_list/search/common/json_response_fetcher.h"
     18 #include "chrome/browser/ui/app_list/search/search_webstore_result.h"
     19 #include "chrome/browser/ui/app_list/search/webstore/webstore_result.h"
     20 #include "extensions/common/extension_urls.h"
     21 #include "url/gurl.h"
     22 
     23 namespace app_list {
     24 
     25 namespace {
     26 
     27 const char kKeyResults[] = "results";
     28 const char kKeyId[] = "id";
     29 const char kKeyLocalizedName[] = "localized_name";
     30 const char kKeyIconUrl[] = "icon_url";
     31 const char kKeyIsPaid[] = "is_paid";
     32 const char kKeyItemType[] = "item_type";
     33 
     34 const char kPlatformAppType[] = "platform_app";
     35 const char kHostedAppType[] = "hosted_app";
     36 const char kLegacyPackagedAppType[] = "legacy_packaged_app";
     37 
     38 // Converts the item type string from the web store to an
     39 // extensions::Manifest::Type.
     40 extensions::Manifest::Type ParseItemType(const std::string& item_type_str) {
     41   if (LowerCaseEqualsASCII(item_type_str, kPlatformAppType))
     42     return extensions::Manifest::TYPE_PLATFORM_APP;
     43 
     44   if (LowerCaseEqualsASCII(item_type_str, kLegacyPackagedAppType))
     45     return extensions::Manifest::TYPE_LEGACY_PACKAGED_APP;
     46 
     47   if (LowerCaseEqualsASCII(item_type_str, kHostedAppType))
     48     return extensions::Manifest::TYPE_HOSTED_APP;
     49 
     50   return extensions::Manifest::TYPE_UNKNOWN;
     51 }
     52 
     53 }  // namespace
     54 
     55 WebstoreProvider::WebstoreProvider(Profile* profile,
     56                                    AppListControllerDelegate* controller)
     57   :  WebserviceSearchProvider(profile),
     58      controller_(controller){}
     59 
     60 WebstoreProvider::~WebstoreProvider() {}
     61 
     62 void WebstoreProvider::Start(const base::string16& query) {
     63   ClearResults();
     64   if (!IsValidQuery(query)) {
     65     query_.clear();
     66     return;
     67   }
     68 
     69   query_ = base::UTF16ToUTF8(query);
     70   const CacheResult result = cache_->Get(WebserviceCache::WEBSTORE, query_);
     71   if (result.second) {
     72     ProcessWebstoreSearchResults(result.second);
     73     if (!webstore_search_fetched_callback_.is_null())
     74       webstore_search_fetched_callback_.Run();
     75     if (result.first == FRESH)
     76       return;
     77   }
     78 
     79   if (!webstore_search_) {
     80     webstore_search_.reset(new JSONResponseFetcher(
     81         base::Bind(&WebstoreProvider::OnWebstoreSearchFetched,
     82                    base::Unretained(this)),
     83         profile_->GetRequestContext()));
     84   }
     85 
     86   StartThrottledQuery(base::Bind(&WebstoreProvider::StartQuery,
     87                                  base::Unretained(this)));
     88 
     89   // Add a placeholder result which when clicked will run the user's query in a
     90   // browser. This placeholder is removed when the search results arrive.
     91   Add(scoped_ptr<SearchResult>(new SearchWebstoreResult(profile_, query_)));
     92 }
     93 
     94 void WebstoreProvider::Stop() {
     95   if (webstore_search_)
     96     webstore_search_->Stop();
     97 }
     98 
     99 void WebstoreProvider::StartQuery() {
    100   // |query_| can be NULL when the query is scheduled but then canceled.
    101   if (!webstore_search_ || query_.empty())
    102     return;
    103 
    104   webstore_search_->Start(extension_urls::GetWebstoreJsonSearchUrl(
    105       query_, g_browser_process->GetApplicationLocale()));
    106 }
    107 
    108 void WebstoreProvider::OnWebstoreSearchFetched(
    109     scoped_ptr<base::DictionaryValue> json) {
    110   ProcessWebstoreSearchResults(json.get());
    111   cache_->Put(WebserviceCache::WEBSTORE, query_, json.Pass());
    112 
    113   if (!webstore_search_fetched_callback_.is_null())
    114     webstore_search_fetched_callback_.Run();
    115 }
    116 
    117 void WebstoreProvider::ProcessWebstoreSearchResults(
    118     const base::DictionaryValue* json) {
    119   const base::ListValue* result_list = NULL;
    120   if (!json ||
    121       !json->GetList(kKeyResults, &result_list) ||
    122       !result_list ||
    123       result_list->empty()) {
    124     return;
    125   }
    126 
    127   bool first_result = true;
    128   for (base::ListValue::const_iterator it = result_list->begin();
    129        it != result_list->end();
    130        ++it) {
    131     const base::DictionaryValue* dict;
    132     if (!(*it)->GetAsDictionary(&dict))
    133       continue;
    134 
    135     scoped_ptr<SearchResult> result(CreateResult(*dict));
    136     if (!result)
    137       continue;
    138 
    139     if (first_result) {
    140       // Clears "search in webstore" place holder results.
    141       ClearResults();
    142       first_result = false;
    143     }
    144 
    145     Add(result.Pass());
    146   }
    147 }
    148 
    149 scoped_ptr<ChromeSearchResult> WebstoreProvider::CreateResult(
    150     const base::DictionaryValue& dict) {
    151   scoped_ptr<ChromeSearchResult> result;
    152 
    153   std::string app_id;
    154   std::string localized_name;
    155   std::string icon_url_string;
    156   bool is_paid = false;
    157   if (!dict.GetString(kKeyId, &app_id) ||
    158       !dict.GetString(kKeyLocalizedName, &localized_name) ||
    159       !dict.GetString(kKeyIconUrl, &icon_url_string) ||
    160       !dict.GetBoolean(kKeyIsPaid, &is_paid)) {
    161     return result.Pass();
    162   }
    163 
    164   GURL icon_url(icon_url_string);
    165   if (!icon_url.is_valid())
    166     return result.Pass();
    167 
    168   std::string item_type_string;
    169   dict.GetString(kKeyItemType, &item_type_string);
    170   extensions::Manifest::Type item_type = ParseItemType(item_type_string);
    171 
    172   result.reset(new WebstoreResult(profile_,
    173                                   app_id,
    174                                   localized_name,
    175                                   icon_url,
    176                                   is_paid,
    177                                   item_type,
    178                                   controller_));
    179   return result.Pass();
    180 }
    181 
    182 }  // namespace app_list
    183