// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/drag_download_file.h" #include "base/file_util.h" #include "base/message_loop.h" #include "chrome/browser/download/download_file.h" #include "chrome/browser/download/download_item.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/profiles/profile.h" #include "content/browser/browser_thread.h" #include "content/browser/tab_contents/tab_contents.h" #include "net/base/file_stream.h" DragDownloadFile::DragDownloadFile( const FilePath& file_name_or_path, linked_ptr<net::FileStream> file_stream, const GURL& url, const GURL& referrer, const std::string& referrer_encoding, TabContents* tab_contents) : file_stream_(file_stream), url_(url), referrer_(referrer), referrer_encoding_(referrer_encoding), tab_contents_(tab_contents), drag_message_loop_(MessageLoop::current()), is_started_(false), is_successful_(false), download_manager_(NULL), download_item_observer_added_(false) { #if defined(OS_WIN) DCHECK(!file_name_or_path.empty() && !file_stream.get()); file_name_ = file_name_or_path; #elif defined(OS_POSIX) DCHECK(!file_name_or_path.empty() && file_stream.get()); file_path_ = file_name_or_path; #endif } DragDownloadFile::~DragDownloadFile() { AssertCurrentlyOnDragThread(); // Since the target application can still hold and use the dragged file, // we do not know the time that it can be safely deleted. To solve this // problem, we schedule it to be removed after the system is restarted. #if defined(OS_WIN) if (!temp_dir_path_.empty()) { if (!file_path_.empty()) file_util::DeleteAfterReboot(file_path_); file_util::DeleteAfterReboot(temp_dir_path_); } #endif if (download_manager_) download_manager_->RemoveObserver(this); } bool DragDownloadFile::Start(ui::DownloadFileObserver* observer) { AssertCurrentlyOnDragThread(); if (is_started_) return true; is_started_ = true; DCHECK(!observer_.get()); observer_ = observer; if (!file_stream_.get()) { // Create a temporary directory to save the temporary download file. We do // not want to use the default download directory since we do not want the // twisted file name shown in the download shelf if the file with the same // name already exists. if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome"), &temp_dir_path_)) return false; file_path_ = temp_dir_path_.Append(file_name_); } InitiateDownload(); // On Windows, we need to wait till the download file is completed. #if defined(OS_WIN) StartNestedMessageLoop(); #endif return is_successful_; } void DragDownloadFile::Stop() { } void DragDownloadFile::InitiateDownload() { #if defined(OS_WIN) // DownloadManager could only be invoked from the UI thread. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &DragDownloadFile::InitiateDownload)); return; } #endif download_manager_ = tab_contents_->profile()->GetDownloadManager(); download_manager_->AddObserver(this); DownloadSaveInfo save_info; save_info.file_path = file_path_; save_info.file_stream = file_stream_; download_manager_->DownloadUrlToFile(url_, referrer_, referrer_encoding_, save_info, tab_contents_); download_util::RecordDownloadCount( download_util::INITIATED_BY_DRAG_N_DROP_COUNT); } void DragDownloadFile::DownloadCompleted(bool is_successful) { #if defined(OS_WIN) // If not in drag-and-drop thread, defer the running to it. if (drag_message_loop_ != MessageLoop::current()) { drag_message_loop_->PostTask( FROM_HERE, NewRunnableMethod(this, &DragDownloadFile::DownloadCompleted, is_successful)); return; } #endif is_successful_ = is_successful; // Call the observer. DCHECK(observer_); if (is_successful) observer_->OnDownloadCompleted(file_path_); else observer_->OnDownloadAborted(); // Release the observer since we do not need it any more. observer_ = NULL; // On Windows, we need to stop the waiting. #if defined(OS_WIN) QuitNestedMessageLoop(); #endif } void DragDownloadFile::ModelChanged() { AssertCurrentlyOnUIThread(); std::vector<DownloadItem*> downloads; download_manager_->GetTemporaryDownloads(file_path_.DirName(), &downloads); for (std::vector<DownloadItem*>::const_iterator i = downloads.begin(); i != downloads.end(); ++i) { if (!download_item_observer_added_ && (*i)->original_url() == url_) { download_item_observer_added_ = true; (*i)->AddObserver(this); } } } void DragDownloadFile::OnDownloadUpdated(DownloadItem* download) { AssertCurrentlyOnUIThread(); if (download->IsCancelled()) { download->RemoveObserver(this); download_manager_->RemoveObserver(this); DownloadCompleted(false); } else if (download->IsComplete()) { download->RemoveObserver(this); download_manager_->RemoveObserver(this); DownloadCompleted(true); } // Ignore other states. } void DragDownloadFile::AssertCurrentlyOnDragThread() { // Only do the check on Windows where two threads are involved. #if defined(OS_WIN) DCHECK(drag_message_loop_ == MessageLoop::current()); #endif } void DragDownloadFile::AssertCurrentlyOnUIThread() { // Only do the check on Windows where two threads are involved. #if defined(OS_WIN) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); #endif } #if defined(OS_WIN) void DragDownloadFile::StartNestedMessageLoop() { AssertCurrentlyOnDragThread(); bool old_state = MessageLoop::current()->NestableTasksAllowed(); MessageLoop::current()->SetNestableTasksAllowed(true); is_running_nested_message_loop_ = true; MessageLoop::current()->Run(); MessageLoop::current()->SetNestableTasksAllowed(old_state); } void DragDownloadFile::QuitNestedMessageLoop() { AssertCurrentlyOnDragThread(); if (is_running_nested_message_loop_) { is_running_nested_message_loop_ = false; MessageLoop::current()->Quit(); } } #endif