blob: 0fb4e79e913f9bc2f2057b641c022b68f464b769 [file] [log] [blame]
[email protected]bdb74a22012-01-25 20:33:331// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]8915f342011-08-29 22:14:372// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]734bcec2012-10-08 20:29:055#include "chrome/browser/extensions/webstore_standalone_installer.h"
[email protected]8915f342011-08-29 22:14:376
[email protected]8915f342011-08-29 22:14:377#include "base/values.h"
[email protected]8915f342011-08-29 22:14:378#include "chrome/browser/extensions/crx_installer.h"
[email protected]d44ec7b2013-03-15 04:34:349#include "chrome/browser/extensions/extension_install_prompt.h"
[email protected]00b38242012-07-18 18:43:2210#include "chrome/browser/extensions/extension_install_ui.h"
[email protected]655b2b1a2011-10-13 17:13:0611#include "chrome/browser/extensions/extension_service.h"
[email protected]7b3941052013-02-13 01:21:5912#include "chrome/browser/extensions/webstore_data_fetcher.h"
[email protected]8915f342011-08-29 22:14:3713#include "chrome/browser/profiles/profile.h"
[email protected]10c2d692012-05-11 05:32:2314#include "content/public/browser/web_contents.h"
[email protected]489db0842014-01-22 18:20:0315#include "extensions/browser/extension_prefs.h"
[email protected]59b0e602014-01-30 00:41:2416#include "extensions/browser/extension_system.h"
[email protected]e4452d32013-11-15 23:07:4117#include "extensions/common/extension.h"
[email protected]a6483d22013-07-03 22:11:0018#include "url/gurl.h"
[email protected]8915f342011-08-29 22:14:3719
[email protected]26b5e322011-12-23 01:36:4720using content::WebContents;
[email protected]631bb742011-11-02 11:29:3921
[email protected]3d61a7f2012-07-12 19:11:2522namespace extensions {
23
[email protected]a221ef092011-09-07 01:34:1024const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
25const char kWebstoreRequestError[] =
26 "Could not fetch data from the Chrome Web Store";
27const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
[email protected]8915f342011-08-29 22:14:3728const char kInvalidManifestError[] = "Invalid manifest";
29const char kUserCancelledError[] = "User cancelled install";
[email protected]f6b88d92014-01-09 19:45:3430const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
[email protected]8915f342011-08-29 22:14:3731
[email protected]734bcec2012-10-08 20:29:0532WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
[email protected]d44ec7b2013-03-15 04:34:3433 const std::string& webstore_item_id,
[email protected]c1b2d042013-02-23 00:31:0434 Profile* profile,
[email protected]c1b2d042013-02-23 00:31:0435 const Callback& callback)
[email protected]d44ec7b2013-03-15 04:34:3436 : id_(webstore_item_id),
[email protected]d2a639e2012-09-17 07:41:2137 callback_(callback),
[email protected]d44ec7b2013-03-15 04:34:3438 profile_(profile),
[email protected]084e35482013-09-25 02:46:1939 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
[email protected]dcde34b32013-07-31 02:28:4540 show_user_count_(true),
[email protected]c7bf7452011-09-12 21:31:5041 average_rating_(0.0),
[email protected]5f2a4752012-04-27 22:18:5842 rating_count_(0) {
[email protected]c1b2d042013-02-23 00:31:0443}
44
[email protected]d44ec7b2013-03-15 04:34:3445WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
46
47//
48// Private interface implementation.
49//
[email protected]8915f342011-08-29 22:14:3750
[email protected]734bcec2012-10-08 20:29:0551void WebstoreStandaloneInstaller::BeginInstall() {
[email protected]3d6d676522013-10-14 20:44:5552 // Add a ref to keep this alive for WebstoreDataFetcher.
53 // All code paths from here eventually lead to either CompleteInstall or
54 // AbortInstall, which both release this ref.
55 AddRef();
[email protected]8915f342011-08-29 22:14:3756
[email protected]3d61a7f2012-07-12 19:11:2557 if (!Extension::IdIsValid(id_)) {
[email protected]8915f342011-08-29 22:14:3758 CompleteInstall(kInvalidWebstoreItemId);
59 return;
60 }
61
[email protected]b4574c02011-11-17 06:19:1362 // Use the requesting page as the referrer both since that is more correct
63 // (it is the page that caused this request to happen) and so that we can
64 // track top sites that trigger inline install requests.
[email protected]7b3941052013-02-13 01:21:5965 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
66 this,
[email protected]c1b2d042013-02-23 00:31:0467 profile_->GetRequestContext(),
[email protected]d44ec7b2013-03-15 04:34:3468 GetRequestorURL(),
[email protected]7b3941052013-02-13 01:21:5969 id_));
70 webstore_data_fetcher_->Start();
[email protected]8915f342011-08-29 22:14:3771}
72
[email protected]ce35418b2013-11-25 01:22:3373bool WebstoreStandaloneInstaller::CheckInstallValid(
74 const base::DictionaryValue& manifest,
75 std::string* error) {
76 return true;
77}
78
[email protected]f8b23b42013-06-27 20:12:1479scoped_ptr<ExtensionInstallPrompt>
80WebstoreStandaloneInstaller::CreateInstallUI() {
81 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
82}
83
[email protected]ce35418b2013-11-25 01:22:3384scoped_ptr<WebstoreInstaller::Approval>
85WebstoreStandaloneInstaller::CreateApproval() const {
86 scoped_ptr<WebstoreInstaller::Approval> approval(
87 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
88 profile_,
89 id_,
90 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
91 true));
92 approval->skip_post_install_ui = !ShouldShowPostInstallUI();
93 approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
94 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
95 return approval.Pass();
96}
97
[email protected]7b3941052013-02-13 01:21:5998void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
99 CompleteInstall(kWebstoreRequestError);
[email protected]8915f342011-08-29 22:14:37100}
101
[email protected]734bcec2012-10-08 20:29:05102void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
[email protected]cb1078de2013-12-23 20:04:22103 scoped_ptr<base::DictionaryValue> webstore_data) {
[email protected]d44ec7b2013-03-15 04:34:34104 if (!CheckRequestorAlive()) {
[email protected]007b3f82013-04-09 08:46:45105 CompleteInstall(std::string());
[email protected]4693243e2011-09-09 23:52:37106 return;
107 }
108
[email protected]d44ec7b2013-03-15 04:34:34109 std::string error;
[email protected]8915f342011-08-29 22:14:37110
[email protected]d44ec7b2013-03-15 04:34:34111 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
112 CompleteInstall(error);
[email protected]8915f342011-08-29 22:14:37113 return;
114 }
115
[email protected]d44ec7b2013-03-15 04:34:34116 if (!CheckRequestorPermitted(*webstore_data, &error)) {
117 CompleteInstall(error);
[email protected]dd5161a2011-09-14 17:40:17118 return;
119 }
120
121 // Manifest, number of users, average rating and rating count are required.
122 std::string manifest;
123 if (!webstore_data->GetString(kManifestKey, &manifest) ||
124 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
[email protected]5fdbd6f2011-09-01 17:33:04125 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
126 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
127 CompleteInstall(kInvalidWebstoreResponseError);
128 return;
129 }
130
[email protected]dcde34b32013-07-31 02:28:45131 // Optional.
132 show_user_count_ = true;
133 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
134
[email protected]c82da8c42012-06-08 19:49:11135 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
136 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
[email protected]5fdbd6f2011-09-01 17:33:04137 CompleteInstall(kInvalidWebstoreResponseError);
138 return;
139 }
140
141 // Localized name and description are optional.
142 if ((webstore_data->HasKey(kLocalizedNameKey) &&
143 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
144 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
145 !webstore_data->GetString(
146 kLocalizedDescriptionKey, &localized_description_))) {
[email protected]8915f342011-08-29 22:14:37147 CompleteInstall(kInvalidWebstoreResponseError);
148 return;
149 }
150
151 // Icon URL is optional.
152 GURL icon_url;
153 if (webstore_data->HasKey(kIconUrlKey)) {
154 std::string icon_url_string;
155 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
156 CompleteInstall(kInvalidWebstoreResponseError);
157 return;
158 }
159 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
160 icon_url_string);
161 if (!icon_url.is_valid()) {
162 CompleteInstall(kInvalidWebstoreResponseError);
163 return;
164 }
165 }
166
[email protected]d44ec7b2013-03-15 04:34:34167 // Assume ownership of webstore_data.
[email protected]b3188ec2013-11-06 10:26:13168 webstore_data_ = webstore_data.Pass();
[email protected]a221ef092011-09-07 01:34:10169
[email protected]007b3f82013-04-09 08:46:45170 scoped_refptr<WebstoreInstallHelper> helper =
171 new WebstoreInstallHelper(this,
172 id_,
173 manifest,
174 std::string(), // We don't have any icon data.
175 icon_url,
176 profile_->GetRequestContext());
[email protected]8915f342011-08-29 22:14:37177 // The helper will call us back via OnWebstoreParseSucces or
178 // OnWebstoreParseFailure.
179 helper->Start();
180}
181
[email protected]734bcec2012-10-08 20:29:05182void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
[email protected]8915f342011-08-29 22:14:37183 const std::string& error) {
184 CompleteInstall(error);
185}
186
[email protected]734bcec2012-10-08 20:29:05187void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
[email protected]98e4e522011-10-25 13:00:16188 const std::string& id,
[email protected]8915f342011-08-29 22:14:37189 const SkBitmap& icon,
190 base::DictionaryValue* manifest) {
[email protected]d44ec7b2013-03-15 04:34:34191 CHECK_EQ(id_, id);
192
193 if (!CheckRequestorAlive()) {
[email protected]007b3f82013-04-09 08:46:45194 CompleteInstall(std::string());
[email protected]2fd920fb2011-09-08 23:33:00195 return;
196 }
197
[email protected]8915f342011-08-29 22:14:37198 manifest_.reset(manifest);
199 icon_ = icon;
200
[email protected]ce35418b2013-11-25 01:22:33201 std::string error;
202 if (!CheckInstallValid(*manifest, &error)) {
203 DCHECK(!error.empty());
204 CompleteInstall(error);
205 return;
206 }
207
[email protected]d44ec7b2013-03-15 04:34:34208 install_prompt_ = CreateInstallPrompt();
209 if (install_prompt_) {
[email protected]f8b23b42013-06-27 20:12:14210 ShowInstallUI();
[email protected]d44ec7b2013-03-15 04:34:34211 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
212 } else {
[email protected]3d6d676522013-10-14 20:44:55213 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
214 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
215 AddRef();
[email protected]77e9261d2013-02-04 22:01:47216 InstallUIProceed();
[email protected]77e9261d2013-02-04 22:01:47217 }
[email protected]8915f342011-08-29 22:14:37218}
219
[email protected]734bcec2012-10-08 20:29:05220void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
[email protected]98e4e522011-10-25 13:00:16221 const std::string& id,
[email protected]8915f342011-08-29 22:14:37222 InstallHelperResultCode result_code,
223 const std::string& error_message) {
224 CompleteInstall(error_message);
225}
226
[email protected]734bcec2012-10-08 20:29:05227void WebstoreStandaloneInstaller::InstallUIProceed() {
[email protected]d44ec7b2013-03-15 04:34:34228 if (!CheckRequestorAlive()) {
[email protected]007b3f82013-04-09 08:46:45229 CompleteInstall(std::string());
[email protected]2fd920fb2011-09-08 23:33:00230 return;
231 }
232
[email protected]f6b88d92014-01-09 19:45:34233 ExtensionService* extension_service =
234 ExtensionSystem::Get(profile_)->extension_service();
235 const Extension* extension =
236 extension_service->GetExtensionById(id_, true /* include disabled */);
237 if (extension) {
238 std::string install_result; // Empty string for install success.
239 if (!extension_service->IsExtensionEnabled(id_)) {
240 if (!ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
241 // If the extension is installed but disabled, and not blacklisted,
242 // enable it.
243 extension_service->EnableExtension(id_);
244 } else { // Don't install a blacklisted extension.
245 install_result = kExtensionIsBlacklisted;
246 }
247 } // else extension is installed and enabled; no work to be done.
248 CompleteInstall(install_result);
249 return;
250 }
251
[email protected]ce35418b2013-11-25 01:22:33252 scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
[email protected]21a5ad62012-04-03 04:48:45253
[email protected]98e4e522011-10-25 13:00:16254 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
[email protected]d44ec7b2013-03-15 04:34:34255 profile_,
256 this,
[email protected]2acf3a52014-02-13 00:26:00257 GetWebContents(),
[email protected]d44ec7b2013-03-15 04:34:34258 id_,
259 approval.Pass(),
[email protected]084e35482013-09-25 02:46:19260 install_source_);
[email protected]98e4e522011-10-25 13:00:16261 installer->Start();
[email protected]8915f342011-08-29 22:14:37262}
263
[email protected]734bcec2012-10-08 20:29:05264void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
[email protected]8915f342011-08-29 22:14:37265 CompleteInstall(kUserCancelledError);
[email protected]3d6d676522013-10-14 20:44:55266 Release(); // Balanced in ShowInstallUI.
[email protected]8915f342011-08-29 22:14:37267}
268
[email protected]734bcec2012-10-08 20:29:05269void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
270 const std::string& id) {
[email protected]cb08ba22011-10-19 21:41:40271 CHECK_EQ(id_, id);
[email protected]007b3f82013-04-09 08:46:45272 CompleteInstall(std::string());
[email protected]3d6d676522013-10-14 20:44:55273 Release(); // Balanced in ShowInstallUI.
[email protected]cb08ba22011-10-19 21:41:40274}
275
[email protected]734bcec2012-10-08 20:29:05276void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
[email protected]bcd1eaf72012-10-03 05:42:29277 const std::string& id,
278 const std::string& error,
279 WebstoreInstaller::FailureReason cancelled) {
[email protected]cb08ba22011-10-19 21:41:40280 CHECK_EQ(id_, id);
281 CompleteInstall(error);
[email protected]3d6d676522013-10-14 20:44:55282 Release(); // Balanced in ShowInstallUI.
[email protected]cb08ba22011-10-19 21:41:40283}
284
[email protected]d44ec7b2013-03-15 04:34:34285void WebstoreStandaloneInstaller::AbortInstall() {
286 callback_.Reset();
287 // Abort any in-progress fetches.
[email protected]3eeddd892013-04-17 17:00:11288 if (webstore_data_fetcher_) {
[email protected]d44ec7b2013-03-15 04:34:34289 webstore_data_fetcher_.reset();
290 Release(); // Matches the AddRef in BeginInstall.
291 }
292}
293
[email protected]ab6c7be42014-01-16 02:05:54294void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) {
[email protected]d2a639e2012-09-17 07:41:21295 if (!callback_.is_null())
296 callback_.Run(error.empty(), error);
[email protected]ab6c7be42014-01-16 02:05:54297}
298
299void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
300 InvokeCallback(error);
[email protected]5db2e882012-12-20 10:17:26301 Release(); // Matches the AddRef in BeginInstall.
[email protected]8915f342011-08-29 22:14:37302}
[email protected]5f2a4752012-04-27 22:18:58303
[email protected]d44ec7b2013-03-15 04:34:34304void
[email protected]f8b23b42013-06-27 20:12:14305WebstoreStandaloneInstaller::ShowInstallUI() {
[email protected]d44ec7b2013-03-15 04:34:34306 std::string error;
307 localized_extension_for_display_ =
308 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
309 manifest_.get(),
310 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
311 id_,
312 localized_name_,
313 localized_description_,
314 &error);
[email protected]dc24976f2013-06-02 21:15:09315 if (!localized_extension_for_display_.get()) {
[email protected]d44ec7b2013-03-15 04:34:34316 CompleteInstall(kInvalidManifestError);
317 return;
[email protected]5f2a4752012-04-27 22:18:58318 }
[email protected]5f2a4752012-04-27 22:18:58319
[email protected]3d6d676522013-10-14 20:44:55320 // Keep this alive as long as the install prompt lives.
321 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
322 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
323 AddRef();
324
[email protected]f8b23b42013-06-27 20:12:14325 install_ui_ = CreateInstallUI();
[email protected]dc24976f2013-06-02 21:15:09326 install_ui_->ConfirmStandaloneInstall(
327 this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
[email protected]5f2a4752012-04-27 22:18:58328}
[email protected]3d61a7f2012-07-12 19:11:25329
330} // namespace extensions