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.
5 #include "chrome/browser/extensions/webstore_standalone_installer.h"
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"
18 using content::WebContents;
20 namespace extensions {
22 const char kManifestKey[] = "manifest";
23 const char kIconUrlKey[] = "icon_url";
24 const char kLocalizedNameKey[] = "localized_name";
25 const char kLocalizedDescriptionKey[] = "localized_description";
26 const char kUsersKey[] = "users";
27 const char kShowUserCountKey[] = "show_user_count";
28 const char kAverageRatingKey[] = "average_rating";
29 const char kRatingCountKey[] = "rating_count";
31 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
32 const char kWebstoreRequestError[] =
33 "Could not fetch data from the Chrome Web Store";
34 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
35 const char kInvalidManifestError[] = "Invalid manifest";
36 const char kUserCancelledError[] = "User cancelled install";
39 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
40 const std::string& webstore_item_id,
42 const Callback& callback)
43 : id_(webstore_item_id),
46 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
47 show_user_count_(true),
50 CHECK(!callback_.is_null());
53 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
56 // Private interface implementation.
59 void WebstoreStandaloneInstaller::BeginInstall() {
60 // Add a ref to keep this alive for WebstoreDataFetcher.
61 // All code paths from here eventually lead to either CompleteInstall or
62 // AbortInstall, which both release this ref.
65 if (!Extension::IdIsValid(id_)) {
66 CompleteInstall(kInvalidWebstoreItemId);
70 // Use the requesting page as the referrer both since that is more correct
71 // (it is the page that caused this request to happen) and so that we can
72 // track top sites that trigger inline install requests.
73 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
75 profile_->GetRequestContext(),
78 webstore_data_fetcher_->Start();
81 scoped_ptr<ExtensionInstallPrompt>
82 WebstoreStandaloneInstaller::CreateInstallUI() {
83 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
86 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
87 CompleteInstall(kWebstoreRequestError);
90 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
91 DictionaryValue* webstore_data) {
92 if (!CheckRequestorAlive()) {
93 CompleteInstall(std::string());
99 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
100 CompleteInstall(error);
104 if (!CheckRequestorPermitted(*webstore_data, &error)) {
105 CompleteInstall(error);
109 // Manifest, number of users, average rating and rating count are required.
110 std::string manifest;
111 if (!webstore_data->GetString(kManifestKey, &manifest) ||
112 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
113 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
114 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
115 CompleteInstall(kInvalidWebstoreResponseError);
120 show_user_count_ = true;
121 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
123 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
124 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
125 CompleteInstall(kInvalidWebstoreResponseError);
129 // Localized name and description are optional.
130 if ((webstore_data->HasKey(kLocalizedNameKey) &&
131 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
132 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
133 !webstore_data->GetString(
134 kLocalizedDescriptionKey, &localized_description_))) {
135 CompleteInstall(kInvalidWebstoreResponseError);
139 // Icon URL is optional.
141 if (webstore_data->HasKey(kIconUrlKey)) {
142 std::string icon_url_string;
143 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
144 CompleteInstall(kInvalidWebstoreResponseError);
147 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
149 if (!icon_url.is_valid()) {
150 CompleteInstall(kInvalidWebstoreResponseError);
155 // Assume ownership of webstore_data.
156 webstore_data_.reset(webstore_data);
158 scoped_refptr<WebstoreInstallHelper> helper =
159 new WebstoreInstallHelper(this,
162 std::string(), // We don't have any icon data.
164 profile_->GetRequestContext());
165 // The helper will call us back via OnWebstoreParseSucces or
166 // OnWebstoreParseFailure.
170 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
171 const std::string& error) {
172 CompleteInstall(error);
175 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
176 const std::string& id,
177 const SkBitmap& icon,
178 base::DictionaryValue* manifest) {
181 if (!CheckRequestorAlive()) {
182 CompleteInstall(std::string());
186 manifest_.reset(manifest);
189 install_prompt_ = CreateInstallPrompt();
190 if (install_prompt_) {
192 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
194 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
195 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
201 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
202 const std::string& id,
203 InstallHelperResultCode result_code,
204 const std::string& error_message) {
205 CompleteInstall(error_message);
208 void WebstoreStandaloneInstaller::InstallUIProceed() {
209 if (!CheckRequestorAlive()) {
210 CompleteInstall(std::string());
214 scoped_ptr<WebstoreInstaller::Approval> approval(
215 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
218 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
220 approval->skip_post_install_ui = !ShouldShowPostInstallUI();
221 approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
222 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
224 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
227 &(GetWebContents()->GetController()),
234 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
235 CompleteInstall(kUserCancelledError);
236 Release(); // Balanced in ShowInstallUI.
239 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
240 const std::string& id) {
242 CompleteInstall(std::string());
243 Release(); // Balanced in ShowInstallUI.
246 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
247 const std::string& id,
248 const std::string& error,
249 WebstoreInstaller::FailureReason cancelled) {
251 CompleteInstall(error);
252 Release(); // Balanced in ShowInstallUI.
255 void WebstoreStandaloneInstaller::AbortInstall() {
257 // Abort any in-progress fetches.
258 if (webstore_data_fetcher_) {
259 webstore_data_fetcher_.reset();
260 Release(); // Matches the AddRef in BeginInstall.
264 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
265 if (!callback_.is_null())
266 callback_.Run(error.empty(), error);
267 Release(); // Matches the AddRef in BeginInstall.
271 WebstoreStandaloneInstaller::ShowInstallUI() {
273 localized_extension_for_display_ =
274 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
276 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
279 localized_description_,
281 if (!localized_extension_for_display_.get()) {
282 CompleteInstall(kInvalidManifestError);
286 // Keep this alive as long as the install prompt lives.
287 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
288 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
291 install_ui_ = CreateInstallUI();
292 install_ui_->ConfirmStandaloneInstall(
293 this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
296 } // namespace extensions