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