[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 | |
| 5 | #include "chrome/browser/extensions/webstore_inline_installer.h" |
| 6 | |
| 7 | #include <vector> |
| 8 | |
[email protected] | 8e6ac4b | 2011-10-17 19:04:31 | [diff] [blame] | 9 | #include "base/bind.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 10 | #include "base/string_util.h" |
| 11 | #include "base/values.h" |
| 12 | #include "chrome/browser/browser_process.h" |
| 13 | #include "chrome/browser/extensions/crx_installer.h" |
| 14 | #include "chrome/browser/extensions/extension_install_dialog.h" |
[email protected] | 655b2b1a | 2011-10-13 17:13:06 | [diff] [blame] | 15 | #include "chrome/browser/extensions/extension_service.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 16 | #include "chrome/browser/profiles/profile.h" |
| 17 | #include "chrome/common/chrome_utility_messages.h" |
| 18 | #include "chrome/common/extensions/extension.h" |
| 19 | #include "chrome/common/extensions/extension_constants.h" |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 20 | #include "chrome/common/extensions/url_pattern.h" |
[email protected] | bdb74a2 | 2012-01-25 20:33:33 | [diff] [blame] | 21 | #include "chrome/common/url_constants.h" |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 22 | #include "content/public/browser/web_contents.h" |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 23 | #include "content/public/browser/utility_process_host.h" |
| 24 | #include "content/public/browser/utility_process_host_client.h" |
[email protected] | 7b419b8 | 2011-10-27 04:23:46 | [diff] [blame] | 25 | #include "content/public/common/url_fetcher.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 26 | #include "net/base/escape.h" |
[email protected] | 41e9a0de | 2011-09-14 17:24:51 | [diff] [blame] | 27 | #include "net/base/load_flags.h" |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 28 | #include "net/url_request/url_request_status.h" |
| 29 | |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame] | 30 | using content::BrowserThread; |
[email protected] | e5d549d | 2011-12-28 01:29:20 | [diff] [blame] | 31 | using content::OpenURLParams; |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 32 | using content::UtilityProcessHost; |
| 33 | using content::UtilityProcessHostClient; |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 34 | using content::WebContents; |
[email protected] | 631bb74 | 2011-11-02 11:29:39 | [diff] [blame] | 35 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 36 | const char kManifestKey[] = "manifest"; |
| 37 | const char kIconUrlKey[] = "icon_url"; |
| 38 | const char kLocalizedNameKey[] = "localized_name"; |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 39 | const char kLocalizedDescriptionKey[] = "localized_description"; |
| 40 | const char kUsersKey[] = "users"; |
| 41 | const char kAverageRatingKey[] = "average_rating"; |
| 42 | const char kRatingCountKey[] = "rating_count"; |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 43 | const char kVerifiedSiteKey[] = "verified_site"; |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 44 | const char kInlineInstallNotSupportedKey[] = "inline_install_not_supported"; |
| 45 | const char kRedirectUrlKey[] = "redirect_url"; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 46 | |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 47 | const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID"; |
| 48 | const char kWebstoreRequestError[] = |
| 49 | "Could not fetch data from the Chrome Web Store"; |
| 50 | const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse"; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 51 | const char kInvalidManifestError[] = "Invalid manifest"; |
| 52 | const char kUserCancelledError[] = "User cancelled install"; |
[email protected] | 5da311ea | 2011-09-14 20:57:32 | [diff] [blame] | 53 | const char kNoVerifiedSiteError[] = |
| 54 | "Inline installs can only be initiated for Chrome Web Store items that " |
| 55 | "have a verified site"; |
| 56 | const char kNotFromVerifiedSiteError[] = |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 57 | "Installs can only be initiated by the Chrome Web Store item's verified " |
| 58 | "site"; |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 59 | const char kInlineInstallSupportedError[] = |
| 60 | "Inline installation is not supported for this item. The user will be " |
| 61 | "redirected to the Chrome Web Store."; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 62 | |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 63 | class SafeWebstoreResponseParser : public UtilityProcessHostClient { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 64 | public: |
| 65 | SafeWebstoreResponseParser(WebstoreInlineInstaller *client, |
| 66 | const std::string& webstore_data) |
| 67 | : client_(client), |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 68 | webstore_data_(webstore_data) {} |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 69 | |
| 70 | void Start() { |
| 71 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 72 | BrowserThread::PostTask( |
| 73 | BrowserThread::IO, |
| 74 | FROM_HERE, |
[email protected] | 8e6ac4b | 2011-10-17 19:04:31 | [diff] [blame] | 75 | base::Bind(&SafeWebstoreResponseParser::StartWorkOnIOThread, this)); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | void StartWorkOnIOThread() { |
| 79 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 80 | UtilityProcessHost* host = |
| 81 | UtilityProcessHost::Create(this, BrowserThread::IO); |
| 82 | host->EnableZygote(); |
| 83 | host->Send(new ChromeUtilityMsg_ParseJSON(webstore_data_)); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 84 | } |
| 85 | |
[email protected] | c4f883a | 2012-02-03 17:02:07 | [diff] [blame] | 86 | // Implementing pieces of the UtilityProcessHostClient interface. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 87 | virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { |
| 88 | bool handled = true; |
| 89 | IPC_BEGIN_MESSAGE_MAP(SafeWebstoreResponseParser, message) |
| 90 | IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded, |
| 91 | OnJSONParseSucceeded) |
| 92 | IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed, |
| 93 | OnJSONParseFailed) |
| 94 | IPC_MESSAGE_UNHANDLED(handled = false) |
| 95 | IPC_END_MESSAGE_MAP() |
| 96 | return handled; |
| 97 | } |
| 98 | |
| 99 | void OnJSONParseSucceeded(const ListValue& wrapper) { |
| 100 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 101 | Value* value = NULL; |
| 102 | CHECK(wrapper.Get(0, &value)); |
| 103 | if (value->IsType(Value::TYPE_DICTIONARY)) { |
| 104 | parsed_webstore_data_.reset( |
| 105 | static_cast<DictionaryValue*>(value)->DeepCopy()); |
| 106 | } else { |
| 107 | error_ = kInvalidWebstoreResponseError; |
| 108 | } |
| 109 | |
| 110 | ReportResults(); |
| 111 | } |
| 112 | |
| 113 | virtual void OnJSONParseFailed(const std::string& error_message) { |
| 114 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 115 | error_ = error_message; |
| 116 | ReportResults(); |
| 117 | } |
| 118 | |
| 119 | void ReportResults() { |
| 120 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 121 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 122 | BrowserThread::PostTask( |
| 123 | BrowserThread::UI, |
| 124 | FROM_HERE, |
[email protected] | 8e6ac4b | 2011-10-17 19:04:31 | [diff] [blame] | 125 | base::Bind(&SafeWebstoreResponseParser::ReportResultOnUIThread, this)); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | void ReportResultOnUIThread() { |
| 129 | CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 130 | if (error_.empty() && parsed_webstore_data_.get()) { |
| 131 | client_->OnWebstoreResponseParseSuccess(parsed_webstore_data_.release()); |
| 132 | } else { |
| 133 | client_->OnWebstoreResponseParseFailure(error_); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | private: |
| 138 | virtual ~SafeWebstoreResponseParser() {} |
| 139 | |
| 140 | WebstoreInlineInstaller* client_; |
| 141 | |
| 142 | std::string webstore_data_; |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 143 | std::string error_; |
| 144 | scoped_ptr<DictionaryValue> parsed_webstore_data_; |
| 145 | }; |
| 146 | |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 147 | WebstoreInlineInstaller::WebstoreInlineInstaller(WebContents* web_contents, |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 148 | int install_id, |
[email protected] | 7b92104 | 2012-02-11 01:41:27 | [diff] [blame] | 149 | int return_route_id, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 150 | std::string webstore_item_id, |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 151 | GURL requestor_url, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 152 | Delegate* delegate) |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 153 | : content::WebContentsObserver(web_contents), |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 154 | install_id_(install_id), |
[email protected] | 7b92104 | 2012-02-11 01:41:27 | [diff] [blame] | 155 | return_route_id_(return_route_id), |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 156 | id_(webstore_item_id), |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 157 | requestor_url_(requestor_url), |
[email protected] | c7bf745 | 2011-09-12 21:31:50 | [diff] [blame] | 158 | delegate_(delegate), |
| 159 | average_rating_(0.0), |
| 160 | rating_count_(0) {} |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 161 | |
| 162 | WebstoreInlineInstaller::~WebstoreInlineInstaller() { |
| 163 | } |
| 164 | |
| 165 | void WebstoreInlineInstaller::BeginInstall() { |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 166 | AddRef(); // Balanced in CompleteInstall or WebContentsDestroyed. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 167 | |
| 168 | if (!Extension::IdIsValid(id_)) { |
| 169 | CompleteInstall(kInvalidWebstoreItemId); |
| 170 | return; |
| 171 | } |
| 172 | |
| 173 | GURL webstore_data_url(extension_urls::GetWebstoreItemJsonDataURL(id_)); |
| 174 | |
[email protected] | 36aea270 | 2011-10-26 01:12:22 | [diff] [blame] | 175 | webstore_data_url_fetcher_.reset(content::URLFetcher::Create( |
| 176 | webstore_data_url, content::URLFetcher::GET, this)); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 177 | Profile* profile = Profile::FromBrowserContext( |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 178 | web_contents()->GetBrowserContext()); |
[email protected] | 7cc6e563 | 2011-10-25 17:56:12 | [diff] [blame] | 179 | webstore_data_url_fetcher_->SetRequestContext( |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 180 | profile->GetRequestContext()); |
[email protected] | b4574c0 | 2011-11-17 06:19:13 | [diff] [blame] | 181 | // Use the requesting page as the referrer both since that is more correct |
| 182 | // (it is the page that caused this request to happen) and so that we can |
| 183 | // track top sites that trigger inline install requests. |
| 184 | webstore_data_url_fetcher_->SetReferrer(requestor_url_.spec()); |
[email protected] | 7cc6e563 | 2011-10-25 17:56:12 | [diff] [blame] | 185 | webstore_data_url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
| 186 | net::LOAD_DO_NOT_SAVE_COOKIES | |
| 187 | net::LOAD_DISABLE_CACHE); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 188 | webstore_data_url_fetcher_->Start(); |
| 189 | } |
| 190 | |
[email protected] | 7cc6e563 | 2011-10-25 17:56:12 | [diff] [blame] | 191 | void WebstoreInlineInstaller::OnURLFetchComplete( |
| 192 | const content::URLFetcher* source) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 193 | CHECK_EQ(webstore_data_url_fetcher_.get(), source); |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 194 | // We shouldn't be getting UrlFetcher callbacks if the WebContents has gone |
| 195 | // away; we stop any in in-progress fetches in WebContentsDestroyed. |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 196 | CHECK(web_contents()); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 197 | |
[email protected] | 7cc6e563 | 2011-10-25 17:56:12 | [diff] [blame] | 198 | if (!webstore_data_url_fetcher_->GetStatus().is_success() || |
| 199 | webstore_data_url_fetcher_->GetResponseCode() != 200) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 200 | CompleteInstall(kWebstoreRequestError); |
| 201 | return; |
| 202 | } |
| 203 | |
| 204 | std::string webstore_json_data; |
| 205 | webstore_data_url_fetcher_->GetResponseAsString(&webstore_json_data); |
| 206 | webstore_data_url_fetcher_.reset(); |
| 207 | |
| 208 | scoped_refptr<SafeWebstoreResponseParser> parser = |
| 209 | new SafeWebstoreResponseParser(this, webstore_json_data); |
| 210 | // The parser will call us back via OnWebstoreResponseParseSucces or |
| 211 | // OnWebstoreResponseParseFailure. |
| 212 | parser->Start(); |
| 213 | } |
| 214 | |
| 215 | void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess( |
| 216 | DictionaryValue* webstore_data) { |
[email protected] | 4693243e | 2011-09-09 23:52:37 | [diff] [blame] | 217 | // Check if the tab has gone away in the meantime. |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 218 | if (!web_contents()) { |
[email protected] | 4693243e | 2011-09-09 23:52:37 | [diff] [blame] | 219 | CompleteInstall(""); |
| 220 | return; |
| 221 | } |
| 222 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 223 | webstore_data_.reset(webstore_data); |
| 224 | |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 225 | // The store may not support inline installs for this item, in which case |
| 226 | // we open the store-provided redirect URL in a new tab and abort the |
| 227 | // installation process. |
| 228 | bool inline_install_not_supported = false; |
| 229 | if (webstore_data->HasKey(kInlineInstallNotSupportedKey) && |
| 230 | !webstore_data->GetBoolean( |
| 231 | kInlineInstallNotSupportedKey, &inline_install_not_supported)) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 232 | CompleteInstall(kInvalidWebstoreResponseError); |
| 233 | return; |
| 234 | } |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 235 | if (inline_install_not_supported) { |
| 236 | std::string redirect_url; |
| 237 | if (!webstore_data->GetString(kRedirectUrlKey, &redirect_url)) { |
| 238 | CompleteInstall(kInvalidWebstoreResponseError); |
| 239 | return; |
| 240 | } |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 241 | |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 242 | web_contents()->OpenURL(OpenURLParams( |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 243 | GURL(redirect_url), |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 244 | content::Referrer(web_contents()->GetURL(), |
[email protected] | bce1f1c | 2011-12-05 15:11:58 | [diff] [blame] | 245 | WebKit::WebReferrerPolicyDefault), |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 246 | NEW_FOREGROUND_TAB, |
[email protected] | e47ae947 | 2011-10-13 19:48:34 | [diff] [blame] | 247 | content::PAGE_TRANSITION_AUTO_BOOKMARK, |
| 248 | false)); |
[email protected] | dd5161a | 2011-09-14 17:40:17 | [diff] [blame] | 249 | CompleteInstall(kInlineInstallSupportedError); |
| 250 | return; |
| 251 | } |
| 252 | |
| 253 | // Manifest, number of users, average rating and rating count are required. |
| 254 | std::string manifest; |
| 255 | if (!webstore_data->GetString(kManifestKey, &manifest) || |
| 256 | !webstore_data->GetString(kUsersKey, &localized_user_count_) || |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 257 | !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) || |
| 258 | !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) { |
| 259 | CompleteInstall(kInvalidWebstoreResponseError); |
| 260 | return; |
| 261 | } |
| 262 | |
| 263 | if (average_rating_ < ExtensionInstallUI::kMinExtensionRating || |
| 264 | average_rating_ >ExtensionInstallUI::kMaxExtensionRating) { |
| 265 | CompleteInstall(kInvalidWebstoreResponseError); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | // Localized name and description are optional. |
| 270 | if ((webstore_data->HasKey(kLocalizedNameKey) && |
| 271 | !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) || |
| 272 | (webstore_data->HasKey(kLocalizedDescriptionKey) && |
| 273 | !webstore_data->GetString( |
| 274 | kLocalizedDescriptionKey, &localized_description_))) { |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 275 | CompleteInstall(kInvalidWebstoreResponseError); |
| 276 | return; |
| 277 | } |
| 278 | |
| 279 | // Icon URL is optional. |
| 280 | GURL icon_url; |
| 281 | if (webstore_data->HasKey(kIconUrlKey)) { |
| 282 | std::string icon_url_string; |
| 283 | if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) { |
| 284 | CompleteInstall(kInvalidWebstoreResponseError); |
| 285 | return; |
| 286 | } |
| 287 | icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve( |
| 288 | icon_url_string); |
| 289 | if (!icon_url.is_valid()) { |
| 290 | CompleteInstall(kInvalidWebstoreResponseError); |
| 291 | return; |
| 292 | } |
| 293 | } |
| 294 | |
[email protected] | 5da311ea | 2011-09-14 20:57:32 | [diff] [blame] | 295 | // Verified site is required |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 296 | if (webstore_data->HasKey(kVerifiedSiteKey)) { |
[email protected] | bdb74a2 | 2012-01-25 20:33:33 | [diff] [blame] | 297 | std::string verified_site; |
| 298 | if (!webstore_data->GetString(kVerifiedSiteKey, &verified_site)) { |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 299 | CompleteInstall(kInvalidWebstoreResponseError); |
| 300 | return; |
| 301 | } |
| 302 | |
[email protected] | bdb74a2 | 2012-01-25 20:33:33 | [diff] [blame] | 303 | if (!IsRequestorURLInVerifiedSite(requestor_url_, verified_site)) { |
[email protected] | 5da311ea | 2011-09-14 20:57:32 | [diff] [blame] | 304 | CompleteInstall(kNotFromVerifiedSiteError); |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 305 | return; |
| 306 | } |
[email protected] | 5da311ea | 2011-09-14 20:57:32 | [diff] [blame] | 307 | } else { |
| 308 | CompleteInstall(kNoVerifiedSiteError); |
| 309 | return; |
[email protected] | a221ef09 | 2011-09-07 01:34:10 | [diff] [blame] | 310 | } |
| 311 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 312 | scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( |
| 313 | this, |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 314 | id_, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 315 | manifest, |
| 316 | "", // We don't have any icon data. |
| 317 | icon_url, |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 318 | Profile::FromBrowserContext(web_contents()->GetBrowserContext())-> |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 319 | GetRequestContext()); |
| 320 | // The helper will call us back via OnWebstoreParseSucces or |
| 321 | // OnWebstoreParseFailure. |
| 322 | helper->Start(); |
| 323 | } |
| 324 | |
[email protected] | bdb74a2 | 2012-01-25 20:33:33 | [diff] [blame] | 325 | // static |
| 326 | bool WebstoreInlineInstaller::IsRequestorURLInVerifiedSite( |
| 327 | const GURL& requestor_url, |
| 328 | const std::string& verified_site) { |
| 329 | // Turn the verified site (which may be a bare domain, or have a port and/or a |
| 330 | // path) into a URL that can be parsed by URLPattern. |
| 331 | std::string verified_site_url = |
| 332 | StringPrintf("http://*.%s%s", |
| 333 | verified_site.c_str(), |
| 334 | verified_site.find('/') == std::string::npos ? "/*" : "*"); |
| 335 | |
| 336 | URLPattern verified_site_pattern( |
| 337 | URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS); |
| 338 | URLPattern::ParseResult parse_result = |
| 339 | verified_site_pattern.Parse(verified_site_url); |
| 340 | if (parse_result != URLPattern::PARSE_SUCCESS) { |
| 341 | DLOG(WARNING) << "Could not parse " << verified_site_url << |
| 342 | " as URL pattern " << parse_result; |
| 343 | return false; |
| 344 | } |
| 345 | verified_site_pattern.SetScheme("*"); |
| 346 | |
| 347 | return verified_site_pattern.MatchesURL(requestor_url); |
| 348 | } |
| 349 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 350 | void WebstoreInlineInstaller::OnWebstoreResponseParseFailure( |
| 351 | const std::string& error) { |
| 352 | CompleteInstall(error); |
| 353 | } |
| 354 | |
| 355 | void WebstoreInlineInstaller::OnWebstoreParseSuccess( |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 356 | const std::string& id, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 357 | const SkBitmap& icon, |
| 358 | base::DictionaryValue* manifest) { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 359 | // Check if the tab has gone away in the meantime. |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 360 | if (!web_contents()) { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 361 | CompleteInstall(""); |
| 362 | return; |
| 363 | } |
| 364 | |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 365 | CHECK_EQ(id_, id); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 366 | manifest_.reset(manifest); |
| 367 | icon_ = icon; |
| 368 | |
| 369 | Profile* profile = Profile::FromBrowserContext( |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 370 | web_contents()->GetBrowserContext()); |
[email protected] | 5fdbd6f | 2011-09-01 17:33:04 | [diff] [blame] | 371 | |
| 372 | ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INLINE_INSTALL_PROMPT); |
[email protected] | 122e8df | 2011-09-02 18:20:24 | [diff] [blame] | 373 | prompt.SetInlineInstallWebstoreData(localized_user_count_, |
| 374 | average_rating_, |
| 375 | rating_count_); |
[email protected] | 514c547 | 2012-04-20 22:56:36 | [diff] [blame^] | 376 | std::string error; |
| 377 | dummy_extension_ = ExtensionInstallUI::GetLocalizedExtensionForDisplay( |
| 378 | manifest, id_, localized_name_, localized_description_, &error); |
| 379 | if (!dummy_extension_) { |
| 380 | OnWebstoreParseFailure(id_, WebstoreInstallHelper::Delegate::MANIFEST_ERROR, |
| 381 | kInvalidManifestError); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 382 | return; |
| 383 | } |
| 384 | |
[email protected] | 514c547 | 2012-04-20 22:56:36 | [diff] [blame^] | 385 | install_ui_.reset(new ExtensionInstallUI(profile)); |
| 386 | install_ui_->ConfirmInlineInstall(this, dummy_extension_, &icon_, prompt); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 387 | // Control flow finishes up in InstallUIProceed or InstallUIAbort. |
| 388 | } |
| 389 | |
| 390 | void WebstoreInlineInstaller::OnWebstoreParseFailure( |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 391 | const std::string& id, |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 392 | InstallHelperResultCode result_code, |
| 393 | const std::string& error_message) { |
| 394 | CompleteInstall(error_message); |
| 395 | } |
| 396 | |
| 397 | void WebstoreInlineInstaller::InstallUIProceed() { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 398 | // Check if the tab has gone away in the meantime. |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 399 | if (!web_contents()) { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 400 | CompleteInstall(""); |
| 401 | return; |
| 402 | } |
| 403 | |
[email protected] | 655b2b1a | 2011-10-13 17:13:06 | [diff] [blame] | 404 | Profile* profile = Profile::FromBrowserContext( |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 405 | web_contents()->GetBrowserContext()); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 406 | |
[email protected] | 21a5ad6 | 2012-04-03 04:48:45 | [diff] [blame] | 407 | scoped_ptr<WebstoreInstaller::Approval> approval( |
| 408 | new WebstoreInstaller::Approval); |
| 409 | approval->extension_id = id_; |
| 410 | approval->profile = profile; |
| 411 | approval->parsed_manifest.reset(manifest_.get()->DeepCopy()); |
| 412 | approval->use_app_installed_bubble = true; |
| 413 | |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 414 | scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( |
[email protected] | 21a5ad6 | 2012-04-03 04:48:45 | [diff] [blame] | 415 | profile, this, &(web_contents()->GetController()), id_, approval.Pass(), |
[email protected] | 98e4e52 | 2011-10-25 13:00:16 | [diff] [blame] | 416 | WebstoreInstaller::FLAG_INLINE_INSTALL); |
| 417 | installer->Start(); |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) { |
| 421 | CompleteInstall(kUserCancelledError); |
| 422 | } |
| 423 | |
[email protected] | 26b5e32 | 2011-12-23 01:36:47 | [diff] [blame] | 424 | void WebstoreInlineInstaller::WebContentsDestroyed(WebContents* web_contents) { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 425 | // Abort any in-progress fetches. |
| 426 | if (webstore_data_url_fetcher_.get()) { |
| 427 | webstore_data_url_fetcher_.reset(); |
| 428 | Release(); // Matches the AddRef in BeginInstall. |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 429 | } |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 430 | } |
| 431 | |
[email protected] | cb08ba2 | 2011-10-19 21:41:40 | [diff] [blame] | 432 | void WebstoreInlineInstaller::OnExtensionInstallSuccess(const std::string& id) { |
| 433 | CHECK_EQ(id_, id); |
| 434 | CompleteInstall(""); |
| 435 | } |
| 436 | |
| 437 | void WebstoreInlineInstaller::OnExtensionInstallFailure( |
| 438 | const std::string& id, const std::string& error) { |
| 439 | CHECK_EQ(id_, id); |
| 440 | CompleteInstall(error); |
| 441 | } |
| 442 | |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 443 | void WebstoreInlineInstaller::CompleteInstall(const std::string& error) { |
| 444 | // Only bother responding if there's still a tab contents to send back the |
| 445 | // response to. |
[email protected] | ea049a0 | 2011-12-25 21:37:09 | [diff] [blame] | 446 | if (web_contents()) { |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 447 | if (error.empty()) { |
[email protected] | 7b92104 | 2012-02-11 01:41:27 | [diff] [blame] | 448 | delegate_->OnInlineInstallSuccess(install_id_, return_route_id_); |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 449 | } else { |
[email protected] | 7b92104 | 2012-02-11 01:41:27 | [diff] [blame] | 450 | delegate_->OnInlineInstallFailure(install_id_, return_route_id_, error); |
[email protected] | 2fd920fb | 2011-09-08 23:33:00 | [diff] [blame] | 451 | } |
| 452 | } |
| 453 | |
[email protected] | 8915f34 | 2011-08-29 22:14:37 | [diff] [blame] | 454 | Release(); // Matches the AddRef in BeginInstall. |
| 455 | } |