Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / webstore_standalone_installer.cc
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 "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"
25
26 using content::WebContents;
27
28 namespace extensions {
29
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";
39
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) {
51 }
52
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();
58
59   if (!crx_file::id_util::IdIsValid(id_)) {
60     CompleteInstall(webstore_install::INVALID_ID, kInvalidWebstoreItemId);
61     return;
62   }
63
64   webstore_install::Result result = webstore_install::OTHER_ERROR;
65   std::string error;
66   if (!EnsureUniqueInstall(&result, &error)) {
67     CompleteInstall(result, error);
68     return;
69   }
70
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();
80 }
81
82 //
83 // Private interface implementation.
84 //
85
86 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
87 }
88
89 void WebstoreStandaloneInstaller::RunCallback(bool success,
90                                               const std::string& error,
91                                               webstore_install::Result result) {
92   callback_.Run(success, error, result);
93 }
94
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.
102   }
103 }
104
105 bool WebstoreStandaloneInstaller::EnsureUniqueInstall(
106     webstore_install::Result* reason,
107     std::string* error) {
108   InstallTracker* tracker = InstallTracker::Get(profile_);
109   DCHECK(tracker);
110
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;
120     }
121     return false;
122   }
123
124   ActiveInstallData install_data(id_);
125   InitInstallData(&install_data);
126   scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data));
127   return true;
128 }
129
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.
137 }
138
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();
146   }
147 }
148
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;
155
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);
165   }
166   return localized_extension_for_display_.get();
167 }
168
169 void WebstoreStandaloneInstaller::InitInstallData(
170     ActiveInstallData* install_data) const {
171   // Default implementation sets no properties.
172 }
173
174 void WebstoreStandaloneInstaller::OnManifestParsed() {
175   ProceedWithInstallPrompt();
176 }
177
178 scoped_ptr<ExtensionInstallPrompt>
179 WebstoreStandaloneInstaller::CreateInstallUI() {
180   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
181 }
182
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();
195 }
196
197 void WebstoreStandaloneInstaller::InstallUIProceed() {
198   if (!CheckRequestorAlive()) {
199     CompleteInstall(webstore_install::ABORTED, std::string());
200     return;
201   }
202
203   scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
204
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;
213
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;
229       }
230
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);
240       }
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.
246
247     if (done) {
248       CompleteInstall(install_result, install_message);
249       return;
250     }
251   }
252
253   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
254       profile_,
255       this,
256       GetWebContents(),
257       id_,
258       approval.Pass(),
259       install_source_);
260   installer->Start();
261 }
262
263 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
264   CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
265 }
266
267 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
268   OnWebStoreDataFetcherDone();
269   CompleteInstall(webstore_install::WEBSTORE_REQUEST_ERROR,
270                   kWebstoreRequestError);
271 }
272
273 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
274     scoped_ptr<base::DictionaryValue> webstore_data) {
275   OnWebStoreDataFetcherDone();
276
277   if (!CheckRequestorAlive()) {
278     CompleteInstall(webstore_install::ABORTED, std::string());
279     return;
280   }
281
282   std::string error;
283
284   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
285     CompleteInstall(webstore_install::NOT_PERMITTED, error);
286     return;
287   }
288
289   if (!CheckRequestorPermitted(*webstore_data, &error)) {
290     CompleteInstall(webstore_install::NOT_PERMITTED, error);
291     return;
292   }
293
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;
303   }
304
305   // Optional.
306   show_user_count_ = true;
307   webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
308
309   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
310       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
311     CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
312                     kInvalidWebstoreResponseError);
313     return;
314   }
315
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;
325   }
326
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;
335     }
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;
342     }
343   }
344
345   // Assume ownership of webstore_data.
346   webstore_data_ = webstore_data.Pass();
347
348   scoped_refptr<WebstoreInstallHelper> helper =
349       new WebstoreInstallHelper(this,
350                                 id_,
351                                 manifest,
352                                 std::string(),  // We don't have any icon data.
353                                 icon_url,
354                                 profile_->GetRequestContext());
355   // The helper will call us back via OnWebstoreParseSucces or
356   // OnWebstoreParseFailure.
357   helper->Start();
358 }
359
360 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
361     const std::string& error) {
362   OnWebStoreDataFetcherDone();
363   CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
364 }
365
366 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
367     const std::string& id,
368     const SkBitmap& icon,
369     base::DictionaryValue* manifest) {
370   CHECK_EQ(id_, id);
371
372   if (!CheckRequestorAlive()) {
373     CompleteInstall(webstore_install::ABORTED, std::string());
374     return;
375   }
376
377   manifest_.reset(manifest);
378   icon_ = icon;
379
380   OnManifestParsed();
381 }
382
383 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
384     const std::string& id,
385     InstallHelperResultCode result_code,
386     const std::string& error_message) {
387   webstore_install::Result install_result = webstore_install::OTHER_ERROR;
388   switch (result_code) {
389     case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
390       install_result = webstore_install::INVALID_MANIFEST;
391       break;
392     case WebstoreInstallHelper::Delegate::ICON_ERROR:
393       install_result = webstore_install::ICON_ERROR;
394       break;
395     default:
396       break;
397   }
398
399   CompleteInstall(install_result, error_message);
400 }
401
402 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
403     const std::string& id) {
404   CHECK_EQ(id_, id);
405   CompleteInstall(webstore_install::SUCCESS, std::string());
406 }
407
408 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
409     const std::string& id,
410     const std::string& error,
411     WebstoreInstaller::FailureReason reason) {
412   CHECK_EQ(id_, id);
413
414   webstore_install::Result install_result = webstore_install::OTHER_ERROR;
415   switch (reason) {
416     case WebstoreInstaller::FAILURE_REASON_CANCELLED:
417       install_result = webstore_install::USER_CANCELLED;
418       break;
419     case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_FOUND:
420     case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE:
421       install_result = webstore_install::MISSING_DEPENDENCIES;
422       break;
423     default:
424       break;
425   }
426
427   CompleteInstall(install_result, error);
428 }
429
430 void WebstoreStandaloneInstaller::ShowInstallUI() {
431   scoped_refptr<const Extension> localized_extension =
432       GetLocalizedExtensionForDisplay();
433   if (!localized_extension.get()) {
434     CompleteInstall(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
435     return;
436   }
437
438   install_ui_ = CreateInstallUI();
439   install_ui_->ConfirmStandaloneInstall(
440       this, localized_extension.get(), &icon_, install_prompt_);
441 }
442
443 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
444   // An instance of this class is passed in as a delegate for the
445   // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
446   // therefore needs to remain alive until they are done. Clear the webstore
447   // data fetcher to avoid calling Release in AbortInstall while any of these
448   // operations are in progress.
449   webstore_data_fetcher_.reset();
450 }
451
452 }  // namespace extensions