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 "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"
20 #include "url/gurl.h"
21
22 using content::WebContents;
23
24 namespace extensions {
25
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";
33
WebstoreStandaloneInstaller(const std::string & webstore_item_id,Profile * profile,const Callback & callback)34 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
35 const std::string& webstore_item_id,
36 Profile* profile,
37 const Callback& callback)
38 : id_(webstore_item_id),
39 callback_(callback),
40 profile_(profile),
41 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
42 show_user_count_(true),
43 average_rating_(0.0),
44 rating_count_(0) {
45 }
46
~WebstoreStandaloneInstaller()47 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
48
49 //
50 // Private interface implementation.
51 //
52
BeginInstall()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 (!Extension::IdIsValid(id_)) {
60 CompleteInstall(kInvalidWebstoreItemId);
61 return;
62 }
63
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(
68 this,
69 profile_->GetRequestContext(),
70 GetRequestorURL(),
71 id_));
72 webstore_data_fetcher_->Start();
73 }
74
CheckInstallValid(const base::DictionaryValue & manifest,std::string * error)75 bool WebstoreStandaloneInstaller::CheckInstallValid(
76 const base::DictionaryValue& manifest,
77 std::string* error) {
78 return true;
79 }
80
81 scoped_ptr<ExtensionInstallPrompt>
CreateInstallUI()82 WebstoreStandaloneInstaller::CreateInstallUI() {
83 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
84 }
85
86 scoped_ptr<WebstoreInstaller::Approval>
CreateApproval() const87 WebstoreStandaloneInstaller::CreateApproval() const {
88 scoped_ptr<WebstoreInstaller::Approval> approval(
89 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
90 profile_,
91 id_,
92 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
93 true));
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();
98 }
99
OnWebstoreRequestFailure()100 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
101 OnWebStoreDataFetcherDone();
102 CompleteInstall(kWebstoreRequestError);
103 }
104
OnWebstoreResponseParseSuccess(scoped_ptr<base::DictionaryValue> webstore_data)105 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
106 scoped_ptr<base::DictionaryValue> webstore_data) {
107 OnWebStoreDataFetcherDone();
108
109 if (!CheckRequestorAlive()) {
110 CompleteInstall(std::string());
111 return;
112 }
113
114 std::string error;
115
116 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
117 CompleteInstall(error);
118 return;
119 }
120
121 if (!CheckRequestorPermitted(*webstore_data, &error)) {
122 CompleteInstall(error);
123 return;
124 }
125
126 // Manifest, number of users, average rating and rating count are required.
127 std::string manifest;
128 if (!webstore_data->GetString(kManifestKey, &manifest) ||
129 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
130 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
131 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
132 CompleteInstall(kInvalidWebstoreResponseError);
133 return;
134 }
135
136 // Optional.
137 show_user_count_ = true;
138 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
139
140 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
141 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
142 CompleteInstall(kInvalidWebstoreResponseError);
143 return;
144 }
145
146 // Localized name and description are optional.
147 if ((webstore_data->HasKey(kLocalizedNameKey) &&
148 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
149 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
150 !webstore_data->GetString(
151 kLocalizedDescriptionKey, &localized_description_))) {
152 CompleteInstall(kInvalidWebstoreResponseError);
153 return;
154 }
155
156 // Icon URL is optional.
157 GURL icon_url;
158 if (webstore_data->HasKey(kIconUrlKey)) {
159 std::string icon_url_string;
160 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
161 CompleteInstall(kInvalidWebstoreResponseError);
162 return;
163 }
164 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
165 icon_url_string);
166 if (!icon_url.is_valid()) {
167 CompleteInstall(kInvalidWebstoreResponseError);
168 return;
169 }
170 }
171
172 // Assume ownership of webstore_data.
173 webstore_data_ = webstore_data.Pass();
174
175 scoped_refptr<WebstoreInstallHelper> helper =
176 new WebstoreInstallHelper(this,
177 id_,
178 manifest,
179 std::string(), // We don't have any icon data.
180 icon_url,
181 profile_->GetRequestContext());
182 // The helper will call us back via OnWebstoreParseSucces or
183 // OnWebstoreParseFailure.
184 helper->Start();
185 }
186
OnWebstoreResponseParseFailure(const std::string & error)187 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
188 const std::string& error) {
189 OnWebStoreDataFetcherDone();
190 CompleteInstall(error);
191 }
192
OnWebstoreParseSuccess(const std::string & id,const SkBitmap & icon,base::DictionaryValue * manifest)193 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
194 const std::string& id,
195 const SkBitmap& icon,
196 base::DictionaryValue* manifest) {
197 CHECK_EQ(id_, id);
198
199 if (!CheckRequestorAlive()) {
200 CompleteInstall(std::string());
201 return;
202 }
203
204 manifest_.reset(manifest);
205 icon_ = icon;
206
207 std::string error;
208 if (!CheckInstallValid(*manifest, &error)) {
209 DCHECK(!error.empty());
210 CompleteInstall(error);
211 return;
212 }
213
214 install_prompt_ = CreateInstallPrompt();
215 if (install_prompt_) {
216 ShowInstallUI();
217 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
218 } else {
219 InstallUIProceed();
220 }
221 }
222
OnWebstoreParseFailure(const std::string & id,InstallHelperResultCode result_code,const std::string & error_message)223 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
224 const std::string& id,
225 InstallHelperResultCode result_code,
226 const std::string& error_message) {
227 CompleteInstall(error_message);
228 }
229
InstallUIProceed()230 void WebstoreStandaloneInstaller::InstallUIProceed() {
231 if (!CheckRequestorAlive()) {
232 CompleteInstall(std::string());
233 return;
234 }
235
236 scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
237
238 ExtensionService* extension_service =
239 ExtensionSystem::Get(profile_)->extension_service();
240 const Extension* extension =
241 extension_service->GetExtensionById(id_, true /* include disabled */);
242 if (extension) {
243 std::string install_result; // Empty string for install success.
244
245 if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
246 // Don't install a blacklisted extension.
247 install_result = kExtensionIsBlacklisted;
248 } else if (util::IsEphemeralApp(extension->id(), profile_) &&
249 !approval->is_ephemeral) {
250 // If the target extension has already been installed ephemerally, it can
251 // be promoted to a regular installed extension and downloading from the
252 // Web Store is not necessary.
253 extension_service->PromoteEphemeralApp(extension, false);
254 } else if (!extension_service->IsExtensionEnabled(id_)) {
255 // If the extension is installed but disabled, and not blacklisted,
256 // enable it.
257 extension_service->EnableExtension(id_);
258 } // else extension is installed and enabled; no work to be done.
259
260 CompleteInstall(install_result);
261 return;
262 }
263
264 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
265 profile_,
266 this,
267 GetWebContents(),
268 id_,
269 approval.Pass(),
270 install_source_);
271 installer->Start();
272 }
273
InstallUIAbort(bool user_initiated)274 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
275 CompleteInstall(kUserCancelledError);
276 }
277
OnExtensionInstallSuccess(const std::string & id)278 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
279 const std::string& id) {
280 CHECK_EQ(id_, id);
281 CompleteInstall(std::string());
282 }
283
OnExtensionInstallFailure(const std::string & id,const std::string & error,WebstoreInstaller::FailureReason cancelled)284 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
285 const std::string& id,
286 const std::string& error,
287 WebstoreInstaller::FailureReason cancelled) {
288 CHECK_EQ(id_, id);
289 CompleteInstall(error);
290 }
291
AbortInstall()292 void WebstoreStandaloneInstaller::AbortInstall() {
293 callback_.Reset();
294 // Abort any in-progress fetches.
295 if (webstore_data_fetcher_) {
296 webstore_data_fetcher_.reset();
297 Release(); // Matches the AddRef in BeginInstall.
298 }
299 }
300
InvokeCallback(const std::string & error)301 void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) {
302 if (!callback_.is_null())
303 callback_.Run(error.empty(), error);
304 }
305
CompleteInstall(const std::string & error)306 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
307 InvokeCallback(error);
308 Release(); // Matches the AddRef in BeginInstall.
309 }
310
ShowInstallUI()311 void WebstoreStandaloneInstaller::ShowInstallUI() {
312 std::string error;
313 localized_extension_for_display_ =
314 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
315 manifest_.get(),
316 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
317 id_,
318 localized_name_,
319 localized_description_,
320 &error);
321 if (!localized_extension_for_display_.get()) {
322 CompleteInstall(kInvalidManifestError);
323 return;
324 }
325
326 install_ui_ = CreateInstallUI();
327 install_ui_->ConfirmStandaloneInstall(
328 this, localized_extension_for_display_.get(), &icon_, install_prompt_);
329 }
330
OnWebStoreDataFetcherDone()331 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
332 // An instance of this class is passed in as a delegate for the
333 // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
334 // therefore needs to remain alive until they are done. Clear the webstore
335 // data fetcher to avoid calling Release in AbortInstall while any of these
336 // operations are in progress.
337 webstore_data_fetcher_.reset();
338 }
339
340 } // namespace extensions
341