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/common/extension.h"
16 #include "url/gurl.h"
17
18 using content::WebContents;
19
20 namespace extensions {
21
22 const char kManifestKey[] = "manifest";
23 const char kIconUrlKey[] = "icon_url";
24 const char kLocalizedNameKey[] = "localized_name";
25 const char kLocalizedDescriptionKey[] = "localized_description";
26 const char kUsersKey[] = "users";
27 const char kShowUserCountKey[] = "show_user_count";
28 const char kAverageRatingKey[] = "average_rating";
29 const char kRatingCountKey[] = "rating_count";
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
38
WebstoreStandaloneInstaller(const std::string & webstore_item_id,Profile * profile,const Callback & callback)39 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
40 const std::string& webstore_item_id,
41 Profile* profile,
42 const Callback& callback)
43 : id_(webstore_item_id),
44 callback_(callback),
45 profile_(profile),
46 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
47 show_user_count_(true),
48 average_rating_(0.0),
49 rating_count_(0) {
50 }
51
~WebstoreStandaloneInstaller()52 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
53
54 //
55 // Private interface implementation.
56 //
57
BeginInstall()58 void WebstoreStandaloneInstaller::BeginInstall() {
59 // Add a ref to keep this alive for WebstoreDataFetcher.
60 // All code paths from here eventually lead to either CompleteInstall or
61 // AbortInstall, which both release this ref.
62 AddRef();
63
64 if (!Extension::IdIsValid(id_)) {
65 CompleteInstall(kInvalidWebstoreItemId);
66 return;
67 }
68
69 // Use the requesting page as the referrer both since that is more correct
70 // (it is the page that caused this request to happen) and so that we can
71 // track top sites that trigger inline install requests.
72 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
73 this,
74 profile_->GetRequestContext(),
75 GetRequestorURL(),
76 id_));
77 webstore_data_fetcher_->Start();
78 }
79
CheckInstallValid(const base::DictionaryValue & manifest,std::string * error)80 bool WebstoreStandaloneInstaller::CheckInstallValid(
81 const base::DictionaryValue& manifest,
82 std::string* error) {
83 return true;
84 }
85
86 scoped_ptr<ExtensionInstallPrompt>
CreateInstallUI()87 WebstoreStandaloneInstaller::CreateInstallUI() {
88 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
89 }
90
91 scoped_ptr<WebstoreInstaller::Approval>
CreateApproval() const92 WebstoreStandaloneInstaller::CreateApproval() const {
93 scoped_ptr<WebstoreInstaller::Approval> approval(
94 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
95 profile_,
96 id_,
97 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
98 true));
99 approval->skip_post_install_ui = !ShouldShowPostInstallUI();
100 approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
101 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
102 return approval.Pass();
103 }
104
OnWebstoreRequestFailure()105 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
106 CompleteInstall(kWebstoreRequestError);
107 }
108
OnWebstoreResponseParseSuccess(scoped_ptr<DictionaryValue> webstore_data)109 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
110 scoped_ptr<DictionaryValue> webstore_data) {
111 if (!CheckRequestorAlive()) {
112 CompleteInstall(std::string());
113 return;
114 }
115
116 std::string error;
117
118 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
119 CompleteInstall(error);
120 return;
121 }
122
123 if (!CheckRequestorPermitted(*webstore_data, &error)) {
124 CompleteInstall(error);
125 return;
126 }
127
128 // Manifest, number of users, average rating and rating count are required.
129 std::string manifest;
130 if (!webstore_data->GetString(kManifestKey, &manifest) ||
131 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
132 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
133 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
134 CompleteInstall(kInvalidWebstoreResponseError);
135 return;
136 }
137
138 // Optional.
139 show_user_count_ = true;
140 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
141
142 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
143 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
144 CompleteInstall(kInvalidWebstoreResponseError);
145 return;
146 }
147
148 // Localized name and description are optional.
149 if ((webstore_data->HasKey(kLocalizedNameKey) &&
150 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
151 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
152 !webstore_data->GetString(
153 kLocalizedDescriptionKey, &localized_description_))) {
154 CompleteInstall(kInvalidWebstoreResponseError);
155 return;
156 }
157
158 // Icon URL is optional.
159 GURL icon_url;
160 if (webstore_data->HasKey(kIconUrlKey)) {
161 std::string icon_url_string;
162 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
163 CompleteInstall(kInvalidWebstoreResponseError);
164 return;
165 }
166 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
167 icon_url_string);
168 if (!icon_url.is_valid()) {
169 CompleteInstall(kInvalidWebstoreResponseError);
170 return;
171 }
172 }
173
174 // Assume ownership of webstore_data.
175 webstore_data_ = webstore_data.Pass();
176
177 scoped_refptr<WebstoreInstallHelper> helper =
178 new WebstoreInstallHelper(this,
179 id_,
180 manifest,
181 std::string(), // We don't have any icon data.
182 icon_url,
183 profile_->GetRequestContext());
184 // The helper will call us back via OnWebstoreParseSucces or
185 // OnWebstoreParseFailure.
186 helper->Start();
187 }
188
OnWebstoreResponseParseFailure(const std::string & error)189 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
190 const std::string& error) {
191 CompleteInstall(error);
192 }
193
OnWebstoreParseSuccess(const std::string & id,const SkBitmap & icon,base::DictionaryValue * manifest)194 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
195 const std::string& id,
196 const SkBitmap& icon,
197 base::DictionaryValue* manifest) {
198 CHECK_EQ(id_, id);
199
200 if (!CheckRequestorAlive()) {
201 CompleteInstall(std::string());
202 return;
203 }
204
205 manifest_.reset(manifest);
206 icon_ = icon;
207
208 std::string error;
209 if (!CheckInstallValid(*manifest, &error)) {
210 DCHECK(!error.empty());
211 CompleteInstall(error);
212 return;
213 }
214
215 install_prompt_ = CreateInstallPrompt();
216 if (install_prompt_) {
217 ShowInstallUI();
218 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
219 } else {
220 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
221 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
222 AddRef();
223 InstallUIProceed();
224 }
225 }
226
OnWebstoreParseFailure(const std::string & id,InstallHelperResultCode result_code,const std::string & error_message)227 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
228 const std::string& id,
229 InstallHelperResultCode result_code,
230 const std::string& error_message) {
231 CompleteInstall(error_message);
232 }
233
InstallUIProceed()234 void WebstoreStandaloneInstaller::InstallUIProceed() {
235 if (!CheckRequestorAlive()) {
236 CompleteInstall(std::string());
237 return;
238 }
239
240 scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
241
242 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
243 profile_,
244 this,
245 &(GetWebContents()->GetController()),
246 id_,
247 approval.Pass(),
248 install_source_);
249 installer->Start();
250 }
251
InstallUIAbort(bool user_initiated)252 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
253 CompleteInstall(kUserCancelledError);
254 Release(); // Balanced in ShowInstallUI.
255 }
256
OnExtensionInstallSuccess(const std::string & id)257 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
258 const std::string& id) {
259 CHECK_EQ(id_, id);
260 CompleteInstall(std::string());
261 Release(); // Balanced in ShowInstallUI.
262 }
263
OnExtensionInstallFailure(const std::string & id,const std::string & error,WebstoreInstaller::FailureReason cancelled)264 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
265 const std::string& id,
266 const std::string& error,
267 WebstoreInstaller::FailureReason cancelled) {
268 CHECK_EQ(id_, id);
269 CompleteInstall(error);
270 Release(); // Balanced in ShowInstallUI.
271 }
272
AbortInstall()273 void WebstoreStandaloneInstaller::AbortInstall() {
274 callback_.Reset();
275 // Abort any in-progress fetches.
276 if (webstore_data_fetcher_) {
277 webstore_data_fetcher_.reset();
278 Release(); // Matches the AddRef in BeginInstall.
279 }
280 }
281
CompleteInstall(const std::string & error)282 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
283 if (!callback_.is_null())
284 callback_.Run(error.empty(), error);
285 Release(); // Matches the AddRef in BeginInstall.
286 }
287
288 void
ShowInstallUI()289 WebstoreStandaloneInstaller::ShowInstallUI() {
290 std::string error;
291 localized_extension_for_display_ =
292 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
293 manifest_.get(),
294 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
295 id_,
296 localized_name_,
297 localized_description_,
298 &error);
299 if (!localized_extension_for_display_.get()) {
300 CompleteInstall(kInvalidManifestError);
301 return;
302 }
303
304 // Keep this alive as long as the install prompt lives.
305 // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
306 // OnExtensionInstallSuccess or OnExtensionInstallFailure.
307 AddRef();
308
309 install_ui_ = CreateInstallUI();
310 install_ui_->ConfirmStandaloneInstall(
311 this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
312 }
313
314 } // namespace extensions
315