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 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 52 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {} 53 54 // 55 // Private interface implementation. 56 // 57 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 80 bool WebstoreStandaloneInstaller::CheckInstallValid( 81 const base::DictionaryValue& manifest, 82 std::string* error) { 83 return true; 84 } 85 86 scoped_ptr<ExtensionInstallPrompt> 87 WebstoreStandaloneInstaller::CreateInstallUI() { 88 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents())); 89 } 90 91 scoped_ptr<WebstoreInstaller::Approval> 92 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 105 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() { 106 CompleteInstall(kWebstoreRequestError); 107 } 108 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 189 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure( 190 const std::string& error) { 191 CompleteInstall(error); 192 } 193 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 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 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 252 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) { 253 CompleteInstall(kUserCancelledError); 254 Release(); // Balanced in ShowInstallUI. 255 } 256 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 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 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 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 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