webstore_provider.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 "chrome/common/extensions/extension_constants.h" 21#include "url/gurl.h" 22 23namespace app_list { 24 25namespace { 26 27const char kKeyResults[] = "results"; 28const char kKeyId[] = "id"; 29const char kKeyLocalizedName[] = "localized_name"; 30const char kKeyIconUrl[] = "icon_url"; 31const char kKeyIsPaid[] = "is_paid"; 32const char kKeyItemType[] = "item_type"; 33 34const char kPlatformAppType[] = "platform_app"; 35const char kHostedAppType[] = "hosted_app"; 36const char kLegacyPackagedAppType[] = "legacy_packaged_app"; 37 38// Converts the item type string from the web store to an 39// extensions::Manifest::Type. 40extensions::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 55WebstoreProvider::WebstoreProvider(Profile* profile, 56 AppListControllerDelegate* controller) 57 : WebserviceSearchProvider(profile), 58 controller_(controller){} 59 60WebstoreProvider::~WebstoreProvider() {} 61 62void 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 94void WebstoreProvider::Stop() { 95 if (webstore_search_) 96 webstore_search_->Stop(); 97} 98 99void 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 108void 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 117void 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 149scoped_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