| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 <time.h> | |
| 6 | |
| 7 #include "chrome/browser/download_manager.h" | |
| 8 | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/message_loop.h" | |
| 12 #include "base/path_service.h" | |
| 13 #include "base/registry.h" | |
| 14 #include "base/string_util.h" | |
| 15 #include "base/task.h" | |
| 16 #include "base/thread.h" | |
| 17 #include "base/timer.h" | |
| 18 #include "base/win_util.h" | |
| 19 #include "chrome/browser/browser_list.h" | |
| 20 #include "chrome/browser/browser_process.h" | |
| 21 #include "chrome/browser/download_file.h" | |
| 22 #include "chrome/browser/download_util.h" | |
| 23 #include "chrome/browser/profile.h" | |
| 24 #include "chrome/browser/render_process_host.h" | |
| 25 #include "chrome/browser/render_view_host.h" | |
| 26 #include "chrome/browser/resource_dispatcher_host.h" | |
| 27 #include "chrome/browser/tab_util.h" | |
| 28 #include "chrome/browser/web_contents.h" | |
| 29 #include "chrome/common/chrome_paths.h" | |
| 30 #include "chrome/common/l10n_util.h" | |
| 31 #include "chrome/common/notification_service.h" | |
| 32 #include "chrome/common/pref_names.h" | |
| 33 #include "chrome/common/pref_service.h" | |
| 34 #include "chrome/common/stl_util-inl.h" | |
| 35 #include "chrome/common/win_util.h" | |
| 36 #include "googleurl/src/gurl.h" | |
| 37 #include "net/base/mime_util.h" | |
| 38 #include "net/base/net_util.h" | |
| 39 #include "net/url_request/url_request_context.h" | |
| 40 | |
| 41 #include "generated_resources.h" | |
| 42 | |
| 43 // Periodically update our observers. | |
| 44 class DownloadItemUpdateTask : public Task { | |
| 45 public: | |
| 46 explicit DownloadItemUpdateTask(DownloadItem* item) : item_(item) {} | |
| 47 void Run() { if (item_) item_->UpdateObservers(); } | |
| 48 | |
| 49 private: | |
| 50 DownloadItem* item_; | |
| 51 }; | |
| 52 | |
| 53 // Update frequency (milliseconds). | |
| 54 static const int kUpdateTimeMs = 1000; | |
| 55 | |
| 56 // Our download table ID starts at 1, so we use 0 to represent a download that | |
| 57 // has started, but has not yet had its data persisted in the table. We use fake | |
| 58 // database handles in incognito mode starting at -1 and progressly getting more | |
| 59 // negative. | |
| 60 static const int kUninitializedHandle = 0; | |
| 61 | |
| 62 // Attempts to modify |path| to be a non-existing path. | |
| 63 // Returns true if |path| points to a non-existing path upon return. | |
| 64 static bool UniquifyPath(std::wstring* path) { | |
| 65 DCHECK(path); | |
| 66 const int kMaxAttempts = 100; | |
| 67 | |
| 68 if (!file_util::PathExists(*path)) | |
| 69 return true; | |
| 70 | |
| 71 std::wstring new_path; | |
| 72 for (int count = 1; count <= kMaxAttempts; ++count) { | |
| 73 new_path.assign(*path); | |
| 74 file_util::InsertBeforeExtension(&new_path, StringPrintf(L" (%d)", count)); | |
| 75 | |
| 76 if (!file_util::PathExists(new_path)) { | |
| 77 path->swap(new_path); | |
| 78 return true; | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 return false; | |
| 83 } | |
| 84 | |
| 85 static bool DownloadPathIsDangerous(const std::wstring& download_path) { | |
| 86 std::wstring desktop_dir; | |
| 87 if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_dir)) { | |
| 88 NOTREACHED(); | |
| 89 return false; | |
| 90 } | |
| 91 return (download_path == desktop_dir); | |
| 92 } | |
| 93 | |
| 94 // DownloadItem implementation ------------------------------------------------- | |
| 95 | |
| 96 // Constructor for reading from the history service. | |
| 97 DownloadItem::DownloadItem(const DownloadCreateInfo& info) | |
| 98 : id_(-1), | |
| 99 full_path_(info.path), | |
| 100 url_(info.url), | |
| 101 total_bytes_(info.total_bytes), | |
| 102 received_bytes_(info.received_bytes), | |
| 103 start_tick_(0), | |
| 104 state_(static_cast<DownloadState>(info.state)), | |
| 105 start_time_(info.start_time), | |
| 106 db_handle_(info.db_handle), | |
| 107 manager_(NULL), | |
| 108 is_paused_(false), | |
| 109 open_when_complete_(false), | |
| 110 render_process_id_(-1), | |
| 111 request_id_(-1) { | |
| 112 if (state_ == IN_PROGRESS) | |
| 113 state_ = CANCELLED; | |
| 114 Init(false /* don't start progress timer */); | |
| 115 } | |
| 116 | |
| 117 // Constructor for DownloadItem created via user action in the main thread. | |
| 118 DownloadItem::DownloadItem(int32 download_id, | |
| 119 const std::wstring& path, | |
| 120 const std::wstring& url, | |
| 121 const Time start_time, | |
| 122 int64 download_size, | |
| 123 int render_process_id, | |
| 124 int request_id) | |
| 125 : id_(download_id), | |
| 126 full_path_(path), | |
| 127 url_(url), | |
| 128 total_bytes_(download_size), | |
| 129 received_bytes_(0), | |
| 130 start_tick_(GetTickCount()), | |
| 131 state_(IN_PROGRESS), | |
| 132 start_time_(start_time), | |
| 133 db_handle_(kUninitializedHandle), | |
| 134 manager_(NULL), | |
| 135 is_paused_(false), | |
| 136 open_when_complete_(false), | |
| 137 render_process_id_(render_process_id), | |
| 138 request_id_(request_id) { | |
| 139 Init(true /* start progress timer */); | |
| 140 } | |
| 141 | |
| 142 void DownloadItem::Init(bool start_timer) { | |
| 143 file_name_ = file_util::GetFilenameFromPath(full_path_); | |
| 144 if (start_timer) | |
| 145 StartProgressTimer(); | |
| 146 } | |
| 147 | |
| 148 DownloadItem::~DownloadItem() { | |
| 149 state_ = REMOVING; | |
| 150 UpdateObservers(); | |
| 151 } | |
| 152 | |
| 153 void DownloadItem::AddObserver(Observer* observer) { | |
| 154 observers_.AddObserver(observer); | |
| 155 } | |
| 156 | |
| 157 void DownloadItem::RemoveObserver(Observer* observer) { | |
| 158 observers_.RemoveObserver(observer); | |
| 159 } | |
| 160 | |
| 161 void DownloadItem::UpdateObservers() { | |
| 162 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); | |
| 163 } | |
| 164 | |
| 165 // If we've received more data than we were expecting (bad server info?), revert | |
| 166 // to 'unknown size mode'. | |
| 167 void DownloadItem::UpdateSize(int64 bytes_so_far) { | |
| 168 received_bytes_ = bytes_so_far; | |
| 169 if (received_bytes_ > total_bytes_) | |
| 170 total_bytes_ = 0; | |
| 171 } | |
| 172 | |
| 173 // Updates from the download thread may have been posted while this download | |
| 174 // was being cancelled in the UI thread, so we'll accept them unless we're | |
| 175 // complete. | |
| 176 void DownloadItem::Update(int64 bytes_so_far) { | |
| 177 if (state_ == COMPLETE) { | |
| 178 NOTREACHED(); | |
| 179 return; | |
| 180 } | |
| 181 UpdateSize(bytes_so_far); | |
| 182 UpdateObservers(); | |
| 183 } | |
| 184 | |
| 185 // Triggered by a user action | |
| 186 void DownloadItem::Cancel(bool update_history) { | |
| 187 if (state_ != IN_PROGRESS) { | |
| 188 // Small downloads might be complete before this method has a chance to run. | |
| 189 return; | |
| 190 } | |
| 191 state_ = CANCELLED; | |
| 192 UpdateObservers(); | |
| 193 StopProgressTimer(); | |
| 194 if (update_history) | |
| 195 manager_->DownloadCancelled(id_); | |
| 196 } | |
| 197 | |
| 198 void DownloadItem::Finished(int64 size) { | |
| 199 state_ = COMPLETE; | |
| 200 UpdateSize(size); | |
| 201 UpdateObservers(); | |
| 202 StopProgressTimer(); | |
| 203 } | |
| 204 | |
| 205 void DownloadItem::Remove() { | |
| 206 Cancel(true); | |
| 207 state_ = REMOVING; | |
| 208 manager_->RemoveDownload(db_handle_); | |
| 209 } | |
| 210 | |
| 211 void DownloadItem::StartProgressTimer() { | |
| 212 update_timer_.Start(TimeDelta::FromMilliseconds(kUpdateTimeMs), this, | |
| 213 &DownloadItem::UpdateObservers); | |
| 214 } | |
| 215 | |
| 216 void DownloadItem::StopProgressTimer() { | |
| 217 update_timer_.Stop(); | |
| 218 } | |
| 219 | |
| 220 bool DownloadItem::TimeRemaining(TimeDelta* remaining) const { | |
| 221 if (total_bytes_ <= 0) | |
| 222 return false; // We never received the content_length for this download. | |
| 223 | |
| 224 int64 speed = CurrentSpeed(); | |
| 225 if (speed == 0) | |
| 226 return false; | |
| 227 | |
| 228 *remaining = | |
| 229 TimeDelta::FromSeconds((total_bytes_ - received_bytes_) / speed); | |
| 230 return true; | |
| 231 } | |
| 232 | |
| 233 int64 DownloadItem::CurrentSpeed() const { | |
| 234 uintptr_t diff = GetTickCount() - start_tick_; | |
| 235 return diff == 0 ? 0 : received_bytes_ * 1000 / diff; | |
| 236 } | |
| 237 | |
| 238 int DownloadItem::PercentComplete() const { | |
| 239 int percent = -1; | |
| 240 if (total_bytes_ > 0) | |
| 241 percent = static_cast<int>(received_bytes_ * 100.0 / total_bytes_); | |
| 242 return percent; | |
| 243 } | |
| 244 | |
| 245 void DownloadItem::Rename(const std::wstring& full_path) { | |
| 246 DCHECK(!full_path.empty()); | |
| 247 full_path_ = full_path; | |
| 248 file_name_ = file_util::GetFilenameFromPath(full_path_); | |
| 249 } | |
| 250 | |
| 251 void DownloadItem::TogglePause() { | |
| 252 DCHECK(state_ == IN_PROGRESS); | |
| 253 manager_->PauseDownload(id_, !is_paused_); | |
| 254 is_paused_ = !is_paused_; | |
| 255 UpdateObservers(); | |
| 256 } | |
| 257 | |
| 258 // DownloadManager implementation ---------------------------------------------- | |
| 259 | |
| 260 // static | |
| 261 void DownloadManager::RegisterUserPrefs(PrefService* prefs) { | |
| 262 prefs->RegisterBooleanPref(prefs::kPromptForDownload, false); | |
| 263 prefs->RegisterStringPref(prefs::kDownloadExtensionsToOpen, L""); | |
| 264 prefs->RegisterBooleanPref(prefs::kDownloadDirUpgraded, false); | |
| 265 | |
| 266 // The default download path is userprofile\download. | |
| 267 std::wstring default_download_path; | |
| 268 if (!PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_download_path)) { | |
| 269 NOTREACHED(); | |
| 270 } | |
| 271 file_util::AppendToPath(&default_download_path, | |
| 272 l10n_util::GetString(IDS_DOWNLOAD_DIRECTORY)); | |
| 273 prefs->RegisterStringPref(prefs::kDownloadDefaultDirectory, | |
| 274 default_download_path); | |
| 275 | |
| 276 // If the download path is dangerous we forcefully reset it. But if we do | |
| 277 // so we set a flag to make sure we only do it once, to avoid fighting | |
| 278 // the user if he really wants it on an unsafe place such as the desktop. | |
| 279 | |
| 280 if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) { | |
| 281 std::wstring current_download_dir = | |
| 282 prefs->GetString(prefs::kDownloadDefaultDirectory); | |
| 283 if (DownloadPathIsDangerous(current_download_dir)) { | |
| 284 prefs->SetString(prefs::kDownloadDefaultDirectory, | |
| 285 default_download_path); | |
| 286 } | |
| 287 prefs->SetBoolean(prefs::kDownloadDirUpgraded, true); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 DownloadManager::DownloadManager() | |
| 292 : shutdown_needed_(false), | |
| 293 profile_(NULL), | |
| 294 file_manager_(NULL), | |
| 295 ui_loop_(MessageLoop::current()), | |
| 296 file_loop_(NULL) { | |
| 297 } | |
| 298 | |
| 299 DownloadManager::~DownloadManager() { | |
| 300 if (shutdown_needed_) | |
| 301 Shutdown(); | |
| 302 } | |
| 303 | |
| 304 void DownloadManager::Shutdown() { | |
| 305 DCHECK(shutdown_needed_) << "Shutdown called when not needed."; | |
| 306 | |
| 307 // Stop receiving download updates | |
| 308 file_manager_->RemoveDownloadManager(this); | |
| 309 | |
| 310 // Stop making history service requests | |
| 311 cancelable_consumer_.CancelAllRequests(); | |
| 312 | |
| 313 // 'in_progress_' may contain DownloadItems that have not finished the start | |
| 314 // complete (from the history service) and thus aren't in downloads_. | |
| 315 DownloadMap::iterator it = in_progress_.begin(); | |
| 316 for (; it != in_progress_.end(); ++it) { | |
| 317 DownloadItem* download = it->second; | |
| 318 if (download->state() == DownloadItem::IN_PROGRESS) { | |
| 319 download->Cancel(false); | |
| 320 UpdateHistoryForDownload(download); | |
| 321 } | |
| 322 if (download->db_handle() == kUninitializedHandle) { | |
| 323 // An invalid handle means that 'download' does not yet exist in | |
| 324 // 'downloads_', so we have to delete it here. | |
| 325 delete download; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 in_progress_.clear(); | |
| 330 STLDeleteValues(&downloads_); | |
| 331 | |
| 332 file_manager_ = NULL; | |
| 333 | |
| 334 // Save our file extensions to auto open. | |
| 335 SaveAutoOpens(); | |
| 336 | |
| 337 // Make sure the save as dialog doesn't notify us back if we're gone before | |
| 338 // it returns. | |
| 339 if (select_file_dialog_.get()) | |
| 340 select_file_dialog_->ListenerDestroyed(); | |
| 341 | |
| 342 shutdown_needed_ = false; | |
| 343 } | |
| 344 | |
| 345 // Issue a history query for downloads matching 'search_text'. If 'search_text' | |
| 346 // is empty, return all downloads that we know about. | |
| 347 void DownloadManager::GetDownloads(Observer* observer, | |
| 348 const std::wstring& search_text) { | |
| 349 DCHECK(observer); | |
| 350 | |
| 351 // Return a empty list if we've not yet received the set of downloads from the | |
| 352 // history system (we'll update all observers once we get that list in | |
| 353 // OnQueryDownloadEntriesComplete), or if there are no downloads at all. | |
| 354 std::vector<DownloadItem*> download_copy; | |
| 355 if (downloads_.empty()) { | |
| 356 observer->SetDownloads(download_copy); | |
| 357 return; | |
| 358 } | |
| 359 | |
| 360 // We already know all the downloads and there is no filter, so just return a | |
| 361 // copy to the observer. | |
| 362 if (search_text.empty()) { | |
| 363 download_copy.reserve(downloads_.size()); | |
| 364 for (DownloadMap::iterator it = downloads_.begin(); | |
| 365 it != downloads_.end(); ++it) { | |
| 366 download_copy.push_back(it->second); | |
| 367 } | |
| 368 | |
| 369 // We retain ownership of the DownloadItems. | |
| 370 observer->SetDownloads(download_copy); | |
| 371 return; | |
| 372 } | |
| 373 | |
| 374 // Issue a request to the history service for a list of downloads matching | |
| 375 // our search text. | |
| 376 HistoryService* hs = | |
| 377 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 378 if (hs) { | |
| 379 HistoryService::Handle h = | |
| 380 hs->SearchDownloads(search_text, | |
| 381 &cancelable_consumer_, | |
| 382 NewCallback(this, | |
| 383 &DownloadManager::OnSearchComplete)); | |
| 384 cancelable_consumer_.SetClientData(hs, h, observer); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 // Query the history service for information about all persisted downloads. | |
| 389 bool DownloadManager::Init(Profile* profile) { | |
| 390 DCHECK(profile); | |
| 391 DCHECK(!shutdown_needed_) << "DownloadManager already initialized."; | |
| 392 shutdown_needed_ = true; | |
| 393 | |
| 394 profile_ = profile; | |
| 395 request_context_ = profile_->GetRequestContext(); | |
| 396 | |
| 397 // 'incognito mode' will have access to past downloads, but we won't store | |
| 398 // information about new downloads while in that mode. | |
| 399 QueryHistoryForDownloads(); | |
| 400 | |
| 401 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host(); | |
| 402 if (!rdh) { | |
| 403 NOTREACHED(); | |
| 404 return false; | |
| 405 } | |
| 406 | |
| 407 file_manager_ = rdh->download_file_manager(); | |
| 408 if (!file_manager_) { | |
| 409 NOTREACHED(); | |
| 410 return false; | |
| 411 } | |
| 412 | |
| 413 file_loop_ = g_browser_process->file_thread()->message_loop(); | |
| 414 if (!file_loop_) { | |
| 415 NOTREACHED(); | |
| 416 return false; | |
| 417 } | |
| 418 | |
| 419 // Get our user preference state. | |
| 420 PrefService* prefs = profile_->GetPrefs(); | |
| 421 DCHECK(prefs); | |
| 422 prompt_for_download_.Init(prefs::kPromptForDownload, prefs, NULL); | |
| 423 | |
| 424 download_path_.Init(prefs::kDownloadDefaultDirectory, prefs, NULL); | |
| 425 | |
| 426 // Ensure that the download directory specified in the preferences exists. | |
| 427 file_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 428 file_manager_, &DownloadFileManager::CreateDirectory, *download_path_)); | |
| 429 | |
| 430 // We store any file extension that should be opened automatically at | |
| 431 // download completion in this pref. | |
| 432 download_util::InitializeExeTypes(&exe_types_); | |
| 433 | |
| 434 std::wstring extensions_to_open = | |
| 435 prefs->GetString(prefs::kDownloadExtensionsToOpen); | |
| 436 std::vector<std::wstring> extensions; | |
| 437 SplitString(extensions_to_open, L':', &extensions); | |
| 438 for (size_t i = 0; i < extensions.size(); ++i) { | |
| 439 if (!extensions[i].empty() && !IsExecutable(extensions[i])) | |
| 440 auto_open_.insert(extensions[i]); | |
| 441 } | |
| 442 | |
| 443 return true; | |
| 444 } | |
| 445 | |
| 446 void DownloadManager::QueryHistoryForDownloads() { | |
| 447 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 448 if (hs) { | |
| 449 hs->QueryDownloads( | |
| 450 &cancelable_consumer_, | |
| 451 NewCallback(this, &DownloadManager::OnQueryDownloadEntriesComplete)); | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 // We have received a message from DownloadFileManager about a new download. We | |
| 456 // create a download item and store it in our download map, and inform the | |
| 457 // history system of a new download. Since this method can be called while the | |
| 458 // history service thread is still reading the persistent state, we do not | |
| 459 // insert the new DownloadItem into 'downloads_' or inform our observers at this | |
| 460 // point. OnCreateDatabaseEntryComplete() handles that finalization of the the | |
| 461 // download creation as a callback from the history thread. | |
| 462 void DownloadManager::StartDownload(DownloadCreateInfo* info) { | |
| 463 DCHECK(MessageLoop::current() == ui_loop_); | |
| 464 DCHECK(info); | |
| 465 | |
| 466 // Determine the proper path for a download, by choosing either the default | |
| 467 // download directory, or prompting the user. | |
| 468 std::wstring generated_name; | |
| 469 GenerateFilename(info, &generated_name); | |
| 470 if (*prompt_for_download_ && !last_download_path_.empty()) | |
| 471 info->suggested_path = last_download_path_; | |
| 472 else | |
| 473 info->suggested_path = *download_path_; | |
| 474 file_util::AppendToPath(&info->suggested_path, generated_name); | |
| 475 | |
| 476 // We need to move over to the download thread because we don't want to stat | |
| 477 // the suggested path on the UI thread. | |
| 478 file_loop_->PostTask(FROM_HERE, | |
| 479 NewRunnableMethod(this, | |
| 480 &DownloadManager::CheckIfSuggestedPathExists, | |
| 481 info)); | |
| 482 } | |
| 483 | |
| 484 void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) { | |
| 485 DCHECK(info); | |
| 486 | |
| 487 // Check writability of the suggested path. If we can't write to it, default | |
| 488 // to the user's "My Documents" directory. We'll prompt them in this case. | |
| 489 std::wstring path = file_util::GetDirectoryFromPath(info->suggested_path); | |
| 490 if (!file_util::PathIsWritable(path)) { | |
| 491 info->save_as = true; | |
| 492 const std::wstring filename = | |
| 493 file_util::GetFilenameFromPath(info->suggested_path); | |
| 494 PathService::Get(chrome::DIR_USER_DOCUMENTS, &info->suggested_path); | |
| 495 file_util::AppendToPath(&info->suggested_path, filename); | |
| 496 } | |
| 497 | |
| 498 info->suggested_path_exists = !UniquifyPath(&info->suggested_path); | |
| 499 | |
| 500 // Now we return to the UI thread. | |
| 501 ui_loop_->PostTask(FROM_HERE, | |
| 502 NewRunnableMethod(this, | |
| 503 &DownloadManager::OnPathExistenceAvailable, | |
| 504 info)); | |
| 505 } | |
| 506 | |
| 507 void DownloadManager::OnPathExistenceAvailable(DownloadCreateInfo* info) { | |
| 508 DCHECK(MessageLoop::current() == ui_loop_); | |
| 509 DCHECK(info); | |
| 510 | |
| 511 if (*prompt_for_download_ || info->save_as || info->suggested_path_exists) { | |
| 512 // We must ask the user for the place to put the download. | |
| 513 if (!select_file_dialog_.get()) | |
| 514 select_file_dialog_ = SelectFileDialog::Create(this); | |
| 515 | |
| 516 TabContents* contents = tab_util::GetTabContentsByID( | |
| 517 info->render_process_id, info->render_view_id); | |
| 518 HWND owning_hwnd = | |
| 519 contents ? GetAncestor(contents->GetContainerHWND(), GA_ROOT) : NULL; | |
| 520 select_file_dialog_->SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE, | |
| 521 std::wstring(), info->suggested_path, | |
| 522 owning_hwnd, info); | |
| 523 } else { | |
| 524 // No prompting for download, just continue with the suggested name. | |
| 525 ContinueStartDownload(info, info->suggested_path); | |
| 526 } | |
| 527 } | |
| 528 | |
| 529 void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info, | |
| 530 const std::wstring& target_path) { | |
| 531 scoped_ptr<DownloadCreateInfo> infop(info); | |
| 532 info->path = target_path; | |
| 533 | |
| 534 DownloadItem* download = NULL; | |
| 535 DownloadMap::iterator it = in_progress_.find(info->download_id); | |
| 536 if (it == in_progress_.end()) { | |
| 537 download = new DownloadItem(info->download_id, | |
| 538 info->path, | |
| 539 info->url, | |
| 540 info->start_time, | |
| 541 info->total_bytes, | |
| 542 info->render_process_id, | |
| 543 info->request_id); | |
| 544 download->set_manager(this); | |
| 545 in_progress_[info->download_id] = download; | |
| 546 } else { | |
| 547 NOTREACHED(); // Should not exist! | |
| 548 return; | |
| 549 } | |
| 550 | |
| 551 // If the download already completed by the time we reached this point, then | |
| 552 // notify observers that it did. | |
| 553 PendingFinishedMap::iterator pending_it = | |
| 554 pending_finished_downloads_.find(info->download_id); | |
| 555 if (pending_it != pending_finished_downloads_.end()) | |
| 556 DownloadFinished(pending_it->first, pending_it->second); | |
| 557 | |
| 558 download->Rename(target_path); | |
| 559 | |
| 560 file_loop_->PostTask(FROM_HERE, | |
| 561 NewRunnableMethod(file_manager_, | |
| 562 &DownloadFileManager::OnFinalDownloadName, | |
| 563 download->id(), | |
| 564 target_path)); | |
| 565 | |
| 566 if (profile_->IsOffTheRecord()) { | |
| 567 // Fake a db handle for incognito mode, since nothing is actually stored in | |
| 568 // the database in this mode. We have to make sure that these handles don't | |
| 569 // collide with normal db handles, so we use a negative value. Eventually, | |
| 570 // they could overlap, but you'd have to do enough downloading that your ISP | |
| 571 // would likely stab you in the neck first. YMMV. | |
| 572 static int64 fake_db_handle = kUninitializedHandle - 1; | |
| 573 OnCreateDownloadEntryComplete(*info, fake_db_handle--); | |
| 574 } else { | |
| 575 // Update the history system with the new download. | |
| 576 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong. | |
| 577 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 578 if (hs) { | |
| 579 hs->CreateDownload( | |
| 580 *info, &cancelable_consumer_, | |
| 581 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete)); | |
| 582 } | |
| 583 } | |
| 584 } | |
| 585 | |
| 586 // Convenience function for updating the history service for a download. | |
| 587 void DownloadManager::UpdateHistoryForDownload(DownloadItem* download) { | |
| 588 DCHECK(download); | |
| 589 | |
| 590 // Don't store info in the database if the download was initiated while in | |
| 591 // incognito mode or if it hasn't been initialized in our database table. | |
| 592 if (download->db_handle() <= kUninitializedHandle) | |
| 593 return; | |
| 594 | |
| 595 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong. | |
| 596 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 597 if (hs) { | |
| 598 hs->UpdateDownload(download->received_bytes(), | |
| 599 download->state(), | |
| 600 download->db_handle()); | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 void DownloadManager::RemoveDownloadFromHistory(DownloadItem* download) { | |
| 605 DCHECK(download); | |
| 606 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong. | |
| 607 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 608 if (download->db_handle() > kUninitializedHandle && hs) | |
| 609 hs->RemoveDownload(download->db_handle()); | |
| 610 } | |
| 611 | |
| 612 void DownloadManager::RemoveDownloadsFromHistoryBetween(const Time remove_begin, | |
| 613 const Time remove_end) { | |
| 614 // FIXME(acw|paulg) see bug 958058. EXPLICIT_ACCESS below is wrong. | |
| 615 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 616 if (hs) | |
| 617 hs->RemoveDownloadsBetween(remove_begin, remove_end); | |
| 618 } | |
| 619 | |
| 620 void DownloadManager::UpdateDownload(int32 download_id, int64 size) { | |
| 621 DownloadMap::iterator it = in_progress_.find(download_id); | |
| 622 if (it != in_progress_.end()) { | |
| 623 DownloadItem* download = it->second; | |
| 624 download->Update(size); | |
| 625 UpdateHistoryForDownload(download); | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 void DownloadManager::DownloadFinished(int32 download_id, int64 size) { | |
| 630 DownloadMap::iterator it = in_progress_.find(download_id); | |
| 631 if (it != in_progress_.end()) { | |
| 632 // Remove the id from the list of pending ids. | |
| 633 PendingFinishedMap::iterator erase_it = | |
| 634 pending_finished_downloads_.find(download_id); | |
| 635 if (erase_it != pending_finished_downloads_.end()) | |
| 636 pending_finished_downloads_.erase(erase_it); | |
| 637 | |
| 638 DownloadItem* download = it->second; | |
| 639 download->Finished(size); | |
| 640 | |
| 641 // Open the download if the user or user prefs indicate it should be. | |
| 642 const std::wstring extension = | |
| 643 file_util::GetFileExtensionFromPath(download->full_path()); | |
| 644 if (download->open_when_complete() || ShouldOpenFileExtension(extension)) | |
| 645 OpenDownloadInShell(download, NULL); | |
| 646 | |
| 647 // Clean up will happen when the history system create callback runs if we | |
| 648 // don't have a valid db_handle yet. | |
| 649 if (download->db_handle() != kUninitializedHandle) { | |
| 650 in_progress_.erase(it); | |
| 651 NotifyAboutDownloadStop(); | |
| 652 UpdateHistoryForDownload(download); | |
| 653 } | |
| 654 } else { | |
| 655 // The download is done, but the user hasn't selected a final location for | |
| 656 // it yet (the Save As dialog box is probably still showing), so just keep | |
| 657 // track of the fact that this download id is complete, when the | |
| 658 // DownloadItem is constructed later we'll notify its completion then. | |
| 659 PendingFinishedMap::iterator erase_it = | |
| 660 pending_finished_downloads_.find(download_id); | |
| 661 DCHECK(erase_it == pending_finished_downloads_.end()); | |
| 662 pending_finished_downloads_[download_id] = size; | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 // static | |
| 667 // We have to tell the ResourceDispatcherHost to cancel the download from this | |
| 668 // thread, since we can't forward tasks from the file thread to the io thread | |
| 669 // reliably (crash on shutdown race condition). | |
| 670 void DownloadManager::CancelDownloadRequest(int render_process_id, | |
| 671 int request_id) { | |
| 672 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host(); | |
| 673 base::Thread* io_thread = g_browser_process->io_thread(); | |
| 674 if (!io_thread || !rdh) | |
| 675 return; | |
| 676 io_thread->message_loop()->PostTask(FROM_HERE, | |
| 677 NewRunnableFunction(&DownloadManager::OnCancelDownloadRequest, | |
| 678 rdh, | |
| 679 render_process_id, | |
| 680 request_id)); | |
| 681 } | |
| 682 | |
| 683 // static | |
| 684 void DownloadManager::OnCancelDownloadRequest(ResourceDispatcherHost* rdh, | |
| 685 int render_process_id, | |
| 686 int request_id) { | |
| 687 rdh->CancelRequest(render_process_id, request_id, false); | |
| 688 } | |
| 689 | |
| 690 void DownloadManager::DownloadCancelled(int32 download_id) { | |
| 691 DownloadMap::iterator it = in_progress_.find(download_id); | |
| 692 if (it == in_progress_.end()) | |
| 693 return; | |
| 694 DownloadItem* download = it->second; | |
| 695 | |
| 696 CancelDownloadRequest(download->render_process_id(), download->request_id()); | |
| 697 | |
| 698 // Clean up will happen when the history system create callback runs if we | |
| 699 // don't have a valid db_handle yet. | |
| 700 if (download->db_handle() != kUninitializedHandle) { | |
| 701 in_progress_.erase(it); | |
| 702 NotifyAboutDownloadStop(); | |
| 703 UpdateHistoryForDownload(download); | |
| 704 } | |
| 705 | |
| 706 // Tell the file manager to cancel the download. | |
| 707 file_manager_->RemoveDownload(download->id(), this); // On the UI thread | |
| 708 file_loop_->PostTask(FROM_HERE, | |
| 709 NewRunnableMethod(file_manager_, | |
| 710 &DownloadFileManager::CancelDownload, | |
| 711 download->id())); | |
| 712 } | |
| 713 | |
| 714 void DownloadManager::PauseDownload(int32 download_id, bool pause) { | |
| 715 DownloadMap::iterator it = in_progress_.find(download_id); | |
| 716 if (it != in_progress_.end()) { | |
| 717 DownloadItem* download = it->second; | |
| 718 if (pause == download->is_paused()) | |
| 719 return; | |
| 720 | |
| 721 // Inform the ResourceDispatcherHost of the new pause state. | |
| 722 base::Thread* io_thread = g_browser_process->io_thread(); | |
| 723 ResourceDispatcherHost* rdh = g_browser_process->resource_dispatcher_host(); | |
| 724 if (!io_thread || !rdh) | |
| 725 return; | |
| 726 | |
| 727 io_thread->message_loop()->PostTask(FROM_HERE, | |
| 728 NewRunnableFunction(&DownloadManager::OnPauseDownloadRequest, | |
| 729 rdh, | |
| 730 download->render_process_id(), | |
| 731 download->request_id(), | |
| 732 pause)); | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 // static | |
| 737 void DownloadManager::OnPauseDownloadRequest(ResourceDispatcherHost* rdh, | |
| 738 int render_process_id, | |
| 739 int request_id, | |
| 740 bool pause) { | |
| 741 rdh->PauseRequest(render_process_id, request_id, pause); | |
| 742 } | |
| 743 | |
| 744 void DownloadManager::RemoveDownload(int64 download_handle) { | |
| 745 DownloadMap::iterator it = downloads_.find(download_handle); | |
| 746 if (it == downloads_.end()) | |
| 747 return; | |
| 748 | |
| 749 // Make history update. | |
| 750 DownloadItem* download = it->second; | |
| 751 RemoveDownloadFromHistory(download); | |
| 752 | |
| 753 // Remove from our tables and delete. | |
| 754 downloads_.erase(it); | |
| 755 delete download; | |
| 756 | |
| 757 // Tell observers to refresh their views. | |
| 758 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); | |
| 759 } | |
| 760 | |
| 761 int DownloadManager::RemoveDownloadsBetween(const Time remove_begin, | |
| 762 const Time remove_end) { | |
| 763 RemoveDownloadsFromHistoryBetween(remove_begin, remove_end); | |
| 764 | |
| 765 int num_deleted = 0; | |
| 766 DownloadMap::iterator it = downloads_.begin(); | |
| 767 while (it != downloads_.end()) { | |
| 768 DownloadItem* download = it->second; | |
| 769 DownloadItem::DownloadState state = download->state(); | |
| 770 if (download->start_time() >= remove_begin && | |
| 771 (remove_end.is_null() || download->start_time() < remove_end) && | |
| 772 (state == DownloadItem::COMPLETE || | |
| 773 state == DownloadItem::CANCELLED)) { | |
| 774 // Remove from the map and move to the next in the list. | |
| 775 it = downloads_.erase(it); | |
| 776 delete download; | |
| 777 | |
| 778 ++num_deleted; | |
| 779 continue; | |
| 780 } | |
| 781 | |
| 782 ++it; | |
| 783 } | |
| 784 | |
| 785 // Tell observers to refresh their views. | |
| 786 if (num_deleted > 0) | |
| 787 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); | |
| 788 | |
| 789 return num_deleted; | |
| 790 } | |
| 791 | |
| 792 int DownloadManager::RemoveDownloads(const Time remove_begin) { | |
| 793 return RemoveDownloadsBetween(remove_begin, Time()); | |
| 794 } | |
| 795 | |
| 796 // Initiate a download of a specific URL. We send the request to the | |
| 797 // ResourceDispatcherHost, and let it send us responses like a regular | |
| 798 // download. | |
| 799 void DownloadManager::DownloadUrl(const GURL& url, | |
| 800 const GURL& referrer, | |
| 801 WebContents* web_contents) { | |
| 802 DCHECK(web_contents); | |
| 803 file_manager_->DownloadUrl(url, | |
| 804 referrer, | |
| 805 web_contents->process()->host_id(), | |
| 806 web_contents->render_view_host()->routing_id(), | |
| 807 request_context_.get()); | |
| 808 } | |
| 809 | |
| 810 void DownloadManager::NotifyAboutDownloadStart() { | |
| 811 NotificationService::current()-> | |
| 812 Notify(NOTIFY_DOWNLOAD_START, NotificationService::AllSources(), | |
| 813 NotificationService::NoDetails()); | |
| 814 } | |
| 815 | |
| 816 void DownloadManager::NotifyAboutDownloadStop() { | |
| 817 NotificationService::current()-> | |
| 818 Notify(NOTIFY_DOWNLOAD_STOP, NotificationService::AllSources(), | |
| 819 NotificationService::NoDetails()); | |
| 820 } | |
| 821 | |
| 822 void DownloadManager::GenerateExtension(const std::wstring& file_name, | |
| 823 const std::string& mime_type, | |
| 824 std::wstring* generated_extension) { | |
| 825 // We're worried about three things here: | |
| 826 // | |
| 827 // 1) Security. Many sites let users upload content, such as buddy icons, to | |
| 828 // their web sites. We want to mitigate the case where an attacker | |
| 829 // supplies a malicious executable with an executable file extension but an | |
| 830 // honest site serves the content with a benign content type, such as | |
| 831 // image/jpeg. | |
| 832 // | |
| 833 // 2) Usability. If the site fails to provide a file extension, we want to | |
| 834 // guess a reasonable file extension based on the content type. | |
| 835 // | |
| 836 // 3) Shell integration. Some file extensions automatically integrate with | |
| 837 // the shell. We block these extensions to prevent a malicious web site | |
| 838 // from integrating with the user's shell. | |
| 839 | |
| 840 static const wchar_t default_extension[] = L"download"; | |
| 841 | |
| 842 // See if our file name already contains an extension. | |
| 843 std::wstring extension(file_util::GetFileExtensionFromPath(file_name)); | |
| 844 | |
| 845 // Rename shell-integrated extensions. | |
| 846 if (win_util::IsShellIntegratedExtension(extension)) | |
| 847 extension.assign(default_extension); | |
| 848 | |
| 849 std::string mime_type_from_extension; | |
| 850 net::GetMimeTypeFromFile(file_name, &mime_type_from_extension); | |
| 851 if (mime_type == mime_type_from_extension) { | |
| 852 // The hinted extension matches the mime type. It looks like a winner. | |
| 853 generated_extension->swap(extension); | |
| 854 return; | |
| 855 } | |
| 856 | |
| 857 if (IsExecutable(extension) && !IsExecutableMimeType(mime_type)) { | |
| 858 // We want to be careful about executable extensions. The worry here is | |
| 859 // that a trusted web site could be tricked into dropping an executable file | |
| 860 // on the user's filesystem. | |
| 861 if (!net::GetPreferredExtensionForMimeType(mime_type, &extension)) { | |
| 862 // We couldn't find a good extension for this content type. Use a dummy | |
| 863 // extension instead. | |
| 864 extension.assign(default_extension); | |
| 865 } | |
| 866 } | |
| 867 | |
| 868 if (extension.empty()) { | |
| 869 net::GetPreferredExtensionForMimeType(mime_type, &extension); | |
| 870 } else { | |
| 871 // Append entension generated from the mime type if: | |
| 872 // 1. New extension is not ".txt" | |
| 873 // 2. New extension is not the same as the already existing extension. | |
| 874 // 3. New extension is not executable. This action mitigates the case when | |
| 875 // an execuatable is hidden in a benign file extension; | |
| 876 // E.g. my-cat.jpg becomes my-cat.jpg.js if content type is | |
| 877 // application/x-javascript. | |
| 878 std::wstring append_extension; | |
| 879 if (net::GetPreferredExtensionForMimeType(mime_type, &append_extension)) { | |
| 880 if (append_extension != L".txt" && append_extension != extension && | |
| 881 !IsExecutable(append_extension)) | |
| 882 extension += append_extension; | |
| 883 } | |
| 884 } | |
| 885 | |
| 886 generated_extension->swap(extension); | |
| 887 } | |
| 888 | |
| 889 void DownloadManager::GenerateFilename(DownloadCreateInfo* info, | |
| 890 std::wstring* generated_name) { | |
| 891 std::wstring file_name = | |
| 892 net::GetSuggestedFilename(GURL(info->url), | |
| 893 info->content_disposition, | |
| 894 L"download"); | |
| 895 DCHECK(!file_name.empty()); | |
| 896 | |
| 897 // Make sure we get the right file extension. | |
| 898 std::wstring extension; | |
| 899 GenerateExtension(file_name, info->mime_type, &extension); | |
| 900 file_util::ReplaceExtension(&file_name, extension); | |
| 901 | |
| 902 // Prepend "_" to the file name if it's a reserved name | |
| 903 if (win_util::IsReservedName(file_name)) | |
| 904 file_name = std::wstring(L"_") + file_name; | |
| 905 | |
| 906 generated_name->assign(file_name); | |
| 907 } | |
| 908 | |
| 909 void DownloadManager::AddObserver(Observer* observer) { | |
| 910 observers_.AddObserver(observer); | |
| 911 observer->ModelChanged(); | |
| 912 } | |
| 913 | |
| 914 void DownloadManager::RemoveObserver(Observer* observer) { | |
| 915 observers_.RemoveObserver(observer); | |
| 916 } | |
| 917 | |
| 918 // Post Windows Shell operations to the Download thread, to avoid blocking the | |
| 919 // user interface. | |
| 920 void DownloadManager::ShowDownloadInShell(const DownloadItem* download) { | |
| 921 DCHECK(file_manager_); | |
| 922 file_loop_->PostTask(FROM_HERE, | |
| 923 NewRunnableMethod(file_manager_, | |
| 924 &DownloadFileManager::OnShowDownloadInShell, | |
| 925 download->full_path())); | |
| 926 } | |
| 927 | |
| 928 void DownloadManager::OpenDownloadInShell(const DownloadItem* download, | |
| 929 HWND parent_window) { | |
| 930 DCHECK(file_manager_); | |
| 931 file_loop_->PostTask(FROM_HERE, | |
| 932 NewRunnableMethod(file_manager_, | |
| 933 &DownloadFileManager::OnOpenDownloadInShell, | |
| 934 download->full_path(), download->url(), parent_window)); | |
| 935 } | |
| 936 | |
| 937 void DownloadManager::OpenFilesOfExtension(const std::wstring& extension, | |
| 938 bool open) { | |
| 939 if (open && !IsExecutable(extension)) | |
| 940 auto_open_.insert(extension); | |
| 941 else | |
| 942 auto_open_.erase(extension); | |
| 943 SaveAutoOpens(); | |
| 944 } | |
| 945 | |
| 946 bool DownloadManager::ShouldOpenFileExtension(const std::wstring& extension) { | |
| 947 if (!IsExecutable(extension) && | |
| 948 auto_open_.find(extension) != auto_open_.end()) | |
| 949 return true; | |
| 950 return false; | |
| 951 } | |
| 952 | |
| 953 // static | |
| 954 bool DownloadManager::IsExecutableMimeType(const std::string& mime_type) { | |
| 955 // JavaScript is just as powerful as EXE. | |
| 956 if (net::MatchesMimeType("text/javascript", mime_type)) | |
| 957 return true; | |
| 958 if (net::MatchesMimeType("text/javascript;version=*", mime_type)) | |
| 959 return true; | |
| 960 | |
| 961 // We don't consider other non-application types to be executable. | |
| 962 if (!net::MatchesMimeType("application/*", mime_type)) | |
| 963 return false; | |
| 964 | |
| 965 // These application types are not executable. | |
| 966 if (net::MatchesMimeType("application/*+xml", mime_type)) | |
| 967 return false; | |
| 968 if (net::MatchesMimeType("application/xml", mime_type)) | |
| 969 return false; | |
| 970 | |
| 971 return true; | |
| 972 } | |
| 973 | |
| 974 bool DownloadManager::IsExecutable(const std::wstring& extension) { | |
| 975 return exe_types_.find(extension) != exe_types_.end(); | |
| 976 } | |
| 977 | |
| 978 void DownloadManager::ResetAutoOpenFiles() { | |
| 979 auto_open_.clear(); | |
| 980 SaveAutoOpens(); | |
| 981 } | |
| 982 | |
| 983 bool DownloadManager::HasAutoOpenFileTypesRegistered() const { | |
| 984 return !auto_open_.empty(); | |
| 985 } | |
| 986 | |
| 987 void DownloadManager::SaveAutoOpens() { | |
| 988 PrefService* prefs = profile_->GetPrefs(); | |
| 989 if (prefs) { | |
| 990 std::wstring extensions; | |
| 991 for (std::set<std::wstring>::iterator it = auto_open_.begin(); | |
| 992 it != auto_open_.end(); ++it) { | |
| 993 extensions += *it + L":"; | |
| 994 } | |
| 995 if (!extensions.empty()) | |
| 996 extensions.erase(extensions.size() - 1); | |
| 997 prefs->SetString(prefs::kDownloadExtensionsToOpen, extensions); | |
| 998 } | |
| 999 } | |
| 1000 | |
| 1001 void DownloadManager::FileSelected(const std::wstring& path, void* params) { | |
| 1002 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params); | |
| 1003 if (*prompt_for_download_) | |
| 1004 last_download_path_ = file_util::GetDirectoryFromPath(path); | |
| 1005 ContinueStartDownload(info, path); | |
| 1006 } | |
| 1007 | |
| 1008 void DownloadManager::FileSelectionCanceled(void* params) { | |
| 1009 // The user didn't pick a place to save the file, so need to cancel the | |
| 1010 // download that's already in progress to the temporary location. | |
| 1011 DownloadCreateInfo* info = reinterpret_cast<DownloadCreateInfo*>(params); | |
| 1012 file_loop_->PostTask(FROM_HERE, | |
| 1013 NewRunnableMethod(file_manager_, &DownloadFileManager::CancelDownload, | |
| 1014 info->download_id)); | |
| 1015 } | |
| 1016 | |
| 1017 // Operations posted to us from the history service ---------------------------- | |
| 1018 | |
| 1019 // The history service has retrieved all download entries. 'entries' contains | |
| 1020 // 'DownloadCreateInfo's in sorted order (by ascending start_time). | |
| 1021 void DownloadManager::OnQueryDownloadEntriesComplete( | |
| 1022 std::vector<DownloadCreateInfo>* entries) { | |
| 1023 for (size_t i = 0; i < entries->size(); ++i) { | |
| 1024 DownloadItem* download = new DownloadItem(entries->at(i)); | |
| 1025 DCHECK(downloads_.find(download->db_handle()) == downloads_.end()); | |
| 1026 downloads_[download->db_handle()] = download; | |
| 1027 download->set_manager(this); | |
| 1028 } | |
| 1029 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); | |
| 1030 } | |
| 1031 | |
| 1032 | |
| 1033 // Once the new DownloadItem's creation info has been committed to the history | |
| 1034 // service, we associate the DownloadItem with the db handle, update our | |
| 1035 // 'downloads_' map and inform observers. | |
| 1036 void DownloadManager::OnCreateDownloadEntryComplete(DownloadCreateInfo info, | |
| 1037 int64 db_handle) { | |
| 1038 DownloadMap::iterator it = in_progress_.find(info.download_id); | |
| 1039 DCHECK(it != in_progress_.end()); | |
| 1040 | |
| 1041 DownloadItem* download = it->second; | |
| 1042 DCHECK(download->db_handle() == kUninitializedHandle); | |
| 1043 download->set_db_handle(db_handle); | |
| 1044 | |
| 1045 // Insert into our full map. | |
| 1046 DCHECK(downloads_.find(download->db_handle()) == downloads_.end()); | |
| 1047 downloads_[download->db_handle()] = download; | |
| 1048 | |
| 1049 // The 'contents' may no longer exist if the user closed the tab before we get | |
| 1050 // this start completion event. If it does, tell the origin WebContents to | |
| 1051 // display its download shelf. | |
| 1052 TabContents* contents = | |
| 1053 tab_util::GetTabContentsByID(info.render_process_id, info.render_view_id); | |
| 1054 | |
| 1055 // If the contents no longer exists or is no longer active, we start the | |
| 1056 // download in the last active browser. This is not ideal but better than | |
| 1057 // fully hiding the download from the user. Note: non active means that the | |
| 1058 // user navigated away from the tab contents. This has nothing to do with | |
| 1059 // tab selection. | |
| 1060 if (!contents || !contents->is_active()) { | |
| 1061 Browser* last_active = BrowserList::GetLastActive(); | |
| 1062 if (last_active) | |
| 1063 contents = last_active->GetSelectedTabContents(); | |
| 1064 } | |
| 1065 | |
| 1066 if (contents) | |
| 1067 contents->OnStartDownload(download); | |
| 1068 | |
| 1069 // Inform interested objects about the new download. | |
| 1070 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); | |
| 1071 NotifyAboutDownloadStart(); | |
| 1072 | |
| 1073 // If this download has been completed before we've received the db handle, | |
| 1074 // post one final message to the history service so that it can be properly | |
| 1075 // in sync with the DownloadItem's completion status, and also inform any | |
| 1076 // observers so that they get more than just the start notification. | |
| 1077 if (download->state() != DownloadItem::IN_PROGRESS) { | |
| 1078 in_progress_.erase(it); | |
| 1079 NotifyAboutDownloadStop(); | |
| 1080 UpdateHistoryForDownload(download); | |
| 1081 download->UpdateObservers(); | |
| 1082 } | |
| 1083 } | |
| 1084 | |
| 1085 // Called when the history service has retrieved the list of downloads that | |
| 1086 // match the search text. | |
| 1087 void DownloadManager::OnSearchComplete(HistoryService::Handle handle, | |
| 1088 std::vector<int64>* results) { | |
| 1089 HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | |
| 1090 Observer* requestor = cancelable_consumer_.GetClientData(hs, handle); | |
| 1091 if (!requestor) | |
| 1092 return; | |
| 1093 | |
| 1094 std::vector<DownloadItem*> searched_downloads; | |
| 1095 for (std::vector<int64>::iterator it = results->begin(); | |
| 1096 it != results->end(); ++it) { | |
| 1097 DownloadMap::iterator dit = downloads_.find(*it); | |
| 1098 if (dit != downloads_.end()) | |
| 1099 searched_downloads.push_back(dit->second); | |
| 1100 } | |
| 1101 | |
| 1102 requestor->SetDownloads(searched_downloads); | |
| 1103 } | |
| 1104 | |
| OLD | NEW |