webstore_standalone_installer.cc revision 3551c9c881056c480085172ff9840cab31610854
1// Copyright (c) 2012 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/extensions/webstore_standalone_installer.h"
6
7#include "base/values.h"
8#include "chrome/browser/extensions/crx_installer.h"
9#include "chrome/browser/extensions/extension_install_prompt.h"
10#include "chrome/browser/extensions/extension_install_ui.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/extensions/webstore_data_fetcher.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/extensions/extension.h"
15#include "content/public/browser/web_contents.h"
16#include "url/gurl.h"
17
18using content::WebContents;
19
20namespace extensions {
21
22const char kManifestKey[] = "manifest";
23const char kIconUrlKey[] = "icon_url";
24const char kLocalizedNameKey[] = "localized_name";
25const char kLocalizedDescriptionKey[] = "localized_description";
26const char kUsersKey[] = "users";
27const char kShowUserCountKey[] = "show_user_count";
28const char kAverageRatingKey[] = "average_rating";
29const char kRatingCountKey[] = "rating_count";
30const char kRedirectUrlKey[] = "redirect_url";
31
32const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
33const char kWebstoreRequestError[] =
34    "Could not fetch data from the Chrome Web Store";
35const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
36const char kInvalidManifestError[] = "Invalid manifest";
37const char kUserCancelledError[] = "User cancelled install";
38
39
40WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
41    const std::string& webstore_item_id,
42    Profile* profile,
43    const Callback& callback)
44    : id_(webstore_item_id),
45      callback_(callback),
46      profile_(profile),
47      show_user_count_(true),
48      average_rating_(0.0),
49      rating_count_(0) {
50  CHECK(!callback_.is_null());
51}
52
53WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
54
55//
56// Private interface implementation.
57//
58
59void WebstoreStandaloneInstaller::BeginInstall() {
60  AddRef();  // Balanced in CompleteInstall or WebContentsDestroyed.
61
62  if (!Extension::IdIsValid(id_)) {
63    CompleteInstall(kInvalidWebstoreItemId);
64    return;
65  }
66
67  // Use the requesting page as the referrer both since that is more correct
68  // (it is the page that caused this request to happen) and so that we can
69  // track top sites that trigger inline install requests.
70  webstore_data_fetcher_.reset(new WebstoreDataFetcher(
71      this,
72      profile_->GetRequestContext(),
73      GetRequestorURL(),
74      id_));
75  webstore_data_fetcher_->Start();
76}
77
78scoped_ptr<ExtensionInstallPrompt>
79WebstoreStandaloneInstaller::CreateInstallUI() {
80  return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
81}
82
83void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
84  CompleteInstall(kWebstoreRequestError);
85}
86
87void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
88    DictionaryValue* webstore_data) {
89  if (!CheckRequestorAlive()) {
90    CompleteInstall(std::string());
91    return;
92  }
93
94  std::string error;
95
96  if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
97    CompleteInstall(error);
98    return;
99  }
100
101  if (!CheckRequestorPermitted(*webstore_data, &error)) {
102    CompleteInstall(error);
103    return;
104  }
105
106  // Manifest, number of users, average rating and rating count are required.
107  std::string manifest;
108  if (!webstore_data->GetString(kManifestKey, &manifest) ||
109      !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
110      !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
111      !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
112    CompleteInstall(kInvalidWebstoreResponseError);
113    return;
114  }
115
116  // Optional.
117  show_user_count_ = true;
118  webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
119
120  if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
121      average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
122    CompleteInstall(kInvalidWebstoreResponseError);
123    return;
124  }
125
126  // Localized name and description are optional.
127  if ((webstore_data->HasKey(kLocalizedNameKey) &&
128      !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
129      (webstore_data->HasKey(kLocalizedDescriptionKey) &&
130      !webstore_data->GetString(
131          kLocalizedDescriptionKey, &localized_description_))) {
132    CompleteInstall(kInvalidWebstoreResponseError);
133    return;
134  }
135
136  // Icon URL is optional.
137  GURL icon_url;
138  if (webstore_data->HasKey(kIconUrlKey)) {
139    std::string icon_url_string;
140    if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
141      CompleteInstall(kInvalidWebstoreResponseError);
142      return;
143    }
144    icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
145        icon_url_string);
146    if (!icon_url.is_valid()) {
147      CompleteInstall(kInvalidWebstoreResponseError);
148      return;
149    }
150  }
151
152  // Assume ownership of webstore_data.
153  webstore_data_.reset(webstore_data);
154
155  scoped_refptr<WebstoreInstallHelper> helper =
156      new WebstoreInstallHelper(this,
157                                id_,
158                                manifest,
159                                std::string(),  // We don't have any icon data.
160                                icon_url,
161                                profile_->GetRequestContext());
162  // The helper will call us back via OnWebstoreParseSucces or
163  // OnWebstoreParseFailure.
164  helper->Start();
165}
166
167void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
168    const std::string& error) {
169  CompleteInstall(error);
170}
171
172void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
173    const std::string& id,
174    const SkBitmap& icon,
175    base::DictionaryValue* manifest) {
176  CHECK_EQ(id_, id);
177
178  if (!CheckRequestorAlive()) {
179    CompleteInstall(std::string());
180    return;
181  }
182
183  manifest_.reset(manifest);
184  icon_ = icon;
185
186  install_prompt_ = CreateInstallPrompt();
187  if (install_prompt_) {
188    ShowInstallUI();
189    // Control flow finishes up in InstallUIProceed or InstallUIAbort.
190  } else {
191    InstallUIProceed();
192  }
193}
194
195void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
196    const std::string& id,
197    InstallHelperResultCode result_code,
198    const std::string& error_message) {
199  CompleteInstall(error_message);
200}
201
202void WebstoreStandaloneInstaller::InstallUIProceed() {
203  if (!CheckRequestorAlive()) {
204    CompleteInstall(std::string());
205    return;
206  }
207
208  scoped_ptr<WebstoreInstaller::Approval> approval(
209      WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
210          profile_,
211          id_,
212          scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy())));
213  approval->skip_post_install_ui = !ShouldShowPostInstallUI();
214  approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
215  approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
216
217  scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
218      profile_,
219      this,
220      &(GetWebContents()->GetController()),
221      id_,
222      approval.Pass(),
223      WebstoreInstaller::FLAG_INLINE_INSTALL);
224  installer->Start();
225}
226
227void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
228  CompleteInstall(kUserCancelledError);
229}
230
231void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
232    const std::string& id) {
233  CHECK_EQ(id_, id);
234  CompleteInstall(std::string());
235}
236
237void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
238    const std::string& id,
239    const std::string& error,
240    WebstoreInstaller::FailureReason cancelled) {
241  CHECK_EQ(id_, id);
242  CompleteInstall(error);
243}
244
245void WebstoreStandaloneInstaller::AbortInstall() {
246  callback_.Reset();
247  // Abort any in-progress fetches.
248  if (webstore_data_fetcher_) {
249    webstore_data_fetcher_.reset();
250    Release();  // Matches the AddRef in BeginInstall.
251  }
252}
253
254void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
255  // Clear webstore_data_fetcher_ so that WebContentsDestroyed will no longer
256  // call Release in case the WebContents is destroyed before this object.
257  scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher(
258      webstore_data_fetcher_.Pass());
259  if (!callback_.is_null())
260    callback_.Run(error.empty(), error);
261
262  Release();  // Matches the AddRef in BeginInstall.
263}
264
265void
266WebstoreStandaloneInstaller::ShowInstallUI() {
267  std::string error;
268  localized_extension_for_display_ =
269      ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
270          manifest_.get(),
271          Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
272          id_,
273          localized_name_,
274          localized_description_,
275          &error);
276  if (!localized_extension_for_display_.get()) {
277    CompleteInstall(kInvalidManifestError);
278    return;
279  }
280
281  install_ui_ = CreateInstallUI();
282  install_ui_->ConfirmStandaloneInstall(
283      this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
284}
285
286}  // namespace extensions
287