[email protected] | bdb74a2 | 2012-01-25 20:33:33 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 5 | #include "chrome/browser/extensions/webstore_standalone_installer.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 6 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 7 | #include "base/values.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 8 | #include "chrome/browser/extensions/crx_installer.h" |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 9 | #include "chrome/browser/extensions/extension_install_prompt.h" |
[email protected] | 00b3824 | 2012-07-18 18:43:22 | [diff] [blame] | 10 | #include "chrome/browser/extensions/extension_install_ui.h" |
[email protected] | 655b2b1a | 2011-10-13 17:13:06 | [diff] [blame] | 11 | #include "chrome/browser/extensions/extension_service.h" |
[email protected] | 7b394105 | 2013-02-13 01:21:59 | [diff] [blame] | 12 | #include "chrome/browser/extensions/webstore_data_fetcher.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 13 | #include "chrome/browser/profiles/profile.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 14 | #include "chrome/common/extensions/extension.h" |
[email protected] | 10c2d69 | 2012-05-11 05:32:23 | [diff] [blame] | 15 | #include "content/public/browser/web_contents.h" |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 16 | #include "googleurl/src/gurl.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 17 | |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 18 | using content::WebContents; |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame] | 19 | |
[email protected] | 3d61a7f | 2012-07-12 19:11:25 | [diff] [blame] | 20 | namespace extensions { |
| 21 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 22 | const char kManifestKey[] = "manifest"; |
| 23 | const char kIconUrlKey[] = "icon_url"; |
| 24 | const char kLocalizedNameKey[] = "localized_name"; |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 25 | const char kLocalizedDescriptionKey[] = "localized_description"; |
| 26 | const char kUsersKey[] = "users"; |
| 27 | const char kAverageRatingKey[] = "average_rating"; |
| 28 | const char kRatingCountKey[] = "rating_count"; |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 29 | const char kRedirectUrlKey[] = "redirect_url"; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 30 | |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 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"; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 35 | const char kInvalidManifestError[] = "Invalid manifest"; |
| 36 | const char kUserCancelledError[] = "User cancelled install"; |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 37 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 38 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 39 | WebstoreStandaloneInstaller::WebstoreStandaloneInstaller( |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 40 | const std::string& webstore_item_id, |
[email protected] | c1b2d04 | 2013-02-23 00:31:04 | [diff] [blame] | 41 | Profile* profile, |
[email protected] | c1b2d04 | 2013-02-23 00:31:04 | [diff] [blame] | 42 | const Callback& callback) |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 43 | : id_(webstore_item_id), |
[email protected] | d2a639e | 2012-09-17 07:41:21 | [diff] [blame] | 44 | callback_(callback), |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 45 | profile_(profile), |
[email protected] | c7bf745 | 2011-09-12 21:31:50 | [diff] [blame] | 46 | average_rating_(0.0), |
[email protected] | 5f2a475 | 2012-04-27 22:18:58 | [diff] [blame] | 47 | rating_count_(0) { |
[email protected] | c1b2d04 | 2013-02-23 00:31:04 | [diff] [blame] | 48 | CHECK(!callback_.is_null()); |
| 49 | } |
| 50 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 51 | WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {} |
| 52 | |
| 53 | // |
| 54 | // Private interface implementation. |
| 55 | // |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 56 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 57 | void WebstoreStandaloneInstaller::BeginInstall() { |
[email protected] | 5db2e88 | 2012-12-20 10:17:26 | [diff] [blame] | 58 | AddRef(); // Balanced in CompleteInstall or WebContentsDestroyed. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 59 | |
[email protected] | 3d61a7f | 2012-07-12 19:11:25 | [diff] [blame] | 60 | if (!Extension::IdIsValid(id_)) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 61 | CompleteInstall(kInvalidWebstoreItemId); |
| 62 | return; |
| 63 | } |
| 64 | |
[email protected] | b4574c0 | 2011-11-17 06:19:13 | [diff] [blame] | 65 | // Use the requesting page as the referrer both since that is more correct |
| 66 | // (it is the page that caused this request to happen) and so that we can |
| 67 | // track top sites that trigger inline install requests. |
[email protected] | 7b394105 | 2013-02-13 01:21:59 | [diff] [blame] | 68 | webstore_data_fetcher_.reset(new WebstoreDataFetcher( |
| 69 | this, |
[email protected] | c1b2d04 | 2013-02-23 00:31:04 | [diff] [blame] | 70 | profile_->GetRequestContext(), |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 71 | GetRequestorURL(), |
[email protected] | 7b394105 | 2013-02-13 01:21:59 | [diff] [blame] | 72 | id_)); |
| 73 | webstore_data_fetcher_->Start(); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 74 | } |
| 75 | |
[email protected] | 7b394105 | 2013-02-13 01:21:59 | [diff] [blame] | 76 | void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() { |
| 77 | CompleteInstall(kWebstoreRequestError); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 78 | } |
| 79 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 80 | void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess( |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 81 | DictionaryValue* webstore_data) { |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 82 | if (!CheckRequestorAlive()) { |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame^] | 83 | CompleteInstall(std::string()); |
[email protected] | 4693243e | 2011-09-09 23:52:37 | [diff] [blame] | 84 | return; |
| 85 | } |
| 86 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 87 | std::string error; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 88 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 89 | if (!CheckInlineInstallPermitted(*webstore_data, &error)) { |
| 90 | CompleteInstall(error); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 91 | return; |
| 92 | } |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 93 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 94 | if (!CheckRequestorPermitted(*webstore_data, &error)) { |
| 95 | CompleteInstall(error); |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 96 | return; |
| 97 | } |
| 98 | |
| 99 | // Manifest, number of users, average rating and rating count are required. |
| 100 | std::string manifest; |
| 101 | if (!webstore_data->GetString(kManifestKey, &manifest) || |
| 102 | !webstore_data->GetString(kUsersKey, &localized_user_count_) || |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 103 | !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) || |
| 104 | !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) { |
| 105 | CompleteInstall(kInvalidWebstoreResponseError); |
| 106 | return; |
| 107 | } |
| 108 | |
[email protected] | c82da8c4 | 2012-06-08 19:49:11 | [diff] [blame] | 109 | if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating || |
| 110 | average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) { |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 111 | CompleteInstall(kInvalidWebstoreResponseError); |
| 112 | return; |
| 113 | } |
| 114 | |
| 115 | // Localized name and description are optional. |
| 116 | if ((webstore_data->HasKey(kLocalizedNameKey) && |
| 117 | !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) || |
| 118 | (webstore_data->HasKey(kLocalizedDescriptionKey) && |
| 119 | !webstore_data->GetString( |
| 120 | kLocalizedDescriptionKey, &localized_description_))) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 121 | CompleteInstall(kInvalidWebstoreResponseError); |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | // Icon URL is optional. |
| 126 | GURL icon_url; |
| 127 | if (webstore_data->HasKey(kIconUrlKey)) { |
| 128 | std::string icon_url_string; |
| 129 | if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) { |
| 130 | CompleteInstall(kInvalidWebstoreResponseError); |
| 131 | return; |
| 132 | } |
| 133 | icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve( |
| 134 | icon_url_string); |
| 135 | if (!icon_url.is_valid()) { |
| 136 | CompleteInstall(kInvalidWebstoreResponseError); |
| 137 | return; |
| 138 | } |
| 139 | } |
| 140 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 141 | // Assume ownership of webstore_data. |
| 142 | webstore_data_.reset(webstore_data); |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 143 | |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame^] | 144 | scoped_refptr<WebstoreInstallHelper> helper = |
| 145 | new WebstoreInstallHelper(this, |
| 146 | id_, |
| 147 | manifest, |
| 148 | std::string(), // We don't have any icon data. |
| 149 | icon_url, |
| 150 | profile_->GetRequestContext()); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 151 | // The helper will call us back via OnWebstoreParseSucces or |
| 152 | // OnWebstoreParseFailure. |
| 153 | helper->Start(); |
| 154 | } |
| 155 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 156 | void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure( |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 157 | const std::string& error) { |
| 158 | CompleteInstall(error); |
| 159 | } |
| 160 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 161 | void WebstoreStandaloneInstaller::OnWebstoreParseSuccess( |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 162 | const std::string& id, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 163 | const SkBitmap& icon, |
| 164 | base::DictionaryValue* manifest) { |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 165 | CHECK_EQ(id_, id); |
| 166 | |
| 167 | if (!CheckRequestorAlive()) { |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame^] | 168 | CompleteInstall(std::string()); |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 169 | return; |
| 170 | } |
| 171 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 172 | manifest_.reset(manifest); |
| 173 | icon_ = icon; |
| 174 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 175 | install_prompt_ = CreateInstallPrompt(); |
| 176 | if (install_prompt_) { |
| 177 | CreateInstallUI(); |
| 178 | // Control flow finishes up in InstallUIProceed or InstallUIAbort. |
| 179 | } else { |
[email protected] | 77e9261d | 2013-02-04 22:01:47 | [diff] [blame] | 180 | InstallUIProceed(); |
[email protected] | 77e9261d | 2013-02-04 22:01:47 | [diff] [blame] | 181 | } |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 182 | } |
| 183 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 184 | void WebstoreStandaloneInstaller::OnWebstoreParseFailure( |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 185 | const std::string& id, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 186 | InstallHelperResultCode result_code, |
| 187 | const std::string& error_message) { |
| 188 | CompleteInstall(error_message); |
| 189 | } |
| 190 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 191 | void WebstoreStandaloneInstaller::InstallUIProceed() { |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 192 | if (!CheckRequestorAlive()) { |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame^] | 193 | CompleteInstall(std::string()); |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 194 | return; |
| 195 | } |
| 196 | |
[email protected] | 21a5ad6 | 2012-04-03 04:48:45 | [diff] [blame] | 197 | scoped_ptr<WebstoreInstaller::Approval> approval( |
[email protected] | 89019d6 | 2012-05-17 18:47:09 | [diff] [blame] | 198 | WebstoreInstaller::Approval::CreateWithNoInstallPrompt( |
[email protected] | c1b2d04 | 2013-02-23 00:31:04 | [diff] [blame] | 199 | profile_, |
[email protected] | 89019d6 | 2012-05-17 18:47:09 | [diff] [blame] | 200 | id_, |
| 201 | scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()))); |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 202 | approval->skip_post_install_ui = !ShouldShowPostInstallUI(); |
| 203 | approval->use_app_installed_bubble = ShouldShowAppInstalledBubble(); |
[email protected] | 21a5ad6 | 2012-04-03 04:48:45 | [diff] [blame] | 204 | |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 205 | scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 206 | profile_, |
| 207 | this, |
| 208 | &(GetWebContents()->GetController()), |
| 209 | id_, |
| 210 | approval.Pass(), |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 211 | WebstoreInstaller::FLAG_INLINE_INSTALL); |
| 212 | installer->Start(); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 213 | } |
| 214 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 215 | void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 216 | CompleteInstall(kUserCancelledError); |
| 217 | } |
| 218 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 219 | void WebstoreStandaloneInstaller::OnExtensionInstallSuccess( |
| 220 | const std::string& id) { |
[email protected] | cb08ba2 | 2011-10-19 21:41:40 | [diff] [blame] | 221 | CHECK_EQ(id_, id); |
[email protected] | 007b3f8 | 2013-04-09 08:46:45 | [diff] [blame^] | 222 | CompleteInstall(std::string()); |
[email protected] | cb08ba2 | 2011-10-19 21:41:40 | [diff] [blame] | 223 | } |
| 224 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 225 | void WebstoreStandaloneInstaller::OnExtensionInstallFailure( |
[email protected] | bcd1eaf7 | 2012-10-03 05:42:29 | [diff] [blame] | 226 | const std::string& id, |
| 227 | const std::string& error, |
| 228 | WebstoreInstaller::FailureReason cancelled) { |
[email protected] | cb08ba2 | 2011-10-19 21:41:40 | [diff] [blame] | 229 | CHECK_EQ(id_, id); |
| 230 | CompleteInstall(error); |
| 231 | } |
| 232 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 233 | void WebstoreStandaloneInstaller::AbortInstall() { |
| 234 | callback_.Reset(); |
| 235 | // Abort any in-progress fetches. |
| 236 | if (webstore_data_fetcher_.get()) { |
| 237 | webstore_data_fetcher_.reset(); |
| 238 | Release(); // Matches the AddRef in BeginInstall. |
| 239 | } |
| 240 | } |
| 241 | |
[email protected] | 734bcec | 2012-10-08 20:29:05 | [diff] [blame] | 242 | void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) { |
[email protected] | 0688d8dc | 2013-02-16 04:10:38 | [diff] [blame] | 243 | // Clear webstore_data_fetcher_ so that WebContentsDestroyed will no longer |
| 244 | // call Release in case the WebContents is destroyed before this object. |
| 245 | scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher( |
| 246 | webstore_data_fetcher_.Pass()); |
[email protected] | d2a639e | 2012-09-17 07:41:21 | [diff] [blame] | 247 | if (!callback_.is_null()) |
| 248 | callback_.Run(error.empty(), error); |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 249 | |
[email protected] | 5db2e88 | 2012-12-20 10:17:26 | [diff] [blame] | 250 | Release(); // Matches the AddRef in BeginInstall. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 251 | } |
[email protected] | 5f2a475 | 2012-04-27 22:18:58 | [diff] [blame] | 252 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 253 | void |
| 254 | WebstoreStandaloneInstaller::CreateInstallUI() { |
| 255 | std::string error; |
| 256 | localized_extension_for_display_ = |
| 257 | ExtensionInstallPrompt::GetLocalizedExtensionForDisplay( |
| 258 | manifest_.get(), |
| 259 | Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE, |
| 260 | id_, |
| 261 | localized_name_, |
| 262 | localized_description_, |
| 263 | &error); |
| 264 | if (!localized_extension_for_display_) { |
| 265 | CompleteInstall(kInvalidManifestError); |
| 266 | return; |
[email protected] | 5f2a475 | 2012-04-27 22:18:58 | [diff] [blame] | 267 | } |
[email protected] | 5f2a475 | 2012-04-27 22:18:58 | [diff] [blame] | 268 | |
[email protected] | d44ec7b | 2013-03-15 04:34:34 | [diff] [blame] | 269 | install_ui_.reset(new ExtensionInstallPrompt(GetWebContents())); |
| 270 | install_ui_->ConfirmStandaloneInstall(this, |
| 271 | localized_extension_for_display_, |
| 272 | &icon_, |
| 273 | *install_prompt_); |
[email protected] | 5f2a475 | 2012-04-27 22:18:58 | [diff] [blame] | 274 | } |
[email protected] | 3d61a7f | 2012-07-12 19:11:25 | [diff] [blame] | 275 | |
| 276 | } // namespace extensions |