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 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 47 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {} 48 49 // 50 // Private interface implementation. 51 // 52 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 75 bool WebstoreStandaloneInstaller::CheckInstallValid( 76 const base::DictionaryValue& manifest, 77 std::string* error) { 78 return true; 79 } 80 81 scoped_ptr<ExtensionInstallPrompt> 82 WebstoreStandaloneInstaller::CreateInstallUI() { 83 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents())); 84 } 85 86 scoped_ptr<WebstoreInstaller::Approval> 87 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 100 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() { 101 OnWebStoreDataFetcherDone(); 102 CompleteInstall(kWebstoreRequestError); 103 } 104 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 187 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure( 188 const std::string& error) { 189 OnWebStoreDataFetcherDone(); 190 CompleteInstall(error); 191 } 192 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 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 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 274 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) { 275 CompleteInstall(kUserCancelledError); 276 } 277 278 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess( 279 const std::string& id) { 280 CHECK_EQ(id_, id); 281 CompleteInstall(std::string()); 282 } 283 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 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 301 void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) { 302 if (!callback_.is_null()) 303 callback_.Run(error.empty(), error); 304 } 305 306 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) { 307 InvokeCallback(error); 308 Release(); // Matches the AddRef in BeginInstall. 309 } 310 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 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