summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorerg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-19 00:13:30 +0000
committererg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-19 00:13:30 +0000
commit5fa4027dd7965e12897c66c907134ba0a96ec676 (patch)
treebbdc06df22e3c6698045794945f2bffe5ecda335 /content
parent329ca5832efb63459b5683cf2dd1b9fb486adb86 (diff)
downloadchromium_src-5fa4027dd7965e12897c66c907134ba0a96ec676.zip
chromium_src-5fa4027dd7965e12897c66c907134ba0a96ec676.tar.gz
chromium_src-5fa4027dd7965e12897c66c907134ba0a96ec676.tar.bz2
content: Move the file chrome/.../tab_contents_drag_source.* to content/.../web_drag_source_gtk.*
This does not change the class name in these files; it's a file rename. Proper class name and namespace changes will happen in the next patch. BUG=none TEST=none Review URL: http://codereview.chromium.org/8349013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106176 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/tab_contents/web_drag_source_gtk.cc378
-rw-r--r--content/browser/tab_contents/web_drag_source_gtk.h110
-rw-r--r--content/content_browser.gypi2
3 files changed, 490 insertions, 0 deletions
diff --git a/content/browser/tab_contents/web_drag_source_gtk.cc b/content/browser/tab_contents/web_drag_source_gtk.cc
new file mode 100644
index 0000000..a40b007
--- /dev/null
+++ b/content/browser/tab_contents/web_drag_source_gtk.cc
@@ -0,0 +1,378 @@
+// Copyright (c) 2011 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 "content/browser/tab_contents/web_drag_source_gtk.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/mime_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/content_browser_client.h"
+#include "content/browser/download/drag_download_file.h"
+#include "content/browser/download/drag_download_util.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/tab_contents/drag_utils_gtk.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/browser/tab_contents/tab_contents_view.h"
+#include "net/base/file_stream.h"
+#include "net/base/net_util.h"
+#include "ui/base/dragdrop/gtk_dnd_util.h"
+#include "ui/base/gtk/gtk_screen_utils.h"
+#include "ui/gfx/gtk_util.h"
+#include "webkit/glue/webdropdata.h"
+
+using WebKit::WebDragOperation;
+using WebKit::WebDragOperationsMask;
+using WebKit::WebDragOperationNone;
+
+TabContentsDragSource::TabContentsDragSource(TabContents* tab_contents)
+ : tab_contents_(tab_contents),
+ drag_pixbuf_(NULL),
+ drag_failed_(false),
+ drag_widget_(gtk_invisible_new()),
+ drag_context_(NULL),
+ drag_icon_(gtk_window_new(GTK_WINDOW_POPUP)) {
+ signals_.Connect(drag_widget_, "drag-failed",
+ G_CALLBACK(OnDragFailedThunk), this);
+ signals_.Connect(drag_widget_, "drag-begin",
+ G_CALLBACK(OnDragBeginThunk),
+ this);
+ signals_.Connect(drag_widget_, "drag-end",
+ G_CALLBACK(OnDragEndThunk), this);
+ signals_.Connect(drag_widget_, "drag-data-get",
+ G_CALLBACK(OnDragDataGetThunk), this);
+
+ signals_.Connect(drag_icon_, "expose-event",
+ G_CALLBACK(OnDragIconExposeThunk), this);
+}
+
+TabContentsDragSource::~TabContentsDragSource() {
+ // Break the current drag, if any.
+ if (drop_data_.get()) {
+ gtk_grab_add(drag_widget_);
+ gtk_grab_remove(drag_widget_);
+ MessageLoopForUI::current()->RemoveObserver(this);
+ drop_data_.reset();
+ }
+
+ gtk_widget_destroy(drag_widget_);
+ gtk_widget_destroy(drag_icon_);
+}
+
+void TabContentsDragSource::StartDragging(const WebDropData& drop_data,
+ WebDragOperationsMask allowed_ops,
+ GdkEventButton* last_mouse_down,
+ const SkBitmap& image,
+ const gfx::Point& image_offset) {
+ // Guard against re-starting before previous drag completed.
+ if (drag_context_) {
+ NOTREACHED();
+ tab_contents_->SystemDragEnded();
+ return;
+ }
+
+ int targets_mask = 0;
+
+ if (!drop_data.plain_text.empty())
+ targets_mask |= ui::TEXT_PLAIN;
+ if (drop_data.url.is_valid()) {
+ targets_mask |= ui::TEXT_URI_LIST;
+ targets_mask |= ui::CHROME_NAMED_URL;
+ targets_mask |= ui::NETSCAPE_URL;
+ }
+ if (!drop_data.text_html.empty())
+ targets_mask |= ui::TEXT_HTML;
+ if (!drop_data.file_contents.empty())
+ targets_mask |= ui::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 |= ui::DIRECT_SAVE_FILE;
+ }
+
+ // NOTE: Begin a drag even if no targets present. Otherwise, things like
+ // draggable list elements will not work.
+
+ drop_data_.reset(new WebDropData(drop_data));
+
+ // The image we get from WebKit makes heavy use of alpha-shading. This looks
+ // bad on non-compositing WMs. Fall back to the default drag icon.
+ if (!image.isNull() && ui::IsScreenComposited())
+ drag_pixbuf_ = gfx::GdkPixbufFromSkBitmap(&image);
+ image_offset_ = image_offset;
+
+ GtkTargetList* list = ui::GetTargetListFromCodeMask(targets_mask);
+ if (targets_mask & ui::CHROME_WEBDROP_FILE_CONTENTS) {
+ // Looking up the mime type can hit the disk. http://crbug.com/84896
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ drag_file_mime_type_ = gdk_atom_intern(
+ mime_util::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
+ gtk_target_list_add(list, drag_file_mime_type_,
+ 0, ui::CHROME_WEBDROP_FILE_CONTENTS);
+ }
+
+ drag_failed_ = false;
+ // If we don't pass an event, GDK won't know what event time to start grabbing
+ // mouse events. Technically it's the mouse motion event and not the mouse
+ // down event that causes the drag, but there's no reliable way to know
+ // *which* motion event initiated the drag, so this will have to do.
+ // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
+ // and holds and doesn't start dragging for a long time. I doubt it matters
+ // much, but we should probably look into the possibility of getting the
+ // initiating event from webkit.
+ drag_context_ = gtk_drag_begin(drag_widget_, list,
+ content::WebDragOpToGdkDragAction(allowed_ops),
+ 1, // Drags are always initiated by the left button.
+ reinterpret_cast<GdkEvent*>(last_mouse_down));
+ // The drag adds a ref; let it own the list.
+ gtk_target_list_unref(list);
+
+ // Sometimes the drag fails to start; |context| will be NULL and we won't
+ // get a drag-end signal.
+ if (!drag_context_) {
+ drag_failed_ = true;
+ drop_data_.reset();
+ tab_contents_->SystemDragEnded();
+ return;
+ }
+
+ MessageLoopForUI::current()->AddObserver(this);
+}
+
+void TabContentsDragSource::WillProcessEvent(GdkEvent* event) {
+ // No-op.
+}
+
+void TabContentsDragSource::DidProcessEvent(GdkEvent* event) {
+ if (event->type != GDK_MOTION_NOTIFY)
+ return;
+
+ GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
+ gfx::Point client = ui::ClientPoint(GetContentNativeView());
+
+ if (tab_contents_->render_view_host()) {
+ tab_contents_->render_view_host()->DragSourceMovedTo(
+ client.x(), client.y(),
+ static_cast<int>(event_motion->x_root),
+ static_cast<int>(event_motion->y_root));
+ }
+}
+
+void TabContentsDragSource::OnDragDataGet(GtkWidget* sender,
+ GdkDragContext* context, GtkSelectionData* selection_data,
+ guint target_type, guint time) {
+ const int kBitsPerByte = 8;
+
+ switch (target_type) {
+ case ui::TEXT_PLAIN: {
+ std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
+ gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
+ utf8_text.length());
+ break;
+ }
+
+ case ui::TEXT_HTML: {
+ // TODO(estade): change relative links to be absolute using
+ // |html_base_url|.
+ std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
+ gtk_selection_data_set(selection_data,
+ ui::GetAtomForTarget(ui::TEXT_HTML),
+ kBitsPerByte,
+ reinterpret_cast<const guchar*>(utf8_text.c_str()),
+ utf8_text.length());
+ break;
+ }
+
+ case ui::TEXT_URI_LIST:
+ case ui::CHROME_NAMED_URL:
+ case ui::NETSCAPE_URL: {
+ ui::WriteURLWithName(selection_data, drop_data_->url,
+ drop_data_->url_title, target_type);
+ break;
+ }
+
+ case ui::CHROME_WEBDROP_FILE_CONTENTS: {
+ gtk_selection_data_set(
+ selection_data,
+ drag_file_mime_type_, kBitsPerByte,
+ reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
+ drop_data_->file_contents.length());
+ break;
+ }
+
+ case ui::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,
+ ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
+ ui::GetAtomForTarget(ui::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));
+ g_free(file_url_value);
+ 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.
+ 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();
+ }
+}
+
+gboolean TabContentsDragSource::OnDragFailed(GtkWidget* sender,
+ GdkDragContext* context,
+ GtkDragResult result) {
+ drag_failed_ = true;
+
+ gfx::Point root = ui::ScreenPoint(GetContentNativeView());
+ gfx::Point client = ui::ClientPoint(GetContentNativeView());
+
+ if (tab_contents_->render_view_host()) {
+ tab_contents_->render_view_host()->DragSourceEndedAt(
+ client.x(), client.y(), root.x(), root.y(),
+ WebDragOperationNone);
+ }
+
+ // Let the native failure animation run.
+ return FALSE;
+}
+
+void TabContentsDragSource::OnDragBegin(GtkWidget* sender,
+ GdkDragContext* drag_context) {
+ if (!download_url_.is_empty()) {
+ // Generate the file name based on both mime type and proposed file name.
+ std::string default_name =
+ content::GetContentClient()->browser()->GetDefaultDownloadName();
+ FilePath generated_download_file_name =
+ net::GenerateFileName(download_url_,
+ std::string(),
+ std::string(),
+ download_file_name_.value(),
+ UTF16ToUTF8(wide_download_mime_type_),
+ default_name);
+
+ // Pass the file name to the drop target by setting the source window's
+ // XdndDirectSave0 property.
+ gdk_property_change(drag_context->source_window,
+ ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE),
+ ui::GetAtomForTarget(ui::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());
+ }
+
+ if (drag_pixbuf_) {
+ gtk_widget_set_size_request(drag_icon_,
+ gdk_pixbuf_get_width(drag_pixbuf_),
+ gdk_pixbuf_get_height(drag_pixbuf_));
+
+ // We only need to do this once.
+ if (!GTK_WIDGET_REALIZED(drag_icon_)) {
+ GdkScreen* screen = gtk_widget_get_screen(drag_icon_);
+ GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
+ if (rgba)
+ gtk_widget_set_colormap(drag_icon_, rgba);
+ }
+
+ gtk_drag_set_icon_widget(drag_context, drag_icon_,
+ image_offset_.x(), image_offset_.y());
+ }
+}
+
+void TabContentsDragSource::OnDragEnd(GtkWidget* sender,
+ GdkDragContext* drag_context) {
+ if (drag_pixbuf_) {
+ g_object_unref(drag_pixbuf_);
+ drag_pixbuf_ = NULL;
+ }
+
+ MessageLoopForUI::current()->RemoveObserver(this);
+
+ if (!download_url_.is_empty()) {
+ gdk_property_delete(drag_context->source_window,
+ ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE));
+ }
+
+ if (!drag_failed_) {
+ gfx::Point root = ui::ScreenPoint(GetContentNativeView());
+ gfx::Point client = ui::ClientPoint(GetContentNativeView());
+
+ if (tab_contents_->render_view_host()) {
+ tab_contents_->render_view_host()->DragSourceEndedAt(
+ client.x(), client.y(), root.x(), root.y(),
+ content::GdkDragActionToWebDragOp(drag_context->action));
+ }
+ }
+
+ tab_contents_->SystemDragEnded();
+
+ drop_data_.reset();
+ drag_context_ = NULL;
+}
+
+gfx::NativeView TabContentsDragSource::GetContentNativeView() const {
+ return tab_contents_->view()->GetContentNativeView();
+}
+
+gboolean TabContentsDragSource::OnDragIconExpose(GtkWidget* sender,
+ GdkEventExpose* event) {
+ cairo_t* cr = gdk_cairo_create(event->window);
+ gdk_cairo_rectangle(cr, &event->area);
+ cairo_clip(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ gdk_cairo_set_source_pixbuf(cr, drag_pixbuf_, 0, 0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ return TRUE;
+}
diff --git a/content/browser/tab_contents/web_drag_source_gtk.h b/content/browser/tab_contents/web_drag_source_gtk.h
new file mode 100644
index 0000000..46061c6
--- /dev/null
+++ b/content/browser/tab_contents/web_drag_source_gtk.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 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 CONTENT_BROWSER_TAB_WEB_DRAG_SOURCE_GTK_H_
+#define CONTENT_BROWSER_TAB_WEB_DRAG_SOURCE_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "content/common/content_export.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h"
+#include "ui/base/gtk/gtk_signal.h"
+#include "ui/base/gtk/gtk_signal_registrar.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
+
+class SkBitmap;
+class TabContents;
+struct WebDropData;
+
+// TabContentsDragSource takes care of managing the drag from a TabContents
+// with Gtk.
+class CONTENT_EXPORT TabContentsDragSource : public MessageLoopForUI::Observer {
+ public:
+ explicit TabContentsDragSource(TabContents* tab_contents);
+ virtual ~TabContentsDragSource();
+
+ TabContents* tab_contents() const { return tab_contents_; }
+
+ // Starts a drag for the tab contents this TabContentsDragSource was
+ // created for.
+ void StartDragging(const WebDropData& drop_data,
+ WebKit::WebDragOperationsMask allowed_ops,
+ GdkEventButton* last_mouse_down,
+ const SkBitmap& image,
+ const gfx::Point& image_offset);
+
+ // MessageLoop::Observer implementation:
+ virtual void WillProcessEvent(GdkEvent* event);
+ virtual void DidProcessEvent(GdkEvent* event);
+
+ private:
+ CHROMEGTK_CALLBACK_2(TabContentsDragSource, gboolean, OnDragFailed,
+ GdkDragContext*, GtkDragResult);
+ CHROMEGTK_CALLBACK_1(TabContentsDragSource, void, OnDragBegin,
+ GdkDragContext*);
+ CHROMEGTK_CALLBACK_1(TabContentsDragSource, void, OnDragEnd,
+ GdkDragContext*);
+ CHROMEGTK_CALLBACK_4(TabContentsDragSource, void, OnDragDataGet,
+ GdkDragContext*, GtkSelectionData*, guint, guint);
+ CHROMEGTK_CALLBACK_1(TabContentsDragSource, gboolean, OnDragIconExpose,
+ GdkEventExpose*);
+
+ gfx::NativeView GetContentNativeView() const;
+
+ // The tab we're manging the drag for.
+ TabContents* tab_contents_;
+
+ // The drop data for the current drag (for drags that originate in the render
+ // view). Non-NULL iff there is a current drag.
+ scoped_ptr<WebDropData> drop_data_;
+
+ // The image used for depicting the drag, and the offset between the cursor
+ // and the top left pixel.
+ GdkPixbuf* drag_pixbuf_;
+ gfx::Point image_offset_;
+
+ // The mime type for the file contents of the current drag (if any).
+ GdkAtom drag_file_mime_type_;
+
+ // Whether the current drag has failed. Meaningless if we are not the source
+ // for a current drag.
+ bool drag_failed_;
+
+ // This is the widget we use to initiate drags. Since we don't use the
+ // renderer widget, we can persist drags even when our contents is switched
+ // out. We can't use an OwnedWidgetGtk because the GtkInvisible widget
+ // initialization code sinks the reference.
+ GtkWidget* drag_widget_;
+
+ // Context created once drag starts. A NULL value indicates that there is
+ // no drag currently in progress.
+ GdkDragContext* drag_context_;
+
+ // 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_;
+
+ // The widget that provides visual feedback for the drag. We can't use
+ // an OwnedWidgetGtk because the GtkWindow initialization code sinks
+ // the reference.
+ GtkWidget* drag_icon_;
+
+ ui::GtkSignalRegistrar signals_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabContentsDragSource);
+};
+
+#endif // CONTENT_BROWSER_TAB_WEB_DRAG_SOURCE_GTK_H_
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index e6975eb..35cd191 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -527,6 +527,8 @@
'browser/tab_contents/web_drag_dest_delegate_gtk.h',
'browser/tab_contents/web_drag_dest_gtk.cc',
'browser/tab_contents/web_drag_dest_gtk.h',
+ 'browser/tab_contents/web_drag_source_gtk.cc',
+ 'browser/tab_contents/web_drag_source_gtk.h',
'browser/trace_controller.cc',
'browser/trace_controller.h',
'browser/trace_message_filter.cc',