diff options
author | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 00:36:58 +0000 |
---|---|---|
committer | jianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 00:36:58 +0000 |
commit | 8ada5e06e95dd941314a0665da2948448fefa820 (patch) | |
tree | a76c5f54eaf16c29f595af2b76d5e5c2e0327b07 /chrome | |
parent | 595d56c647fc96cc9b6a5b8ec5b4787e719f981c (diff) | |
download | chromium_src-8ada5e06e95dd941314a0665da2948448fefa820.zip chromium_src-8ada5e06e95dd941314a0665da2948448fefa820.tar.gz chromium_src-8ada5e06e95dd941314a0665da2948448fefa820.tar.bz2 |
Support dragging a virtual file out of the browser on Linux. This is based on the discussion of drag-out feature on whatwg: http://list.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-August/022122.html
BUG=none
TEST=To test, drag an element that adds the DownloadURL format data via event.DataTransfer.setData, to the desktop or a folder.
Review URL: http://codereview.chromium.org/600154
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39405 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/cocoa/web_drag_source.mm | 75 | ||||
-rw-r--r-- | chrome/browser/download/drag_download_file.cc | 2 | ||||
-rw-r--r-- | chrome/browser/download/drag_download_util.cc | 57 | ||||
-rw-r--r-- | chrome/browser/download/drag_download_util.h | 34 | ||||
-rw-r--r-- | chrome/browser/gtk/tab_contents_drag_source.cc | 112 | ||||
-rw-r--r-- | chrome/browser/gtk/tab_contents_drag_source.h | 23 |
6 files changed, 231 insertions, 72 deletions
diff --git a/chrome/browser/cocoa/web_drag_source.mm b/chrome/browser/cocoa/web_drag_source.mm index b7818a6..1408db9 100644 --- a/chrome/browser/cocoa/web_drag_source.mm +++ b/chrome/browser/cocoa/web_drag_source.mm @@ -5,7 +5,6 @@ #import "chrome/browser/cocoa/web_drag_source.h" #include "base/file_path.h" -#include "base/file_util.h" #include "base/nsimage_cache_mac.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" @@ -19,7 +18,6 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view_mac.h" #include "net/base/file_stream.h" -#include "net/base/net_errors.h" #include "net/base/net_util.h" #import "third_party/mozilla/include/NSPasteboard+Utils.h" #include "webkit/glue/webdropdata.h" @@ -104,42 +102,6 @@ void PromiseWriterTask::Run() { // Let our destructor take care of business. } -class PromiseFileFinalizer : public DownloadFileObserver { - public: - PromiseFileFinalizer(DragDownloadFile* drag_file_downloader) - : drag_file_downloader_(drag_file_downloader) { - } - virtual ~PromiseFileFinalizer() { } - - // DownloadFileObserver methods. - virtual void OnDownloadCompleted(const FilePath& file_path); - virtual void OnDownloadAborted(); - - private: - void Cleanup(); - - scoped_refptr<DragDownloadFile> drag_file_downloader_; - - DISALLOW_COPY_AND_ASSIGN(PromiseFileFinalizer); -}; - -void PromiseFileFinalizer::Cleanup() { - if (drag_file_downloader_.get()) - drag_file_downloader_ = NULL; -} - -void PromiseFileFinalizer::OnDownloadCompleted(const FilePath& file_path) { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup)); -} - -void PromiseFileFinalizer::OnDownloadAborted() { - ChromeThread::PostTask( - ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup)); -} - } // namespace @@ -310,35 +272,14 @@ void PromiseFileFinalizer::OnDownloadAborted() { return nil; } - FileStream* fileStream = new FileStream; - DCHECK(fileStream); - if (!fileStream) - return nil; - - FilePath baseFileName = downloadFileName_.empty() ? + FilePath fileName = downloadFileName_.empty() ? GetFileNameFromDragData(*dropData_) : downloadFileName_; - FilePath pathName(SysNSStringToUTF8(path)); - FilePath fileName; - FilePath filePath; - unsigned seq; - const unsigned kMaxSeq = 99; - for (seq = 0; seq <= kMaxSeq; seq++) { - fileName = (seq == 0) ? baseFileName : - baseFileName.InsertBeforeExtension( - std::string("-") + UintToString(seq)); - filePath = pathName.Append(fileName); - - // Explicitly (and redundantly check) for file -- despite the fact that our - // open won't overwrite -- just to avoid log spew. - if (!file_util::PathExists(filePath) && - fileStream->Open(filePath, - base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE) == net::OK) - break; - } - if (seq > kMaxSeq) { - delete fileStream; + FilePath filePath(SysNSStringToUTF8(path)); + filePath = filePath.Append(fileName); + FileStream* fileStream = + drag_download_util::CreateFileStreamForDrop(&filePath); + if (!fileStream) return nil; - } if (downloadURL_.is_valid()) { TabContents* tabContents = [contentsView_ tabContents]; @@ -352,7 +293,7 @@ void PromiseFileFinalizer::OnDownloadAborted() { // The finalizer will take care of closing and deletion. dragFileDownloader->Start( - new PromiseFileFinalizer(dragFileDownloader)); + new drag_download_util::PromiseFileFinalizer(dragFileDownloader)); } else { // The writer will take care of closing and deletion. g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, @@ -360,7 +301,7 @@ void PromiseFileFinalizer::OnDownloadAborted() { } // Once we've created the file, we should return the file name. - return SysUTF8ToNSString(fileName.value()); + return SysUTF8ToNSString(filePath.BaseName().value()); } @end // @implementation WebDragSource diff --git a/chrome/browser/download/drag_download_file.cc b/chrome/browser/download/drag_download_file.cc index e55ccf5..8267621 100644 --- a/chrome/browser/download/drag_download_file.cc +++ b/chrome/browser/download/drag_download_file.cc @@ -33,7 +33,7 @@ DragDownloadFile::DragDownloadFile( #if defined(OS_WIN) DCHECK(!file_name_or_path.empty() && !file_stream.get()); file_name_ = file_name_or_path; -#elif defined(OS_MACOSX) +#elif defined(OS_POSIX) DCHECK(!file_name_or_path.empty() && file_stream.get()); file_path_ = file_name_or_path; #endif diff --git a/chrome/browser/download/drag_download_util.cc b/chrome/browser/download/drag_download_util.cc index ed34e70..a617d41 100644 --- a/chrome/browser/download/drag_download_util.cc +++ b/chrome/browser/download/drag_download_util.cc @@ -5,8 +5,17 @@ #include "chrome/browser/download/drag_download_util.h" #include "base/string_util.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/task.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/chrome_thread.h" #include "googleurl/src/gurl.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" + +using net::FileStream; namespace drag_download_util { @@ -45,4 +54,52 @@ bool ParseDownloadMetadata(const string16& metadata, return true; } +FileStream* CreateFileStreamForDrop(FilePath* file_path) { + DCHECK(file_path && !file_path->empty()); + + scoped_ptr<FileStream> file_stream(new FileStream); + const int kMaxSeq = 99; + for (int seq = 0; seq <= kMaxSeq; seq++) { + FilePath new_file_path; + if (seq == 0) { + new_file_path = *file_path; + } else { + #if defined(OS_WIN) + std::wstring suffix = std::wstring(L"-") + IntToWString(seq); + #else + std::string suffix = std::string("-") + IntToString(seq); + #endif + new_file_path = file_path->InsertBeforeExtension(suffix); + } + + // Explicitly (and redundantly check) for file -- despite the fact that our + // open won't overwrite -- just to avoid log spew. + if (!file_util::PathExists(new_file_path) && + file_stream->Open(new_file_path, base::PLATFORM_FILE_CREATE | + base::PLATFORM_FILE_WRITE) == net::OK) { + *file_path = new_file_path; + return file_stream.release(); + } + } + + return NULL; +} + +void PromiseFileFinalizer::Cleanup() { + if (drag_file_downloader_.get()) + drag_file_downloader_ = NULL; +} + +void PromiseFileFinalizer::OnDownloadCompleted(const FilePath& file_path) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup)); +} + +void PromiseFileFinalizer::OnDownloadAborted() { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup)); +} + } // namespace drag_download_util diff --git a/chrome/browser/download/drag_download_util.h b/chrome/browser/download/drag_download_util.h index 914dfda..c6f8442 100644 --- a/chrome/browser/download/drag_download_util.h +++ b/chrome/browser/download/drag_download_util.h @@ -5,10 +5,17 @@ #ifndef CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ #define CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ +#include "app/download_file_interface.h" #include "base/basictypes.h" -#include "base/file_path.h" +#include "base/ref_counted.h" +#include "base/string16.h" +#include "chrome/browser/download/drag_download_file.h" +class FilePath; class GURL; +namespace net { +class FileStream; +} namespace drag_download_util { @@ -26,6 +33,31 @@ bool ParseDownloadMetadata(const string16& metadata, FilePath* file_name, GURL* url); +// Create a new file at the specified path. If the file already exists, try to +// insert the sequential unifier to produce a new file, like foo-01.txt. +// Return a FileStream if successful. +net::FileStream* CreateFileStreamForDrop(FilePath* file_path); + +// Implementation of DownloadFileObserver to finalize the download process. +class PromiseFileFinalizer : public DownloadFileObserver { + public: + explicit PromiseFileFinalizer(DragDownloadFile* drag_file_downloader) + : drag_file_downloader_(drag_file_downloader) { + } + virtual ~PromiseFileFinalizer() { } + + // DownloadFileObserver methods. + virtual void OnDownloadCompleted(const FilePath& file_path); + virtual void OnDownloadAborted(); + + private: + void Cleanup(); + + scoped_refptr<DragDownloadFile> drag_file_downloader_; + + DISALLOW_COPY_AND_ASSIGN(PromiseFileFinalizer); +}; + } // namespace drag_download_util #endif // CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_UTIL_H_ diff --git a/chrome/browser/gtk/tab_contents_drag_source.cc b/chrome/browser/gtk/tab_contents_drag_source.cc index 15b111c..22b78b2 100644 --- a/chrome/browser/gtk/tab_contents_drag_source.cc +++ b/chrome/browser/gtk/tab_contents_drag_source.cc @@ -5,13 +5,19 @@ #include "chrome/browser/gtk/tab_contents_drag_source.h" #include "app/gtk_dnd_util.h" +#include "base/file_util.h" #include "base/mime_util.h" #include "base/string_util.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/drag_download_file.h" +#include "chrome/browser/download/drag_download_util.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/gtk_util.h" +#include "net/base/file_stream.h" +#include "net/base/net_util.h" #include "webkit/glue/webdropdata.h" using WebKit::WebDragOperation; @@ -25,6 +31,8 @@ TabContentsDragSource::TabContentsDragSource( drag_widget_ = gtk_invisible_new(); g_signal_connect(drag_widget_, "drag-failed", G_CALLBACK(OnDragFailedThunk), this); + g_signal_connect(drag_widget_, "drag-begin", G_CALLBACK(OnDragBeginThunk), + this); g_signal_connect(drag_widget_, "drag-end", G_CALLBACK(OnDragEndThunk), this); g_signal_connect(drag_widget_, "drag-data-get", G_CALLBACK(OnDragDataGetThunk), this); @@ -35,6 +43,8 @@ TabContentsDragSource::~TabContentsDragSource() { g_signal_handlers_disconnect_by_func(drag_widget_, reinterpret_cast<gpointer>(OnDragFailedThunk), this); g_signal_handlers_disconnect_by_func(drag_widget_, + reinterpret_cast<gpointer>(OnDragBeginThunk), this); + g_signal_handlers_disconnect_by_func(drag_widget_, reinterpret_cast<gpointer>(OnDragEndThunk), this); g_signal_handlers_disconnect_by_func(drag_widget_, reinterpret_cast<gpointer>(OnDragDataGetThunk), this); @@ -71,6 +81,13 @@ void TabContentsDragSource::StartDragging(const WebDropData& drop_data, targets_mask |= GtkDndUtil::TEXT_HTML; if (!drop_data.file_contents.empty()) targets_mask |= GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS; + if (!drop_data.download_metadata.empty() && + drag_download_util::ParseDownloadMetadata(drop_data.download_metadata, + &wide_download_mime_type_, + &download_file_name_, + &download_url_)) { + targets_mask |= GtkDndUtil::DIRECT_SAVE_FILE; + } if (targets_mask == 0) { NOTIMPLEMENTED(); @@ -179,6 +196,64 @@ void TabContentsDragSource::OnDragDataGet( break; } + case GtkDndUtil::DIRECT_SAVE_FILE: { + char status_code = 'E'; + + // Retrieves the full file path (in file URL format) provided by the + // drop target by reading from the source window's XdndDirectSave0 + // property. + gint file_url_len = 0; + guchar* file_url_value = NULL; + if (gdk_property_get(context->source_window, + GtkDndUtil::GetAtomForTarget( + GtkDndUtil::DIRECT_SAVE_FILE), + GtkDndUtil::GetAtomForTarget( + GtkDndUtil::TEXT_PLAIN_NO_CHARSET), + 0, + 1024, + FALSE, + NULL, + NULL, + &file_url_len, + &file_url_value) && + file_url_value) { + // Convert from the file url to the file path. + GURL file_url(std::string(reinterpret_cast<char*>(file_url_value), + file_url_len)); + FilePath file_path; + if (net::FileURLToFilePath(file_url, &file_path)) { + // Open the file as a stream. + net::FileStream* file_stream = + drag_download_util::CreateFileStreamForDrop(&file_path); + if (file_stream) { + // Start downloading the file to the stream. + TabContents* tab_contents = tab_contents_view_->tab_contents(); + scoped_refptr<DragDownloadFile> drag_file_downloader = + new DragDownloadFile(file_path, + linked_ptr<net::FileStream>(file_stream), + download_url_, + tab_contents->GetURL(), + tab_contents->encoding(), + tab_contents); + drag_file_downloader->Start( + new drag_download_util::PromiseFileFinalizer( + drag_file_downloader)); + + // Set the status code to success. + status_code = 'S'; + } + } + + // Return the status code to the file manager. + gtk_selection_data_set(selection_data, + selection_data->target, + 8, + reinterpret_cast<guchar*>(&status_code), + 1); + } + break; + } + default: NOTREACHED(); } @@ -200,9 +275,44 @@ gboolean TabContentsDragSource::OnDragFailed() { return FALSE; } -void TabContentsDragSource::OnDragEnd(WebDragOperation operation) { +void TabContentsDragSource::OnDragBegin(GdkDragContext* drag_context) { + if (!download_url_.is_empty()) { + // Generate the file name based on both mime type and proposed file name. + std::string download_mime_type = UTF16ToUTF8(wide_download_mime_type_); + std::string content_disposition("attachment; filename="); + content_disposition += download_file_name_.value(); + FilePath generated_download_file_name; + DownloadManager::GenerateFileName(download_url_, + content_disposition, + std::string(), + download_mime_type, + &generated_download_file_name); + + // Pass the file name to the drop target by setting the source window's + // XdndDirectSave0 property. + gdk_property_change(drag_context->source_window, + GtkDndUtil::GetAtomForTarget( + GtkDndUtil::DIRECT_SAVE_FILE), + GtkDndUtil::GetAtomForTarget( + GtkDndUtil::TEXT_PLAIN_NO_CHARSET), + 8, + GDK_PROP_MODE_REPLACE, + reinterpret_cast<const guchar*>( + generated_download_file_name.value().c_str()), + generated_download_file_name.value().length()); + } +} + +void TabContentsDragSource::OnDragEnd(GdkDragContext* drag_context, + WebDragOperation operation) { MessageLoopForUI::current()->RemoveObserver(this); + if (!download_url_.is_empty()) { + gdk_property_delete(drag_context->source_window, + GtkDndUtil::GetAtomForTarget( + GtkDndUtil::DIRECT_SAVE_FILE)); + } + if (!drag_failed_) { gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView()); gfx::Point client = gtk_util::ClientPoint(GetContentNativeView()); diff --git a/chrome/browser/gtk/tab_contents_drag_source.h b/chrome/browser/gtk/tab_contents_drag_source.h index 8a81647..edef7ad 100644 --- a/chrome/browser/gtk/tab_contents_drag_source.h +++ b/chrome/browser/gtk/tab_contents_drag_source.h @@ -9,7 +9,10 @@ #include "app/gfx/native_widget_types.h" #include "base/basictypes.h" +#include "base/file_path.h" #include "base/message_loop.h" +#include "base/string16.h" +#include "googleurl/src/gurl.h" #include "third_party/WebKit/WebKit/chromium/public/WebDragOperation.h" class TabContents; @@ -42,13 +45,20 @@ class TabContentsDragSource : public MessageLoopForUI::Observer { return handler->OnDragFailed(); } gboolean OnDragFailed(); + static void OnDragBeginThunk(GtkWidget* widget, + GdkDragContext* drag_context, + TabContentsDragSource* handler) { + handler->OnDragBegin(drag_context); + } + void OnDragBegin(GdkDragContext* drag_context); static void OnDragEndThunk(GtkWidget* widget, GdkDragContext* drag_context, TabContentsDragSource* handler) { - handler->OnDragEnd(WebKit::WebDragOperationCopy); + handler->OnDragEnd(drag_context, WebKit::WebDragOperationCopy); // TODO(snej): Pass actual operation instead of hardcoding copy } - void OnDragEnd(WebKit::WebDragOperation operation); + void OnDragEnd(GdkDragContext* drag_context, + WebKit::WebDragOperation operation); static void OnDragDataGetThunk(GtkWidget* drag_widget, GdkDragContext* context, GtkSelectionData* selection_data, @@ -81,6 +91,15 @@ class TabContentsDragSource : public MessageLoopForUI::Observer { // out. GtkWidget* drag_widget_; + // The file mime type for a drag-out download. + string16 wide_download_mime_type_; + + // The file name to be saved to for a drag-out download. + FilePath download_file_name_; + + // The URL to download from for a drag-out download. + GURL download_url_; + DISALLOW_COPY_AND_ASSIGN(TabContentsDragSource); }; |