diff options
author | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-10 00:00:32 +0000 |
---|---|---|
committer | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-10 00:00:32 +0000 |
commit | 8af9d0341d6c00ee537adc089f938b120d1d8d34 (patch) | |
tree | 334fcf5a09c2b3e12face9d46ae4f25ee62921eb /chrome/browser | |
parent | 8d292399e6dcdfa776a1de94da07841bf1487f5b (diff) | |
download | chromium_src-8af9d0341d6c00ee537adc089f938b120d1d8d34.zip chromium_src-8af9d0341d6c00ee537adc089f938b120d1d8d34.tar.gz chromium_src-8af9d0341d6c00ee537adc089f938b120d1d8d34.tar.bz2 |
Refactor DragDownloadFile so that it can be used by both Windows and MacOSX.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/572014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38545 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
18 files changed, 556 insertions, 384 deletions
diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc index 25a0900..b152d18 100644 --- a/chrome/browser/download/download_file.cc +++ b/chrome/browser/download/download_file.cc @@ -57,7 +57,7 @@ class DownloadFileUpdateTask : public Task { // DownloadFile implementation ------------------------------------------------- DownloadFile::DownloadFile(const DownloadCreateInfo* info) - : file_(NULL), + : file_stream_(info->save_info.file_stream), source_url_(info->url), referrer_url_(info->referrer_url), id_(info->download_id), @@ -65,9 +65,11 @@ DownloadFile::DownloadFile(const DownloadCreateInfo* info) render_view_id_(info->render_view_id), request_id_(info->request_id), bytes_so_far_(0), + full_path_(info->save_info.file_path), path_renamed_(false), in_progress_(true), - dont_sleep_(true) { + dont_sleep_(true), + save_info_(info->save_info) { } DownloadFile::~DownloadFile() { @@ -75,15 +77,15 @@ DownloadFile::~DownloadFile() { } bool DownloadFile::Initialize() { - if (file_util::CreateTemporaryFile(&full_path_)) - return Open("wb"); + if (!full_path_.empty() || file_util::CreateTemporaryFile(&full_path_)) + return Open(); return false; } bool DownloadFile::AppendDataToFile(const char* data, int data_len) { - if (file_) { + if (file_stream_.get()) { // FIXME bug 595247: handle errors on file writes. - size_t written = fwrite(data, 1, data_len, file_); + size_t written = file_stream_->Write(data, data_len, NULL); bytes_so_far_ += written; return true; } @@ -92,13 +94,18 @@ bool DownloadFile::AppendDataToFile(const char* data, int data_len) { void DownloadFile::Cancel() { Close(); - file_util::Delete(full_path_, false); + if (!full_path_.empty()) + file_util::Delete(full_path_, false); } // The UI has provided us with our finalized name. bool DownloadFile::Rename(const FilePath& new_path) { Close(); + // Nothing more to do if the new path is same as the old one. + if (new_path == full_path_) + return true; + #if defined(OS_WIN) // We cannot rename because rename will keep the same security descriptor // on the destination file. We want to recreate the security descriptor @@ -119,23 +126,30 @@ bool DownloadFile::Rename(const FilePath& new_path) { if (!in_progress_) return true; - if (!Open("a+b")) + if (!Open()) return false; return true; } void DownloadFile::Close() { - if (file_) { - file_util::CloseFile(file_); - file_ = NULL; + if (file_stream_.get()) { + file_stream_->Close(); + file_stream_.reset(); } } -bool DownloadFile::Open(const char* open_mode) { +bool DownloadFile::Open() { DCHECK(!full_path_.empty()); - file_ = file_util::OpenFile(full_path_, open_mode); - if (!file_) { - return false; + + // Create a new file steram if it is not provided. + if (!file_stream_.get()) { + file_stream_.reset(new net::FileStream); + if (file_stream_->Open(full_path_, + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_WRITE) != net::OK) { + file_stream_.reset(); + return false; + } } #if defined(OS_WIN) @@ -429,7 +443,7 @@ void DownloadFileManager::DownloadUrl( const GURL& url, const GURL& referrer, const std::string& referrer_charset, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter) { @@ -441,7 +455,7 @@ void DownloadFileManager::DownloadUrl( url, referrer, referrer_charset, - save_file_path, + save_info, render_process_host_id, render_view_id, request_context_getter)); @@ -526,7 +540,7 @@ void DownloadFileManager::OnDownloadUrl( const GURL& url, const GURL& referrer, const std::string& referrer_charset, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter) { @@ -537,7 +551,7 @@ void DownloadFileManager::OnDownloadUrl( resource_dispatcher_host_->BeginDownload(url, referrer, - save_file_path, + save_info, render_process_host_id, render_view_id, context); diff --git a/chrome/browser/download/download_file.h b/chrome/browser/download/download_file.h index 352f937..d4df8c4 100644 --- a/chrome/browser/download/download_file.h +++ b/chrome/browser/download/download_file.h @@ -49,11 +49,13 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/hash_tables.h" +#include "base/linked_ptr.h" #include "base/lock.h" #include "base/ref_counted.h" #include "base/timer.h" #include "chrome/browser/power_save_blocker.h" #include "googleurl/src/gurl.h" +#include "net/base/file_stream.h" namespace net { class IOBuffer; @@ -78,6 +80,16 @@ struct DownloadBuffer { std::vector<Contents> contents; }; +// DownloadSaveInfo ------------------------------------------------------------ + +// Holds the information about how to save a download file. +struct DownloadSaveInfo { + FilePath file_path; + linked_ptr<net::FileStream> file_stream; + + DownloadSaveInfo() { } +}; + // DownloadFile ---------------------------------------------------------------- // These objects live exclusively on the download thread and handle the writing @@ -111,18 +123,18 @@ class DownloadFile { int render_view_id() const { return render_view_id_; } int request_id() const { return request_id_; } bool path_renamed() const { return path_renamed_; } - bool in_progress() const { return file_ != NULL; } + bool in_progress() const { return file_stream_ != NULL; } void set_in_progress(bool in_progress) { in_progress_ = in_progress; } private: - // Open or Close the OS file handle. The file is opened in the constructor + // Open or Close the OS file stream. The stream is opened in the constructor // based on creation information passed to it, and automatically closed in // the destructor. void Close(); - bool Open(const char* open_mode); + bool Open(); - // OS file handle for writing - FILE* file_; + // OS file stream for writing + linked_ptr<net::FileStream> file_stream_; // Source URL for the file being downloaded. GURL source_url_; @@ -157,6 +169,9 @@ class DownloadFile { // RAII handle to keep the system from sleeping while we're downloading. PowerSaveBlocker dont_sleep_; + // The provider used to save the download data. + DownloadSaveInfo save_info_; + DISALLOW_COPY_AND_ASSIGN(DownloadFile); }; @@ -192,7 +207,7 @@ class DownloadFileManager void DownloadUrl(const GURL& url, const GURL& referrer, const std::string& referrer_charset, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter); @@ -201,7 +216,7 @@ class DownloadFileManager void OnDownloadUrl(const GURL& url, const GURL& referrer, const std::string& referrer_charset, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter); diff --git a/chrome/browser/download/download_manager.cc b/chrome/browser/download/download_manager.cc index 1248d94..a69703b 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -598,21 +598,22 @@ void DownloadManager::StartDownload(DownloadCreateInfo* info) { info->save_as = true; } - // Determine the proper path for a download, by either one of the following: - // 1) using the provided save file path. - // 2) using the default download directory. - // 3) prompting the user. - FilePath generated_name; - GenerateFileNameFromInfo(info, &generated_name); - if (!info->save_file_path.empty()) - info->suggested_path = info->save_file_path; - else if (info->save_as && !last_download_path_.empty()) - info->suggested_path = last_download_path_; - else - info->suggested_path = download_path(); - info->suggested_path = info->suggested_path.Append(generated_name); + if (info->save_info.file_path.empty()) { + // Determine the proper path for a download, by either one of the following: + // 1) using the default download directory. + // 2) prompting the user. + FilePath generated_name; + GenerateFileNameFromInfo(info, &generated_name); + if (info->save_as && !last_download_path_.empty()) + info->suggested_path = last_download_path_; + else + info->suggested_path = download_path(); + info->suggested_path = info->suggested_path.Append(generated_name); + } else { + info->suggested_path = info->save_info.file_path; + } - if (!info->save_as && info->save_file_path.empty()) { + if (!info->save_as && info->save_info.file_path.empty()) { // Downloads can be marked as dangerous for two reasons: // a) They have a dangerous-looking filename // b) They are an extension that is not from the gallery @@ -646,7 +647,10 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) { info->suggested_path = info->suggested_path.Append(filename); } - info->path_uniquifier = GetUniquePathNumber(info->suggested_path); + // Do not add the path uniquifier if we are saving to a specific path as in + // the drag-out case. + if (info->save_info.file_path.empty()) + info->path_uniquifier = GetUniquePathNumber(info->suggested_path); // If the download is deemed dangerous, we'll use a temporary name for it. if (info->is_dangerous) { @@ -676,7 +680,7 @@ void DownloadManager::CheckIfSuggestedPathExists(DownloadCreateInfo* info) { } } - if (!info->save_as) { + if (!info->save_as && info->save_info.file_path.empty()) { // Create an empty file at the suggested path so that we don't allocate the // same "non-existant" path to multiple downloads. // See: http://code.google.com/p/chromium/issues/detail?id=3662 @@ -743,7 +747,7 @@ void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info, info->is_dangerous, info->save_as, info->is_extension_install, - !info->save_file_path.empty()); + !info->save_info.file_path.empty()); download->set_manager(this); in_progress_[info->download_id] = download; } else { @@ -1204,7 +1208,7 @@ void DownloadManager::DownloadUrl(const GURL& url, file_manager_->DownloadUrl(url, referrer, referrer_charset, - FilePath(), + DownloadSaveInfo(), tab_contents->GetRenderProcessHost()->id(), tab_contents->render_view_host()->routing_id(), request_context_getter_); @@ -1213,13 +1217,13 @@ void DownloadManager::DownloadUrl(const GURL& url, void DownloadManager::DownloadUrlToFile(const GURL& url, const GURL& referrer, const std::string& referrer_charset, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, TabContents* tab_contents) { DCHECK(tab_contents); file_manager_->DownloadUrl(url, referrer, referrer_charset, - save_file_path, + save_info, tab_contents->GetRenderProcessHost()->id(), tab_contents->render_view_host()->routing_id(), request_context_getter_); diff --git a/chrome/browser/download/download_manager.h b/chrome/browser/download/download_manager.h index 4d7a709..3ab0a53 100644 --- a/chrome/browser/download/download_manager.h +++ b/chrome/browser/download/download_manager.h @@ -62,6 +62,7 @@ class Profile; class ResourceDispatcherHost; class URLRequestContextGetter; class TabContents; +struct DownloadSaveInfo; namespace base { class Thread; @@ -414,7 +415,7 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, void DownloadUrlToFile(const GURL& url, const GURL& referrer, const std::string& referrer_encoding, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, TabContents* tab_contents); // Allow objects to observe the download creation process. diff --git a/chrome/browser/download/drag_download_file.cc b/chrome/browser/download/drag_download_file.cc new file mode 100644 index 0000000..e55ccf5 --- /dev/null +++ b/chrome/browser/download/drag_download_file.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2009-2010 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/chrome_thread.h" +#include "chrome/browser/download/download_file.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/profile.h" +#include "chrome/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_MACOSX) + 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(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 (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::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_); +} + +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(); + + download_manager_->GetTemporaryDownloads(this, file_path_.DirName()); +} + +void DragDownloadFile::SetDownloads(std::vector<DownloadItem*>& downloads) { + AssertCurrentlyOnUIThread(); + + std::vector<DownloadItem*>::const_iterator it = downloads.begin(); + for (; it != downloads.end(); ++it) { + if (!download_item_observer_added_ && (*it)->url() == url_) { + download_item_observer_added_ = true; + (*it)->AddObserver(this); + } + } +} + +void DragDownloadFile::OnDownloadUpdated(DownloadItem* download) { + AssertCurrentlyOnUIThread(); + + if (download->state() == DownloadItem::CANCELLED) { + download->RemoveObserver(this); + download_manager_->RemoveObserver(this); + + DownloadCompleted(false); + } +} + +void DragDownloadFile::OnDownloadFileCompleted(DownloadItem* download) { + AssertCurrentlyOnUIThread(); + DCHECK(download->state() == DownloadItem::COMPLETE); + + download->RemoveObserver(this); + download_manager_->RemoveObserver(this); + + DownloadCompleted(true); +} + +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(ChromeThread::CurrentlyOn(ChromeThread::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 diff --git a/chrome/browser/download/drag_download_file.h b/chrome/browser/download/drag_download_file.h new file mode 100644 index 0000000..31064ec --- /dev/null +++ b/chrome/browser/download/drag_download_file.h @@ -0,0 +1,112 @@ +// Copyright (c) 2009-2010 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. + +#ifndef CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_H_ +#define CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_H_ + +#include "app/download_file_interface.h" +#include "base/file_path.h" +#include "base/linked_ptr.h" +#include "chrome/browser/download/download_file.h" +#include "chrome/browser/download/download_manager.h" +#include "googleurl/src/gurl.h" + +class TabContents; + +namespace net { +class FileSteram; +} + +class DragDownloadFile : public DownloadFileProvider, + public DownloadManager::Observer, + public DownloadItem::Observer { + public: + // On Windows, we need to download into a temporary file. Two threads are + // involved: background drag-and-drop thread and UI thread. + // The first parameter file_name_or_path should contain file name while the + // second parameter file_stream should be NULL. + // + // On MacOSX, we need to download into a file stream that has already been + // created. Only UI thread is involved. + // The file path and file stream should be provided as the first two + // parameters. + 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); + + // DownloadFileProvider methods. + // Called on drag-and-drop thread (Windows). + // Called on UI thread (MacOSX). + virtual bool Start(DownloadFileObserver* observer); + virtual void Stop(); +#if defined(OS_WIN) + virtual IStream* GetStream() { return NULL; } +#endif + + // DownloadManager::Observer methods. + // Called on UI thread. + virtual void ModelChanged(); + virtual void SetDownloads(std::vector<DownloadItem*>& downloads); + + // DownloadItem::Observer methods. + // Called on UI thread. + virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download); + virtual void OnDownloadOpened(DownloadItem* download) { } + + private: + // Called on drag-and-drop thread (Windows). + // Called on UI thread (Windows). + virtual ~DragDownloadFile(); + + // Called on drag-and-drop thread (Windows only). +#if defined(OS_WIN) + void StartNestedMessageLoop(); + void QuitNestedMessageLoop(); +#endif + + // Called on either drag-and-drop thread or UI thread (Windows). + // Called on UI thread (MacOSX). + void InitiateDownload(); + void DownloadCompleted(bool is_successful); + + // Helper methods to make sure we're in the correct thread. + void AssertCurrentlyOnDragThread(); + void AssertCurrentlyOnUIThread(); + + // Initialized on drag-and-drop thread. Accessed on either thread after that + // (Windows). + // Accessed on UI thread (MacOSX). + FilePath file_path_; + FilePath file_name_; + linked_ptr<net::FileStream> file_stream_; + GURL url_; + GURL referrer_; + std::string referrer_encoding_; + TabContents* tab_contents_; + MessageLoop* drag_message_loop_; + FilePath temp_dir_path_; + + // Accessed on drag-and-drop thread (Windows). + // Accessed on UI thread (MacOSX). + bool is_started_; + bool is_successful_; + scoped_refptr<DownloadFileObserver> observer_; + + // Accessed on drag-and-drop thread (Windows only). +#if defined(OS_WIN) + bool is_running_nested_message_loop_; +#endif + + // Access on UI thread. + DownloadManager* download_manager_; + bool download_item_observer_added_; + + DISALLOW_COPY_AND_ASSIGN(DragDownloadFile); +}; + +#endif // CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_H_ diff --git a/chrome/browser/download/drag_download_file_win.cc b/chrome/browser/download/drag_download_file_win.cc deleted file mode 100644 index 77a9112..0000000 --- a/chrome/browser/download/drag_download_file_win.cc +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2009 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_win.h" - -#include "base/file_util.h" -#include "base/message_loop.h" -#include "chrome/browser/chrome_thread.h" -#include "chrome/browser/download/download_manager.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/tab_contents/tab_contents.h" - -DragDownloadFile::DragDownloadFile( - const GURL& url, - const GURL& referrer, - const std::string& referrer_encoding, - TabContents* tab_contents) - : url_(url), - referrer_(referrer), - referrer_encoding_(referrer_encoding), - tab_contents_(tab_contents), - drag_message_loop_(MessageLoop::current()), - is_started_(false), - is_running_nested_message_loop_(false), - initiate_download_result_(false), - format_(0), - download_manager_(NULL), - download_item_observer_added_(false) { -} - -DragDownloadFile::~DragDownloadFile() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - // 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 (!dir_path_.empty()) { - if (!file_path_.empty()) - file_util::DeleteAfterReboot(file_path_); - file_util::DeleteAfterReboot(dir_path_); - } -#endif - - if (download_manager_) - download_manager_->RemoveObserver(this); -} - -bool DragDownloadFile::Start(OSExchangeData::DownloadFileObserver* observer, - int format) { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - if (is_started_) - return true; - is_started_ = true; - - DCHECK(!observer_.get()); - observer_ = observer; - format_ = format; - - if (!InitiateDownload()) - return false; - - // Wait till the download is fully initiated. - StartNestedMessageLoop(); - - return initiate_download_result_; -} - -void DragDownloadFile::Stop() { -} - -bool DragDownloadFile::InitiateDownload() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - // 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"), - &dir_path_)) - return false; - - // DownloadManager could only be invoked from the UI thread. - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, - &DragDownloadFile::OnInitiateDownload, - dir_path_)); - - return true; -} - -void DragDownloadFile::OnInitiateDownload(const FilePath& dir_path) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - download_manager_ = tab_contents_->profile()->GetDownloadManager(); - download_manager_->AddObserver(this); - - // Start the download. - download_manager_->DownloadUrlToFile(url_, - referrer_, - referrer_encoding_, - dir_path, - tab_contents_); -} - -void DragDownloadFile::InitiateDownloadSucceeded( - const std::vector<OSExchangeData::DownloadFileInfo*>& downloads) { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - // Notify the drag-and-drop observer about the file info. - DCHECK(observer_); - observer_->OnDataReady(format_, downloads); - - InitiateDownloadCompleted(true); -} - -void DragDownloadFile::InitiateDownloadFailed() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - InitiateDownloadCompleted(false); -} - -void DragDownloadFile::InitiateDownloadCompleted(bool result) { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - // Release the observer since we do not need it any more. - observer_ = NULL; - - initiate_download_result_ = result; - QuitNestedMessageLoopIfNeeded(); -} - -void DragDownloadFile::StartNestedMessageLoop() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - 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::QuitNestedMessageLoopIfNeeded() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - if (is_running_nested_message_loop_) { - is_running_nested_message_loop_ = false; - MessageLoop::current()->Quit(); - } -} - -void DragDownloadFile::ModelChanged() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - download_manager_->GetTemporaryDownloads(this, dir_path_); -} - -void DragDownloadFile::SetDownloads(std::vector<DownloadItem*>& downloads) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - std::vector<DownloadItem*>::const_iterator it = downloads.begin(); - for (; it != downloads.end(); ++it) { - if (!download_item_observer_added_ && (*it)->url() == url_) { - download_item_observer_added_ = true; - (*it)->AddObserver(this); - } - } -} - -void DragDownloadFile::OnDownloadUpdated(DownloadItem* download) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - if (download->state() == DownloadItem::CANCELLED) { - download->RemoveObserver(this); - download_manager_->RemoveObserver(this); - - drag_message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &DragDownloadFile::DownloadCancelled)); - } -} - -void DragDownloadFile::OnDownloadFileCompleted(DownloadItem* download) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - DCHECK(download->state() == DownloadItem::COMPLETE); - - download->RemoveObserver(this); - download_manager_->RemoveObserver(this); - - drag_message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &DragDownloadFile::DownloadCompleted, - download->full_path())); -} - -void DragDownloadFile::DownloadCancelled() { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - InitiateDownloadFailed(); -} - -void DragDownloadFile::DownloadCompleted(const FilePath& file_path) { - DCHECK(drag_message_loop_ == MessageLoop::current()); - - file_path_ = file_path; - - // The download has been successfully initiated. - std::vector<OSExchangeData::DownloadFileInfo*> downloads; - downloads.push_back( - new OSExchangeData::DownloadFileInfo(file_path, 0, NULL)); - InitiateDownloadSucceeded(downloads); -} diff --git a/chrome/browser/download/drag_download_file_win.h b/chrome/browser/download/drag_download_file_win.h deleted file mode 100644 index a1ef5ed..0000000 --- a/chrome/browser/download/drag_download_file_win.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2009 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. - -#ifndef CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_WIN_H_ -#define CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_WIN_H_ - -#include "app/os_exchange_data.h" -#include "base/file_path.h" -#include "chrome/browser/download/download_manager.h" -#include "googleurl/src/gurl.h" - -class TabContents; - -class DragDownloadFile : public OSExchangeData::DownloadFileProvider, - public DownloadManager::Observer, - public DownloadItem::Observer { - public: - DragDownloadFile(const GURL& url, - const GURL& referrer, - const std::string& referrer_encoding, - TabContents* tab_contents); - - // OSExchangeData::DownloadFileProvider methods. - // Called on drag-and-drop thread. - virtual bool Start(OSExchangeData::DownloadFileObserver* observer, - int format); - virtual void Stop(); - - // DownloadManager::Observer methods. - // Called on UI thread. - virtual void ModelChanged(); - virtual void SetDownloads(std::vector<DownloadItem*>& downloads); - - // DownloadItem::Observer methods. - // Called on UI thread. - virtual void OnDownloadUpdated(DownloadItem* download); - virtual void OnDownloadFileCompleted(DownloadItem* download); - virtual void OnDownloadOpened(DownloadItem* download) { } - - private: - // Called on drag-and-drop thread. - virtual ~DragDownloadFile(); - - bool InitiateDownload(); - void InitiateDownloadSucceeded( - const std::vector<OSExchangeData::DownloadFileInfo*>& downloads); - void InitiateDownloadFailed(); - void InitiateDownloadCompleted(bool result); - - void DownloadCancelled(); - void DownloadCompleted(const FilePath& file_path); - - void StartNestedMessageLoop(); - void QuitNestedMessageLoopIfNeeded(); - - // Called on UI thread. - void OnInitiateDownload(const FilePath& dir_path); - void CheckDownloadStatus(DownloadItem* download); - - // Initialized on drag-and-drop thread. Can be accessed on either thread. - GURL url_; - GURL referrer_; - std::string referrer_encoding_; - TabContents* tab_contents_; - MessageLoop* drag_message_loop_; - - // Accessed on drag-and-drop thread. - bool is_started_; - bool is_running_nested_message_loop_; - bool initiate_download_result_; - scoped_refptr<OSExchangeData::DownloadFileObserver> observer_; - int format_; - FilePath dir_path_; - FilePath file_path_; - - // Access on UI thread. - DownloadManager* download_manager_; - bool download_item_observer_added_; - - DISALLOW_COPY_AND_ASSIGN(DragDownloadFile); -}; - -#endif // CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_FILE_WIN_H_ diff --git a/chrome/browser/download/drag_download_util.cc b/chrome/browser/download/drag_download_util.cc new file mode 100644 index 0000000..ed34e70 --- /dev/null +++ b/chrome/browser/download/drag_download_util.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2010 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_util.h" + +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "googleurl/src/gurl.h" + +namespace drag_download_util { + +bool ParseDownloadMetadata(const string16& metadata, + string16* mime_type, + FilePath* file_name, + GURL* url) { + const char16 separator = L':'; + + size_t mime_type_end_pos = metadata.find(separator); + if (mime_type_end_pos == string16::npos) + return false; + + size_t file_name_end_pos = metadata.find(separator, mime_type_end_pos + 1); + if (file_name_end_pos == string16::npos) + return false; + + GURL parsed_url = GURL(metadata.substr(file_name_end_pos + 1)); + if (!parsed_url.is_valid()) + return false; + + if (mime_type) + *mime_type = metadata.substr(0, mime_type_end_pos); + if (file_name) { + string16 file_name_str = metadata.substr( + mime_type_end_pos + 1, file_name_end_pos - mime_type_end_pos - 1); +#if defined(OS_WIN) + *file_name = FilePath(file_name_str); +#else + *file_name = FilePath(UTF16ToUTF8(file_name_str)); +#endif + } + if (url) + *url = parsed_url; + + return true; +} + +} // namespace drag_download_util diff --git a/chrome/browser/download/drag_download_util.h b/chrome/browser/download/drag_download_util.h new file mode 100644 index 0000000..7f2a853 --- /dev/null +++ b/chrome/browser/download/drag_download_util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ +#define CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ + +#include "base/basictypes.h" +#include "base/file_path.h" + +class GURL; + +namespace drag_download_util { + +// Parse the download metadata set in DataTransfer.setData. The metadata +// consists of a set of the following values separated by ":" +// * MIME type +// * File name +// * URL +// For example, we can have +// text/plain:example.txt:http://example.com/example.txt +bool ParseDownloadMetadata(const string16& metadata, + string16* mime_type, + FilePath* file_name, + GURL* url); + +} // namespace drag_download_util + +#endif // CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ diff --git a/chrome/browser/history/download_types.h b/chrome/browser/history/download_types.h index 7025b1d..50c7e26 100644 --- a/chrome/browser/history/download_types.h +++ b/chrome/browser/history/download_types.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/time.h" +#include "chrome/browser/download/download_file.h" #include "googleurl/src/gurl.h" // Used for informing the download database of a new download, where we don't @@ -87,8 +88,8 @@ struct DownloadCreateInfo { // The charset of the referring page where the download request comes from. // It's used to construct a suggested filename. std::string referrer_charset; - // The file path to save to. - FilePath save_file_path; + // The download file save info. + DownloadSaveInfo save_info; }; #endif // CHROME_BROWSER_HISTORY_DOWNLOAD_TYPES_H_ diff --git a/chrome/browser/renderer_host/download_resource_handler.cc b/chrome/browser/renderer_host/download_resource_handler.cc index e414587..1bbb757 100644 --- a/chrome/browser/renderer_host/download_resource_handler.cc +++ b/chrome/browser/renderer_host/download_resource_handler.cc @@ -13,15 +13,16 @@ #include "net/base/io_buffer.h" #include "net/url_request/url_request_context.h" -DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, - int render_process_host_id, - int render_view_id, - int request_id, - const GURL& url, - DownloadFileManager* manager, - URLRequest* request, - bool save_as, - const FilePath& save_file_path) +DownloadResourceHandler::DownloadResourceHandler( + ResourceDispatcherHost* rdh, + int render_process_host_id, + int render_view_id, + int request_id, + const GURL& url, + DownloadFileManager* manager, + URLRequest* request, + bool save_as, + const DownloadSaveInfo& save_info) : download_id_(-1), global_id_(render_process_host_id, request_id), render_view_id_(render_view_id), @@ -30,7 +31,7 @@ DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, download_manager_(manager), request_(request), save_as_(save_as), - save_file_path_(save_file_path), + save_info_(save_info), buffer_(new DownloadBuffer), rdh_(rdh), is_paused_(false) { @@ -69,10 +70,10 @@ bool DownloadResourceHandler::OnResponseStarted(int request_id, info->request_id = global_id_.request_id; info->content_disposition = content_disposition_; info->mime_type = response->response_head.mime_type; - info->save_as = save_as_ && save_file_path_.empty(); + info->save_as = save_as_ && save_info_.file_path.empty(); info->is_dangerous = false; info->referrer_charset = request_->context()->referrer_charset(); - info->save_file_path = save_file_path_; + info->save_info = save_info_; ChromeThread::PostTask( ChromeThread::FILE, FROM_HERE, NewRunnableMethod( diff --git a/chrome/browser/renderer_host/download_resource_handler.h b/chrome/browser/renderer_host/download_resource_handler.h index 59b4cb4..bb8d9cd 100644 --- a/chrome/browser/renderer_host/download_resource_handler.h +++ b/chrome/browser/renderer_host/download_resource_handler.h @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/timer.h" +#include "chrome/browser/download/download_file.h" #include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_handler.h" @@ -28,7 +29,7 @@ class DownloadResourceHandler : public ResourceHandler { DownloadFileManager* manager, URLRequest* request, bool save_as, - const FilePath& save_file_path); + const DownloadSaveInfo& save_info); // Not needed, as this event handler ought to be the final resource. bool OnRequestRedirected(int request_id, const GURL& url, @@ -72,7 +73,7 @@ class DownloadResourceHandler : public ResourceHandler { DownloadFileManager* download_manager_; URLRequest* request_; bool save_as_; // Request was initiated via "Save As" by the user. - FilePath save_file_path_; + DownloadSaveInfo save_info_; DownloadBuffer* buffer_; ResourceDispatcherHost* rdh_; bool is_paused_; diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.cc b/chrome/browser/renderer_host/download_throttling_resource_handler.cc index 4b02880..87314dc 100644 --- a/chrome/browser/renderer_host/download_throttling_resource_handler.cc +++ b/chrome/browser/renderer_host/download_throttling_resource_handler.cc @@ -130,7 +130,6 @@ void DownloadThrottlingResourceHandler::CancelDownload() { void DownloadThrottlingResourceHandler::ContinueDownload() { DCHECK(!download_handler_.get()); - FilePath save_file_path; download_handler_ = new DownloadResourceHandler(host_, render_process_host_id_, @@ -140,7 +139,7 @@ void DownloadThrottlingResourceHandler::ContinueDownload() { host_->download_file_manager(), request_, false, - save_file_path); + DownloadSaveInfo()); if (response_.get()) download_handler_->OnResponseStarted(request_id_, response_.get()); diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 971888c..b92c6a9 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -560,12 +560,13 @@ void ResourceDispatcherHost::OnClosePageACK( } // We are explicitly forcing the download of 'url'. -void ResourceDispatcherHost::BeginDownload(const GURL& url, - const GURL& referrer, - const FilePath& save_file_path, - int child_id, - int route_id, - URLRequestContext* request_context) { +void ResourceDispatcherHost::BeginDownload( + const GURL& url, + const GURL& referrer, + const DownloadSaveInfo& save_info, + int child_id, + int route_id, + URLRequestContext* request_context) { if (is_shutdown_) return; @@ -593,7 +594,7 @@ void ResourceDispatcherHost::BeginDownload(const GURL& url, download_file_manager_.get(), request, true, - save_file_path); + save_info); if (safe_browsing_->enabled() && safe_browsing_->CanCheckUrl(url)) { diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h index 4e10dae..c410f3a 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -42,6 +42,7 @@ class SSLClientAuthHandler; class UserScriptListener; class URLRequestContext; class WebKitThread; +struct DownloadSaveInfo; struct GlobalRequestID; struct ViewHostMsg_Resource_Request; struct ViewMsg_ClosePage_Params; @@ -107,7 +108,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { // request from the renderer or another child process). void BeginDownload(const GURL& url, const GURL& referrer, - const FilePath& save_file_path, + const DownloadSaveInfo& save_info, int process_unique_id, int route_id, URLRequestContext* request_context); diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 2c04259..ae3ddd4 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -15,6 +15,7 @@ #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/chrome_plugin_browsing_context.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/download/download_file.h" #include "chrome/browser/extensions/extension_file_util.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/host_zoom_map.h" @@ -778,10 +779,9 @@ void ResourceMessageFilter::OnDownloadUrl(const IPC::Message& message, const GURL& url, const GURL& referrer) { URLRequestContext* context = request_context_->GetURLRequestContext(); - FilePath save_file_path; resource_dispatcher_host_->BeginDownload(url, referrer, - save_file_path, + DownloadSaveInfo(), id(), message.routing_id(), context); diff --git a/chrome/browser/views/tab_contents/tab_contents_drag_win.cc b/chrome/browser/views/tab_contents/tab_contents_drag_win.cc index 013dd40..59ddfd3 100644 --- a/chrome/browser/views/tab_contents/tab_contents_drag_win.cc +++ b/chrome/browser/views/tab_contents/tab_contents_drag_win.cc @@ -10,10 +10,12 @@ #include "base/message_loop.h" #include "base/task.h" #include "base/thread.h" +#include "base/utf_string_conversions.h" #include "base/win_util.h" #include "chrome/browser/bookmarks/bookmark_drag_data.h" #include "chrome/browser/chrome_thread.h" -#include "chrome/browser/download/drag_download_file_win.h" +#include "chrome/browser/download/drag_download_file.h" +#include "chrome/browser/download/drag_download_util.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/web_drag_source_win.h" @@ -112,7 +114,7 @@ void TabContentsDragWin::StartDragging(const WebDropData& drop_data, const std::string& page_encoding = view_->tab_contents()->encoding(); // If it is not drag-out, do the drag-and-drop in the current UI thread. - if (!drop_data.download_url.is_valid()) { + if (drop_data.download_metadata.empty()) { DoDragging(drop_data, ops, page_url, page_encoding); EndDragging(false); return; @@ -171,17 +173,38 @@ void TabContentsDragWin::PrepareDragForDownload( OSExchangeData* data, const GURL& page_url, const std::string& page_encoding) { + // Parse the download metadata. + string16 mime_type; + FilePath file_name; + GURL download_url; + if (!drag_download_util::ParseDownloadMetadata(drop_data.download_metadata, + &mime_type, + &file_name, + &download_url)) + return; + + // Generate the download filename. + std::string content_disposition = + "attachment; filename=" + UTF16ToUTF8(file_name.value()); + FilePath generated_file_name; + DownloadManager::GenerateFileName(download_url, + content_disposition, + std::string(), + UTF16ToUTF8(mime_type), + &generated_file_name); + // Provide the data as file (CF_HDROP). A temporary download file with the // Zone.Identifier ADS (Alternate Data Stream) attached will be created. + linked_ptr<net::FileStream> empty_file_stream; scoped_refptr<DragDownloadFile> download_file = - new DragDownloadFile(drop_data.download_url, + new DragDownloadFile(generated_file_name, + empty_file_stream, + download_url, page_url, page_encoding, view_->tab_contents()); - OSExchangeData::DownloadFileInfo* file_download = - new OSExchangeData::DownloadFileInfo(FilePath(), - 0, - download_file.get()); + OSExchangeData::DownloadFileInfo file_download(FilePath(), + download_file.get()); data->SetDownloadFileInfo(file_download); // Enable asynchronous operation. @@ -238,7 +261,7 @@ void TabContentsDragWin::DoDragging(const WebDropData& drop_data, // TODO(tc): Generate an appropriate drag image. - if (drop_data.download_url.is_valid()) { + if (!drop_data.download_metadata.empty()) { PrepareDragForDownload(drop_data, &data, page_url, page_encoding); // Set the observer. |