| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extension_updater.h" | 5 #include "chrome/browser/extensions/extension_updater.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 const int kStartupWaitSeconds = 60 * 5; | 53 const int kStartupWaitSeconds = 60 * 5; |
| 54 | 54 |
| 55 // For sanity checking on update frequency - enforced in release mode only. | 55 // For sanity checking on update frequency - enforced in release mode only. |
| 56 static const int kMinUpdateFrequencySeconds = 30; | 56 static const int kMinUpdateFrequencySeconds = 30; |
| 57 static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days | 57 static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days |
| 58 | 58 |
| 59 // A utility class to do file handling on the file I/O thread. | 59 // A utility class to do file handling on the file I/O thread. |
| 60 class ExtensionUpdaterFileHandler | 60 class ExtensionUpdaterFileHandler |
| 61 : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> { | 61 : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> { |
| 62 public: | 62 public: |
| 63 ExtensionUpdaterFileHandler(MessageLoop* updater_loop, | |
| 64 MessageLoop* file_io_loop) | |
| 65 : updater_loop_(updater_loop), file_io_loop_(file_io_loop) {} | |
| 66 | |
| 67 // Writes crx file data into a tempfile, and calls back the updater. | 63 // Writes crx file data into a tempfile, and calls back the updater. |
| 68 void WriteTempFile(const std::string& extension_id, const std::string& data, | 64 void WriteTempFile(const std::string& extension_id, const std::string& data, |
| 69 scoped_refptr<ExtensionUpdater> updater) { | 65 scoped_refptr<ExtensionUpdater> updater) { |
| 70 // Make sure we're running in the right thread. | 66 // Make sure we're running in the right thread. |
| 71 DCHECK(MessageLoop::current() == file_io_loop_); | 67 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 72 | 68 |
| 73 FilePath path; | 69 FilePath path; |
| 74 if (!file_util::CreateTemporaryFile(&path)) { | 70 if (!file_util::CreateTemporaryFile(&path)) { |
| 75 LOG(WARNING) << "Failed to create temporary file path"; | 71 LOG(WARNING) << "Failed to create temporary file path"; |
| 76 return; | 72 return; |
| 77 } | 73 } |
| 78 if (file_util::WriteFile(path, data.c_str(), data.length()) != | 74 if (file_util::WriteFile(path, data.c_str(), data.length()) != |
| 79 static_cast<int>(data.length())) { | 75 static_cast<int>(data.length())) { |
| 80 // TODO(asargent) - It would be nice to back off updating alltogether if | 76 // TODO(asargent) - It would be nice to back off updating alltogether if |
| 81 // the disk is full. (http://crbug.com/12763). | 77 // the disk is full. (http://crbug.com/12763). |
| 82 LOG(ERROR) << "Failed to write temporary file"; | 78 LOG(ERROR) << "Failed to write temporary file"; |
| 83 file_util::Delete(path, false); | 79 file_util::Delete(path, false); |
| 84 return; | 80 return; |
| 85 } | 81 } |
| 86 | 82 |
| 87 // The ExtensionUpdater is now responsible for cleaning up the temp file | 83 // The ExtensionUpdater is now responsible for cleaning up the temp file |
| 88 // from disk. | 84 // from disk. |
| 89 updater_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 85 ChromeThread::PostTask( |
| 90 updater.get(), &ExtensionUpdater::OnCRXFileWritten, extension_id, | 86 ChromeThread::UI, FROM_HERE, |
| 91 path)); | 87 NewRunnableMethod( |
| 88 updater.get(), &ExtensionUpdater::OnCRXFileWritten, extension_id, |
| 89 path)); |
| 92 } | 90 } |
| 93 | 91 |
| 94 void DeleteFile(const FilePath& path) { | 92 void DeleteFile(const FilePath& path) { |
| 95 DCHECK(MessageLoop::current() == file_io_loop_); | 93 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 96 if (!file_util::Delete(path, false)) { | 94 if (!file_util::Delete(path, false)) { |
| 97 LOG(WARNING) << "Failed to delete temp file " << path.value(); | 95 LOG(WARNING) << "Failed to delete temp file " << path.value(); |
| 98 } | 96 } |
| 99 } | 97 } |
| 100 | |
| 101 private: | |
| 102 // The MessageLoop we use to call back the ExtensionUpdater. | |
| 103 MessageLoop* updater_loop_; | |
| 104 | |
| 105 // The MessageLoop we should be operating on for file operations. | |
| 106 MessageLoop* file_io_loop_; | |
| 107 }; | 98 }; |
| 108 | 99 |
| 109 | 100 |
| 110 ExtensionUpdater::ExtensionUpdater(ExtensionUpdateService* service, | 101 ExtensionUpdater::ExtensionUpdater(ExtensionUpdateService* service, |
| 111 PrefService* prefs, | 102 PrefService* prefs, |
| 112 int frequency_seconds, | 103 int frequency_seconds) |
| 113 MessageLoop* file_io_loop, | |
| 114 MessageLoop* io_loop) | |
| 115 : service_(service), frequency_seconds_(frequency_seconds), | 104 : service_(service), frequency_seconds_(frequency_seconds), |
| 116 file_io_loop_(file_io_loop), io_loop_(io_loop), prefs_(prefs), | 105 prefs_(prefs), file_handler_(new ExtensionUpdaterFileHandler()), |
| 117 file_handler_(new ExtensionUpdaterFileHandler(MessageLoop::current(), | |
| 118 file_io_loop_)), | |
| 119 blacklist_checks_enabled_(true) { | 106 blacklist_checks_enabled_(true) { |
| 120 Init(); | 107 Init(); |
| 121 } | 108 } |
| 122 | 109 |
| 123 void ExtensionUpdater::Init() { | 110 void ExtensionUpdater::Init() { |
| 124 // Unless we're in a unit test, expect that the file_io_loop_ is on the | |
| 125 // browser file thread. | |
| 126 if (g_browser_process->file_thread() != NULL) { | |
| 127 DCHECK(file_io_loop_ == g_browser_process->file_thread()->message_loop()); | |
| 128 } | |
| 129 | |
| 130 DCHECK_GE(frequency_seconds_, 5); | 111 DCHECK_GE(frequency_seconds_, 5); |
| 131 DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds); | 112 DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds); |
| 132 #ifdef NDEBUG | 113 #ifdef NDEBUG |
| 133 // In Release mode we enforce that update checks don't happen too often. | 114 // In Release mode we enforce that update checks don't happen too often. |
| 134 frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds); | 115 frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds); |
| 135 #endif | 116 #endif |
| 136 frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds); | 117 frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds); |
| 137 } | 118 } |
| 138 | 119 |
| 139 ExtensionUpdater::~ExtensionUpdater() {} | 120 ExtensionUpdater::~ExtensionUpdater() {} |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 } else if (source == extension_fetcher_.get()) { | 199 } else if (source == extension_fetcher_.get()) { |
| 219 OnCRXFetchComplete(url, status, response_code, data); | 200 OnCRXFetchComplete(url, status, response_code, data); |
| 220 } else { | 201 } else { |
| 221 NOTREACHED(); | 202 NOTREACHED(); |
| 222 } | 203 } |
| 223 } | 204 } |
| 224 | 205 |
| 225 // Utility class to handle doing xml parsing in a sandboxed utility process. | 206 // Utility class to handle doing xml parsing in a sandboxed utility process. |
| 226 class SafeManifestParser : public UtilityProcessHost::Client { | 207 class SafeManifestParser : public UtilityProcessHost::Client { |
| 227 public: | 208 public: |
| 228 SafeManifestParser(const std::string& xml, ExtensionUpdater* updater, | 209 SafeManifestParser(const std::string& xml, ExtensionUpdater* updater) |
| 229 MessageLoop* updater_loop, MessageLoop* io_loop) | 210 : xml_(xml), updater_(updater) { |
| 230 : xml_(xml), updater_loop_(updater_loop), io_loop_(io_loop), | |
| 231 updater_(updater) { | |
| 232 } | 211 } |
| 233 | 212 |
| 234 ~SafeManifestParser() {} | 213 ~SafeManifestParser() {} |
| 235 | 214 |
| 236 // Posts a task over to the IO loop to start the parsing of xml_ in a | 215 // Posts a task over to the IO loop to start the parsing of xml_ in a |
| 237 // utility process. | 216 // utility process. |
| 238 void Start() { | 217 void Start() { |
| 239 DCHECK(MessageLoop::current() == updater_loop_); | 218 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 240 io_loop_->PostTask(FROM_HERE, | 219 ChromeThread::PostTask( |
| 241 NewRunnableMethod(this, &SafeManifestParser::ParseInSandbox, | 220 ChromeThread::IO, FROM_HERE, |
| 221 NewRunnableMethod( |
| 222 this, &SafeManifestParser::ParseInSandbox, |
| 242 g_browser_process->resource_dispatcher_host())); | 223 g_browser_process->resource_dispatcher_host())); |
| 243 } | 224 } |
| 244 | 225 |
| 245 // Creates the sandboxed utility process and tells it to start parsing. | 226 // Creates the sandboxed utility process and tells it to start parsing. |
| 246 void ParseInSandbox(ResourceDispatcherHost* rdh) { | 227 void ParseInSandbox(ResourceDispatcherHost* rdh) { |
| 247 DCHECK(MessageLoop::current() == io_loop_); | 228 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 248 | 229 |
| 249 // TODO(asargent) we shouldn't need to do this branch here - instead | 230 // TODO(asargent) we shouldn't need to do this branch here - instead |
| 250 // UtilityProcessHost should handle it for us. (http://crbug.com/19192) | 231 // UtilityProcessHost should handle it for us. (http://crbug.com/19192) |
| 251 bool use_utility_process = rdh && | 232 bool use_utility_process = rdh && |
| 252 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); | 233 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); |
| 253 | 234 |
| 254 #if defined(OS_POSIX) | 235 #if defined(OS_POSIX) |
| 255 // TODO(port): Don't use a utility process on linux (crbug.com/22703) or | 236 // TODO(port): Don't use a utility process on linux (crbug.com/22703) or |
| 256 // MacOS (crbug.com/8102) until problems related to autoupdate are fixed. | 237 // MacOS (crbug.com/8102) until problems related to autoupdate are fixed. |
| 257 use_utility_process = false; | 238 use_utility_process = false; |
| 258 #endif | 239 #endif |
| 259 | 240 |
| 260 if (use_utility_process) { | 241 if (use_utility_process) { |
| 261 UtilityProcessHost* host = new UtilityProcessHost( | 242 UtilityProcessHost* host = new UtilityProcessHost( |
| 262 rdh, this, updater_loop_); | 243 rdh, this, ChromeThread::UI); |
| 263 host->StartUpdateManifestParse(xml_); | 244 host->StartUpdateManifestParse(xml_); |
| 264 } else { | 245 } else { |
| 265 UpdateManifest manifest; | 246 UpdateManifest manifest; |
| 266 if (manifest.Parse(xml_)) { | 247 if (manifest.Parse(xml_)) { |
| 267 updater_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 248 ChromeThread::PostTask( |
| 268 &SafeManifestParser::OnParseUpdateManifestSucceeded, | 249 ChromeThread::UI, FROM_HERE, |
| 269 manifest.results())); | 250 NewRunnableMethod( |
| 251 this, &SafeManifestParser::OnParseUpdateManifestSucceeded, |
| 252 manifest.results())); |
| 270 } else { | 253 } else { |
| 271 updater_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 254 ChromeThread::PostTask( |
| 272 &SafeManifestParser::OnParseUpdateManifestFailed, | 255 ChromeThread::UI, FROM_HERE, |
| 273 manifest.errors())); | 256 NewRunnableMethod( |
| 257 this, &SafeManifestParser::OnParseUpdateManifestFailed, |
| 258 manifest.errors())); |
| 274 } | 259 } |
| 275 } | 260 } |
| 276 } | 261 } |
| 277 | 262 |
| 278 // Callback from the utility process when parsing succeeded. | 263 // Callback from the utility process when parsing succeeded. |
| 279 virtual void OnParseUpdateManifestSucceeded( | 264 virtual void OnParseUpdateManifestSucceeded( |
| 280 const UpdateManifest::ResultList& list) { | 265 const UpdateManifest::ResultList& list) { |
| 281 DCHECK(MessageLoop::current() == updater_loop_); | 266 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 282 updater_->HandleManifestResults(list); | 267 updater_->HandleManifestResults(list); |
| 283 } | 268 } |
| 284 | 269 |
| 285 // Callback from the utility process when parsing failed. | 270 // Callback from the utility process when parsing failed. |
| 286 virtual void OnParseUpdateManifestFailed(const std::string& error_message) { | 271 virtual void OnParseUpdateManifestFailed(const std::string& error_message) { |
| 287 DCHECK(MessageLoop::current() == updater_loop_); | 272 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 288 LOG(WARNING) << "Error parsing update manifest:\n" << error_message; | 273 LOG(WARNING) << "Error parsing update manifest:\n" << error_message; |
| 289 } | 274 } |
| 290 | 275 |
| 291 private: | 276 private: |
| 292 const std::string& xml_; | 277 const std::string& xml_; |
| 293 | 278 |
| 294 // The MessageLoop we use to call back the ExtensionUpdater. | |
| 295 MessageLoop* updater_loop_; | |
| 296 | |
| 297 // The MessageLoop where we create the utility process. | |
| 298 MessageLoop* io_loop_; | |
| 299 | |
| 300 scoped_refptr<ExtensionUpdater> updater_; | 279 scoped_refptr<ExtensionUpdater> updater_; |
| 301 }; | 280 }; |
| 302 | 281 |
| 303 | 282 |
| 304 void ExtensionUpdater::OnManifestFetchComplete(const GURL& url, | 283 void ExtensionUpdater::OnManifestFetchComplete(const GURL& url, |
| 305 const URLRequestStatus& status, | 284 const URLRequestStatus& status, |
| 306 int response_code, | 285 int response_code, |
| 307 const std::string& data) { | 286 const std::string& data) { |
| 308 // We want to try parsing the manifest, and if it indicates updates are | 287 // We want to try parsing the manifest, and if it indicates updates are |
| 309 // available, we want to fire off requests to fetch those updates. | 288 // available, we want to fire off requests to fetch those updates. |
| 310 if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) { | 289 if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) { |
| 311 scoped_refptr<SafeManifestParser> safe_parser = | 290 scoped_refptr<SafeManifestParser> safe_parser = |
| 312 new SafeManifestParser(data, this, MessageLoop::current(), io_loop_); | 291 new SafeManifestParser(data, this); |
| 313 safe_parser->Start(); | 292 safe_parser->Start(); |
| 314 } else { | 293 } else { |
| 315 // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546). | 294 // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546). |
| 316 LOG(INFO) << "Failed to fetch manifst '" << url.possibly_invalid_spec() << | 295 LOG(INFO) << "Failed to fetch manifst '" << url.possibly_invalid_spec() << |
| 317 "' response code:" << response_code; | 296 "' response code:" << response_code; |
| 318 } | 297 } |
| 319 manifest_fetcher_.reset(); | 298 manifest_fetcher_.reset(); |
| 320 | 299 |
| 321 // If we have any pending manifest requests, fire off the next one. | 300 // If we have any pending manifest requests, fire off the next one. |
| 322 if (!manifests_pending_.empty()) { | 301 if (!manifests_pending_.empty()) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 LOG(ERROR) << "Called with unexpected url:'" << url.spec() | 347 LOG(ERROR) << "Called with unexpected url:'" << url.spec() |
| 369 << "' expected:'" << current_extension_fetch_.url.spec() << "'"; | 348 << "' expected:'" << current_extension_fetch_.url.spec() << "'"; |
| 370 NOTREACHED(); | 349 NOTREACHED(); |
| 371 } else if (status.status() == URLRequestStatus::SUCCESS && | 350 } else if (status.status() == URLRequestStatus::SUCCESS && |
| 372 response_code == 200) { | 351 response_code == 200) { |
| 373 if (current_extension_fetch_.id == kBlacklistAppID) { | 352 if (current_extension_fetch_.id == kBlacklistAppID) { |
| 374 ProcessBlacklist(data); | 353 ProcessBlacklist(data); |
| 375 } else { | 354 } else { |
| 376 // Successfully fetched - now write crx to a file so we can have the | 355 // Successfully fetched - now write crx to a file so we can have the |
| 377 // ExtensionsService install it. | 356 // ExtensionsService install it. |
| 378 file_io_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 357 ChromeThread::PostTask( |
| 379 file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile, | 358 ChromeThread::FILE, FROM_HERE, |
| 380 current_extension_fetch_.id, data, this)); | 359 NewRunnableMethod( |
| 360 file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile, |
| 361 current_extension_fetch_.id, data, this)); |
| 381 } | 362 } |
| 382 } else { | 363 } else { |
| 383 // TODO(asargent) do things like exponential backoff, handling | 364 // TODO(asargent) do things like exponential backoff, handling |
| 384 // 503 Service Unavailable / Retry-After headers, etc. here. | 365 // 503 Service Unavailable / Retry-After headers, etc. here. |
| 385 // (http://crbug.com/12546). | 366 // (http://crbug.com/12546). |
| 386 LOG(INFO) << "Failed to fetch extension '" << | 367 LOG(INFO) << "Failed to fetch extension '" << |
| 387 url.possibly_invalid_spec() << "' response code:" << response_code; | 368 url.possibly_invalid_spec() << "' response code:" << response_code; |
| 388 } | 369 } |
| 389 extension_fetcher_.reset(); | 370 extension_fetcher_.reset(); |
| 390 current_extension_fetch_ = ExtensionFetch(); | 371 current_extension_fetch_ = ExtensionFetch(); |
| 391 | 372 |
| 392 // If there are any pending downloads left, start one. | 373 // If there are any pending downloads left, start one. |
| 393 if (extensions_pending_.size() > 0) { | 374 if (extensions_pending_.size() > 0) { |
| 394 ExtensionFetch next = extensions_pending_.front(); | 375 ExtensionFetch next = extensions_pending_.front(); |
| 395 extensions_pending_.pop_front(); | 376 extensions_pending_.pop_front(); |
| 396 FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version); | 377 FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version); |
| 397 } | 378 } |
| 398 } | 379 } |
| 399 | 380 |
| 400 void ExtensionUpdater::OnCRXFileWritten(const std::string& id, | 381 void ExtensionUpdater::OnCRXFileWritten(const std::string& id, |
| 401 const FilePath& path) { | 382 const FilePath& path) { |
| 402 service_->UpdateExtension(id, path); | 383 service_->UpdateExtension(id, path); |
| 403 } | 384 } |
| 404 | 385 |
| 405 void ExtensionUpdater::OnExtensionInstallFinished(const FilePath& path, | 386 void ExtensionUpdater::OnExtensionInstallFinished(const FilePath& path, |
| 406 Extension* extension) { | 387 Extension* extension) { |
| 407 // Have the file_handler_ delete the temp file on the file I/O thread. | 388 // Have the file_handler_ delete the temp file on the file I/O thread. |
| 408 file_io_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 389 ChromeThread::PostTask( |
| 409 file_handler_.get(), &ExtensionUpdaterFileHandler::DeleteFile, path)); | 390 ChromeThread::FILE, FROM_HERE, |
| 391 NewRunnableMethod( |
| 392 file_handler_.get(), &ExtensionUpdaterFileHandler::DeleteFile, path)); |
| 410 } | 393 } |
| 411 | 394 |
| 412 | 395 |
| 413 // Helper function for building up request parameters in update check urls. It | 396 // Helper function for building up request parameters in update check urls. It |
| 414 // appends information about one extension to a request parameter string. The | 397 // appends information about one extension to a request parameter string. The |
| 415 // format for request parameters in update checks is: | 398 // format for request parameters in update checks is: |
| 416 // | 399 // |
| 417 // ?x=EXT1_INFO&x=EXT2_INFO | 400 // ?x=EXT1_INFO&x=EXT2_INFO |
| 418 // | 401 // |
| 419 // where EXT1_INFO and EXT2_INFO are url-encoded strings of the form: | 402 // where EXT1_INFO and EXT2_INFO are url-encoded strings of the form: |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 extension_fetcher_.reset( | 620 extension_fetcher_.reset( |
| 638 URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this)); | 621 URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this)); |
| 639 extension_fetcher_->set_request_context( | 622 extension_fetcher_->set_request_context( |
| 640 Profile::GetDefaultRequestContext()); | 623 Profile::GetDefaultRequestContext()); |
| 641 extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | | 624 extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | |
| 642 net::LOAD_DO_NOT_SAVE_COOKIES); | 625 net::LOAD_DO_NOT_SAVE_COOKIES); |
| 643 extension_fetcher_->Start(); | 626 extension_fetcher_->Start(); |
| 644 current_extension_fetch_ = ExtensionFetch(id, url, hash, version); | 627 current_extension_fetch_ = ExtensionFetch(id, url, hash, version); |
| 645 } | 628 } |
| 646 } | 629 } |
| OLD | NEW |