diff options
author | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 18:49:58 +0000 |
---|---|---|
committer | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 18:49:58 +0000 |
commit | 6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1 (patch) | |
tree | 8e833c393312e866250077c15bc1d90464fe99d7 | |
parent | 963dfb5a05c5b0e3fa8ed74d803f01cb10fd455e (diff) | |
download | chromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.zip chromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.tar.gz chromium_src-6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1.tar.bz2 |
Support dragging a virtual file out of the browser.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/351029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36378 0039d316-1c4b-4281-b951-d872f2087c98
48 files changed, 1398 insertions, 205 deletions
diff --git a/app/clipboard/clipboard_util_win.cc b/app/clipboard/clipboard_util_win.cc index 38927bb..6b09074 100644 --- a/app/clipboard/clipboard_util_win.cc +++ b/app/clipboard/clipboard_util_win.cc @@ -143,7 +143,7 @@ FORMATETC* ClipboardUtil::GetFileContentFormatZero() { FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() { static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format"); - static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL}; + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; return &format; } diff --git a/app/os_exchange_data.cc b/app/os_exchange_data.cc index 8f647c2..dc57cd1 100644 --- a/app/os_exchange_data.cc +++ b/app/os_exchange_data.cc @@ -128,4 +128,8 @@ bool OSExchangeData::GetFileContents(std::wstring* filename, bool OSExchangeData::GetHtml(std::wstring* html, GURL* base_url) const { return provider_->GetHtml(html, base_url); } + +void OSExchangeData::SetDownloadFileInfo(DownloadFileInfo* download) { + return provider_->SetDownloadFileInfo(download); +} #endif diff --git a/app/os_exchange_data.h b/app/os_exchange_data.h index 7efea43..5677409 100644 --- a/app/os_exchange_data.h +++ b/app/os_exchange_data.h @@ -18,6 +18,8 @@ #endif #include "base/basictypes.h" +#include "base/file_path.h" +#include "base/ref_counted.h" #include "base/scoped_ptr.h" class GURL; @@ -59,6 +61,48 @@ class OSExchangeData { #endif }; + struct DownloadFileInfo; + + // Defines the interface to observe the status of file download. + class DownloadFileObserver : public base::RefCounted<DownloadFileObserver> { + public: + // The caller is responsible to free the DownloadFileInfo objects passed + // in the vector parameter. + virtual void OnDataReady( + int format, + const std::vector<DownloadFileInfo*>& downloads) = 0; + + protected: + friend class base::RefCounted<DownloadFileObserver>; + virtual ~DownloadFileObserver() {} + }; + + // Defines the interface to control how a file is downloaded. + class DownloadFileProvider : + public base::RefCountedThreadSafe<DownloadFileProvider> { + public: + virtual bool Start(DownloadFileObserver* observer, int format) = 0; + virtual void Stop() = 0; + + protected: + friend class base::RefCountedThreadSafe<DownloadFileProvider>; + virtual ~DownloadFileProvider() {} + }; + + // Encapsulates the info about a file to be downloaded. + struct DownloadFileInfo { + FilePath filename; + uint64 size; + scoped_refptr<DownloadFileProvider> downloader; + + DownloadFileInfo(const FilePath& filename, + uint64 size, + DownloadFileProvider* downloader) + : filename(filename), + size(size), + downloader(downloader) {} + }; + // Provider defines the platform specific part of OSExchangeData that // interacts with the native system. class Provider { @@ -91,6 +135,7 @@ class OSExchangeData { virtual bool GetHtml(std::wstring* html, GURL* base_url) const = 0; virtual bool HasFileContents() const = 0; virtual bool HasHtml() const = 0; + virtual void SetDownloadFileInfo(DownloadFileInfo* download) = 0; #endif }; @@ -164,6 +209,9 @@ class OSExchangeData { bool GetFileContents(std::wstring* filename, std::string* file_contents) const; bool GetHtml(std::wstring* html, GURL* base_url) const; + + // Adds a download file with full path (CF_HDROP). + void SetDownloadFileInfo(DownloadFileInfo* download); #endif private: diff --git a/app/os_exchange_data_provider_win.cc b/app/os_exchange_data_provider_win.cc index 9efa5ee..dc28597 100644 --- a/app/os_exchange_data_provider_win.cc +++ b/app/os_exchange_data_provider_win.cc @@ -29,6 +29,8 @@ static void GetInternetShortcutFileContents(const GURL& url, std::string* data); static void CreateValidFileNameFromTitle(const GURL& url, const std::wstring& title, std::wstring* validated); +// Creates a new STGMEDIUM object to hold a file. +static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path); // Creates a File Descriptor for the creation of a file to the given URL and // returns a handle to it. static STGMEDIUM* GetStorageForFileDescriptor( @@ -226,12 +228,26 @@ bool OSExchangeDataProviderWin::GetPlainTextURL(IDataObject* source, } // static +DataObjectImpl* OSExchangeDataProviderWin::GetDataObjectImpl( + const OSExchangeData& data) { + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())-> + data_.get(); +} + +// static IDataObject* OSExchangeDataProviderWin::GetIDataObject( const OSExchangeData& data) { - return static_cast<const OSExchangeDataProviderWin&>(data.provider()). + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())-> data_object(); } +// static +IAsyncOperation* OSExchangeDataProviderWin::GetIAsyncOperation( + const OSExchangeData& data) { + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())-> + async_operation(); +} + OSExchangeDataProviderWin::OSExchangeDataProviderWin(IDataObject* source) : data_(new DataObjectImpl()), source_object_(source) { @@ -300,28 +316,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, } void OSExchangeDataProviderWin::SetFilename(const std::wstring& full_path) { - const size_t drop_size = sizeof(DROPFILES); - const size_t bytes = drop_size + (full_path.length() + 2) * sizeof(wchar_t); - HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes); - if (!hdata) - return; - - ScopedHGlobal<DROPFILES> locked_mem(hdata); - DROPFILES* drop_files = locked_mem.get(); - drop_files->pFiles = sizeof(DROPFILES); - drop_files->fWide = TRUE; - wchar_t* data = reinterpret_cast<wchar_t*>((BYTE*)(drop_files) + drop_size); - const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t); - memcpy(data, full_path.c_str(), copy_size); - data[full_path.length() + 1] = L'\0'; // Double NULL - - // Set up the STGMEDIUM - STGMEDIUM* storage = new STGMEDIUM; - storage->tymed = TYMED_HGLOBAL; - storage->hGlobal = drop_files; - storage->pUnkForRelease = NULL; - - // Set up the StoredDataInfo + STGMEDIUM* storage = GetStorageForFileName(full_path); DataObjectImpl::StoredDataInfo* info = new DataObjectImpl::StoredDataInfo(CF_HDROP, storage); data_->contents_.push_back(info); @@ -347,7 +342,7 @@ void OSExchangeDataProviderWin::SetFileContents( // Add CFSTR_FILECONTENTS storage = GetStorageForBytes(file_contents.data(), file_contents.length()); data_->contents_.push_back(new DataObjectImpl::StoredDataInfo( - ClipboardUtil::GetFileContentFormatZero()->cfFormat, storage)); + ClipboardUtil::GetFileContentFormatZero(), storage)); } void OSExchangeDataProviderWin::SetHtml(const std::wstring& html, @@ -459,6 +454,21 @@ bool OSExchangeDataProviderWin::HasCustomFormat(CLIPFORMAT format) const { return (source_object_->QueryGetData(&format_etc) == S_OK); } +void OSExchangeDataProviderWin::SetDownloadFileInfo( + OSExchangeData::DownloadFileInfo* download) { + // If the filename is not provided, set stoarge to NULL to indicate that + // the delay rendering will be used. + STGMEDIUM* storage = NULL; + if (!download->filename.empty()) + storage = GetStorageForFileName(download->filename.value()); + + // Add CF_HDROP. + DataObjectImpl::StoredDataInfo* info = new DataObjectImpl::StoredDataInfo( + ClipboardUtil::GetCFHDropFormat()->cfFormat, storage); + info->downloads.push_back(download); + data_->contents_.push_back(info); +} + /////////////////////////////////////////////////////////////////////////////// // DataObjectImpl, IDataObject implementation: @@ -533,19 +543,131 @@ static void DuplicateMedium(CLIPFORMAT source_clipformat, destination->pUnkForRelease->AddRef(); } -DataObjectImpl::DataObjectImpl() : ref_count_(0) { - STLDeleteContainerPointers(contents_.begin(), contents_.end()); +DataObjectImpl::DataObjectImpl() + : is_aborting_(false), + in_async_mode_(false), + async_operation_started_(false), + observer_(NULL) { } DataObjectImpl::~DataObjectImpl() { + StopDownloads(); STLDeleteContainerPointers(contents_.begin(), contents_.end()); + if (observer_) + observer_->OnDataObjectDisposed(); +} + +void DataObjectImpl::StopDownloads() { + for (StoredData::iterator iter = contents_.begin(); + iter != contents_.end(); ++iter) { + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) { + if ((*iter)->downloads[i]->downloader) { + (*iter)->downloads[i]->downloader->Stop(); + (*iter)->downloads[i]->downloader = 0; + } + delete (*iter)->downloads[i]; + } + (*iter)->downloads.clear(); + } +} + +void DataObjectImpl::OnDataReady( + int format, + const std::vector<OSExchangeData::DownloadFileInfo*>& downloads) { + // Find and update the data corresponding to the format. + CLIPFORMAT clip_format = static_cast<CLIPFORMAT>(format); + DCHECK(clip_format == ClipboardUtil::GetCFHDropFormat()->cfFormat); + DataObjectImpl::StoredData::iterator iter = contents_.begin(); + for (; iter != contents_.end(); ++iter) { + if ((*iter)->format_etc.cfFormat == clip_format) { + // Update the downloads. + DCHECK(downloads.size() == (*iter)->downloads.size()); + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) { + OSExchangeData::DownloadFileInfo* old_download = (*iter)->downloads[i]; + (*iter)->downloads[i] = downloads[i]; + (*iter)->downloads[i]->downloader = old_download->downloader; + delete old_download; + } + + // Release the old storage. + if ((*iter)->owns_medium) { + ReleaseStgMedium((*iter)->medium); + delete (*iter)->medium; + } + + // Update the storage. + (*iter)->owns_medium = true; + (*iter)->medium = GetStorageForFileName(downloads[0]->filename.value()); + + break; + } + } + DCHECK(iter != contents_.end()); } HRESULT DataObjectImpl::GetData(FORMATETC* format_etc, STGMEDIUM* medium) { - StoredData::const_iterator iter = contents_.begin(); + if (is_aborting_) + return DV_E_FORMATETC; + + StoredData::iterator iter = contents_.begin(); while (iter != contents_.end()) { - if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) { - DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium); + if ((*iter)->format_etc.cfFormat == format_etc->cfFormat && + (*iter)->format_etc.lindex == format_etc->lindex && + ((*iter)->format_etc.tymed & format_etc->tymed)) { + // If medium is NULL, delay-rendering will be used. + if ((*iter)->medium) { + DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium); + } else { + // Check if the left button is down. + bool is_left_button_down = (GetKeyState(VK_LBUTTON) & 0x8000) != 0; + + bool wait_for_data = false; + if ((*iter)->in_delay_rendering) { + // Make sure the left button is up. Sometimes the drop target, like + // Shell, might be too aggresive in calling GetData when the left + // button is not released. + if (is_left_button_down) + return DV_E_FORMATETC; + + wait_for_data = true; + } else { + // If the left button is up and the target has not requested the data + // yet, it probably means that the target does not support delay- + // rendering. So instead, we wait for the data. + if (is_left_button_down) { + (*iter)->in_delay_rendering = true; + memset(medium, 0, sizeof(STGMEDIUM)); + } else { + wait_for_data = true; + } + } + + if (wait_for_data) { + // Notify the observer we start waiting for the data. This gives + // an observer a chance to end the drag and drop. + if (observer_) + observer_->OnWaitForData(); + + // Now we can start the downloads. Each download will wait till the + // necessary data is ready and then return the control. + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) { + if ((*iter)->downloads[i]->downloader) { + if (!(*iter)->downloads[i]->downloader->Start( + this, format_etc->cfFormat)) { + // If any of the download fails to start, abort the whole + // process. + is_aborting_ = true; + StopDownloads(); + return DV_E_FORMATETC; + } + } + } + + // The stored data should have been updated with the final version. + // So we just need to call this function again to retrieve it. + return GetData(format_etc, medium); + } + } return S_OK; } ++iter; @@ -619,13 +741,46 @@ HRESULT DataObjectImpl::EnumDAdvise(IEnumSTATDATA** enumerator) { } /////////////////////////////////////////////////////////////////////////////// +// DataObjectImpl, IAsyncOperation implementation: + +HRESULT DataObjectImpl::EndOperation( + HRESULT result, IBindCtx* reserved, DWORD effects) { + async_operation_started_ = false; + return S_OK; +} + +HRESULT DataObjectImpl::GetAsyncMode(BOOL* is_op_async) { + *is_op_async = in_async_mode_ ? TRUE : FALSE; + return S_OK; +} + +HRESULT DataObjectImpl::InOperation(BOOL* in_async_op) { + *in_async_op = async_operation_started_ ? TRUE : FALSE; + return S_OK; +} + +HRESULT DataObjectImpl::SetAsyncMode(BOOL do_op_async) { + in_async_mode_ = (do_op_async == TRUE); + return S_OK; +} + +HRESULT DataObjectImpl::StartOperation(IBindCtx* reserved) { + async_operation_started_ = true; + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// // DataObjectImpl, IUnknown implementation: HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) { - *object = NULL; - if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDataObject)) { - *object = this; + if (!object) + return E_POINTER; + if (IsEqualIID(iid, IID_IDataObject) || IsEqualIID(iid, IID_IUnknown)) { + *object = static_cast<IDataObject*>(this); + } else if (in_async_mode_ && IsEqualIID(iid, IID_IAsyncOperation)) { + *object = static_cast<IAsyncOperation*>(this); } else { + *object = NULL; return E_NOINTERFACE; } AddRef(); @@ -633,16 +788,13 @@ HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) { } ULONG DataObjectImpl::AddRef() { - return InterlockedIncrement(&ref_count_); + base::RefCounted<OSExchangeData::DownloadFileObserver>::AddRef(); + return 0; } ULONG DataObjectImpl::Release() { - if (InterlockedDecrement(&ref_count_) == 0) { - ULONG copied_refcnt = ref_count_; - delete this; - return copied_refcnt; - } - return ref_count_; + base::RefCounted<OSExchangeData::DownloadFileObserver>::Release(); + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -721,6 +873,29 @@ static void CreateValidFileNameFromTitle(const GURL& url, *validated += extension; } +static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path) { + const size_t kDropSize = sizeof(DROPFILES); + const size_t kTotalBytes = + kDropSize + (full_path.length() + 2) * sizeof(wchar_t); + HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes); + + ScopedHGlobal<DROPFILES> locked_mem(hdata); + DROPFILES* drop_files = locked_mem.get(); + drop_files->pFiles = sizeof(DROPFILES); + drop_files->fWide = TRUE; + wchar_t* data = reinterpret_cast<wchar_t*>( + reinterpret_cast<BYTE*>(drop_files) + kDropSize); + const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t); + memcpy(data, full_path.c_str(), copy_size); + data[full_path.length() + 1] = L'\0'; // Double NULL + + STGMEDIUM* storage = new STGMEDIUM; + storage->tymed = TYMED_HGLOBAL; + storage->hGlobal = drop_files; + storage->pUnkForRelease = NULL; + return storage; +} + static STGMEDIUM* GetStorageForFileDescriptor( const std::wstring& valid_file_name) { DCHECK(!valid_file_name.empty() && valid_file_name.size() + 1 <= MAX_PATH); diff --git a/app/os_exchange_data_provider_win.h b/app/os_exchange_data_provider_win.h index 42b11e2..4bd0d80 100644 --- a/app/os_exchange_data_provider_win.h +++ b/app/os_exchange_data_provider_win.h @@ -6,15 +6,33 @@ #define APP_OS_EXCHANGE_DATA_PROVIDER_WIN_H_ #include <objidl.h> +#include <shlobj.h> #include <string> #include "app/os_exchange_data.h" #include "base/scoped_comptr_win.h" -class DataObjectImpl : public IDataObject { +class DataObjectImpl : public OSExchangeData::DownloadFileObserver, + public IDataObject, + public IAsyncOperation { public: + class Observer { + public: + virtual void OnWaitForData() = 0; + virtual void OnDataObjectDisposed() = 0; + protected: + virtual ~Observer() { } + }; + DataObjectImpl(); - ~DataObjectImpl(); + + // Accessors. + void set_observer(Observer* observer) { observer_ = observer; } + + // DownloadFileObserver implementation: + virtual void OnDataReady( + int format, + const std::vector<OSExchangeData::DownloadFileInfo*>& downloads); // IDataObject implementation: HRESULT __stdcall GetData(FORMATETC* format_etc, STGMEDIUM* medium); @@ -31,6 +49,14 @@ class DataObjectImpl : public IDataObject { HRESULT __stdcall DUnadvise(DWORD connection); HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** enumerator); + // IAsyncOperation implementation: + HRESULT __stdcall EndOperation( + HRESULT result, IBindCtx* reserved, DWORD effects); + HRESULT __stdcall GetAsyncMode(BOOL* is_op_async); + HRESULT __stdcall InOperation(BOOL* in_async_op); + HRESULT __stdcall SetAsyncMode(BOOL do_op_async); + HRESULT __stdcall StartOperation(IBindCtx* reserved); + // IUnknown implementation: HRESULT __stdcall QueryInterface(const IID& iid, void** object); ULONG __stdcall AddRef(); @@ -41,22 +67,34 @@ class DataObjectImpl : public IDataObject { friend class FormatEtcEnumerator; friend class OSExchangeDataProviderWin; + virtual ~DataObjectImpl(); + + void StopDownloads(); + // Our internal representation of stored data & type info. struct StoredDataInfo { FORMATETC format_etc; STGMEDIUM* medium; bool owns_medium; + bool in_delay_rendering; + std::vector<OSExchangeData::DownloadFileInfo*> downloads; - StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* a_medium) { + StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* medium) + : medium(medium), + owns_medium(true), + in_delay_rendering(false) { format_etc.cfFormat = cf; format_etc.dwAspect = DVASPECT_CONTENT; format_etc.lindex = -1; format_etc.ptd = NULL; - format_etc.tymed = a_medium->tymed; - - owns_medium = true; + format_etc.tymed = medium ? medium->tymed : TYMED_HGLOBAL; + } - medium = a_medium; + StoredDataInfo(FORMATETC* format_etc, STGMEDIUM* medium) + : format_etc(*format_etc), + medium(medium), + owns_medium(true), + in_delay_rendering(false) { } ~StoredDataInfo() { @@ -64,6 +102,11 @@ class DataObjectImpl : public IDataObject { ReleaseStgMedium(medium); delete medium; } + for (size_t i = 0; i < downloads.size(); ++i) { + if (downloads[i]->downloader) + downloads[i]->downloader->Stop(); + } + downloads.clear(); } }; @@ -72,7 +115,10 @@ class DataObjectImpl : public IDataObject { ScopedComPtr<IDataObject> source_object_; - LONG ref_count_; + bool is_aborting_; + bool in_async_mode_; + bool async_operation_started_; + Observer* observer_; }; class OSExchangeDataProviderWin : public OSExchangeData::Provider { @@ -84,7 +130,9 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider { // that url. static bool GetPlainTextURL(IDataObject* source, GURL* url); + static DataObjectImpl* GetDataObjectImpl(const OSExchangeData& data); static IDataObject* GetIDataObject(const OSExchangeData& data); + static IAsyncOperation* GetIAsyncOperation(const OSExchangeData& data); explicit OSExchangeDataProviderWin(IDataObject* source); OSExchangeDataProviderWin(); @@ -92,6 +140,7 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider { virtual ~OSExchangeDataProviderWin(); IDataObject* data_object() const { return data_.get(); } + IAsyncOperation* async_operation() const { return data_.get(); } // OSExchangeData::Provider methods. virtual void SetString(const std::wstring& data); @@ -117,6 +166,8 @@ class OSExchangeDataProviderWin : public OSExchangeData::Provider { virtual bool HasFileContents() const; virtual bool HasHtml() const; virtual bool HasCustomFormat(OSExchangeData::CustomFormat format) const; + virtual void SetDownloadFileInfo( + OSExchangeData::DownloadFileInfo* download_info); private: scoped_refptr<DataObjectImpl> data_; diff --git a/app/os_exchange_data_win_unittest.cc b/app/os_exchange_data_win_unittest.cc index a237a2f..f4ec8fb 100644 --- a/app/os_exchange_data_win_unittest.cc +++ b/app/os_exchange_data_win_unittest.cc @@ -270,7 +270,7 @@ TEST(OSExchangeDataTest, TestURLExchangeFormats) { CLIPFORMAT cfstr_file_contents = RegisterClipboardFormat(CFSTR_FILECONTENTS); FORMATETC format_etc = - { cfstr_file_contents, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + { cfstr_file_contents, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL }; EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc)); STGMEDIUM medium; diff --git a/base/base_drag_source.cc b/base/base_drag_source.cc index bc81395..9b335a7 100644 --- a/base/base_drag_source.cc +++ b/base/base_drag_source.cc @@ -7,7 +7,7 @@ /////////////////////////////////////////////////////////////////////////////// // BaseDragSource, public: -BaseDragSource::BaseDragSource() : ref_count_(0), cancel_drag_(false) { +BaseDragSource::BaseDragSource() : cancel_drag_(false) { } /////////////////////////////////////////////////////////////////////////////// @@ -51,13 +51,11 @@ HRESULT BaseDragSource::QueryInterface(const IID& iid, void** object) { } ULONG BaseDragSource::AddRef() { - return ++ref_count_; + base::RefCountedThreadSafe<BaseDragSource>::AddRef(); + return 0; } ULONG BaseDragSource::Release() { - if (--ref_count_ == 0) { - delete this; - return 0U; - } - return ref_count_; + base::RefCountedThreadSafe<BaseDragSource>::Release(); + return 0; } diff --git a/base/base_drag_source.h b/base/base_drag_source.h index 3a4a94c..2ea531f 100644 --- a/base/base_drag_source.h +++ b/base/base_drag_source.h @@ -8,6 +8,7 @@ #include <objidl.h> #include "base/basictypes.h" +#include "base/ref_counted.h" /////////////////////////////////////////////////////////////////////////////// // @@ -18,7 +19,8 @@ // system. This object tells Windows whether or not the drag should continue, // and supplies the appropriate cursors. // -class BaseDragSource : public IDropSource { +class BaseDragSource : public IDropSource, + public base::RefCountedThreadSafe<BaseDragSource> { public: BaseDragSource(); virtual ~BaseDragSource() { } @@ -45,8 +47,6 @@ class BaseDragSource : public IDropSource { virtual void OnDragSourceMove() { } private: - LONG ref_count_; - // Set to true if we want to cancel the drag operation. bool cancel_drag_; diff --git a/base/base_drop_target.cc b/base/base_drop_target.cc index 77fe675..1ee878f 100644 --- a/base/base_drop_target.cc +++ b/base/base_drop_target.cc @@ -15,7 +15,7 @@ int32 BaseDropTarget::drag_identity_ = 0; BaseDropTarget::BaseDropTarget(HWND hwnd) : hwnd_(hwnd), - suspend_(false), + suspended_(false), ref_count_(0) { DCHECK(hwnd); HRESULT result = RegisterDragDrop(hwnd, this); @@ -50,7 +50,7 @@ HRESULT BaseDropTarget::DragEnter(IDataObject* data_object, } // You can't drag and drop within the same HWND. - if (suspend_) { + if (suspended_) { *effect = DROPEFFECT_NONE; return S_OK; } @@ -73,7 +73,7 @@ HRESULT BaseDropTarget::DragOver(DWORD key_state, if (drop_helper) drop_helper->DragOver(reinterpret_cast<POINT*>(&cursor_position), *effect); - if (suspend_) { + if (suspended_) { *effect = DROPEFFECT_NONE; return S_OK; } @@ -89,6 +89,9 @@ HRESULT BaseDropTarget::DragLeave() { if (drop_helper) drop_helper->DragLeave(); + if (suspended_) + return S_OK; + OnDragLeave(current_data_object_); current_data_object_ = NULL; @@ -106,7 +109,7 @@ HRESULT BaseDropTarget::Drop(IDataObject* data_object, reinterpret_cast<POINT*>(&cursor_position), *effect); } - if (suspend_) { + if (suspended_) { *effect = DROPEFFECT_NONE; return S_OK; } diff --git a/base/base_drop_target.h b/base/base_drop_target.h index cf63be28..8be0d36 100644 --- a/base/base_drop_target.h +++ b/base/base_drop_target.h @@ -26,12 +26,13 @@ class BaseDropTarget : public IDropTarget { explicit BaseDropTarget(HWND hwnd); virtual ~BaseDropTarget(); - // When suspend is set to |true|, the drop target does not receive drops from - // drags initiated within the owning HWND. + // When suspended is set to |true|, the drop target does not receive drops + // from drags initiated within the owning HWND. // TODO(beng): (http://b/1085385) figure out how we will handle legitimate // drag-drop operations within the same HWND, such as dragging // selected text to an edit field. - void set_suspend(bool suspend) { suspend_ = suspend; } + bool suspended() const { return suspended_; } + void set_suspended(bool suspended) { suspended_ = suspended; } // IDropTarget implementation: HRESULT __stdcall DragEnter(IDataObject* data_object, @@ -120,7 +121,7 @@ class BaseDropTarget : public IDropTarget { // Whether or not we are currently processing drag notifications for drags // initiated in this window. - bool suspend_; + bool suspended_; LONG ref_count_; diff --git a/base/file_util.h b/base/file_util.h index 543a86c..efa4633 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -128,6 +128,15 @@ bool Delete(const FilePath& path, bool recursive); // Deprecated temporary compatibility function. bool Delete(const std::wstring& path, bool recursive); +#if defined(OS_WIN) +// Schedules to delete the given path, whether it's a file or a directory, until +// the operating system is restarted. +// Note: +// 1) The file/directory to be deleted should exist in a temp folder. +// 2) The directory to be deleted must be empty. +bool DeleteAfterReboot(const FilePath& path); +#endif + // Moves the given path, whether it's a file or a directory. // If a simple rename is not possible, such as in the case where the paths are // on different volumes, this will attempt to copy and delete. Returns diff --git a/base/file_util_win.cc b/base/file_util_win.cc index e066422..7c3de4d 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -98,6 +98,15 @@ bool Delete(const FilePath& path, bool recursive) { return (err == 0 || err == ERROR_FILE_NOT_FOUND); } +bool DeleteAfterReboot(const FilePath& path) { + if (path.value().length() >= MAX_PATH) + return false; + + return MoveFileEx(path.value().c_str(), NULL, + MOVEFILE_DELAY_UNTIL_REBOOT | + MOVEFILE_REPLACE_EXISTING) != FALSE; +} + bool Move(const FilePath& from_path, const FilePath& to_path) { // NOTE: I suspect we could support longer paths, but that would involve // analyzing all our usage of files. diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index 74491ce..00a1dfe 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -1107,7 +1107,12 @@ class DispatcherImpl : public MessageLoopForUI::Dispatcher { virtual bool Dispatch(const MSG& msg) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - return (++dispatch_count_ != 2); + // Do not count WM_TIMER since it is not what we post and it will cause + // flakiness. + if (msg.message != WM_TIMER) + ++dispatch_count_; + // We treat WM_LBUTTONUP as the last message. + return msg.message != WM_LBUTTONUP; } int dispatch_count_; @@ -1130,6 +1135,37 @@ void RunTest_Dispatcher(MessageLoop::Type message_loop_type) { ASSERT_EQ(2, dispatcher.dispatch_count_); } +LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) { + if (code == base::MessagePumpForUI::kMessageFilterCode) { + MSG* msg = reinterpret_cast<MSG*>(lparam); + if (msg->message == WM_LBUTTONDOWN) + return TRUE; + } + return FALSE; +} + +void RunTest_DispatcherWithMessageHook(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + class MyTask : public Task { + public: + virtual void Run() { + PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); + PostMessage(NULL, WM_LBUTTONUP, 'A', 0); + } + }; + Task* task = new MyTask(); + MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100); + HHOOK msg_hook = SetWindowsHookEx(WH_MSGFILTER, + MsgFilterProc, + NULL, + GetCurrentThreadId()); + DispatcherImpl dispatcher; + MessageLoopForUI::current()->Run(&dispatcher); + ASSERT_EQ(1, dispatcher.dispatch_count_); + UnhookWindowsHookEx(msg_hook); +} + class TestIOHandler : public MessageLoopForIO::IOHandler { public: TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); @@ -1423,6 +1459,11 @@ TEST(MessageLoopTest, Dispatcher) { RunTest_Dispatcher(MessageLoop::TYPE_UI); } +TEST(MessageLoopTest, DispatcherWithMessageHook) { + // This test requires a UI loop + RunTest_DispatcherWithMessageHook(MessageLoop::TYPE_UI); +} + TEST(MessageLoopTest, IOHandler) { RunTest_IOHandler(); } diff --git a/base/message_pump_win.cc b/base/message_pump_win.cc index 737676c..f40c872 100644 --- a/base/message_pump_win.cc +++ b/base/message_pump_win.cc @@ -351,6 +351,9 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) return ProcessPumpReplacementMessage(); + if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) + return true; + WillProcessMessage(msg); if (state_->dispatcher) { diff --git a/base/message_pump_win.h b/base/message_pump_win.h index 63222a8..e6ea233 100644 --- a/base/message_pump_win.h +++ b/base/message_pump_win.h @@ -157,6 +157,9 @@ class MessagePumpWin : public MessagePump { // class MessagePumpForUI : public MessagePumpWin { public: + // The application-defined code passed to the hook procedure. + static const int kMessageFilterCode = 0x5001; + MessagePumpForUI(); virtual ~MessagePumpForUI(); diff --git a/chrome/browser/cocoa/download_item_mac.h b/chrome/browser/cocoa/download_item_mac.h index a7f41a8..a54abe4 100644 --- a/chrome/browser/cocoa/download_item_mac.h +++ b/chrome/browser/cocoa/download_item_mac.h @@ -31,6 +31,7 @@ class DownloadItemMac : DownloadItem::Observer { // DownloadItem::Observer implementation virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download) { } virtual void OnDownloadOpened(DownloadItem* download) { } BaseDownloadItemModel* download_model() { return download_model_.get(); } diff --git a/chrome/browser/dom_ui/downloads_dom_handler.h b/chrome/browser/dom_ui/downloads_dom_handler.h index 52a54da..ad7d459 100644 --- a/chrome/browser/dom_ui/downloads_dom_handler.h +++ b/chrome/browser/dom_ui/downloads_dom_handler.h @@ -29,6 +29,7 @@ class DownloadsDOMHandler : public DOMMessageHandler, // DownloadItem::Observer interface virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download) { } virtual void OnDownloadOpened(DownloadItem* download) { } // DownloadManager::Observer interface diff --git a/chrome/browser/download/download_exe.cc b/chrome/browser/download/download_exe.cc index 01585f5..4e12022 100644 --- a/chrome/browser/download/download_exe.cc +++ b/chrome/browser/download/download_exe.cc @@ -8,6 +8,7 @@ #include "chrome/browser/download/download_util.h" #include "base/logging.h" +#include "base/string_util.h" namespace download_util { @@ -158,10 +159,12 @@ static const char* const g_executables[] = { #endif }; -void InitializeExeTypes(std::set<std::string>* exe_extensions) { - DCHECK(exe_extensions); - for (size_t i = 0; i < arraysize(g_executables); ++i) - exe_extensions->insert(g_executables[i]); +bool IsExecutableExtension(const std::string& extension) { + for (size_t i = 0; i < arraysize(g_executables); ++i) { + if (LowerCaseEqualsASCII(extension, g_executables[i])) + return true; + } + return false; } } // namespace download_util diff --git a/chrome/browser/download/download_file.cc b/chrome/browser/download/download_file.cc index 575da50..25a0900 100644 --- a/chrome/browser/download/download_file.cc +++ b/chrome/browser/download/download_file.cc @@ -429,6 +429,7 @@ void DownloadFileManager::DownloadUrl( const GURL& url, const GURL& referrer, const std::string& referrer_charset, + const FilePath& save_file_path, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter) { @@ -440,6 +441,7 @@ void DownloadFileManager::DownloadUrl( url, referrer, referrer_charset, + save_file_path, render_process_host_id, render_view_id, request_context_getter)); @@ -524,6 +526,7 @@ void DownloadFileManager::OnDownloadUrl( const GURL& url, const GURL& referrer, const std::string& referrer_charset, + const FilePath& save_file_path, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter) { @@ -534,6 +537,7 @@ void DownloadFileManager::OnDownloadUrl( resource_dispatcher_host_->BeginDownload(url, referrer, + save_file_path, 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 8b58f99..352f937 100644 --- a/chrome/browser/download/download_file.h +++ b/chrome/browser/download/download_file.h @@ -192,6 +192,7 @@ class DownloadFileManager void DownloadUrl(const GURL& url, const GURL& referrer, const std::string& referrer_charset, + const FilePath& save_file_path, int render_process_host_id, int render_view_id, URLRequestContextGetter* request_context_getter); @@ -200,6 +201,7 @@ class DownloadFileManager void OnDownloadUrl(const GURL& url, const GURL& referrer, const std::string& referrer_charset, + const FilePath& save_file_path, 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 04c3b8a..b242d84 100644 --- a/chrome/browser/download/download_manager.cc +++ b/chrome/browser/download/download_manager.cc @@ -135,7 +135,10 @@ DownloadItem::DownloadItem(const DownloadCreateInfo& info) auto_opened_(false), original_name_(info.original_name), render_process_id_(-1), - request_id_(-1) { + request_id_(-1), + save_as_(false), + name_finalized_(false), + is_temporary_(false) { if (state_ == IN_PROGRESS) state_ = CANCELLED; Init(false /* don't start progress timer */); @@ -155,7 +158,8 @@ DownloadItem::DownloadItem(int32 download_id, int request_id, bool is_dangerous, bool save_as, - bool is_extension_install) + bool is_extension_install, + bool is_temporary) : id_(download_id), full_path_(path), path_uniquifier_(path_uniquifier), @@ -177,7 +181,9 @@ DownloadItem::DownloadItem(int32 download_id, render_process_id_(render_process_id), request_id_(request_id), save_as_(save_as), - is_extension_install_(is_extension_install) { + is_extension_install_(is_extension_install), + name_finalized_(false), + is_temporary_(is_temporary) { Init(true /* start progress timer */); } @@ -204,6 +210,10 @@ void DownloadItem::UpdateObservers() { FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); } +void DownloadItem::NotifyObserversDownloadFileCompleted() { + FOR_EACH_OBSERVER(Observer, observers_, OnDownloadFileCompleted(this)); +} + void DownloadItem::NotifyObserversDownloadOpened() { FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); } @@ -470,6 +480,22 @@ void DownloadManager::GetDownloads(Observer* observer, } } +void DownloadManager::GetTemporaryDownloads(Observer* observer, + const FilePath& dir_path) { + DCHECK(observer); + + std::vector<DownloadItem*> download_copy; + + for (DownloadMap::iterator it = downloads_.begin(); + it != downloads_.end(); ++it) { + if (it->second->is_temporary() && + it->second->full_path().DirName() == dir_path) + download_copy.push_back(it->second); + } + + observer->SetDownloads(download_copy); +} + // Query the history service for information about all persisted downloads. bool DownloadManager::Init(Profile* profile) { DCHECK(profile); @@ -507,9 +533,6 @@ bool DownloadManager::Init(Profile* profile) { ChromeThread::FILE, FROM_HERE, NewRunnableFunction(&file_util::CreateDirectory, download_path())); - // We use this to determine possibly dangerous downloads. - download_util::InitializeExeTypes(&exe_types_); - // We store any file extension that should be opened automatically at // download completion in this pref. std::wstring extensions_to_open = @@ -563,17 +586,21 @@ void DownloadManager::StartDownload(DownloadCreateInfo* info) { info->save_as = true; } - // Determine the proper path for a download, by choosing either the default - // download directory, or prompting the user. + // 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; - GenerateFilename(info, &generated_name); - if (info->save_as && !last_download_path_.empty()) + 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_as) { + if (!info->save_as && info->save_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 @@ -703,7 +730,8 @@ void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info, info->request_id, info->is_dangerous, info->save_as, - info->is_extension_install); + info->is_extension_install, + !info->save_file_path.empty()); download->set_manager(this); in_progress_[info->download_id] = download; } else { @@ -731,11 +759,13 @@ void DownloadManager::ContinueStartDownload(DownloadCreateInfo* info, // Do not store the download in the history database for a few special cases: // - incognito mode (that is the point of this mode) // - extensions (users don't think of extension installation as 'downloading') + // - temporary download, like in drag-and-drop // We have to make sure that these handles don't collide with normal db // handles, so we use a negative value. Eventually, they could overlap, but // you'd have to do enough downloading that your ISP would likely stab you in // the neck first. YMMV. - if (profile_->IsOffTheRecord() || download->is_extension_install()) { + if (profile_->IsOffTheRecord() || download->is_extension_install() || + download->is_temporary()) { static int64 fake_db_handle = kUninitializedHandle - 1; OnCreateDownloadEntryComplete(*info, fake_db_handle--); } else { @@ -848,6 +878,21 @@ void DownloadManager::DownloadFinished(int32 download_id, int64 size) { void DownloadManager::DownloadRenamedToFinalName(int download_id, const FilePath& full_path) { + DownloadMap::iterator it = downloads_.begin(); + while (it != downloads_.end()) { + DownloadItem* download = it->second; + if (download->id() == download_id) { + // The download file is meant to be completed if both the filename is + // finalized and the file data is downloaded. The ordering of these two + // actions is indeterministic. Thus, if we are still in downloading the + // file, delay the notification. + download->set_name_finalized(true); + if (download->state() == DownloadItem::COMPLETE) + download->NotifyObserversDownloadFileCompleted(); + return; + } + it++; + } } void DownloadManager::ContinueDownloadFinished(DownloadItem* download) { @@ -863,14 +908,26 @@ void DownloadManager::ContinueDownloadFinished(DownloadItem* download) { download->referrer_url()); download->set_auto_opened(true); } else if (download->open_when_complete() || - ShouldOpenFileBasedOnExtension(download->full_path())) { - OpenDownloadInShell(download, NULL); + ShouldOpenFileBasedOnExtension(download->full_path()) || + download->is_temporary()) { + // If the download is temporary, like in drag-and-drop, do not open it but + // we still need to set it auto-opened so that it can be removed from the + // download shelf. + if (!download->is_temporary()) + OpenDownloadInShell(download, NULL); download->set_auto_opened(true); } // Notify our observers that we are complete (the call to Finished() set the // state to complete but did not notify). download->UpdateObservers(); + + // The download file is meant to be completed if both the filename is + // finalized and the file data is downloaded. The ordering of these two + // actions is indeterministic. Thus, if the filename is not finalized yet, + // delay the notification. + if (download->name_finalized()) + download->NotifyObserversDownloadFileCompleted(); } // Called on the file thread. Renames the downloaded file to its original name. @@ -1097,10 +1154,25 @@ void DownloadManager::DownloadUrl(const GURL& url, const GURL& referrer, const std::string& referrer_charset, TabContents* tab_contents) { + file_manager_->DownloadUrl(url, + referrer, + referrer_charset, + FilePath(), + tab_contents->process()->id(), + tab_contents->render_view_host()->routing_id(), + request_context_getter_); +} + +void DownloadManager::DownloadUrlToFile(const GURL& url, + const GURL& referrer, + const std::string& referrer_charset, + const FilePath& save_file_path, + TabContents* tab_contents) { DCHECK(tab_contents); file_manager_->DownloadUrl(url, referrer, referrer_charset, + save_file_path, tab_contents->process()->id(), tab_contents->render_view_host()->routing_id(), request_context_getter_); @@ -1189,7 +1261,19 @@ void DownloadManager::GenerateExtension( generated_extension->swap(extension); } -void DownloadManager::GenerateFilename(DownloadCreateInfo* info, +void DownloadManager::GenerateFileNameFromInfo(DownloadCreateInfo* info, + FilePath* generated_name) { + GenerateFileName(GURL(info->url), + info->content_disposition, + info->referrer_charset, + info->mime_type, + generated_name); +} + +void DownloadManager::GenerateFileName(const GURL& url, + const std::string& content_disposition, + const std::string& referrer_charset, + const std::string& mime_type, FilePath* generated_name) { std::wstring default_name = l10n_util::GetString(IDS_DEFAULT_DOWNLOAD_FILENAME); @@ -1199,14 +1283,14 @@ void DownloadManager::GenerateFilename(DownloadCreateInfo* info, FilePath default_file_path(base::SysWideToNativeMB(default_name)); #endif - *generated_name = net::GetSuggestedFilename(GURL(info->url), - info->content_disposition, - info->referrer_charset, + *generated_name = net::GetSuggestedFilename(GURL(url), + content_disposition, + referrer_charset, default_file_path); DCHECK(!generated_name->empty()); - GenerateSafeFilename(info->mime_type, generated_name); + GenerateSafeFileName(mime_type, generated_name); } void DownloadManager::AddObserver(Observer* observer) { @@ -1363,7 +1447,7 @@ bool DownloadManager::IsExecutableFile(const FilePath& path) const { } bool DownloadManager::IsExecutableExtension( - const FilePath::StringType& extension) const { + const FilePath::StringType& extension) { if (extension.empty()) return false; if (!IsStringASCII(extension)) @@ -1373,13 +1457,12 @@ bool DownloadManager::IsExecutableExtension( #elif defined(OS_POSIX) std::string ascii_extension = extension; #endif - StringToLowerASCII(&ascii_extension); // Strip out leading dot if it's still there if (ascii_extension[0] == FilePath::kExtensionSeparator) ascii_extension.erase(0, 1); - return exe_types_.find(ascii_extension) != exe_types_.end(); + return download_util::IsExecutableExtension(ascii_extension); } void DownloadManager::ResetAutoOpenFiles() { @@ -1455,7 +1538,7 @@ void DownloadManager::DangerousDownloadValidated(DownloadItem* download) { download->original_name())); } -void DownloadManager::GenerateSafeFilename(const std::string& mime_type, +void DownloadManager::GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) { // Make sure we get the right file extension FilePath::StringType extension; diff --git a/chrome/browser/download/download_manager.h b/chrome/browser/download/download_manager.h index 1d27625..5a5bda2 100644 --- a/chrome/browser/download/download_manager.h +++ b/chrome/browser/download/download_manager.h @@ -95,6 +95,9 @@ class DownloadItem { public: virtual void OnDownloadUpdated(DownloadItem* download) = 0; + // Called when a downloaded file has been completed. + virtual void OnDownloadFileCompleted(DownloadItem* download) = 0; + // Called when a downloaded file has been opened. virtual void OnDownloadOpened(DownloadItem* download) = 0; @@ -119,7 +122,8 @@ class DownloadItem { int request_id, bool is_dangerous, bool save_as, - bool is_extension_install); + bool is_extension_install, + bool is_temporary); ~DownloadItem(); @@ -133,6 +137,9 @@ class DownloadItem { // Notifies our observers periodically. void UpdateObservers(); + // Notifies our observers the downloaded file has been completed. + void NotifyObserversDownloadFileCompleted(); + // Notifies our observers the downloaded file has been opened. void NotifyObserversDownloadOpened(); @@ -217,6 +224,12 @@ class DownloadItem { void set_original_name(const FilePath& name) { original_name_ = name; } bool save_as() const { return save_as_; } bool is_extension_install() const { return is_extension_install_; } + bool name_finalized() const { return name_finalized_; } + void set_name_finalized(bool name_finalized) { + name_finalized_ = name_finalized; + } + bool is_temporary() const { return is_temporary_; } + void set_is_temporary(bool is_temporary) { is_temporary_ = is_temporary; } // Returns the file-name that should be reported to the user, which is // file_name_ for safe downloads and original_name_ for dangerous ones with @@ -305,6 +318,12 @@ class DownloadItem { // True if the item was downloaded for an extension installation. bool is_extension_install_; + // True if the filename is finalized. + bool name_finalized_; + + // True if the item was downloaded temporarily. + bool is_temporary_; + DISALLOW_COPY_AND_ASSIGN(DownloadItem); }; @@ -347,6 +366,10 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, void GetDownloads(Observer* observer, const std::wstring& search_text); + // Return all temporary downloads that reside in the specified directory. + void GetTemporaryDownloads(Observer* observer, + const FilePath& dir_path); + // Returns true if initialized properly. bool Init(Profile* profile); @@ -387,6 +410,15 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, const std::string& referrer_encoding, TabContents* tab_contents); + // Download the object at the URL and save it to the specified path. The + // download is treated as the temporary download and thus will not appear + // in the download history. Used in cases such as drag and drop. + void DownloadUrlToFile(const GURL& url, + const GURL& referrer, + const std::string& referrer_encoding, + const FilePath& save_file_path, + TabContents* tab_contents); + // Allow objects to observe the download creation process. void AddObserver(Observer* observer); @@ -440,7 +472,7 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, bool IsExecutableFile(const FilePath& path) const; // Tests if a file type is considered executable. - bool IsExecutableExtension(const FilePath::StringType& extension) const; + static bool IsExecutableExtension(const FilePath::StringType& extension); // Resets the automatic open preference. void ResetAutoOpenFiles(); @@ -462,14 +494,21 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, // Used to make sure we have a safe file extension and filename for a // download. |file_name| can either be just the file name or it can be a // full path to a file. - void GenerateSafeFilename(const std::string& mime_type, - FilePath* file_name); + static void GenerateSafeFileName(const std::string& mime_type, + FilePath* file_name); // Runs the network cancel. Must be called on the IO thread. static void OnCancelDownloadRequest(ResourceDispatcherHost* rdh, int render_process_id, int request_id); + // Create a file name based on the response from the server. + static void GenerateFileName(const GURL& url, + const std::string& content_disposition, + const std::string& referrer_charset, + const std::string& mime_type, + FilePath* generated_name); + private: friend class base::RefCountedThreadSafe<DownloadManager>; @@ -508,12 +547,13 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, const base::Time remove_before); // Create an extension based on the file name and mime type. - void GenerateExtension(const FilePath& file_name, - const std::string& mime_type, - FilePath::StringType* generated_extension); + static void GenerateExtension(const FilePath& file_name, + const std::string& mime_type, + FilePath::StringType* generated_extension); // Create a file name based on the response from the server. - void GenerateFilename(DownloadCreateInfo* info, FilePath* generated_name); + static void GenerateFileNameFromInfo(DownloadCreateInfo* info, + FilePath* generated_name); // Persist the automatic opening preference. void SaveAutoOpens(); @@ -619,9 +659,6 @@ class DownloadManager : public base::RefCountedThreadSafe<DownloadManager>, typedef std::set<FilePath::StringType, AutoOpenCompareFunctor> AutoOpenSet; AutoOpenSet auto_open_; - // Set of file extensions that are executables and shouldn't be auto opened. - std::set<std::string> exe_types_; - // Keep track of downloads that are completed before the user selects the // destination, so that observers are appropriately notified of completion // after this determination is made. diff --git a/chrome/browser/download/download_manager_unittest.cc b/chrome/browser/download/download_manager_unittest.cc index 7a4a9bd..7c839f4 100644 --- a/chrome/browser/download/download_manager_unittest.cc +++ b/chrome/browser/download/download_manager_unittest.cc @@ -7,7 +7,6 @@ #include "base/string_util.h" #include "build/build_config.h" #include "chrome/browser/download/download_manager.h" -#include "chrome/browser/download/download_util.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_LINUX) @@ -35,7 +34,6 @@ class DownloadManagerTest : public testing::Test { public: DownloadManagerTest() { download_manager_ = new DownloadManager(); - download_util::InitializeExeTypes(&download_manager_->exe_types_); } void GetGeneratedFilename(const std::string& content_disposition, @@ -49,7 +47,7 @@ class DownloadManagerTest : public testing::Test { info.mime_type = mime_type; info.referrer_charset = referrer_charset; FilePath generated_name; - download_manager_->GenerateFilename(&info, &generated_name); + DownloadManager::GenerateFileNameFromInfo(&info, &generated_name); *generated_name_string = generated_name.ToWStringHack(); } @@ -579,7 +577,7 @@ const struct { TEST_F(DownloadManagerTest, GetSafeFilename) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSafeFilenameCases); ++i) { FilePath path(kSafeFilenameCases[i].path); - download_manager_->GenerateSafeFilename(kSafeFilenameCases[i].mime_type, + download_manager_->GenerateSafeFileName(kSafeFilenameCases[i].mime_type, &path); EXPECT_EQ(kSafeFilenameCases[i].expected_path, path.value()); } diff --git a/chrome/browser/download/download_util.h b/chrome/browser/download/download_util.h index a88c538..7636b55 100644 --- a/chrome/browser/download/download_util.h +++ b/chrome/browser/download/download_util.h @@ -131,8 +131,8 @@ void DragDownload(const DownloadItem* download, // Executable file support ----------------------------------------------------- -// Copy all executable file extensions. -void InitializeExeTypes(std::set<std::string>* exe_extensions); +// Determine if the specified extension is an executable extension. +bool IsExecutableExtension(const std::string& extension); } // namespace download_util diff --git a/chrome/browser/download/drag_download_file_win.cc b/chrome/browser/download/drag_download_file_win.cc new file mode 100644 index 0000000..77a9112 --- /dev/null +++ b/chrome/browser/download/drag_download_file_win.cc @@ -0,0 +1,217 @@ +// 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 new file mode 100644 index 0000000..a1ef5ed --- /dev/null +++ b/chrome/browser/download/drag_download_file_win.h @@ -0,0 +1,84 @@ +// 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/save_package.cc b/chrome/browser/download/save_package.cc index 03a4cb4..446022d 100644 --- a/chrome/browser/download/save_package.cc +++ b/chrome/browser/download/save_package.cc @@ -304,7 +304,7 @@ bool SavePackage::Init() { // Create the fake DownloadItem and display the view. download_ = new DownloadItem(1, saved_main_file_path_, 0, page_url_, GURL(), - "", FilePath(), Time::Now(), 0, -1, -1, false, false, false); + "", FilePath(), Time::Now(), 0, -1, -1, false, false, false, false); download_->set_manager(tab_contents_->profile()->GetDownloadManager()); tab_contents_->OnStartDownload(download_); @@ -335,7 +335,7 @@ bool SavePackage::Init() { } // Generate name for saving resource. -bool SavePackage::GenerateFilename(const std::string& disposition, +bool SavePackage::GenerateFileName(const std::string& disposition, const GURL& url, bool need_html_ext, FilePath::StringType* generated_name) { @@ -448,7 +448,7 @@ void SavePackage::StartSave(const SaveFileCreateInfo* info) { // instead of opening it as HTML. bool need_html_ext = info->save_source == SaveFileCreateInfo::SAVE_FILE_FROM_DOM; - if (!GenerateFilename(info->content_disposition, + if (!GenerateFileName(info->content_disposition, GURL(info->url), need_html_ext, &generated_name)) { @@ -470,7 +470,7 @@ void SavePackage::StartSave(const SaveFileCreateInfo* info) { DCHECK(save_type_ == SAVE_AS_COMPLETE_HTML); DCHECK(!saved_main_directory_path_.empty()); - // Now we get final name retrieved from GenerateFilename, we will use it + // Now we get final name retrieved from GenerateFileName, we will use it // rename the SaveItem. FilePath final_name = saved_main_directory_path_.Append(generated_name); save_item->Rename(final_name); @@ -1172,7 +1172,7 @@ void SavePackage::ContinueSave(SavePackageParam* param, param->saved_main_file_path = final_name; DownloadManager* dlm = tab_contents_->profile()->GetDownloadManager(); DCHECK(dlm); - dlm->GenerateSafeFilename(param->current_tab_mime_type, + dlm->GenerateSafeFileName(param->current_tab_mime_type, ¶m->saved_main_file_path); // The option index is not zero-based. diff --git a/chrome/browser/download/save_package.h b/chrome/browser/download/save_package.h index 4d52864..4df7174 100644 --- a/chrome/browser/download/save_package.h +++ b/chrome/browser/download/save_package.h @@ -212,7 +212,7 @@ class SavePackage : public base::RefCountedThreadSafe<SavePackage>, void DoSavingProcess(); // Create a file name based on the response from the server. - bool GenerateFilename(const std::string& disposition, + bool GenerateFileName(const std::string& disposition, const GURL& url, bool need_html_ext, FilePath::StringType* generated_name); diff --git a/chrome/browser/download/save_package_unittest.cc b/chrome/browser/download/save_package_unittest.cc index d13bc61..bc2ea7c 100644 --- a/chrome/browser/download/save_package_unittest.cc +++ b/chrome/browser/download/save_package_unittest.cc @@ -84,7 +84,7 @@ class SavePackageTest : public testing::Test { save_package = save_package_success_.get(); else save_package = save_package_fail_.get(); - return save_package->GenerateFilename(disposition, GURL(url), need_htm_ext, + return save_package->GenerateFileName(disposition, GURL(url), need_htm_ext, generated_name); } diff --git a/chrome/browser/gtk/download_item_gtk.h b/chrome/browser/gtk/download_item_gtk.h index 36889a8..1976be8 100644 --- a/chrome/browser/gtk/download_item_gtk.h +++ b/chrome/browser/gtk/download_item_gtk.h @@ -39,6 +39,7 @@ class DownloadItemGtk : public DownloadItem::Observer, // DownloadItem::Observer implementation. virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download) { } virtual void OnDownloadOpened(DownloadItem* download) { } // AnimationDelegate implementation. diff --git a/chrome/browser/history/download_types.h b/chrome/browser/history/download_types.h index 536ca30..7025b1d 100644 --- a/chrome/browser/history/download_types.h +++ b/chrome/browser/history/download_types.h @@ -87,6 +87,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; }; #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 6c64396..e414587 100644 --- a/chrome/browser/renderer_host/download_resource_handler.cc +++ b/chrome/browser/renderer_host/download_resource_handler.cc @@ -20,7 +20,8 @@ DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, const GURL& url, DownloadFileManager* manager, URLRequest* request, - bool save_as) + bool save_as, + const FilePath& save_file_path) : download_id_(-1), global_id_(render_process_host_id, request_id), render_view_id_(render_view_id), @@ -29,6 +30,7 @@ DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, download_manager_(manager), request_(request), save_as_(save_as), + save_file_path_(save_file_path), buffer_(new DownloadBuffer), rdh_(rdh), is_paused_(false) { @@ -67,9 +69,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_; + info->save_as = save_as_ && save_file_path_.empty(); info->is_dangerous = false; info->referrer_charset = request_->context()->referrer_charset(); + info->save_file_path = save_file_path_; 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 24251f6..59b4cb4 100644 --- a/chrome/browser/renderer_host/download_resource_handler.h +++ b/chrome/browser/renderer_host/download_resource_handler.h @@ -7,6 +7,7 @@ #include <string> +#include "base/file_path.h" #include "base/timer.h" #include "chrome/browser/renderer_host/global_request_id.h" #include "chrome/browser/renderer_host/resource_handler.h" @@ -26,7 +27,8 @@ class DownloadResourceHandler : public ResourceHandler { const GURL& url, DownloadFileManager* manager, URLRequest* request, - bool save_as); + bool save_as, + const FilePath& save_file_path); // Not needed, as this event handler ought to be the final resource. bool OnRequestRedirected(int request_id, const GURL& url, @@ -70,6 +72,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_; 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 8ca251f..4b02880 100644 --- a/chrome/browser/renderer_host/download_throttling_resource_handler.cc +++ b/chrome/browser/renderer_host/download_throttling_resource_handler.cc @@ -130,6 +130,7 @@ void DownloadThrottlingResourceHandler::CancelDownload() { void DownloadThrottlingResourceHandler::ContinueDownload() { DCHECK(!download_handler_.get()); + FilePath save_file_path; download_handler_ = new DownloadResourceHandler(host_, render_process_host_id_, @@ -138,7 +139,8 @@ void DownloadThrottlingResourceHandler::ContinueDownload() { url_, host_->download_file_manager(), request_, - false); + false, + save_file_path); 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 b6db297..19096a4 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -648,6 +648,7 @@ 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) { @@ -677,7 +678,8 @@ void ResourceDispatcherHost::BeginDownload(const GURL& url, url, download_file_manager_.get(), request, - true); + true, + save_file_path); 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 da043eb..3f2816d 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -108,6 +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, 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 eafa767..b6ece5c 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -684,8 +684,10 @@ 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, id(), message.routing_id(), context); diff --git a/chrome/browser/tab_contents/web_drag_source_win.cc b/chrome/browser/tab_contents/web_drag_source_win.cc index 9934e82..0f47aac 100644 --- a/chrome/browser/tab_contents/web_drag_source_win.cc +++ b/chrome/browser/tab_contents/web_drag_source_win.cc @@ -4,6 +4,8 @@ #include "chrome/browser/tab_contents/web_drag_source_win.h" +#include "base/task.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/notification_type.h" @@ -24,6 +26,7 @@ static void GetCursorPositions(gfx::NativeWindow wnd, gfx::Point* client, } } // namespace + /////////////////////////////////////////////////////////////////////////////// // WebDragSource, public: @@ -39,6 +42,14 @@ WebDragSource::WebDragSource(gfx::NativeWindow source_wnd, } void WebDragSource::OnDragSourceCancel() { + // Delegate to the UI thread if we do drag-and-drop in the background thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &WebDragSource::OnDragSourceCancel)); + return; + } + if (!render_view_host_) return; @@ -51,6 +62,14 @@ void WebDragSource::OnDragSourceCancel() { } void WebDragSource::OnDragSourceDrop() { + // Delegate to the UI thread if we do drag-and-drop in the background thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &WebDragSource::OnDragSourceDrop)); + return; + } + if (!render_view_host_) return; @@ -64,6 +83,14 @@ void WebDragSource::OnDragSourceDrop() { } void WebDragSource::OnDragSourceMove() { + // Delegate to the UI thread if we do drag-and-drop in the background thread. + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &WebDragSource::OnDragSourceMove)); + return; + } + if (!render_view_host_) return; @@ -82,6 +109,9 @@ void WebDragSource::Observe(NotificationType type, // our drag source. render_view_host_ = NULL; } else if (NotificationType::TAB_CONTENTS_DISCONNECTED == type) { - NOTREACHED(); + // This could be possible when we close the tab and the source is still + // being used in DoDragDrop at the time that the virtual file is being + // downloaded. + render_view_host_ = NULL; } } diff --git a/chrome/browser/views/download_item_view.h b/chrome/browser/views/download_item_view.h index f773931..fcecaa4 100644 --- a/chrome/browser/views/download_item_view.h +++ b/chrome/browser/views/download_item_view.h @@ -52,6 +52,7 @@ class DownloadItemView : public views::ButtonListener, // DownloadObserver method virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download) { } virtual void OnDownloadOpened(DownloadItem* download); // View overrides diff --git a/chrome/browser/views/tab_contents/tab_contents_drag_win.cc b/chrome/browser/views/tab_contents/tab_contents_drag_win.cc new file mode 100644 index 0000000..a507c6c --- /dev/null +++ b/chrome/browser/views/tab_contents/tab_contents_drag_win.cc @@ -0,0 +1,328 @@ +// 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/views/tab_contents/tab_contents_drag_win.h" + +#include <windows.h> + +#include "base/file_path.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/thread.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/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/web_drag_source_win.h" +#include "chrome/browser/tab_contents/web_drop_target_win.h" +#include "chrome/browser/views/tab_contents/tab_contents_view_win.h" +#include "chrome/common/url_constants.h" +#include "net/base/net_util.h" +#include "webkit/glue/webdropdata.h" + +using WebKit::WebDragOperationsMask; + +namespace { + +HHOOK msg_hook = NULL; +DWORD drag_out_thread_id = 0; +bool mouse_up_received = false; + +LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) { + if (code == base::MessagePumpForUI::kMessageFilterCode && + !mouse_up_received) { + MSG* msg = reinterpret_cast<MSG*>(lparam); + // We do not care about WM_SYSKEYDOWN and WM_SYSKEYUP because when ALT key + // is pressed down on drag-and-drop, it means to create a link. + if (msg->message == WM_MOUSEMOVE || msg->message == WM_LBUTTONUP || + msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) { + // Forward the message from the UI thread to the drag-and-drop thread. + PostThreadMessage(drag_out_thread_id, + msg->message, + msg->wParam, + msg->lParam); + + // If the left button is up, we do not need to forward the message any + // more. + if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000)) + mouse_up_received = true; + + return TRUE; + } + } + return CallNextHookEx(msg_hook, code, wparam, lparam); +} + +} // namespace + +class DragDropThread : public base::Thread { + public: + explicit DragDropThread(TabContentsDragWin* drag_handler) + : base::Thread("Chrome_DragDropThread"), + drag_handler_(drag_handler) { + } + + virtual ~DragDropThread() { + Thread::Stop(); + } + + protected: + // base::Thread implementations: + virtual void Init() { + int ole_result = OleInitialize(NULL); + DCHECK(ole_result == S_OK); + } + + virtual void CleanUp() { + OleUninitialize(); + } + + private: + // Hold a reference count to TabContentsDragWin to make sure that it is always + // alive in the thread lifetime. + scoped_refptr<TabContentsDragWin> drag_handler_; + + DISALLOW_COPY_AND_ASSIGN(DragDropThread); +}; + +TabContentsDragWin::TabContentsDragWin(TabContentsViewWin* view) + : view_(view), + drag_ended_(false), + old_drop_target_suspended_state_(false) { +#ifndef NDEBUG + drag_drop_thread_id_ = 0; +#endif +} + +TabContentsDragWin::~TabContentsDragWin() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + DCHECK(!drag_source_.get()); + DCHECK(!drag_drop_thread_.get()); +} + +void TabContentsDragWin::StartDragging(const WebDropData& drop_data, + WebDragOperationsMask ops) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + drag_source_ = new WebDragSource(view_->GetNativeView(), + view_->tab_contents()); + + const GURL& page_url = view_->tab_contents()->GetURL(); + 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()) { + DoDragging(drop_data, ops, page_url, page_encoding); + EndDragging(false); + return; + } + + // We do not want to drag and drop the download to itself. + old_drop_target_suspended_state_ = view_->drop_target()->suspended(); + view_->drop_target()->set_suspended(true); + + // Start a background thread to do the drag-and-drop. + DCHECK(!drag_drop_thread_.get()); + drag_drop_thread_.reset(new DragDropThread(this)); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_UI; + if (drag_drop_thread_->StartWithOptions(options)) { + drag_drop_thread_->message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &TabContentsDragWin::StartBackgroundDragging, + drop_data, + ops, + page_url, + page_encoding)); + } + + // Install a hook procedure to monitor the messages so that we can forward + // the appropriate ones to the background thread. + drag_out_thread_id = drag_drop_thread_->thread_id(); + mouse_up_received = false; + DCHECK(!msg_hook); + msg_hook = SetWindowsHookEx(WH_MSGFILTER, + MsgFilterProc, + NULL, + GetCurrentThreadId()); + + // Attach the input state of the background thread to the UI thread so that + // SetCursor can work from the background thread. + AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE); +} + +void TabContentsDragWin::StartBackgroundDragging( + const WebDropData& drop_data, + WebDragOperationsMask ops, + const GURL& page_url, + const std::string& page_encoding) { +#ifndef NDEBUG + drag_drop_thread_id_ = PlatformThread::CurrentId(); +#endif + + DoDragging(drop_data, ops, page_url, page_encoding); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true)); +} + +void TabContentsDragWin::PrepareDragForDownload( + const WebDropData& drop_data, + OSExchangeData* data, + const GURL& page_url, + const std::string& page_encoding) { + // Provide the data as file (CF_HDROP). A temporary download file with the + // Zone.Identifier ADS (Alternate Data Stream) attached will be created. + scoped_refptr<DragDownloadFile> download_file = + new DragDownloadFile(drop_data.download_url, + page_url, + page_encoding, + view_->tab_contents()); + OSExchangeData::DownloadFileInfo* file_download = + new OSExchangeData::DownloadFileInfo(FilePath(), + 0, + download_file.get()); + data->SetDownloadFileInfo(file_download); + + // Enable asynchronous operation. + OSExchangeDataProviderWin::GetIAsyncOperation(*data)->SetAsyncMode(TRUE); +} + +void TabContentsDragWin::PrepareDragForFileContents( + const WebDropData& drop_data, OSExchangeData* data) { + // Images without ALT text will only have a file extension so we need to + // synthesize one from the provided extension and URL. + FilePath file_name(drop_data.file_description_filename); + file_name = file_name.BaseName().RemoveExtension(); + if (file_name.value().empty()) { + // Retrieve the name from the URL. + file_name = net::GetSuggestedFilename(drop_data.url, "", "", FilePath()); + if (file_name.value().size() + drop_data.file_extension.size() + 1 > + MAX_PATH) { + file_name = FilePath(file_name.value().substr( + 0, MAX_PATH - drop_data.file_extension.size() - 2)); + } + } + file_name = file_name.ReplaceExtension(drop_data.file_extension); + data->SetFileContents(file_name.value(), drop_data.file_contents); +} + +void TabContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data, + OSExchangeData* data) { + if (drop_data.url.SchemeIs(chrome::kJavaScriptScheme)) { + // We don't want to allow javascript URLs to be dragged to the desktop, + // but we do want to allow them to be added to the bookmarks bar + // (bookmarklets). So we create a fake bookmark entry (BookmarkDragData + // object) which explorer.exe cannot handle, and write the entry to data. + BookmarkDragData::Element bm_elt; + bm_elt.is_url = true; + bm_elt.url = drop_data.url; + bm_elt.title = drop_data.url_title; + + BookmarkDragData bm_drag_data; + bm_drag_data.elements.push_back(bm_elt); + + // Pass in NULL as the profile so that the bookmark always adds the url + // rather than trying to move an existing url. + bm_drag_data.Write(NULL, data); + } else { + data->SetURL(drop_data.url, drop_data.url_title); + } +} + +void TabContentsDragWin::DoDragging(const WebDropData& drop_data, + WebDragOperationsMask ops, + const GURL& page_url, + const std::string& page_encoding) { + OSExchangeData data; + + // TODO(tc): Generate an appropriate drag image. + + if (drop_data.download_url.is_valid()) { + PrepareDragForDownload(drop_data, &data, page_url, page_encoding); + + // Set the observer. + OSExchangeDataProviderWin::GetDataObjectImpl(data)->set_observer(this); + } else { + // We set the file contents before the URL because the URL also sets file + // contents (to a .URL shortcut). We want to prefer file content data over + // a shortcut so we add it first. + if (!drop_data.file_contents.empty()) + PrepareDragForFileContents(drop_data, &data); + if (!drop_data.text_html.empty()) + data.SetHtml(drop_data.text_html, drop_data.html_base_url); + if (drop_data.url.is_valid()) + PrepareDragForUrl(drop_data, &data); + if (!drop_data.plain_text.empty()) + data.SetString(drop_data.plain_text); + } + + DWORD effects = 0; + + // We need to enable recursive tasks on the message loop so we can get + // updates while in the system DoDragDrop loop. + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source_, + DROPEFFECT_COPY | DROPEFFECT_LINK, &effects); + // TODO(snej): Use 'ops' param instead of hardcoding dropeffects + MessageLoop::current()->SetNestableTasksAllowed(old_state); +} + +void TabContentsDragWin::EndDragging(bool restore_suspended_state) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + if (drag_ended_) + return; + drag_ended_ = true; + + if (restore_suspended_state) + view_->drop_target()->set_suspended(old_drop_target_suspended_state_); + + drag_source_ = NULL; + + if (msg_hook) { + AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE); + UnhookWindowsHookEx(msg_hook); + msg_hook = NULL; + } + + view_->EndDragging(); +} + +void TabContentsDragWin::CancelDrag() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + drag_source_->CancelDrag(); +} + +void TabContentsDragWin::CloseThread() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + drag_drop_thread_.reset(); +} + +void TabContentsDragWin::OnWaitForData() { + DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId()); + + // When the left button is release and we start to wait for the data, end + // the dragging before DoDragDrop returns. This makes the page leave the drag + // mode so that it can start to process the normal input events. + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true)); +} + +void TabContentsDragWin::OnDataObjectDisposed() { + DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId()); + + // The drag-and-drop thread is only closed after OLE is done with + // DataObjectImpl. + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &TabContentsDragWin::CloseThread)); +} diff --git a/chrome/browser/views/tab_contents/tab_contents_drag_win.h b/chrome/browser/views/tab_contents/tab_contents_drag_win.h new file mode 100644 index 0000000..5fbddd9 --- /dev/null +++ b/chrome/browser/views/tab_contents/tab_contents_drag_win.h @@ -0,0 +1,93 @@ +// 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_VIEWS_TAB_CONTENTS_TAB_CONTENTS_DRAG_WIN_H_ +#define CHROME_BROWSER_VIEWS_TAB_CONTENTS_TAB_CONTENTS_DRAG_WIN_H_ + +#include "app/os_exchange_data_provider_win.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDragOperation.h" + +class DragDropThread; +class TabContentsViewWin; +class WebDragSource; +struct WebDropData; + +// Windows-specific drag-and-drop handling in TabContentsView. +// If we are dragging a virtual file out of the browser, we use a background +// thread to do the drag-and-drop because we do not want to run nested +// message loop in the UI thread. For all other cases, the drag-and-drop happens +// in the UI thread. +class TabContentsDragWin + : public DataObjectImpl::Observer, + public base::RefCountedThreadSafe<TabContentsDragWin> { + public: + explicit TabContentsDragWin(TabContentsViewWin* view); + virtual ~TabContentsDragWin(); + + // Called on UI thread. + void StartDragging(const WebDropData& drop_data, + WebKit::WebDragOperationsMask ops); + void CancelDrag(); + + // DataObjectImpl::Observer implementation. + // Called on drag-and-drop thread. + virtual void OnWaitForData(); + virtual void OnDataObjectDisposed(); + + private: + // Called on either UI thread or drag-and-drop thread. + void PrepareDragForDownload(const WebDropData& drop_data, + OSExchangeData* data, + const GURL& page_url, + const std::string& page_encoding); + void PrepareDragForFileContents(const WebDropData& drop_data, + OSExchangeData* data); + void PrepareDragForUrl(const WebDropData& drop_data, OSExchangeData* data); + void DoDragging(const WebDropData& drop_data, + WebKit::WebDragOperationsMask ops, + const GURL& page_url, + const std::string& page_encoding); + + // Called on drag-and-drop thread. + void StartBackgroundDragging(const WebDropData& drop_data, + WebKit::WebDragOperationsMask ops, + const GURL& page_url, + const std::string& page_encoding); + // Called on UI thread. + void EndDragging(bool restore_suspended_state); + void CloseThread(); + + // For debug check only. Access only on drag-and-drop thread. +#ifndef NDEBUG + PlatformThreadId drag_drop_thread_id_; +#endif + + // All the member variables below are accessed on UI thread. + + // Keep track of the TabContentsViewWin it is associated with. + TabContentsViewWin* view_; + + // |drag_source_| is our callback interface passed to the system when we + // want to initiate a drag and drop operation. We use it to tell if a + // drag operation is happening. + scoped_refptr<WebDragSource> drag_source_; + + // The thread used by the drag-out download. This is because we want to avoid + // running nested message loop in main UI thread. + scoped_ptr<DragDropThread> drag_drop_thread_; + + // The flag to guard that EndDragging is not called twice. + bool drag_ended_; + + // Keep track of the old suspended state of the drop target. + bool old_drop_target_suspended_state_; + + DISALLOW_COPY_AND_ASSIGN(TabContentsDragWin); +}; + + +#endif // CHROME_BROWSER_VIEWS_TAB_CONTENTS_TAB_CONTENTS_DRAG_WIN_H_ diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.cc b/chrome/browser/views/tab_contents/tab_contents_view_win.cc index a6f8b3e..f6bb820 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_win.cc +++ b/chrome/browser/views/tab_contents/tab_contents_view_win.cc @@ -8,12 +8,10 @@ #include "app/gfx/canvas_paint.h" #include "app/os_exchange_data.h" -#include "app/os_exchange_data_provider_win.h" #include "base/file_path.h" #include "base/keyboard_codes.h" #include "base/time.h" #include "base/win_util.h" -#include "chrome/browser/bookmarks/bookmark_drag_data.h" #include "chrome/browser/browser.h" // TODO(beng): this dependency is awful. #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_request_manager.h" @@ -24,16 +22,13 @@ #include "chrome/browser/tab_contents/interstitial_page.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" -#include "chrome/browser/tab_contents/web_drag_source_win.h" #include "chrome/browser/tab_contents/web_drop_target_win.h" #include "chrome/browser/views/sad_tab_view.h" #include "chrome/browser/views/tab_contents/render_view_context_menu_win.h" -#include "chrome/common/url_constants.h" -#include "net/base/net_util.h" +#include "chrome/browser/views/tab_contents/tab_contents_drag_win.h" #include "views/focus/view_storage.h" #include "views/screen.h" #include "views/widget/root_view.h" -#include "webkit/glue/webdropdata.h" using WebKit::WebDragOperation; using WebKit::WebDragOperationNone; @@ -62,8 +57,6 @@ TabContentsViewWin::~TabContentsViewWin() { views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL) view_storage->RemoveView(last_focused_view_storage_id_); - - DCHECK(!drag_source_.get()); } void TabContentsViewWin::Unparent() { @@ -132,70 +125,11 @@ void TabContentsViewWin::GetContainerBounds(gfx::Rect* out) const { void TabContentsViewWin::StartDragging(const WebDropData& drop_data, WebDragOperationsMask ops) { - OSExchangeData data; - - // TODO(tc): Generate an appropriate drag image. - - // We set the file contents before the URL because the URL also sets file - // contents (to a .URL shortcut). We want to prefer file content data over a - // shortcut so we add it first. - if (!drop_data.file_contents.empty()) { - // Images without ALT text will only have a file extension so we need to - // synthesize one from the provided extension and URL. - FilePath file_name(drop_data.file_description_filename); - file_name = file_name.BaseName().RemoveExtension(); - if (file_name.value().empty()) { - // Retrieve the name from the URL. - file_name = net::GetSuggestedFilename(drop_data.url, "", "", FilePath()); - if (file_name.value().size() + drop_data.file_extension.size() + 1 > - MAX_PATH) { - file_name = FilePath(file_name.value().substr( - 0, MAX_PATH - drop_data.file_extension.size() - 2)); - } - } - file_name = file_name.ReplaceExtension(drop_data.file_extension); - data.SetFileContents(file_name.value(), drop_data.file_contents); - } - if (!drop_data.text_html.empty()) - data.SetHtml(drop_data.text_html, drop_data.html_base_url); - if (drop_data.url.is_valid()) { - if (drop_data.url.SchemeIs(chrome::kJavaScriptScheme)) { - // We don't want to allow javascript URLs to be dragged to the desktop, - // but we do want to allow them to be added to the bookmarks bar - // (bookmarklets). So we create a fake bookmark entry (BookmarkDragData - // object) which explorer.exe cannot handle, and write the entry to data. - BookmarkDragData::Element bm_elt; - bm_elt.is_url = true; - bm_elt.url = drop_data.url; - bm_elt.title = drop_data.url_title; - - BookmarkDragData bm_drag_data; - bm_drag_data.elements.push_back(bm_elt); - - // Pass in NULL as the profile so that the bookmark always adds the url - // rather than trying to move an existing url. - bm_drag_data.Write(NULL, &data); - } else { - data.SetURL(drop_data.url, drop_data.url_title); - } - } - if (!drop_data.plain_text.empty()) - data.SetString(drop_data.plain_text); - - drag_source_ = new WebDragSource(GetNativeView(), tab_contents()); - - DWORD effects; - - // We need to enable recursive tasks on the message loop so we can get - // updates while in the system DoDragDrop loop. - bool old_state = MessageLoop::current()->NestableTasksAllowed(); - MessageLoop::current()->SetNestableTasksAllowed(true); - DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source_, - DROPEFFECT_COPY | DROPEFFECT_LINK, &effects); - // TODO(snej): Use 'ops' param instead of hardcoding dropeffects - MessageLoop::current()->SetNestableTasksAllowed(old_state); + drag_handler_ = new TabContentsDragWin(this); + drag_handler_->StartDragging(drop_data, ops); +} - drag_source_ = NULL; +void TabContentsViewWin::EndDragging() { if (close_tab_after_drag_ends_) { close_tab_timer_.Start(base::TimeDelta::FromMilliseconds(0), this, &TabContentsViewWin::CloseTab); @@ -203,6 +137,8 @@ void TabContentsViewWin::StartDragging(const WebDropData& drop_data, if (tab_contents()->render_view_host()) tab_contents()->render_view_host()->DragSourceSystemDragEnded(); + + drag_handler_ = NULL; } void TabContentsViewWin::OnDestroy() { @@ -328,15 +264,15 @@ void TabContentsViewWin::RestoreFocus() { } bool TabContentsViewWin::IsDoingDrag() const { - return drag_source_.get() != NULL; + return drag_handler_.get() != NULL; } void TabContentsViewWin::CancelDragAndCloseTab() { DCHECK(IsDoingDrag()); // We can't close the tab while we're in the drag and - // |drag_source_->CancelDrag()| is async. Instead, set a flag to cancel + // |drag_handler_->CancelDrag()| is async. Instead, set a flag to cancel // the drag and when the drag nested message loop ends, close the tab. - drag_source_->CancelDrag(); + drag_handler_->CancelDrag(); close_tab_after_drag_ends_ = true; } diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.h b/chrome/browser/views/tab_contents/tab_contents_view_win.h index 5987baa..dc1e257 100644 --- a/chrome/browser/views/tab_contents/tab_contents_view_win.h +++ b/chrome/browser/views/tab_contents/tab_contents_view_win.h @@ -13,6 +13,7 @@ class RenderViewContextMenuWin; class SadTabView; +class TabContentsDragWin; struct WebDropData; class WebDragSource; class WebDropTarget; @@ -62,6 +63,10 @@ class TabContentsViewWin : public TabContentsView, // WidgetWin overridde. virtual views::FocusManager* GetFocusManager(); + void EndDragging(); + + WebDropTarget* drop_target() const { return drop_target_.get(); } + private: // A helper method for closing the tab. void CloseTab(); @@ -118,11 +123,6 @@ class TabContentsViewWin : public TabContentsView, // accessible when unparented. views::FocusManager* focus_manager_; - // |drag_source_| is our callback interface passed to the system when we - // want to initiate a drag and drop operation. We use it to tell if a - // drag operation is happening. - scoped_refptr<WebDragSource> drag_source_; - // Set to true if we want to close the tab after the system drag operation // has finished. bool close_tab_after_drag_ends_; @@ -130,6 +130,9 @@ class TabContentsViewWin : public TabContentsView, // Used to close the tab after the stack has unwound. base::OneShotTimer<TabContentsViewWin> close_tab_timer_; + // Used to handle the drag-and-drop. + scoped_refptr<TabContentsDragWin> drag_handler_; + DISALLOW_COPY_AND_ASSIGN(TabContentsViewWin); }; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 287324c..9a0674c 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -668,6 +668,8 @@ 'browser/download/download_started_animation.h', 'browser/download/download_util.cc', 'browser/download/download_util.h', + 'browser/download/drag_download_file_win.cc', + 'browser/download/drag_download_file_win.h', 'browser/download/save_file.cc', 'browser/download/save_file.h', 'browser/download/save_file_manager.cc', @@ -1823,6 +1825,8 @@ 'browser/views/tab_contents/render_view_context_menu_win.h', 'browser/views/tab_contents/render_view_context_menu_external_win.cc', 'browser/views/tab_contents/render_view_context_menu_external_win.h', + 'browser/views/tab_contents/tab_contents_drag_win.cc', + 'browser/views/tab_contents/tab_contents_drag_win.h', 'browser/views/tab_contents/tab_contents_view_gtk.cc', 'browser/views/tab_contents/tab_contents_view_gtk.h', 'browser/views/tab_contents/tab_contents_view_win.cc', diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 88288e9..5efe75e 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -1741,6 +1741,7 @@ struct ParamTraits<WebDropData> { WriteParam(m, p.identity); WriteParam(m, p.url); WriteParam(m, p.url_title); + WriteParam(m, p.download_url); WriteParam(m, p.file_extension); WriteParam(m, p.filenames); WriteParam(m, p.plain_text); @@ -1754,6 +1755,7 @@ struct ParamTraits<WebDropData> { ReadParam(m, iter, &p->identity) && ReadParam(m, iter, &p->url) && ReadParam(m, iter, &p->url_title) && + ReadParam(m, iter, &p->download_url) && ReadParam(m, iter, &p->file_extension) && ReadParam(m, iter, &p->filenames) && ReadParam(m, iter, &p->plain_text) && diff --git a/chrome/test/ui_test_utils.cc b/chrome/test/ui_test_utils.cc index 6723084..add23d5 100644 --- a/chrome/test/ui_test_utils.cc +++ b/chrome/test/ui_test_utils.cc @@ -162,6 +162,7 @@ class DownloadsCompleteObserver : public DownloadManager::Observer, } } + virtual void OnDownloadFileCompleted(DownloadItem* download) { } virtual void OnDownloadOpened(DownloadItem* download) {} // DownloadManager::Observer diff --git a/webkit/glue/webdropdata.cc b/webkit/glue/webdropdata.cc index b48c04c..bf2de89 100644 --- a/webkit/glue/webdropdata.cc +++ b/webkit/glue/webdropdata.cc @@ -19,6 +19,7 @@ WebDropData::WebDropData(const WebDragData& drag_data) : identity(0), url(drag_data.url()), url_title(drag_data.urlTitle()), + download_url(drag_data.downloadURL()), file_extension(drag_data.fileExtension()), plain_text(drag_data.plainText()), text_html(drag_data.htmlText()), diff --git a/webkit/glue/webdropdata.h b/webkit/glue/webdropdata.h index fe8db6a..96edfea 100644 --- a/webkit/glue/webdropdata.h +++ b/webkit/glue/webdropdata.h @@ -38,6 +38,9 @@ struct WebDropData { GURL url; string16 url_title; // The title associated with |url|. + // User is dragging a link out-of the webview. + GURL download_url; + // File extension for dragging images from a webview to the desktop. string16 file_extension; |