291 lines
11 KiB
C++
291 lines
11 KiB
C++
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
|
|
// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "libcef/web_drop_target_gtk.h"
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <string>
|
|
|
|
#include "libcef/browser_impl.h"
|
|
#include "libcef/cef_context.h"
|
|
#include "libcef/drag_data_impl.h"
|
|
|
|
#include "base/utf_string_conversions.h"
|
|
#include "base/bind.h"
|
|
#include "googleurl/src/gurl.h"
|
|
#include "net/base/net_util.h"
|
|
#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/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"
|
|
#include "webkit/glue/window_open_disposition.h"
|
|
|
|
using WebKit::WebDragOperation;
|
|
using WebKit::WebDragOperationNone;
|
|
using WebKit::WebDragOperationCopy;
|
|
using WebKit::WebDragOperationLink;
|
|
using WebKit::WebDragOperationMove;
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
WebDropTarget::WebDropTarget(CefBrowserImpl* browser)
|
|
: browser_(browser),
|
|
data_requests_(0),
|
|
context_(NULL),
|
|
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);
|
|
}
|
|
|
|
WebDropTarget::~WebDropTarget() {
|
|
}
|
|
|
|
WebKit::WebView* WebDropTarget::getView() {
|
|
return browser_->UIT_GetWebView();
|
|
}
|
|
|
|
BrowserWebViewDelegate* WebDropTarget::getDelegate() {
|
|
return browser_->UIT_GetWebViewDelegate();
|
|
}
|
|
|
|
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.
|
|
if (context_ != context) {
|
|
drop_data_.reset(new WebDropData);
|
|
data_requests_ = arraysize(supported_targets);
|
|
for (size_t i = 0; i < arraysize(supported_targets); ++i) {
|
|
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);
|
|
}
|
|
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* 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;
|
|
}
|
|
}
|