webstore_provider.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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
23namespace app_list {
24
25namespace {
26
27const char kKeyResults[] = "results";
28const char kKeyId[] = "id";
29const char kKeyLocalizedName[] = "localized_name";
30const char kKeyIconUrl[] = "icon_url";
31
32// Returns true if the launcher should send queries to the web store server.
33bool UseWebstoreSearch() {
34  const char kFieldTrialName[] = "LauncherUseWebstoreSearch";
35  const char kEnable[] = "Enable";
36  return base::FieldTrialList::FindFullName(kFieldTrialName) == kEnable;
37}
38
39}  // namespace
40
41WebstoreProvider::WebstoreProvider(Profile* profile,
42                                   AppListControllerDelegate* controller)
43  :  WebserviceSearchProvider(profile),
44     controller_(controller){}
45
46WebstoreProvider::~WebstoreProvider() {}
47
48void 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<ChromeSearchResult>(
80      new SearchWebstoreResult(profile_, query_)).Pass());
81}
82
83void WebstoreProvider::Stop() {
84  if (webstore_search_)
85    webstore_search_->Stop();
86}
87
88void 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
97void 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
106void 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 (base::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
138scoped_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