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 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 const char kRedirectUrlKey[] = "redirect_url"; 31 32 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID"; 33 const char kWebstoreRequestError[] = 34 "Could not fetch data from the Chrome Web Store"; 35 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse"; 36 const char kInvalidManifestError[] = "Invalid manifest"; 37 const char kUserCancelledError[] = "User cancelled install"; 38 39 40 WebstoreStandaloneInstaller::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 53 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {} 54 55 // 56 // Private interface implementation. 57 // 58 59 void 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 78 scoped_ptr<ExtensionInstallPrompt> 79 WebstoreStandaloneInstaller::CreateInstallUI() { 80 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents())); 81 } 82 83 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() { 84 CompleteInstall(kWebstoreRequestError); 85 } 86 87 void 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 167 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure( 168 const std::string& error) { 169 CompleteInstall(error); 170 } 171 172 void 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 195 void WebstoreStandaloneInstaller::OnWebstoreParseFailure( 196 const std::string& id, 197 InstallHelperResultCode result_code, 198 const std::string& error_message) { 199 CompleteInstall(error_message); 200 } 201 202 void 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 216 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( 217 profile_, 218 this, 219 &(GetWebContents()->GetController()), 220 id_, 221 approval.Pass(), 222 WebstoreInstaller::FLAG_INLINE_INSTALL); 223 installer->Start(); 224 } 225 226 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) { 227 CompleteInstall(kUserCancelledError); 228 } 229 230 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess( 231 const std::string& id) { 232 CHECK_EQ(id_, id); 233 CompleteInstall(std::string()); 234 } 235 236 void WebstoreStandaloneInstaller::OnExtensionInstallFailure( 237 const std::string& id, 238 const std::string& error, 239 WebstoreInstaller::FailureReason cancelled) { 240 CHECK_EQ(id_, id); 241 CompleteInstall(error); 242 } 243 244 void WebstoreStandaloneInstaller::AbortInstall() { 245 callback_.Reset(); 246 // Abort any in-progress fetches. 247 if (webstore_data_fetcher_) { 248 webstore_data_fetcher_.reset(); 249 Release(); // Matches the AddRef in BeginInstall. 250 } 251 } 252 253 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) { 254 // Clear webstore_data_fetcher_ so that WebContentsDestroyed will no longer 255 // call Release in case the WebContents is destroyed before this object. 256 scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher( 257 webstore_data_fetcher_.Pass()); 258 if (!callback_.is_null()) 259 callback_.Run(error.empty(), error); 260 261 Release(); // Matches the AddRef in BeginInstall. 262 } 263 264 void 265 WebstoreStandaloneInstaller::ShowInstallUI() { 266 std::string error; 267 localized_extension_for_display_ = 268 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay( 269 manifest_.get(), 270 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE, 271 id_, 272 localized_name_, 273 localized_description_, 274 &error); 275 if (!localized_extension_for_display_.get()) { 276 CompleteInstall(kInvalidManifestError); 277 return; 278 } 279 280 install_ui_ = CreateInstallUI(); 281 install_ui_->ConfirmStandaloneInstall( 282 this, localized_extension_for_display_.get(), &icon_, *install_prompt_); 283 } 284 285 } // namespace extensions 286