OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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_inline_installer.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/string_util.h" |
| 10 #include "base/values.h" |
| 11 #include "chrome/browser/browser_process.h" |
| 12 #include "chrome/browser/extensions/crx_installer.h" |
| 13 #include "chrome/browser/extensions/extension_install_dialog.h" |
| 14 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/common/chrome_utility_messages.h" |
| 16 #include "chrome/common/extensions/extension.h" |
| 17 #include "chrome/common/extensions/extension_constants.h" |
| 18 #include "content/browser/tab_contents/tab_contents.h" |
| 19 #include "content/browser/utility_process_host.h" |
| 20 #include "googleurl/src/gurl.h" |
| 21 #include "net/base/escape.h" |
| 22 #include "net/url_request/url_request_status.h" |
| 23 |
| 24 const char kManifestKey[] = "manifest"; |
| 25 const char kIconUrlKey[] = "icon_url"; |
| 26 const char kLocalizedNameKey[] = "localized_name"; |
| 27 |
| 28 const char kInvalidWebstoreItemId[] = "Invalid webstore item ID"; |
| 29 const char kWebstoreRequestError[] = "Could not fetch data from webstore"; |
| 30 const char kInvalidWebstoreResponseError[] = "Invalid webstore reponse"; |
| 31 const char kInvalidManifestError[] = "Invalid manifest"; |
| 32 const char kUserCancelledError[] = "User cancelled install"; |
| 33 |
| 34 class SafeWebstoreResponseParser : public UtilityProcessHost::Client { |
| 35 public: |
| 36 SafeWebstoreResponseParser(WebstoreInlineInstaller *client, |
| 37 const std::string& webstore_data) |
| 38 : client_(client), |
| 39 webstore_data_(webstore_data), |
| 40 utility_host_(NULL) {} |
| 41 |
| 42 void Start() { |
| 43 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 44 BrowserThread::PostTask( |
| 45 BrowserThread::IO, |
| 46 FROM_HERE, |
| 47 NewRunnableMethod(this, |
| 48 &SafeWebstoreResponseParser::StartWorkOnIOThread)); |
| 49 } |
| 50 |
| 51 void StartWorkOnIOThread() { |
| 52 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 53 utility_host_ = new UtilityProcessHost(this, BrowserThread::IO); |
| 54 utility_host_->Send(new ChromeUtilityMsg_ParseJSON(webstore_data_)); |
| 55 } |
| 56 |
| 57 // Implementing pieces of the UtilityProcessHost::Client interface. |
| 58 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { |
| 59 bool handled = true; |
| 60 IPC_BEGIN_MESSAGE_MAP(SafeWebstoreResponseParser, message) |
| 61 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded, |
| 62 OnJSONParseSucceeded) |
| 63 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed, |
| 64 OnJSONParseFailed) |
| 65 IPC_MESSAGE_UNHANDLED(handled = false) |
| 66 IPC_END_MESSAGE_MAP() |
| 67 return handled; |
| 68 } |
| 69 |
| 70 void OnJSONParseSucceeded(const ListValue& wrapper) { |
| 71 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 72 Value* value = NULL; |
| 73 CHECK(wrapper.Get(0, &value)); |
| 74 if (value->IsType(Value::TYPE_DICTIONARY)) { |
| 75 parsed_webstore_data_.reset( |
| 76 static_cast<DictionaryValue*>(value)->DeepCopy()); |
| 77 } else { |
| 78 error_ = kInvalidWebstoreResponseError; |
| 79 } |
| 80 |
| 81 ReportResults(); |
| 82 } |
| 83 |
| 84 virtual void OnJSONParseFailed(const std::string& error_message) { |
| 85 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 86 error_ = error_message; |
| 87 ReportResults(); |
| 88 } |
| 89 |
| 90 void ReportResults() { |
| 91 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 92 |
| 93 // The utility_host_ will take care of deleting itself after this call. |
| 94 utility_host_ = NULL; |
| 95 |
| 96 BrowserThread::PostTask( |
| 97 BrowserThread::UI, |
| 98 FROM_HERE, |
| 99 NewRunnableMethod(this, |
| 100 &SafeWebstoreResponseParser::ReportResultOnUIThread)); |
| 101 } |
| 102 |
| 103 void ReportResultOnUIThread() { |
| 104 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 105 if (error_.empty() && parsed_webstore_data_.get()) { |
| 106 client_->OnWebstoreResponseParseSuccess(parsed_webstore_data_.release()); |
| 107 } else { |
| 108 client_->OnWebstoreResponseParseFailure(error_); |
| 109 } |
| 110 } |
| 111 |
| 112 private: |
| 113 virtual ~SafeWebstoreResponseParser() {} |
| 114 |
| 115 WebstoreInlineInstaller* client_; |
| 116 |
| 117 std::string webstore_data_; |
| 118 |
| 119 UtilityProcessHost* utility_host_; |
| 120 |
| 121 std::string error_; |
| 122 scoped_ptr<DictionaryValue> parsed_webstore_data_; |
| 123 }; |
| 124 |
| 125 WebstoreInlineInstaller::WebstoreInlineInstaller(TabContents* tab_contents, |
| 126 std::string webstore_item_id, |
| 127 Delegate* delegate) |
| 128 : tab_contents_(tab_contents), |
| 129 id_(webstore_item_id), |
| 130 delegate_(delegate) {} |
| 131 |
| 132 WebstoreInlineInstaller::~WebstoreInlineInstaller() { |
| 133 } |
| 134 |
| 135 void WebstoreInlineInstaller::BeginInstall() { |
| 136 AddRef(); // Balanced in CompleteInstall. |
| 137 |
| 138 if (!Extension::IdIsValid(id_)) { |
| 139 CompleteInstall(kInvalidWebstoreItemId); |
| 140 return; |
| 141 } |
| 142 |
| 143 GURL webstore_data_url(extension_urls::GetWebstoreItemJsonDataURL(id_)); |
| 144 |
| 145 webstore_data_url_fetcher_.reset( |
| 146 new URLFetcher(webstore_data_url, URLFetcher::GET, this)); |
| 147 Profile* profile = Profile::FromBrowserContext( |
| 148 tab_contents_->browser_context()); |
| 149 webstore_data_url_fetcher_->set_request_context( |
| 150 profile->GetRequestContext()); |
| 151 webstore_data_url_fetcher_->Start(); |
| 152 } |
| 153 |
| 154 void WebstoreInlineInstaller::OnURLFetchComplete(const URLFetcher* source) { |
| 155 CHECK_EQ(webstore_data_url_fetcher_.get(), source); |
| 156 |
| 157 if (!webstore_data_url_fetcher_->status().is_success() || |
| 158 webstore_data_url_fetcher_->response_code() != 200) { |
| 159 CompleteInstall(kWebstoreRequestError); |
| 160 return; |
| 161 } |
| 162 |
| 163 std::string webstore_json_data; |
| 164 webstore_data_url_fetcher_->GetResponseAsString(&webstore_json_data); |
| 165 webstore_data_url_fetcher_.reset(); |
| 166 |
| 167 scoped_refptr<SafeWebstoreResponseParser> parser = |
| 168 new SafeWebstoreResponseParser(this, webstore_json_data); |
| 169 // The parser will call us back via OnWebstoreResponseParseSucces or |
| 170 // OnWebstoreResponseParseFailure. |
| 171 parser->Start(); |
| 172 } |
| 173 |
| 174 void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess( |
| 175 DictionaryValue* webstore_data) { |
| 176 webstore_data_.reset(webstore_data); |
| 177 |
| 178 std::string manifest; |
| 179 if (!webstore_data->GetString(kManifestKey, &manifest)) { |
| 180 CompleteInstall(kInvalidWebstoreResponseError); |
| 181 return; |
| 182 } |
| 183 |
| 184 // Localized name is optional. |
| 185 if (webstore_data->HasKey(kLocalizedNameKey) && |
| 186 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) { |
| 187 CompleteInstall(kInvalidWebstoreResponseError); |
| 188 return; |
| 189 } |
| 190 |
| 191 // Icon URL is optional. |
| 192 GURL icon_url; |
| 193 if (webstore_data->HasKey(kIconUrlKey)) { |
| 194 std::string icon_url_string; |
| 195 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) { |
| 196 CompleteInstall(kInvalidWebstoreResponseError); |
| 197 return; |
| 198 } |
| 199 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve( |
| 200 icon_url_string); |
| 201 if (!icon_url.is_valid()) { |
| 202 CompleteInstall(kInvalidWebstoreResponseError); |
| 203 return; |
| 204 } |
| 205 } |
| 206 |
| 207 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( |
| 208 this, |
| 209 manifest, |
| 210 "", // We don't have any icon data. |
| 211 icon_url, |
| 212 Profile::FromBrowserContext(tab_contents_->browser_context())-> |
| 213 GetRequestContext()); |
| 214 // The helper will call us back via OnWebstoreParseSucces or |
| 215 // OnWebstoreParseFailure. |
| 216 helper->Start(); |
| 217 } |
| 218 |
| 219 void WebstoreInlineInstaller::OnWebstoreResponseParseFailure( |
| 220 const std::string& error) { |
| 221 CompleteInstall(error); |
| 222 } |
| 223 |
| 224 void WebstoreInlineInstaller::OnWebstoreParseSuccess( |
| 225 const SkBitmap& icon, |
| 226 base::DictionaryValue* manifest) { |
| 227 manifest_.reset(manifest); |
| 228 icon_ = icon; |
| 229 |
| 230 Profile* profile = Profile::FromBrowserContext( |
| 231 tab_contents_->browser_context()); |
| 232 scoped_refptr<Extension> dummy_extension; |
| 233 ShowExtensionInstallDialogForManifest(profile, |
| 234 this, |
| 235 manifest, |
| 236 id_, |
| 237 localized_name_, |
| 238 &icon_, |
| 239 ExtensionInstallUI::INSTALL_PROMPT, |
| 240 &dummy_extension); |
| 241 |
| 242 if (!dummy_extension.get()) { |
| 243 CompleteInstall(kInvalidManifestError); |
| 244 return; |
| 245 } |
| 246 |
| 247 // Control flow finishes up in InstallUIProceed or InstallUIAbort. |
| 248 } |
| 249 |
| 250 void WebstoreInlineInstaller::OnWebstoreParseFailure( |
| 251 InstallHelperResultCode result_code, |
| 252 const std::string& error_message) { |
| 253 CompleteInstall(error_message); |
| 254 } |
| 255 |
| 256 void WebstoreInlineInstaller::InstallUIProceed() { |
| 257 CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry; |
| 258 |
| 259 entry->parsed_manifest.reset(manifest_.get()->DeepCopy()); |
| 260 entry->localized_name = localized_name_; |
| 261 entry->use_app_installed_bubble = true; |
| 262 CrxInstaller::SetWhitelistEntry(id_, entry); |
| 263 |
| 264 GURL install_url(extension_urls::GetWebstoreInstallUrl( |
| 265 id_, g_browser_process->GetApplicationLocale())); |
| 266 |
| 267 NavigationController& controller = tab_contents_->controller(); |
| 268 // TODO(mihaip): we pretend like the referrer is the gallery in order to pass |
| 269 // the checks in ExtensionService::IsDownloadFromGallery. We should instead |
| 270 // pass the real referrer, track that this is an inline install in the |
| 271 // whitelist entry and look that up when checking that this is a valid |
| 272 // download. |
| 273 GURL referrer(extension_urls::GetWebstoreItemDetailURLPrefix() + id_); |
| 274 controller.LoadURL(install_url, referrer, PageTransition::LINK); |
| 275 |
| 276 // TODO(mihaip): the success message should happen later, when the extension |
| 277 // is actually downloaded and installed (when NOTIFICATION_EXTENSION_INSTALLED |
| 278 // or NOTIFICATION_EXTENSION_INSTALL_ERROR fire). |
| 279 CompleteInstall(""); |
| 280 } |
| 281 |
| 282 void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) { |
| 283 CompleteInstall(kUserCancelledError); |
| 284 } |
| 285 |
| 286 void WebstoreInlineInstaller::CompleteInstall(const std::string& error) { |
| 287 if (error.empty()) { |
| 288 delegate_->OnInlineInstallSuccess(); |
| 289 } else { |
| 290 delegate_->OnInlineInstallFailure(error); |
| 291 } |
| 292 Release(); // Matches the AddRef in BeginInstall. |
| 293 } |
OLD | NEW |