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/metrics/field_trial.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 "chrome/common/extensions/extension_constants.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 
     32 // Returns true if the launcher should send queries to the web store server.
     33 bool UseWebstoreSearch() {
     34   const char kFieldTrialName[] = "LauncherUseWebstoreSearch";
     35   const char kEnable[] = "Enable";
     36   return base::FieldTrialList::FindFullName(kFieldTrialName) == kEnable;
     37 }
     38 
     39 }  // namespace
     40 
     41 WebstoreProvider::WebstoreProvider(Profile* profile,
     42                                    AppListControllerDelegate* controller)
     43   :  WebserviceSearchProvider(profile),
     44      controller_(controller){}
     45 
     46 WebstoreProvider::~WebstoreProvider() {}
     47 
     48 void WebstoreProvider::Start(const base::string16& query) {
     49   ClearResults();
     50   if (!IsValidQuery(query)) {
     51     query_.clear();
     52     return;
     53   }
     54 
     55   query_ = UTF16ToUTF8(query);
     56   const CacheResult result = cache_->Get(WebserviceCache::WEBSTORE, query_);
     57   if (result.second) {
     58     ProcessWebstoreSearchResults(result.second);
     59     if (!webstore_search_fetched_callback_.is_null())
     60       webstore_search_fetched_callback_.Run();
     61     if (result.first == FRESH)
     62       return;
     63   }
     64 
     65   if (UseWebstoreSearch()) {
     66     if (!webstore_search_) {
     67       webstore_search_.reset(new JSONResponseFetcher(
     68           base::Bind(&WebstoreProvider::OnWebstoreSearchFetched,
     69                      base::Unretained(this)),
     70           profile_->GetRequestContext()));
     71     }
     72 
     73     StartThrottledQuery(base::Bind(&WebstoreProvider::StartQuery,
     74                                    base::Unretained(this)));
     75   }
     76 
     77   // Add a placeholder result which when clicked will run the user's query in a
     78   // browser. This placeholder is removed when the search results arrive.
     79   Add(scoped_ptr<ChromeSearchResult>(
     80       new SearchWebstoreResult(profile_, query_)).Pass());
     81 }
     82 
     83 void WebstoreProvider::Stop() {
     84   if (webstore_search_)
     85     webstore_search_->Stop();
     86 }
     87 
     88 void WebstoreProvider::StartQuery() {
     89   // |query_| can be NULL when the query is scheduled but then canceled.
     90   if (!webstore_search_ || query_.empty())
     91     return;
     92 
     93   webstore_search_->Start(extension_urls::GetWebstoreJsonSearchUrl(
     94       query_, g_browser_process->GetApplicationLocale()));
     95 }
     96 
     97 void WebstoreProvider::OnWebstoreSearchFetched(
     98     scoped_ptr<base::DictionaryValue> json) {
     99   ProcessWebstoreSearchResults(json.get());
    100   cache_->Put(WebserviceCache::WEBSTORE, query_, json.Pass());
    101 
    102   if (!webstore_search_fetched_callback_.is_null())
    103     webstore_search_fetched_callback_.Run();
    104 }
    105 
    106 void WebstoreProvider::ProcessWebstoreSearchResults(
    107     const base::DictionaryValue* json) {
    108   const base::ListValue* result_list = NULL;
    109   if (!json ||
    110       !json->GetList(kKeyResults, &result_list) ||
    111       !result_list ||
    112       result_list->empty()) {
    113     return;
    114   }
    115 
    116   bool first_result = true;
    117   for (ListValue::const_iterator it = result_list->begin();
    118        it != result_list->end();
    119        ++it) {
    120     const base::DictionaryValue* dict;
    121     if (!(*it)->GetAsDictionary(&dict))
    122       continue;
    123 
    124     scoped_ptr<ChromeSearchResult> result(CreateResult(*dict));
    125     if (!result)
    126       continue;
    127 
    128     if (first_result) {
    129       // Clears "search in webstore" place holder results.
    130       ClearResults();
    131       first_result = false;
    132     }
    133 
    134     Add(result.Pass());
    135   }
    136 }
    137 
    138 scoped_ptr<ChromeSearchResult> WebstoreProvider::CreateResult(
    139     const base::DictionaryValue& dict) {
    140   scoped_ptr<ChromeSearchResult> result;
    141 
    142   std::string app_id;
    143   std::string localized_name;
    144   std::string icon_url_string;
    145   if (!dict.GetString(kKeyId, &app_id) ||
    146       !dict.GetString(kKeyLocalizedName, &localized_name) ||
    147       !dict.GetString(kKeyIconUrl, &icon_url_string)) {
    148     return result.Pass();
    149   }
    150 
    151   GURL icon_url(icon_url_string);
    152   if (!icon_url.is_valid())
    153     return result.Pass();
    154 
    155   result.reset(new WebstoreResult(
    156       profile_, app_id, localized_name, icon_url, controller_));
    157   return result.Pass();
    158 }
    159 
    160 }  // namespace app_list
    161