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