Linux: Update the drag&drop implementation to match other platforms (issue #876).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1074 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2013-02-01 17:54:45 +00:00
parent 868fa28fa1
commit 6983ac5ae3
11 changed files with 799 additions and 298 deletions

View File

@ -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

View File

@ -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,

View File

@ -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<int64, CefFrameImpl*> FrameMap;
FrameMap frames_;

View File

@ -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<GdkEventButton*>(
gdk_event_copy(reinterpret_cast<GdkEvent*>(event)));
}
if (last_mouse_down_)
gdk_event_free(reinterpret_cast<GdkEvent*>(last_mouse_down_));
last_mouse_down_ = temp;
}

View File

@ -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() {

View File

@ -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<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();
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<GdkEventMotion*>(event);
gfx::Point client = ui::ClientPoint(getNativeView());
gfx::Point screen(static_cast<int>(event_motion->x_root),
static_cast<int>(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<const guchar*>(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<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.
scoped_ptr<net::FileStream> 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<DragDownloadFile> 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<guchar*>(&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<const guchar*>(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<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_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;
}

View File

@ -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 <gtk/gtk.h>
#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<WebDragSource> {
class WebDragSource : public base::RefCounted<WebDragSource>,
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<WebDropData> 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<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::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_

View File

@ -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<GdkDragAction>(0);
if (op & WebDragOperationCopy)
action = static_cast<GdkDragAction>(action | GDK_ACTION_COPY);
if (op & WebDragOperationLink)
action = static_cast<GdkDragAction>(action | GDK_ACTION_LINK);
if (op & WebDragOperationMove)
action = static_cast<GdkDragAction>(action | GDK_ACTION_MOVE);
return action;
}
WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action) {
WebDragOperationsMask op = WebDragOperationNone;
if (action & GDK_ACTION_COPY)
op = static_cast<WebDragOperationsMask>(op | WebDragOperationCopy);
if (action & GDK_ACTION_LINK)
op = static_cast<WebDragOperationsMask>(op | WebDragOperationLink);
if (action & GDK_ACTION_MOVE)
op = static_cast<WebDragOperationsMask>(op | WebDragOperationMove);
return op;
}
} // namespace web_drag_utils_gtk

View File

@ -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 <gdk/gdk.h>
#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_

View File

@ -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<GdkDragAction>(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<GtkDestDefaults>(0),
NULL, 0,
static_cast<GdkDragAction>(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<const char*>(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<const char*>(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<const char*>(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<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefDragHandler> handler = client->GetDragHandler();
if (handler.get()) {
CefRefPtr<CefDragData> data(new CefDragDataImpl(*drop_data_.get()));
handled = handler->OnDragEnter(
browser_, data,
static_cast<cef_drag_operations_mask_t>(
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<char*>(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<char*>(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<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefDragHandler> handler = client->GetDragHandler();
if (handler.get()) {
CefRefPtr<CefDragData> 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;
}
}

View File

@ -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<WebDropTarget> {
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<WebDropData> 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<WebDropData> 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<WebDropTarget> method_factory_;
};