summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-19 00:36:58 +0000
committerjianli@chromium.org <jianli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-19 00:36:58 +0000
commit8ada5e06e95dd941314a0665da2948448fefa820 (patch)
treea76c5f54eaf16c29f595af2b76d5e5c2e0327b07 /chrome
parent595d56c647fc96cc9b6a5b8ec5b4787e719f981c (diff)
downloadchromium_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.mm75
-rw-r--r--chrome/browser/download/drag_download_file.cc2
-rw-r--r--chrome/browser/download/drag_download_util.cc57
-rw-r--r--chrome/browser/download/drag_download_util.h34
-rw-r--r--chrome/browser/gtk/tab_contents_drag_source.cc112
-rw-r--r--chrome/browser/gtk/tab_contents_drag_source.h23
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);
};