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