summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-15 18:49:58 +0000
committerjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-15 18:49:58 +0000
commit6aa4a1c041ca9bd2c3087c3c059a87193b1a82e1 (patch)
tree8e833c393312e866250077c15bc1d90464fe99d7
parent963dfb5a05c5b0e3fa8ed74d803f01cb10fd455e (diff)
downloadchromium_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
-rw-r--r--app/clipboard/clipboard_util_win.cc2
-rw-r--r--app/os_exchange_data.cc4
-rw-r--r--app/os_exchange_data.h48
-rw-r--r--app/os_exchange_data_provider_win.cc253
-rw-r--r--app/os_exchange_data_provider_win.h67
-rw-r--r--app/os_exchange_data_win_unittest.cc2
-rw-r--r--base/base_drag_source.cc12
-rw-r--r--base/base_drag_source.h6
-rw-r--r--base/base_drop_target.cc11
-rw-r--r--base/base_drop_target.h9
-rw-r--r--base/file_util.h9
-rw-r--r--base/file_util_win.cc9
-rw-r--r--base/message_loop_unittest.cc43
-rw-r--r--base/message_pump_win.cc3
-rw-r--r--base/message_pump_win.h3
-rw-r--r--chrome/browser/cocoa/download_item_mac.h1
-rw-r--r--chrome/browser/dom_ui/downloads_dom_handler.h1
-rw-r--r--chrome/browser/download/download_exe.cc11
-rw-r--r--chrome/browser/download/download_file.cc4
-rw-r--r--chrome/browser/download/download_file.h2
-rw-r--r--chrome/browser/download/download_manager.cc131
-rw-r--r--chrome/browser/download/download_manager.h59
-rw-r--r--chrome/browser/download/download_manager_unittest.cc6
-rw-r--r--chrome/browser/download/download_util.h4
-rw-r--r--chrome/browser/download/drag_download_file_win.cc217
-rw-r--r--chrome/browser/download/drag_download_file_win.h84
-rw-r--r--chrome/browser/download/save_package.cc10
-rw-r--r--chrome/browser/download/save_package.h2
-rw-r--r--chrome/browser/download/save_package_unittest.cc2
-rw-r--r--chrome/browser/gtk/download_item_gtk.h1
-rw-r--r--chrome/browser/history/download_types.h2
-rw-r--r--chrome/browser/renderer_host/download_resource_handler.cc7
-rw-r--r--chrome/browser/renderer_host/download_resource_handler.h5
-rw-r--r--chrome/browser/renderer_host/download_throttling_resource_handler.cc4
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc4
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.h1
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc2
-rw-r--r--chrome/browser/tab_contents/web_drag_source_win.cc32
-rw-r--r--chrome/browser/views/download_item_view.h1
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_drag_win.cc328
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_drag_win.h93
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.cc84
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.h13
-rwxr-xr-xchrome/chrome_browser.gypi4
-rw-r--r--chrome/common/render_messages.h2
-rw-r--r--chrome/test/ui_test_utils.cc1
-rw-r--r--webkit/glue/webdropdata.cc1
-rw-r--r--webkit/glue/webdropdata.h3
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,
&param->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;