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 |