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 "content/public/browser/web_contents.h"
15 #include "extensions/browser/extension_prefs.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/browser/extension_util.h"
19 #include "extensions/common/extension.h"
22 using content::WebContents
;
24 namespace extensions
{
26 const char kInvalidWebstoreItemId
[] = "Invalid Chrome Web Store item ID";
27 const char kWebstoreRequestError
[] =
28 "Could not fetch data from the Chrome Web Store";
29 const char kInvalidWebstoreResponseError
[] = "Invalid Chrome Web Store reponse";
30 const char kInvalidManifestError
[] = "Invalid manifest";
31 const char kUserCancelledError
[] = "User cancelled install";
32 const char kExtensionIsBlacklisted
[] = "Extension is blacklisted";
34 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
35 const std::string
& webstore_item_id
,
37 const Callback
& callback
)
38 : id_(webstore_item_id
),
41 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE
),
42 show_user_count_(true),
47 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
50 // Private interface implementation.
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.
59 if (!Extension::IdIsValid(id_
)) {
60 CompleteInstall(kInvalidWebstoreItemId
);
64 // Use the requesting page as the referrer both since that is more correct
65 // (it is the page that caused this request to happen) and so that we can
66 // track top sites that trigger inline install requests.
67 webstore_data_fetcher_
.reset(new WebstoreDataFetcher(
69 profile_
->GetRequestContext(),
72 webstore_data_fetcher_
->Start();
75 bool WebstoreStandaloneInstaller::CheckInstallValid(
76 const base::DictionaryValue
& manifest
,
81 scoped_ptr
<ExtensionInstallPrompt
>
82 WebstoreStandaloneInstaller::CreateInstallUI() {
83 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
86 scoped_ptr
<WebstoreInstaller::Approval
>
87 WebstoreStandaloneInstaller::CreateApproval() const {
88 scoped_ptr
<WebstoreInstaller::Approval
> approval(
89 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
92 scoped_ptr
<base::DictionaryValue
>(manifest_
.get()->DeepCopy()),
94 approval
->skip_post_install_ui
= !ShouldShowPostInstallUI();
95 approval
->use_app_installed_bubble
= ShouldShowAppInstalledBubble();
96 approval
->installing_icon
= gfx::ImageSkia::CreateFrom1xBitmap(icon_
);
97 return approval
.Pass();
100 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
101 CompleteInstall(kWebstoreRequestError
);
104 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
105 scoped_ptr
<base::DictionaryValue
> webstore_data
) {
106 if (!CheckRequestorAlive()) {
107 CompleteInstall(std::string());
113 if (!CheckInlineInstallPermitted(*webstore_data
, &error
)) {
114 CompleteInstall(error
);
118 if (!CheckRequestorPermitted(*webstore_data
, &error
)) {
119 CompleteInstall(error
);
123 // Manifest, number of users, average rating and rating count are required.
124 std::string manifest
;
125 if (!webstore_data
->GetString(kManifestKey
, &manifest
) ||
126 !webstore_data
->GetString(kUsersKey
, &localized_user_count_
) ||
127 !webstore_data
->GetDouble(kAverageRatingKey
, &average_rating_
) ||
128 !webstore_data
->GetInteger(kRatingCountKey
, &rating_count_
)) {
129 CompleteInstall(kInvalidWebstoreResponseError
);
134 show_user_count_
= true;
135 webstore_data
->GetBoolean(kShowUserCountKey
, &show_user_count_
);
137 if (average_rating_
< ExtensionInstallPrompt::kMinExtensionRating
||
138 average_rating_
> ExtensionInstallPrompt::kMaxExtensionRating
) {
139 CompleteInstall(kInvalidWebstoreResponseError
);
143 // Localized name and description are optional.
144 if ((webstore_data
->HasKey(kLocalizedNameKey
) &&
145 !webstore_data
->GetString(kLocalizedNameKey
, &localized_name_
)) ||
146 (webstore_data
->HasKey(kLocalizedDescriptionKey
) &&
147 !webstore_data
->GetString(
148 kLocalizedDescriptionKey
, &localized_description_
))) {
149 CompleteInstall(kInvalidWebstoreResponseError
);
153 // Icon URL is optional.
155 if (webstore_data
->HasKey(kIconUrlKey
)) {
156 std::string icon_url_string
;
157 if (!webstore_data
->GetString(kIconUrlKey
, &icon_url_string
)) {
158 CompleteInstall(kInvalidWebstoreResponseError
);
161 icon_url
= GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
163 if (!icon_url
.is_valid()) {
164 CompleteInstall(kInvalidWebstoreResponseError
);
169 // Assume ownership of webstore_data.
170 webstore_data_
= webstore_data
.Pass();
172 scoped_refptr
<WebstoreInstallHelper
> helper
=
173 new WebstoreInstallHelper(this,
176 std::string(), // We don't have any icon data.
178 profile_
->GetRequestContext());
179 // The helper will call us back via OnWebstoreParseSucces or
180 // OnWebstoreParseFailure.
184 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
185 const std::string
& error
) {
186 CompleteInstall(error
);
189 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
190 const std::string
& id
,
191 const SkBitmap
& icon
,
192 base::DictionaryValue
* manifest
) {
195 if (!CheckRequestorAlive()) {
196 CompleteInstall(std::string());
200 manifest_
.reset(manifest
);
204 if (!CheckInstallValid(*manifest
, &error
)) {
205 DCHECK(!error
.empty());
206 CompleteInstall(error
);
210 install_prompt_
= CreateInstallPrompt();
211 if (install_prompt_
) {
213 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
215 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
216 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
222 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
223 const std::string
& id
,
224 InstallHelperResultCode result_code
,
225 const std::string
& error_message
) {
226 CompleteInstall(error_message
);
229 void WebstoreStandaloneInstaller::InstallUIProceed() {
230 if (!CheckRequestorAlive()) {
231 CompleteInstall(std::string());
235 scoped_ptr
<WebstoreInstaller::Approval
> approval
= CreateApproval();
237 ExtensionService
* extension_service
=
238 ExtensionSystem::Get(profile_
)->extension_service();
239 const Extension
* extension
=
240 extension_service
->GetExtensionById(id_
, true /* include disabled */);
242 std::string install_result
; // Empty string for install success.
244 if (ExtensionPrefs::Get(profile_
)->IsExtensionBlacklisted(id_
)) {
245 // Don't install a blacklisted extension.
246 install_result
= kExtensionIsBlacklisted
;
247 } else if (util::IsEphemeralApp(extension
->id(), profile_
) &&
248 !approval
->is_ephemeral
) {
249 // If the target extension has already been installed ephemerally, it can
250 // be promoted to a regular installed extension and downloading from the
251 // Web Store is not necessary.
252 extension_service
->PromoteEphemeralApp(extension
, false);
253 } else if (!extension_service
->IsExtensionEnabled(id_
)) {
254 // If the extension is installed but disabled, and not blacklisted,
256 extension_service
->EnableExtension(id_
);
257 } // else extension is installed and enabled; no work to be done.
259 CompleteInstall(install_result
);
263 scoped_refptr
<WebstoreInstaller
> installer
= new WebstoreInstaller(
273 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated
) {
274 CompleteInstall(kUserCancelledError
);
275 Release(); // Balanced in ShowInstallUI.
278 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
279 const std::string
& id
) {
281 CompleteInstall(std::string());
282 Release(); // Balanced in ShowInstallUI.
285 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
286 const std::string
& id
,
287 const std::string
& error
,
288 WebstoreInstaller::FailureReason cancelled
) {
290 CompleteInstall(error
);
291 Release(); // Balanced in ShowInstallUI.
294 void WebstoreStandaloneInstaller::AbortInstall() {
296 // Abort any in-progress fetches.
297 if (webstore_data_fetcher_
) {
298 webstore_data_fetcher_
.reset();
299 Release(); // Matches the AddRef in BeginInstall.
303 void WebstoreStandaloneInstaller::InvokeCallback(const std::string
& error
) {
304 if (!callback_
.is_null())
305 callback_
.Run(error
.empty(), error
);
308 void WebstoreStandaloneInstaller::CompleteInstall(const std::string
& error
) {
309 InvokeCallback(error
);
310 Release(); // Matches the AddRef in BeginInstall.
314 WebstoreStandaloneInstaller::ShowInstallUI() {
316 localized_extension_for_display_
=
317 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
319 Extension::REQUIRE_KEY
| Extension::FROM_WEBSTORE
,
322 localized_description_
,
324 if (!localized_extension_for_display_
.get()) {
325 CompleteInstall(kInvalidManifestError
);
329 // Keep this alive as long as the install prompt lives.
330 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
331 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
334 install_ui_
= CreateInstallUI();
335 install_ui_
->ConfirmStandaloneInstall(
336 this, localized_extension_for_display_
.get(), &icon_
, *install_prompt_
);
339 } // namespace extensions