From 6f54e8b117f7fd49148ad3c7cc9a48f85985da1a Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 1 Feb 2013 17:55:48 +0000 Subject: [PATCH] Merge revision 1074 changes: - Linux: Update the drag&drop implementation to match other platforms (issue #876). git-svn-id: https://chromiumembedded.googlecode.com/svn/branches/1364@1075 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef1/cef.gyp | 2 + cef1/libcef/browser_impl.cc | 13 +- cef1/libcef/browser_impl.h | 7 + cef1/libcef/browser_impl_gtk.cc | 37 +- cef1/libcef/browser_webview_delegate_gtk.cc | 5 +- cef1/libcef/web_drag_source_gtk.cc | 399 ++++++++++++++++---- cef1/libcef/web_drag_source_gtk.h | 92 ++++- cef1/libcef/web_drag_utils_gtk.cc | 42 +++ cef1/libcef/web_drag_utils_gtk.h | 23 ++ cef1/libcef/web_drop_target_gtk.cc | 399 +++++++++++--------- cef1/libcef/web_drop_target_gtk.h | 78 +++- 11 files changed, 799 insertions(+), 298 deletions(-) create mode 100644 cef1/libcef/web_drag_utils_gtk.cc create mode 100644 cef1/libcef/web_drag_utils_gtk.h diff --git a/cef1/cef.gyp b/cef1/cef.gyp index 79f685cd1..15233f81f 100644 --- a/cef1/cef.gyp +++ b/cef1/cef.gyp @@ -854,6 +854,8 @@ 'libcef/webwidget_host_gtk.cc', 'libcef/web_drag_source_gtk.cc', 'libcef/web_drag_source_gtk.h', + 'libcef/web_drag_utils_gtk.cc', + 'libcef/web_drag_utils_gtk.h', 'libcef/web_drop_target_gtk.cc', 'libcef/web_drop_target_gtk.h', # Include the files needed for drag&drop that are otherwise excluded diff --git a/cef1/libcef/browser_impl.cc b/cef1/libcef/browser_impl.cc index 87f9fe61d..f829d939c 100644 --- a/cef1/libcef/browser_impl.cc +++ b/cef1/libcef/browser_impl.cc @@ -170,12 +170,13 @@ CefBrowserImpl::CefBrowserImpl(const CefWindowInfo& windowInfo, has_document_(false), is_dropping_(false), is_in_onsetfocus_(false), - browser_id_(_Context->GetNextBrowserID()) #if defined(OS_WIN) - , opener_was_disabled_by_modal_loop_(false), - internal_modal_message_loop_is_active_(false) + opener_was_disabled_by_modal_loop_(false), + internal_modal_message_loop_is_active_(false), +#elif defined(OS_LINUX) + last_mouse_down_(NULL), #endif -{ // NOLINT(whitespace/braces) + browser_id_(_Context->GetNextBrowserID()) { delegate_.reset(new BrowserWebViewDelegate(this)); popup_delegate_.reset(new BrowserWebViewDelegate(this)); nav_controller_.reset(new BrowserNavigationController(this)); @@ -796,6 +797,10 @@ void CefBrowserImpl::UIT_DestroyBrowser() { main_frame_ = NULL; +#if defined(OS_LINUX) + set_last_mouse_down(NULL); +#endif + if (request_context_proxy_.get()) { // Delete the proxy on the IO thread. CefThread::DeleteSoon(CefThread::IO, FROM_HERE, diff --git a/cef1/libcef/browser_impl.h b/cef1/libcef/browser_impl.h index b08b1b551..fe64555ae 100644 --- a/cef1/libcef/browser_impl.h +++ b/cef1/libcef/browser_impl.h @@ -351,6 +351,9 @@ class CefBrowserImpl : public CefBrowser { void set_internal_modal_message_loop_is_active(bool active) { internal_modal_message_loop_is_active_ = active; } +#elif defined(OS_LINUX) + void set_last_mouse_down(GdkEventButton* event); + GdkEventButton* last_mouse_down() { return last_mouse_down_; } #endif void set_popup_rect(const gfx::Rect& rect) { popup_rect_ = rect; } @@ -412,6 +415,10 @@ class CefBrowserImpl : public CefBrowser { bool internal_modal_message_loop_is_active_; #endif +#if defined(OS_LINUX) + GdkEventButton* last_mouse_down_; +#endif + // Map of frame id to reference. typedef std::map FrameMap; FrameMap frames_; diff --git a/cef1/libcef/browser_impl_gtk.cc b/cef1/libcef/browser_impl_gtk.cc index 1f44b07d1..5544b53fb 100644 --- a/cef1/libcef/browser_impl_gtk.cc +++ b/cef1/libcef/browser_impl_gtk.cc @@ -24,6 +24,22 @@ void window_destroyed(GtkWidget* widget, CefBrowserImpl* browser) { browser->UIT_DestroyBrowser(); } +gboolean button_press_event( + GtkWidget* widget, + GdkEventButton* event, + CefBrowserImpl* browser) { + browser->set_last_mouse_down(event); + return FALSE; +} + +gboolean focus_out_event( + GtkWidget* widget, + GdkEventFocus* focus, + CefBrowserImpl* browser) { + browser->set_last_mouse_down(NULL); + return FALSE; +} + } // namespace void CefBrowserImpl::ParentWindowWillClose() { @@ -98,8 +114,14 @@ bool CefBrowserImpl::UIT_CreateBrowser(const CefString& url) { g_signal_connect(G_OBJECT(window_info_.m_Widget), "destroy", G_CALLBACK(window_destroyed), this); - if (!settings_.drag_drop_disabled) + if (!settings_.drag_drop_disabled) { + g_signal_connect(G_OBJECT(window_info_.m_Widget), "button-press-event", + G_CALLBACK(button_press_event), this); + g_signal_connect(G_OBJECT(window_info_.m_Widget), "focus-out-event", + G_CALLBACK(focus_out_event), this); + delegate_->RegisterDragDrop(); + } Unlock(); @@ -202,3 +224,16 @@ bool CefBrowserImpl::UIT_IsViewVisible(gfx::NativeView view) { else return false; } + +void CefBrowserImpl::set_last_mouse_down(GdkEventButton* event) { + GdkEventButton* temp = NULL; + if (event) { + temp = reinterpret_cast( + gdk_event_copy(reinterpret_cast(event))); + } + + if (last_mouse_down_) + gdk_event_free(reinterpret_cast(last_mouse_down_)); + + last_mouse_down_ = temp; +} diff --git a/cef1/libcef/browser_webview_delegate_gtk.cc b/cef1/libcef/browser_webview_delegate_gtk.cc index 6829baa59..dfb95b5a7 100644 --- a/cef1/libcef/browser_webview_delegate_gtk.cc +++ b/cef1/libcef/browser_webview_delegate_gtk.cc @@ -435,7 +435,10 @@ void BrowserWebViewDelegate::startDragging( } } drag_source_ = new WebDragSource(browser_); - drag_source_->StartDragging(drop_data, mask, image, image_offset); + DCHECK(browser_->last_mouse_down()); + drag_source_->StartDragging(drop_data, mask, browser_->last_mouse_down(), + image.getSkBitmap(), + gfx::Vector2d(image_offset.x, image_offset.y)); } void BrowserWebViewDelegate::runModal() { diff --git a/cef1/libcef/web_drag_source_gtk.cc b/cef1/libcef/web_drag_source_gtk.cc index cc4d92589..fd5208035 100644 --- a/cef1/libcef/web_drag_source_gtk.cc +++ b/cef1/libcef/web_drag_source_gtk.cc @@ -1,5 +1,5 @@ -// Copyright (c) 2012 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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. @@ -11,25 +11,34 @@ #include "libcef/browser_impl.h" #include "libcef/cef_context.h" #include "libcef/drag_data_impl.h" +#include "libcef/drag_download_file.h" +#include "libcef/drag_download_util.h" +#include "libcef/web_drag_utils_gtk.h" #include "libcef/web_drop_target_gtk.h" +#include "base/nix/mime_util_xdg.h" +#include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "googleurl/src/gurl.h" +#include "net/base/file_stream.h" #include "net/base/net_util.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebDragData.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebImage.h" +#include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_gtk.h" -#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/gtk_dnd_util.h" +#include "ui/base/gtk/gtk_screen_util.h" #include "ui/gfx/gtk_util.h" #include "webkit/glue/webdropdata.h" #include "webkit/glue/window_open_disposition.h" - using WebKit::WebDragOperation; +using WebKit::WebDragOperationsMask; using WebKit::WebDragOperationNone; using WebKit::WebDragOperationCopy; using WebKit::WebDragOperationLink; @@ -37,107 +46,172 @@ using WebKit::WebDragOperationMove; using WebKit::WebDragOperationGeneric; using WebKit::WebPoint; using WebKit::WebView; -using WebKit::WebDragOperationsMask; -using ui::DragDropTypes; - -namespace { - -void drag_end(GtkWidget* widget, GdkDragContext* context, - WebDragSource* user_data) { - user_data->OnDragEnd(widget, context); -} - -bool drag_failed(GtkWidget* widget, GdkDragContext* context, - GtkDragResult result, WebDragSource* user_data) { - return user_data->OnDragFailed(widget, context, result); -} - -void drag_data_get(GtkWidget* widget, GdkDragContext* context, - GtkSelectionData* selection_data, guint target_type, - guint time, WebDragSource* user_data) { - user_data->OnDragDataGet(widget, context, selection_data, target_type, time); -} - -} // namespace WebDragSource::WebDragSource(CefBrowserImpl* browser) - : browser_(browser) { - widget_ = gtk_invisible_new(); - // At some point we might want to listen to drag-begin - g_signal_connect(widget_, "drag-end", G_CALLBACK(&drag_end), this); - g_signal_connect(widget_, "drag-failed", G_CALLBACK(&drag_failed), this); - g_signal_connect(widget_, "drag-data-get", G_CALLBACK(&drag_data_get), this); + : browser_(browser), + 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); } WebDragSource::~WebDragSource() { - gtk_widget_destroy(widget_); + // 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 WebDragSource::StartDragging(const WebDropData& drop_data, - WebDragOperationsMask mask, - const WebKit::WebImage& image, - const WebKit::WebPoint& image_offset) { - drop_data_.reset(new WebDropData(drop_data)); + WebDragOperationsMask allowed_ops, + GdkEventButton* last_mouse_down, + const SkBitmap& image, + const gfx::Vector2d& image_offset) { + // Guard against re-starting before previous drag completed. + if (drag_context_) { + NOTREACHED(); + getView()->dragSourceSystemDragEnded(); + return; + } + int targets_mask = 0; - if (!drop_data.text.is_null() && !drop_data.text.string().empty()) + + if (!drop_data.text.string().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.html.is_null() && !drop_data.html.string().empty()) + if (!drop_data.html.string().empty()) targets_mask |= ui::TEXT_HTML; - GtkTargetList* tl = ui::GetTargetListFromCodeMask(targets_mask); - - GdkEvent* event = gtk_get_current_event(); - GdkDragContext* context = gtk_drag_begin(widget_, tl, - (GdkDragAction)DragDropTypes::DragOperationToGdkDragAction(mask), 1, - event); - if (!image.isNull()) { - const SkBitmap& bitmap = image.getSkBitmap(); - GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bitmap); - gtk_drag_set_icon_pixbuf(context, pixbuf, image_offset.x, image_offset.y); - g_object_unref(pixbuf); - } else { - gtk_drag_set_icon_default(context); + 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; } - gdk_event_free(event); + if (!drop_data.custom_data.empty()) + targets_mask |= ui::CUSTOM_DATA; + + // 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( + base::nix::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. + GdkDragAction action = + web_drag_utils_gtk::WebDragOpToGdkDragAction(allowed_ops); + drag_context_ = gtk_drag_begin(drag_widget_, list, action, + 1, // Drags are always initiated by the left button. + reinterpret_cast(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(); + getView()->dragSourceSystemDragEnded(); + return; + } + + MessageLoopForUI::current()->AddObserver(this); } -void WebDragSource::OnDragEnd(GtkWidget* widget, GdkDragContext* context) { - gfx::Point client(0, 0); - gfx::Point screen(0, 0); - getView()->dragSourceEndedAt(client, screen, WebDragOperationNone); - getView()->dragSourceSystemDragEnded(); +WebKit::WebView* WebDragSource::getView() { + return browser_->UIT_GetWebView(); } -bool WebDragSource::OnDragFailed(GtkWidget* widget, GdkDragContext* context, - GtkDragResult result) { - gfx::Point client(0, 0); - gfx::Point screen(0, 0); - getView()->dragSourceEndedAt(client, screen, WebDragOperationNone); - return FALSE; +GtkWidget* WebDragSource::getNativeView() { + return browser_->UIT_GetWebViewHost()->view_handle(); } -void WebDragSource::OnDragDataGet(GtkWidget* sender, GdkDragContext* context, +void WebDragSource::WillProcessEvent(GdkEvent* event) { + // No-op. +} + +void WebDragSource::DidProcessEvent(GdkEvent* event) { + if (event->type != GDK_MOTION_NOTIFY) + return; + + GdkEventMotion* event_motion = reinterpret_cast(event); + gfx::Point client = ui::ClientPoint(getNativeView()); + gfx::Point screen(static_cast(event_motion->x_root), + static_cast(event_motion->y_root)); + + getView()->dragSourceMovedTo(client, screen, WebDragOperationNone); +} + +void WebDragSource::OnDragDataGet(GtkWidget* sender, + GdkDragContext* context, GtkSelectionData* selection_data, - guint target_type, guint time) { + guint target_type, + guint time) { + const int kBitsPerByte = 8; + switch (target_type) { case ui::TEXT_PLAIN: { - std::string utf8_text = drop_data_->text.is_null() ? - std::string() : UTF16ToUTF8(drop_data_->text.string()); + std::string utf8_text = UTF16ToUTF8(drop_data_->text.string()); gtk_selection_data_set_text(selection_data, utf8_text.c_str(), utf8_text.length()); break; } case ui::TEXT_HTML: { - std::string utf8_text = drop_data_->html.is_null() ? - std::string() : UTF16ToUTF8(drop_data_->html.string()); + // TODO(estade): change relative links to be absolute using + // |html_base_url|. + std::string utf8_text = UTF16ToUTF8(drop_data_->html.string()); gtk_selection_data_set(selection_data, ui::GetAtomForTarget(ui::TEXT_HTML), - 8, + kBitsPerByte, reinterpret_cast(utf8_text.c_str()), utf8_text.length()); break; @@ -151,12 +225,191 @@ void WebDragSource::OnDragDataGet(GtkWidget* sender, GdkDragContext* context, break; } + case ui::CHROME_WEBDROP_FILE_CONTENTS: { + gtk_selection_data_set( + selection_data, + drag_file_mime_type_, kBitsPerByte, + reinterpret_cast(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(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. + scoped_ptr file_stream( + drag_download_util::CreateFileStreamForDrop(&file_path)); + if (file_stream.get()) { + WebView* webview = getView(); + const GURL& page_url = webview->mainFrame()->document().url(); + const std::string& page_encoding = + webview->mainFrame()->document().encoding().utf8(); + + // Start downloading the file to the stream. + scoped_refptr drag_file_downloader = + new DragDownloadFile( + file_path, + file_stream.Pass(), + download_url_, + page_url, + page_encoding, + browser_->UIT_GetWebViewDelegate()); + 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, + gtk_selection_data_get_target(selection_data), + kBitsPerByte, + reinterpret_cast(&status_code), + 1); + } + break; + } + + case ui::CUSTOM_DATA: { + Pickle custom_data; + ui::WriteCustomDataToPickle(drop_data_->custom_data, &custom_data); + gtk_selection_data_set( + selection_data, + ui::GetAtomForTarget(ui::CUSTOM_DATA), + kBitsPerByte, + reinterpret_cast(custom_data.data()), + custom_data.size()); + break; + } + default: NOTREACHED(); } } -WebKit::WebView* WebDragSource::getView() { - return browser_->UIT_GetWebView(); +gboolean WebDragSource::OnDragFailed(GtkWidget* sender, + GdkDragContext* context, + GtkDragResult result) { + drag_failed_ = true; + + GtkWidget* native_view = getNativeView(); + gfx::Point client = ui::ClientPoint(native_view); + gfx::Point screen = ui::ScreenPoint(native_view); + + getView()->dragSourceEndedAt(client, screen, WebDragOperationNone); + + // Let the native failure animation run. + return FALSE; +} + +void WebDragSource::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 = "download"; + 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( + 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_get_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 WebDragSource::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_) { + GtkWidget* native_view = getNativeView(); + gfx::Point client = ui::ClientPoint(native_view); + gfx::Point screen = ui::ScreenPoint(native_view); + + getView()->dragSourceEndedAt(client, screen, + web_drag_utils_gtk::GdkDragActionToWebDragOp(drag_context->action)); + } + + getView()->dragSourceSystemDragEnded(); + + drop_data_.reset(); + drag_context_ = NULL; +} + +gboolean WebDragSource::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/cef1/libcef/web_drag_source_gtk.h b/cef1/libcef/web_drag_source_gtk.h index 076747b65..157f6b598 100644 --- a/cef1/libcef/web_drag_source_gtk.h +++ b/cef1/libcef/web_drag_source_gtk.h @@ -1,5 +1,5 @@ -// Copyright (c) 2012 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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. @@ -7,12 +7,20 @@ #define CEF_LIBCEF_WEB_DRAG_SOURCE_GTK_H_ #include +#include "base/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/string16.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/vector2d.h" class CefBrowserImpl; -class WebDropData; +struct WebDropData; namespace WebKit { class WebImage; @@ -20,28 +28,82 @@ class WebPoint; class WebView; } -class WebDragSource : public base::RefCounted { +class WebDragSource : public base::RefCounted, + public MessageLoopForUI::Observer { public: explicit WebDragSource(CefBrowserImpl* browser); virtual ~WebDragSource(); void StartDragging(const WebDropData& drop_data, - WebKit::WebDragOperationsMask mask, - const WebKit::WebImage& image, - const WebKit::WebPoint& image_offset); + WebKit::WebDragOperationsMask allowed_ops, + GdkEventButton* last_mouse_down, + const SkBitmap& image, + const gfx::Vector2d& image_offset); - void OnDragEnd(GtkWidget* widget, GdkDragContext* context); - bool OnDragFailed(GtkWidget* widget, GdkDragContext* context, - GtkDragResult result); - void OnDragDataGet(GtkWidget* sender, GdkDragContext* context, - GtkSelectionData* selection_data, guint target_type, - guint time); + // MessageLoop::Observer implementation: + virtual void WillProcessEvent(GdkEvent* event) OVERRIDE; + virtual void DidProcessEvent(GdkEvent* event) OVERRIDE; private: WebKit::WebView* getView(); - scoped_ptr drop_data_; + GtkWidget* getNativeView(); + + CHROMEGTK_CALLBACK_2(WebDragSource, gboolean, OnDragFailed, + GdkDragContext*, GtkDragResult); + CHROMEGTK_CALLBACK_1(WebDragSource, void, OnDragBegin, + GdkDragContext*); + CHROMEGTK_CALLBACK_1(WebDragSource, void, OnDragEnd, + GdkDragContext*); + CHROMEGTK_CALLBACK_4(WebDragSource, void, OnDragDataGet, + GdkDragContext*, GtkSelectionData*, guint, guint); + CHROMEGTK_CALLBACK_1(WebDragSource, gboolean, OnDragIconExpose, + GdkEventExpose*); + CefBrowserImpl* browser_; - GtkWidget* widget_; + + // 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 drop_data_; + + // The image used for depicting the drag, and the offset between the cursor + // and the top left pixel. + GdkPixbuf* drag_pixbuf_; + gfx::Vector2d 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(WebDragSource); }; #endif // CEF_LIBCEF_WEB_DRAG_SOURCE_GTK_H_ diff --git a/cef1/libcef/web_drag_utils_gtk.cc b/cef1/libcef/web_drag_utils_gtk.cc new file mode 100644 index 000000000..3b7a8af41 --- /dev/null +++ b/cef1/libcef/web_drag_utils_gtk.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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 "libcef/web_drag_utils_gtk.h" + +using WebKit::WebDragOperationsMask; +using WebKit::WebDragOperation; +using WebKit::WebDragOperationNone; +using WebKit::WebDragOperationCopy; +using WebKit::WebDragOperationLink; +using WebKit::WebDragOperationMove; + +namespace web_drag_utils_gtk { + +// From content/browser/web_contents/drag_utils_gtk.cc. + +GdkDragAction WebDragOpToGdkDragAction(WebDragOperationsMask op) { + GdkDragAction action = static_cast(0); + if (op & WebDragOperationCopy) + action = static_cast(action | GDK_ACTION_COPY); + if (op & WebDragOperationLink) + action = static_cast(action | GDK_ACTION_LINK); + if (op & WebDragOperationMove) + action = static_cast(action | GDK_ACTION_MOVE); + return action; +} + +WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action) { + WebDragOperationsMask op = WebDragOperationNone; + if (action & GDK_ACTION_COPY) + op = static_cast(op | WebDragOperationCopy); + if (action & GDK_ACTION_LINK) + op = static_cast(op | WebDragOperationLink); + if (action & GDK_ACTION_MOVE) + op = static_cast(op | WebDragOperationMove); + return op; +} + +} // namespace web_drag_utils_gtk + diff --git a/cef1/libcef/web_drag_utils_gtk.h b/cef1/libcef/web_drag_utils_gtk.h new file mode 100644 index 000000000..e3dbbc7a1 --- /dev/null +++ b/cef1/libcef/web_drag_utils_gtk.h @@ -0,0 +1,23 @@ +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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 CEF_LIBCEF_WEB_DRAG_UTILS_GTK_H_ +#define CEF_LIBCEF_WEB_DRAG_UTILS_GTK_H_ +#pragma once + +#include +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h" + +namespace web_drag_utils_gtk { + +// Convenience methods for converting between web drag operations and the GDK +// equivalent. +GdkDragAction WebDragOpToGdkDragAction(WebKit::WebDragOperationsMask op); +WebKit::WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action); + +} // namespace web_drag_utils_gtk + +#endif // CEF_LIBCEF_WEB_DRAG_UTILS_GTK_H_ + diff --git a/cef1/libcef/web_drop_target_gtk.cc b/cef1/libcef/web_drop_target_gtk.cc index 44af7bcfe..15e66bc71 100644 --- a/cef1/libcef/web_drop_target_gtk.cc +++ b/cef1/libcef/web_drop_target_gtk.cc @@ -1,5 +1,5 @@ -// Copyright (c) 2012 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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. @@ -11,6 +11,7 @@ #include "libcef/browser_impl.h" #include "libcef/cef_context.h" #include "libcef/drag_data_impl.h" +#include "libcef/web_drag_utils_gtk.h" #include "base/utf_string_conversions.h" #include "base/bind.h" @@ -19,9 +20,9 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebDragData.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_gtk.h" -#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/gtk_dnd_util.h" #include "ui/base/gtk/gtk_screen_util.h" #include "webkit/glue/webdropdata.h" @@ -36,120 +37,243 @@ using WebKit::WebDragOperationGeneric; using WebKit::WebDragOperationsMask; using WebKit::WebPoint; using WebKit::WebView; -using ui::DragDropTypes; namespace { -/// GTK callbacks -gboolean drag_motion(GtkWidget* widget, GdkDragContext* context, gint x, - gint y, guint time, WebDropTarget* user_data) { - return user_data->OnDragMove(widget, context, x, y, time); +int GetModifierFlags(GtkWidget* widget) { + int modifier_state = 0; + GdkModifierType state; + gdk_window_get_pointer(gtk_widget_get_window(widget), NULL, NULL, &state); + + if (state & GDK_SHIFT_MASK) + modifier_state |= WebKit::WebInputEvent::ShiftKey; + if (state & GDK_CONTROL_MASK) + modifier_state |= WebKit::WebInputEvent::ControlKey; + if (state & GDK_MOD1_MASK) + modifier_state |= WebKit::WebInputEvent::AltKey; + if (state & GDK_META_MASK) + modifier_state |= WebKit::WebInputEvent::MetaKey; + return modifier_state; } -void drag_leave(GtkWidget* widget, GdkDragContext* context, guint time, - WebDropTarget* user_data) { - user_data->OnDragLeave(widget, context, time); -} - -gboolean drag_drop(GtkWidget* widget, GdkDragContext* context, gint x, - gint y, guint time, WebDropTarget* user_data) { - return user_data->OnDragDrop(widget, context, x, y, time); -} - -void drag_data_received(GtkWidget* widget, GdkDragContext* context, - gint x, gint y, GtkSelectionData* data, guint info, - guint time, WebDropTarget* user_data) { - user_data->OnDragDataReceived(widget, context, x, y, data, info, time); -} - -int supported_targets[] = { - ui::TEXT_PLAIN, - ui::TEXT_URI_LIST, - ui::TEXT_HTML, - ui::NETSCAPE_URL, - ui::CHROME_NAMED_URL, - ui::TEXT_PLAIN_NO_CHARSET, -}; - -WebDragOperationsMask _mask(GdkDragContext* context) { - GdkDragAction propsed_action = context->suggested_action; - return (WebDragOperationsMask)DragDropTypes::GdkDragActionToDragOperation( - propsed_action); +WebDragOperationsMask GetOperationsMask(GdkDragContext* context) { + return web_drag_utils_gtk::GdkDragActionToWebDragOp(context->actions); } } // namespace WebDropTarget::WebDropTarget(CefBrowserImpl* browser) : browser_(browser), - data_requests_(0), context_(NULL), + data_requests_(0), + is_drop_target_(false), sent_drag_enter_(false), method_factory_(this) { - GtkWidget* widget = browser->UIT_GetWebViewHost()->view_handle(); - gtk_drag_dest_set(widget, (GtkDestDefaults)0, NULL, 0, - static_cast(GDK_ACTION_COPY | GDK_ACTION_LINK | - GDK_ACTION_MOVE)); - g_signal_connect(widget, "drag-motion", G_CALLBACK(&drag_motion), this); - g_signal_connect(widget, "drag-leave", G_CALLBACK(&drag_leave), this); - g_signal_connect(widget, "drag-drop", G_CALLBACK(&drag_drop), this); - g_signal_connect(widget, "drag-data-received", - G_CALLBACK(&drag_data_received), this); + widget_ = browser->UIT_GetWebViewHost()->view_handle(); + gtk_drag_dest_set(widget_, static_cast(0), + NULL, 0, + static_cast(GDK_ACTION_COPY | + GDK_ACTION_LINK | + GDK_ACTION_MOVE)); + g_signal_connect(widget_, "drag-motion", + G_CALLBACK(OnDragMotionThunk), this); + g_signal_connect(widget_, "drag-leave", + G_CALLBACK(OnDragLeaveThunk), this); + g_signal_connect(widget_, "drag-drop", + G_CALLBACK(OnDragDropThunk), this); + g_signal_connect(widget_, "drag-data-received", + G_CALLBACK(OnDragDataReceivedThunk), this); + // TODO(tony): Need a drag-data-delete handler for moving content out of + // the WebContents. http://crbug.com/38989 + + destroy_handler_ = g_signal_connect( + widget_, "destroy", G_CALLBACK(gtk_widget_destroyed), &widget_); } WebDropTarget::~WebDropTarget() { + if (widget_) { + gtk_drag_dest_unset(widget_); + g_signal_handler_disconnect(widget_, destroy_handler_); + } +} + +void WebDropTarget::DragLeave() { + getView()->dragTargetDragLeave(); + + drop_data_.reset(); } WebKit::WebView* WebDropTarget::getView() { return browser_->UIT_GetWebView(); } -BrowserWebViewDelegate* WebDropTarget::getDelegate() { - return browser_->UIT_GetWebViewDelegate(); +void WebDropTarget::UpdateDragStatus(WebDragOperation operation, gint time) { + if (context_) { + is_drop_target_ = operation != WebDragOperationNone; + GdkDragAction action = web_drag_utils_gtk::WebDragOpToGdkDragAction(operation); + gdk_drag_status(context_, action, time); + } } -bool WebDropTarget::OnDragMove(GtkWidget* widget, GdkDragContext* context, - gint x, gint y, guint time) { - WebView* webview = getView(); - gint widget_x, widget_y; - WebDragOperation operation; - gtk_widget_translate_coordinates(gtk_widget_get_toplevel(widget), widget, x, - y, &widget_x, &widget_y); - - // Request all the data and potentially start the DnD. +gboolean WebDropTarget::OnDragMotion(GtkWidget* sender, + GdkDragContext* context, + gint x, gint y, + guint time) { if (context_ != context) { + context_ = context; drop_data_.reset(new WebDropData); + is_drop_target_ = false; + + // text/plain must come before text/uri-list. This is a hack that works in + // conjunction with OnDragDataReceived. Since some file managers populate + // text/plain with file URLs when dragging files, we want to handle + // text/uri-list after text/plain so that the plain text can be cleared if + // it's a file drag. + static int supported_targets[] = { + ui::TEXT_PLAIN, + ui::TEXT_URI_LIST, + ui::TEXT_HTML, + ui::NETSCAPE_URL, + ui::CHROME_NAMED_URL, + // TODO(estade): support image drags? + ui::CUSTOM_DATA, + }; + data_requests_ = arraysize(supported_targets); for (size_t i = 0; i < arraysize(supported_targets); ++i) { - gtk_drag_get_data(widget, context, + gtk_drag_get_data(widget_, context, ui::GetAtomForTarget(supported_targets[i]), time); } } else if (data_requests_ == 0) { - operation = webview->dragTargetDragOver(WebPoint(x, y), - WebPoint(widget_x, widget_y), - _mask(context), - 0); - gdk_drag_status(context, - (GdkDragAction)DragDropTypes::DragOperationToGdkDragAction(operation), - time); + WebDragOperation operation = getView()->dragTargetDragOver( + ui::ClientPoint(widget_), + ui::ScreenPoint(widget_), + GetOperationsMask(context), + GetModifierFlags(widget_)); + + UpdateDragStatus(operation, time); } + return TRUE; } -// Real DragLeave -void WebDropTarget::DragLeave() { - WebView* webview = getView(); - webview->dragTargetDragLeave(); +void WebDropTarget::OnDragDataReceived( + GtkWidget* sender, GdkDragContext* context, gint x, gint y, + GtkSelectionData* data, guint info, guint time) { + // We might get the data from an old get_data() request that we no longer + // care about. + if (context != context_) + return; + + data_requests_--; + + // Decode the data. + gint data_length = gtk_selection_data_get_length(data); + const guchar* raw_data = gtk_selection_data_get_data(data); + GdkAtom target = gtk_selection_data_get_target(data); + if (raw_data && data_length > 0) { + // If the source can't provide us with valid data for a requested target, + // raw_data will be NULL. + if (target == ui::GetAtomForTarget(ui::TEXT_PLAIN)) { + guchar* text = gtk_selection_data_get_text(data); + if (text) { + drop_data_->text = NullableString16( + UTF8ToUTF16(std::string(reinterpret_cast(text))), + false); + g_free(text); + } + } else if (target == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) { + gchar** uris = gtk_selection_data_get_uris(data); + if (uris) { + drop_data_->url = GURL(); + for (gchar** uri_iter = uris; *uri_iter; uri_iter++) { + // Most file managers populate text/uri-list with file URLs when + // dragging files. To avoid exposing file system paths to web content, + // file URLs are never set as the URL content for the drop. + // TODO(estade): Can the filenames have a non-UTF8 encoding? + GURL url(*uri_iter); + FilePath file_path; + if (url.SchemeIs("file") && + net::FileURLToFilePath(url, &file_path)) { + drop_data_->filenames.push_back( + WebDropData::FileInfo(UTF8ToUTF16(file_path.value()), + string16())); + // This is a hack. Some file managers also populate text/plain with + // a file URL when dragging files, so we clear it to avoid exposing + // it to the web content. + drop_data_->text = NullableString16(true); + } else if (!drop_data_->url.is_valid()) { + // Also set the first non-file URL as the URL content for the drop. + drop_data_->url = url; + } + } + g_strfreev(uris); + } + } else if (target == ui::GetAtomForTarget(ui::TEXT_HTML)) { + // TODO(estade): Can the html have a non-UTF8 encoding? + drop_data_->html = NullableString16( + UTF8ToUTF16(std::string(reinterpret_cast(raw_data), + data_length)), + false); + // We leave the base URL empty. + } else if (target == ui::GetAtomForTarget(ui::NETSCAPE_URL)) { + std::string netscape_url(reinterpret_cast(raw_data), + data_length); + size_t split = netscape_url.find_first_of('\n'); + if (split != std::string::npos) { + drop_data_->url = GURL(netscape_url.substr(0, split)); + if (split < netscape_url.size() - 1) + drop_data_->url_title = UTF8ToUTF16(netscape_url.substr(split + 1)); + } + } else if (target == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) { + ui::ExtractNamedURL(data, &drop_data_->url, &drop_data_->url_title); + } else if (target == ui::GetAtomForTarget(ui::CUSTOM_DATA)) { + ui::ReadCustomDataIntoMap( + raw_data, data_length, &drop_data_->custom_data); + } + } + + if (data_requests_ == 0) { + WebDragOperation operation = WebDragOperationNone; + bool handled = false; + + CefRefPtr client = browser_->GetClient(); + if (client.get()) { + CefRefPtr handler = client->GetDragHandler(); + if (handler.get()) { + CefRefPtr data(new CefDragDataImpl(*drop_data_.get())); + handled = handler->OnDragEnter( + browser_, data, + static_cast( + GetOperationsMask(context))); + } + } + + sent_drag_enter_ = !handled; + if (!handled) { + // Tell the renderer about the drag. + // |x| and |y| are seemingly arbitrary at this point. + operation = getView()->dragTargetDragEnter( + drop_data_->ToDragData(), + ui::ClientPoint(widget_), + ui::ScreenPoint(widget_), + GetOperationsMask(context), + GetModifierFlags(widget_)); + } + + UpdateDragStatus(operation, time); + } } -// GTK Sends DragDrop (immediately) after DragLeave -// So post re-post DragLeave allowing us to behave like chromium expects. -void WebDropTarget::OnDragLeave(GtkWidget* widget, GdkDragContext* context, +// The drag has left our widget; forward this information to the renderer. +void WebDropTarget::OnDragLeave(GtkWidget* sender, GdkDragContext* context, guint time) { + // Set |context_| to NULL to make sure we will recognize the next DragMotion + // as an enter. context_ = NULL; - drop_data_.reset(); - // Don't send the drag leave if we didn't send the drag enter. + // Don't send the drag leave event if we didn't send a drag enter event. if (!sent_drag_enter_) return; @@ -167,124 +291,27 @@ void WebDropTarget::OnDragLeave(GtkWidget* widget, GdkDragContext* context, base::Bind(&WebDropTarget::DragLeave, method_factory_.GetWeakPtr())); } -bool WebDropTarget::OnDragDrop(GtkWidget* widget, GdkDragContext* context, - gint x, gint y, guint time) { +// Called by GTK when the user releases the mouse, executing a drop. +gboolean WebDropTarget::OnDragDrop(GtkWidget* sender, GdkDragContext* context, + gint x, gint y, guint time) { + // Cancel that drag leave! method_factory_.InvalidateWeakPtrs(); - gint widget_x, widget_y; - gtk_widget_translate_coordinates(gtk_widget_get_toplevel(widget), widget, x, - y, &widget_x, &widget_y); browser_->set_is_dropping(true); - if (browser_->UIT_GetWebView()) { - browser_->UIT_GetWebView()->dragTargetDrop( - WebPoint(x, y), - WebPoint(widget_x, widget_y), - 0); + if (getView()) { + getView()->dragTargetDrop(ui::ClientPoint(widget_), + ui::ScreenPoint(widget_), + GetModifierFlags(widget_)); } + browser_->set_is_dropping(false); - context_ = NULL; - gtk_drag_finish(context, TRUE, FALSE, time); + + // The second parameter is just an educated guess as to whether or not the + // drag succeeded, but at least we will get the drag-end animation right + // sometimes. + gtk_drag_finish(context, is_drop_target_, FALSE, time); return TRUE; } - -void WebDropTarget::OnDragDataReceived(GtkWidget* widget, - GdkDragContext* context, gint x, gint y, - GtkSelectionData* data, guint info, - guint time) { - WebDragOperation operation; - data_requests_--; - - // If the source can't provide us with valid data for a requested target, - // data->data will be NULL. - if (data->data && data->length > 0) { - if (data->target == ui::GetAtomForTarget(ui::TEXT_PLAIN) || - data->target == ui::GetAtomForTarget(ui::TEXT_PLAIN_NO_CHARSET)) { - guchar* text = gtk_selection_data_get_text(data); - if (text) { - drop_data_->text = - NullableString16(UTF8ToUTF16((const char*)text), false); - g_free(text); - } - } else if (data->target == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) { - gchar** uris = gtk_selection_data_get_uris(data); - if (uris) { - drop_data_->url = GURL(); - for (gchar** uri_iter = uris; *uri_iter; uri_iter++) { - // Most file managers populate text/uri-list with file URLs when - // dragging files. To avoid exposing file system paths to web content, - // file URLs are never set as the URL content for the drop. - // TODO(estade): Can the filenames have a non-UTF8 encoding? - GURL url(*uri_iter); - FilePath file_path; - if (url.SchemeIs("file") && - net::FileURLToFilePath(url, &file_path)) { - drop_data_->filenames.push_back( - WebDropData::FileInfo(UTF8ToUTF16(file_path.value()), - string16())); - // This is a hack. Some file managers also populate text/plain with - // a file URL when dragging files, so we clear it to avoid exposing - // it to the web content. - // drop_data_->text = NullableString16(true); - } else if (!drop_data_->url.is_valid()) { - // Also set the first non-file URL as the URL content for the drop. - drop_data_->url = url; - } - } - g_strfreev(uris); - } - } else if (data->target == ui::GetAtomForTarget(ui::TEXT_HTML)) { - drop_data_->html = NullableString16( - UTF8ToUTF16(std::string(reinterpret_cast(data->data), - data->length)), false); - // We leave the base URL empty. - } else if (data->target == ui::GetAtomForTarget(ui::NETSCAPE_URL)) { - std::string netscape_url(reinterpret_cast(data->data), - data->length); - size_t split = netscape_url.find_first_of('\n'); - if (split != std::string::npos) { - drop_data_->url = GURL(netscape_url.substr(0, split)); - if (split < netscape_url.size() - 1) - drop_data_->url_title = UTF8ToUTF16(netscape_url.substr(split + 1)); - } - } else if (data->target == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) { - ui::ExtractNamedURL(data, &drop_data_->url, &drop_data_->url_title); - } - } - - if (data_requests_ == 0) { - CefRefPtr client = browser_->GetClient(); - if (client.get()) { - CefRefPtr handler = client->GetDragHandler(); - if (handler.get()) { - CefRefPtr data(new CefDragDataImpl(*drop_data_.get())); - if (handler->OnDragEnter(browser_, data, - (cef_drag_operations_mask_t)_mask(context))) { - operation = WebDragOperationNone; - sent_drag_enter_ = false; - gdk_drag_status(context, - (GdkDragAction)DragDropTypes::DragOperationToGdkDragAction( - operation), - time); - return; - } - } - } - gint widget_x, widget_y; - gtk_widget_translate_coordinates(gtk_widget_get_toplevel(widget), widget, - x, y, &widget_x, &widget_y); - WebView* webview = getView(); - sent_drag_enter_ = true; - operation = webview->dragTargetDragEnter(drop_data_->ToDragData(), - WebPoint(x, y), - WebPoint(widget_x, widget_y), - _mask(context), - 0); - gdk_drag_status(context, - (GdkDragAction)DragDropTypes::DragOperationToGdkDragAction(operation), - time); - context_ = context; - } -} diff --git a/cef1/libcef/web_drop_target_gtk.h b/cef1/libcef/web_drop_target_gtk.h index 96c980bef..72d98f8ab 100644 --- a/cef1/libcef/web_drop_target_gtk.h +++ b/cef1/libcef/web_drop_target_gtk.h @@ -1,5 +1,5 @@ -// Copyright (c) 2012 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2012 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. @@ -10,6 +10,8 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h" +#include "ui/base/gtk/gtk_signal.h" class BrowserWebViewDelegate; class CefBrowserImpl; @@ -24,28 +26,68 @@ class WebDropTarget : public base::RefCounted { explicit WebDropTarget(CefBrowserImpl* browser); virtual ~WebDropTarget(); - WebKit::WebView* getView(); - BrowserWebViewDelegate* getDelegate(); - - // Called by GTK callbacks - bool OnDragMove(GtkWidget* widget, GdkDragContext* context, gint x, gint y, - guint time); - void OnDragLeave(GtkWidget* widget, GdkDragContext* context, guint time); - bool OnDragDrop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, - guint time); - void OnDragEnd(GtkWidget* widget, GdkDragContext* context, guint time); - void OnDragDataReceived(GtkWidget* widget, GdkDragContext* context, gint x, - gint y, GtkSelectionData* data, guint info, - guint time); + // Informs the renderer when a system drag has left the render view. void DragLeave(); private: + WebKit::WebView* getView(); + + // This is called when the renderer responds to a drag motion event. We must + // update the system drag cursor. + void UpdateDragStatus(WebKit::WebDragOperation operation, gint time); + + // Called when a system drag crosses over the render view. As there is no drag + // enter event, we treat it as an enter event (and not a regular motion event) + // when |context_| is NULL. + CHROMEGTK_CALLBACK_4(WebDropTarget, gboolean, OnDragMotion, GdkDragContext*, + gint, gint, guint); + + // We make a series of requests for the drag data when the drag first enters + // the render view. This is the callback that is used to give us the data + // for each individual target. When |data_requests_| reaches 0, we know we + // have attained all the data, and we can finally tell the renderer about the + // drag. + CHROMEGTK_CALLBACK_6(WebDropTarget, void, OnDragDataReceived, + GdkDragContext*, gint, gint, GtkSelectionData*, + guint, guint); + + // The drag has left our widget; forward this information to the renderer. + CHROMEGTK_CALLBACK_2(WebDropTarget, void, OnDragLeave, GdkDragContext*, + guint); + + // Called by GTK when the user releases the mouse, executing a drop. + CHROMEGTK_CALLBACK_4(WebDropTarget, gboolean, OnDragDrop, GdkDragContext*, + gint, gint, guint); + CefBrowserImpl* browser_; - scoped_ptr drop_data_; - bool entered_; - int data_requests_; + + // The render view. + GtkWidget* widget_; + + // The current drag context for system drags over our render view, or NULL if + // there is no system drag or the system drag is not over our render view. GdkDragContext* context_; + + // The data for the current drag, or NULL if |context_| is NULL. + scoped_ptr drop_data_; + + // The number of outstanding drag data requests we have sent to the drag + // source. + int data_requests_; + + // Whether the cursor is over a drop target, according to the last message we + // got from the renderer. + bool is_drop_target_; + + // Handler ID for the destroy signal handler. We connect to the destroy + // signal handler so that we won't call dest_unset on it after it is + // destroyed, but we have to cancel the handler if we are destroyed before + // |widget_| is. + int destroy_handler_; + + // Whether the drag enter event was sent to the renderer. bool sent_drag_enter_; + base::WeakPtrFactory method_factory_; };