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