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:
parent
868fa28fa1
commit
6983ac5ae3
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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,179 +37,153 @@ 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();
|
||||
}
|
||||
|
||||
// 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,
|
||||
guint time) {
|
||||
context_ = NULL;
|
||||
drop_data_.reset();
|
||||
|
||||
// Don't send the drag leave if we didn't send the drag enter.
|
||||
if (!sent_drag_enter_)
|
||||
return;
|
||||
|
||||
// Sometimes we get a drag-leave event before getting a drag-data-received
|
||||
// event. In that case, we don't want to bother the renderer with a
|
||||
// DragLeave event.
|
||||
if (data_requests_ != 0)
|
||||
return;
|
||||
|
||||
// When GTK sends us a drag-drop signal, it is shortly (and synchronously)
|
||||
// preceded by a drag-leave. The renderer doesn't like getting the signals
|
||||
// in this order so delay telling it about the drag-leave till we are sure
|
||||
// we are not getting a drop as well.
|
||||
MessageLoop::current()->PostTask(FROM_HERE,
|
||||
base::Bind(&WebDropTarget::DragLeave, method_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
bool WebDropTarget::OnDragDrop(GtkWidget* widget, GdkDragContext* context,
|
||||
gint x, gint y, guint time) {
|
||||
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);
|
||||
}
|
||||
browser_->set_is_dropping(false);
|
||||
context_ = NULL;
|
||||
gtk_drag_finish(context, TRUE, FALSE, time);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)) {
|
||||
// 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((const char*)text), false);
|
||||
drop_data_->text = NullableString16(
|
||||
UTF8ToUTF16(std::string(reinterpret_cast<const char*>(text))),
|
||||
false);
|
||||
g_free(text);
|
||||
}
|
||||
} else if (data->target == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
|
||||
} else if (target == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
|
||||
gchar** uris = gtk_selection_data_get_uris(data);
|
||||
if (uris) {
|
||||
drop_data_->url = GURL();
|
||||
|
@ -227,7 +202,7 @@ void WebDropTarget::OnDragDataReceived(GtkWidget* widget,
|
|||
// 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);
|
||||
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;
|
||||
|
@ -235,56 +210,108 @@ void WebDropTarget::OnDragDataReceived(GtkWidget* widget,
|
|||
}
|
||||
g_strfreev(uris);
|
||||
}
|
||||
} else if (data->target == ui::GetAtomForTarget(ui::TEXT_HTML)) {
|
||||
} 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<char*>(data->data),
|
||||
data->length)), false);
|
||||
UTF8ToUTF16(std::string(reinterpret_cast<const char*>(raw_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);
|
||||
} 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 (data->target == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
|
||||
} 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()));
|
||||
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;
|
||||
}
|
||||
handled = handler->OnDragEnter(
|
||||
browser_, data,
|
||||
static_cast<cef_drag_operations_mask_t>(
|
||||
GetOperationsMask(context)));
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Don't send the drag leave event if we didn't send a drag enter event.
|
||||
if (!sent_drag_enter_)
|
||||
return;
|
||||
|
||||
// Sometimes we get a drag-leave event before getting a drag-data-received
|
||||
// event. In that case, we don't want to bother the renderer with a
|
||||
// DragLeave event.
|
||||
if (data_requests_ != 0)
|
||||
return;
|
||||
|
||||
// When GTK sends us a drag-drop signal, it is shortly (and synchronously)
|
||||
// preceded by a drag-leave. The renderer doesn't like getting the signals
|
||||
// in this order so delay telling it about the drag-leave till we are sure
|
||||
// we are not getting a drop as well.
|
||||
MessageLoop::current()->PostTask(FROM_HERE,
|
||||
base::Bind(&WebDropTarget::DragLeave, method_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
browser_->set_is_dropping(true);
|
||||
|
||||
if (getView()) {
|
||||
getView()->dragTargetDrop(ui::ClientPoint(widget_),
|
||||
ui::ScreenPoint(widget_),
|
||||
GetModifierFlags(widget_));
|
||||
}
|
||||
|
||||
browser_->set_is_dropping(false);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue