Extensions cleanup: Merge IsSyncableApp+Extension, ShouldSyncApp+Extension
[chromium-blink-merge.git] / chrome / browser / extensions / webstore_standalone_installer.cc
blob5b3ad15745c4e3ac4ebcf06b0a0a5adcf298558f
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 "base/version.h"
9 #include "chrome/browser/extensions/crx_installer.h"
10 #include "chrome/browser/extensions/extension_install_prompt.h"
11 #include "chrome/browser/extensions/extension_install_ui_util.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/install_tracker.h"
14 #include "chrome/browser/extensions/webstore_data_fetcher.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "components/crx_file/id_util.h"
17 #include "content/public/browser/web_contents.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/browser/extension_util.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/extension_urls.h"
24 #include "url/gurl.h"
26 using content::WebContents;
28 namespace extensions {
30 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
31 const char kWebstoreRequestError[] =
32 "Could not fetch data from the Chrome Web Store";
33 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
34 const char kInvalidManifestError[] = "Invalid manifest";
35 const char kUserCancelledError[] = "User cancelled install";
36 const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
37 const char kInstallInProgressError[] = "An install is already in progress";
38 const char kLaunchInProgressError[] = "A launch is already in progress";
40 WebstoreStandaloneInstaller::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 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
48 show_user_count_(true),
49 average_rating_(0.0),
50 rating_count_(0) {
53 void WebstoreStandaloneInstaller::BeginInstall() {
54 // Add a ref to keep this alive for WebstoreDataFetcher.
55 // All code paths from here eventually lead to either CompleteInstall or
56 // AbortInstall, which both release this ref.
57 AddRef();
59 if (!crx_file::id_util::IdIsValid(id_)) {
60 CompleteInstall(webstore_install::INVALID_ID, kInvalidWebstoreItemId);
61 return;
64 webstore_install::Result result = webstore_install::OTHER_ERROR;
65 std::string error;
66 if (!EnsureUniqueInstall(&result, &error)) {
67 CompleteInstall(result, error);
68 return;
71 // Use the requesting page as the referrer both since that is more correct
72 // (it is the page that caused this request to happen) and so that we can
73 // track top sites that trigger inline install requests.
74 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
75 this,
76 profile_->GetRequestContext(),
77 GetRequestorURL(),
78 id_));
79 webstore_data_fetcher_->Start();
83 // Private interface implementation.
86 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
89 void WebstoreStandaloneInstaller::RunCallback(bool success,
90 const std::string& error,
91 webstore_install::Result result) {
92 callback_.Run(success, error, result);
95 void WebstoreStandaloneInstaller::AbortInstall() {
96 callback_.Reset();
97 // Abort any in-progress fetches.
98 if (webstore_data_fetcher_) {
99 webstore_data_fetcher_.reset();
100 scoped_active_install_.reset();
101 Release(); // Matches the AddRef in BeginInstall.
105 bool WebstoreStandaloneInstaller::EnsureUniqueInstall(
106 webstore_install::Result* reason,
107 std::string* error) {
108 InstallTracker* tracker = InstallTracker::Get(profile_);
109 DCHECK(tracker);
111 const ActiveInstallData* existing_install_data =
112 tracker->GetActiveInstall(id_);
113 if (existing_install_data) {
114 if (existing_install_data->is_ephemeral) {
115 *reason = webstore_install::LAUNCH_IN_PROGRESS;
116 *error = kLaunchInProgressError;
117 } else {
118 *reason = webstore_install::INSTALL_IN_PROGRESS;
119 *error = kInstallInProgressError;
121 return false;
124 ActiveInstallData install_data(id_);
125 InitInstallData(&install_data);
126 scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data));
127 return true;
130 void WebstoreStandaloneInstaller::CompleteInstall(
131 webstore_install::Result result,
132 const std::string& error) {
133 scoped_active_install_.reset();
134 if (!callback_.is_null())
135 callback_.Run(result == webstore_install::SUCCESS, error, result);
136 Release(); // Matches the AddRef in BeginInstall.
139 void WebstoreStandaloneInstaller::ProceedWithInstallPrompt() {
140 install_prompt_ = CreateInstallPrompt();
141 if (install_prompt_.get()) {
142 ShowInstallUI();
143 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
144 } else {
145 InstallUIProceed();
149 scoped_refptr<const Extension>
150 WebstoreStandaloneInstaller::GetLocalizedExtensionForDisplay() {
151 if (!localized_extension_for_display_.get()) {
152 DCHECK(manifest_.get());
153 if (!manifest_.get())
154 return NULL;
156 std::string error;
157 localized_extension_for_display_ =
158 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
159 manifest_.get(),
160 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
161 id_,
162 localized_name_,
163 localized_description_,
164 &error);
166 return localized_extension_for_display_.get();
169 void WebstoreStandaloneInstaller::InitInstallData(
170 ActiveInstallData* install_data) const {
171 // Default implementation sets no properties.
174 void WebstoreStandaloneInstaller::OnManifestParsed() {
175 ProceedWithInstallPrompt();
178 scoped_ptr<ExtensionInstallPrompt>
179 WebstoreStandaloneInstaller::CreateInstallUI() {
180 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
183 scoped_ptr<WebstoreInstaller::Approval>
184 WebstoreStandaloneInstaller::CreateApproval() const {
185 scoped_ptr<WebstoreInstaller::Approval> approval(
186 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
187 profile_,
188 id_,
189 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
190 true));
191 approval->skip_post_install_ui = !ShouldShowPostInstallUI();
192 approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
193 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
194 return approval.Pass();
197 void WebstoreStandaloneInstaller::InstallUIProceed() {
198 if (!CheckRequestorAlive()) {
199 CompleteInstall(webstore_install::ABORTED, std::string());
200 return;
203 scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
205 ExtensionService* extension_service =
206 ExtensionSystem::Get(profile_)->extension_service();
207 const Extension* installed_extension =
208 extension_service->GetExtensionById(id_, true /* include disabled */);
209 if (installed_extension) {
210 std::string install_message;
211 webstore_install::Result install_result = webstore_install::SUCCESS;
212 bool done = true;
214 if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
215 // Don't install a blacklisted extension.
216 install_result = webstore_install::BLACKLISTED;
217 install_message = kExtensionIsBlacklisted;
218 } else if (util::IsEphemeralApp(installed_extension->id(), profile_) &&
219 !approval->is_ephemeral) {
220 // If the target extension has already been installed ephemerally and is
221 // up to date, it can be promoted to a regular installed extension and
222 // downloading from the Web Store is not necessary.
223 scoped_refptr<const Extension> extension_to_install =
224 GetLocalizedExtensionForDisplay();
225 if (!extension_to_install.get()) {
226 CompleteInstall(webstore_install::INVALID_MANIFEST,
227 kInvalidManifestError);
228 return;
231 if (installed_extension->version()->CompareTo(
232 *extension_to_install->version()) < 0) {
233 // If the existing extension is out of date, proceed with the install
234 // to update the extension.
235 done = false;
236 } else {
237 install_ui::ShowPostInstallUIForApproval(
238 profile_, *approval, installed_extension);
239 extension_service->PromoteEphemeralApp(installed_extension, false);
241 } else if (!extension_service->IsExtensionEnabled(id_)) {
242 // If the extension is installed but disabled, and not blacklisted,
243 // enable it.
244 extension_service->EnableExtension(id_);
245 } // else extension is installed and enabled; no work to be done.
247 if (done) {
248 CompleteInstall(install_result, install_message);
249 return;
253 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
254 profile_,
255 this,
256 GetWebContents(),
257 id_,
258 approval.Pass(),
259 install_source_);
260 installer->Start();
263 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
264 CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
267 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
268 OnWebStoreDataFetcherDone();
269 CompleteInstall(webstore_install::WEBSTORE_REQUEST_ERROR,
270 kWebstoreRequestError);
273 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
274 scoped_ptr<base::DictionaryValue> webstore_data) {
275 OnWebStoreDataFetcherDone();
277 if (!CheckRequestorAlive()) {
278 CompleteInstall(webstore_install::ABORTED, std::string());
279 return;
282 std::string error;
284 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
285 CompleteInstall(webstore_install::NOT_PERMITTED, error);
286 return;
289 if (!CheckRequestorPermitted(*webstore_data, &error)) {
290 CompleteInstall(webstore_install::NOT_PERMITTED, error);
291 return;
294 // Manifest, number of users, average rating and rating count are required.
295 std::string manifest;
296 if (!webstore_data->GetString(kManifestKey, &manifest) ||
297 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
298 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
299 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
300 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
301 kInvalidWebstoreResponseError);
302 return;
305 // Optional.
306 show_user_count_ = true;
307 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
309 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
310 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
311 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
312 kInvalidWebstoreResponseError);
313 return;
316 // Localized name and description are optional.
317 if ((webstore_data->HasKey(kLocalizedNameKey) &&
318 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
319 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
320 !webstore_data->GetString(
321 kLocalizedDescriptionKey, &localized_description_))) {
322 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
323 kInvalidWebstoreResponseError);
324 return;
327 // Icon URL is optional.
328 GURL icon_url;
329 if (webstore_data->HasKey(kIconUrlKey)) {
330 std::string icon_url_string;
331 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
332 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
333 kInvalidWebstoreResponseError);
334 return;
336 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
337 icon_url_string);
338 if (!icon_url.is_valid()) {
339 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
340 kInvalidWebstoreResponseError);
341 return;
345 // Assume ownership of webstore_data.
346 webstore_data_ = webstore_data.Pass();
348 scoped_refptr<WebstoreInstallHelper> helper =
349 new WebstoreInstallHelper(this,
350 id_,
351 manifest,
352 icon_url,
353 profile_->GetRequestContext());
354 // The helper will call us back via OnWebstoreParseSucces or
355 // OnWebstoreParseFailure.
356 helper->Start();
359 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
360 const std::string& error) {
361 OnWebStoreDataFetcherDone();
362 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
365 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
366 const std::string& id,
367 const SkBitmap& icon,
368 base::DictionaryValue* manifest) {
369 CHECK_EQ(id_, id);
371 if (!CheckRequestorAlive()) {
372 CompleteInstall(webstore_install::ABORTED, std::string());
373 return;
376 manifest_.reset(manifest);
377 icon_ = icon;
379 OnManifestParsed();
382 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
383 const std::string& id,
384 InstallHelperResultCode result_code,
385 const std::string& error_message) {
386 webstore_install::Result install_result = webstore_install::OTHER_ERROR;
387 switch (result_code) {
388 case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
389 install_result = webstore_install::INVALID_MANIFEST;
390 break;
391 case WebstoreInstallHelper::Delegate::ICON_ERROR:
392 install_result = webstore_install::ICON_ERROR;
393 break;
394 default:
395 break;
398 CompleteInstall(install_result, error_message);
401 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
402 const std::string& id) {
403 CHECK_EQ(id_, id);
404 CompleteInstall(webstore_install::SUCCESS, std::string());
407 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
408 const std::string& id,
409 const std::string& error,
410 WebstoreInstaller::FailureReason reason) {
411 CHECK_EQ(id_, id);
413 webstore_install::Result install_result = webstore_install::OTHER_ERROR;
414 switch (reason) {
415 case WebstoreInstaller::FAILURE_REASON_CANCELLED:
416 install_result = webstore_install::USER_CANCELLED;
417 break;
418 case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_FOUND:
419 case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE:
420 install_result = webstore_install::MISSING_DEPENDENCIES;
421 break;
422 default:
423 break;
426 CompleteInstall(install_result, error);
429 void WebstoreStandaloneInstaller::ShowInstallUI() {
430 scoped_refptr<const Extension> localized_extension =
431 GetLocalizedExtensionForDisplay();
432 if (!localized_extension.get()) {
433 CompleteInstall(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
434 return;
437 install_ui_ = CreateInstallUI();
438 install_ui_->ConfirmStandaloneInstall(
439 this, localized_extension.get(), &icon_, install_prompt_);
442 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
443 // An instance of this class is passed in as a delegate for the
444 // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
445 // therefore needs to remain alive until they are done. Clear the webstore
446 // data fetcher to avoid calling Release in AbortInstall while any of these
447 // operations are in progress.
448 webstore_data_fetcher_.reset();
451 } // namespace extensions