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_ = base::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<SearchResult>(new SearchWebstoreResult(profile_, query_)));
     80 }
     81 
     82 void WebstoreProvider::Stop() {
     83   if (webstore_search_)
     84     webstore_search_->Stop();
     85 }
     86 
     87 void WebstoreProvider::StartQuery() {
     88   // |query_| can be NULL when the query is scheduled but then canceled.
     89   if (!webstore_search_ || query_.empty())
     90     return;
     91 
     92   webstore_search_->Start(extension_urls::GetWebstoreJsonSearchUrl(
     93       query_, g_browser_process->GetApplicationLocale()));
     94 }
     95 
     96 void WebstoreProvider::OnWebstoreSearchFetched(
     97     scoped_ptr<base::DictionaryValue> json) {
     98   ProcessWebstoreSearchResults(json.get());
     99   cache_->Put(WebserviceCache::WEBSTORE, query_, json.Pass());
    100 
    101   if (!webstore_search_fetched_callback_.is_null())
    102     webstore_search_fetched_callback_.Run();
    103 }
    104 
    105 void WebstoreProvider::ProcessWebstoreSearchResults(
    106     const base::DictionaryValue* json) {
    107   const base::ListValue* result_list = NULL;
    108   if (!json ||
    109       !json->GetList(kKeyResults, &result_list) ||
    110       !result_list ||
    111       result_list->empty()) {
    112     return;
    113   }
    114 
    115   bool first_result = true;
    116   for (base::ListValue::const_iterator it = result_list->begin();
    117        it != result_list->end();
    118        ++it) {
    119     const base::DictionaryValue* dict;
    120     if (!(*it)->GetAsDictionary(&dict))
    121       continue;
    122 
    123     scoped_ptr<SearchResult> result(CreateResult(*dict));
    124     if (!result)
    125       continue;
    126 
    127     if (first_result) {
    128       // Clears "search in webstore" place holder results.
    129       ClearResults();
    130       first_result = false;
    131     }
    132 
    133     Add(result.Pass());
    134   }
    135 }
    136 
    137 scoped_ptr<ChromeSearchResult> WebstoreProvider::CreateResult(
    138     const base::DictionaryValue& dict) {
    139   scoped_ptr<ChromeSearchResult> result;
    140 
    141   std::string app_id;
    142   std::string localized_name;
    143   std::string icon_url_string;
    144   if (!dict.GetString(kKeyId, &app_id) ||
    145       !dict.GetString(kKeyLocalizedName, &localized_name) ||
    146       !dict.GetString(kKeyIconUrl, &icon_url_string)) {
    147     return result.Pass();
    148   }
    149 
    150   GURL icon_url(icon_url_string);
    151   if (!icon_url.is_valid())
    152     return result.Pass();
    153 
    154   result.reset(new WebstoreResult(
    155       profile_, app_id, localized_name, icon_url, controller_));
    156   return result.Pass();
    157 }
    158 
    159 }  // namespace app_list
    160