summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tab_contents
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 21:49:26 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 21:49:26 +0000
commit7a4f563b87e5319d2b7892da301b4aed36160f34 (patch)
tree22ed0b456fb089316b18afe8950a340703be0d8a /chrome/browser/tab_contents
parent9ec8a8ce8b2cb0c29387ddccce7fb0fb356e0b2c (diff)
downloadchromium_src-7a4f563b87e5319d2b7892da301b4aed36160f34.zip
chromium_src-7a4f563b87e5319d2b7892da301b4aed36160f34.tar.gz
chromium_src-7a4f563b87e5319d2b7892da301b4aed36160f34.tar.bz2
GTK: Fancy html5 draggy stuff.
Also, don't crash in release mode when the tab contents is deleted during a drag. 1) Source side: when the tab contents is going down, abort the current drag (if any). Initiate drags on a GtkInvisible (matching firefox, among other programs I assume). This lets us give up ownership of the drag widget and let the drag unwind. 2) Destination side: make a seemingly harmless GTK assert point to a crbug bug. This warrants further investigation. BUG=16249 TEST=drag around ntp thumbnails BUG=18557 In release mode, you should be able to do all the following without crashing or getting any asserts besides the two mentioned in 18557 TEST=drag something from gedit over a tab that closes TEST=drag something from the render view over a tab that closes TEST=drag something from the render view off of the browser from within a tab that closes TEST=repeat all the above with a tab that is being swapped out according to the repro steps in bug 16073 Review URL: http://codereview.chromium.org/159889 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22542 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tab_contents')
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.cc165
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.h52
2 files changed, 157 insertions, 60 deletions
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
index 7adc0a2..0c3a072 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.cc
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
@@ -189,6 +189,21 @@ int GdkEventKeyToLayoutIndependentKeyval(const GdkEventKey* event) {
return event->keyval;
}
+// Get the current location of the mouse cursor relative to the screen.
+gfx::Point ScreenPoint(GtkWidget* widget) {
+ int x, y;
+ gdk_display_get_pointer(gtk_widget_get_display(widget), NULL, &x, &y,
+ NULL);
+ return gfx::Point(x, y);
+}
+
+// Get the current location of the mouse cursor relative to the widget.
+gfx::Point ClientPoint(GtkWidget* widget) {
+ int x, y;
+ gtk_widget_get_pointer(widget, &x, &y);
+ return gfx::Point(x, y);
+}
+
} // namespace
// A helper class that handles DnD for drops in the renderer. In GTK parlance,
@@ -287,7 +302,7 @@ class WebDragDest {
}
} else if (data_requests_ == 0) {
tab_contents_->render_view_host()->
- DragTargetDragOver(ClientPoint(), ScreenPoint());
+ DragTargetDragOver(ClientPoint(widget_), ScreenPoint(widget_));
drag_over_time_ = time;
}
@@ -348,7 +363,8 @@ class WebDragDest {
// Tell the renderer about the drag.
// |x| and |y| are seemingly arbitrary at this point.
tab_contents_->render_view_host()->
- DragTargetDragEnter(*drop_data_.get(), ClientPoint(), ScreenPoint());
+ DragTargetDragEnter(*drop_data_.get(),
+ ClientPoint(widget_), ScreenPoint(widget_));
drag_over_time_ = time;
}
}
@@ -373,7 +389,7 @@ class WebDragDest {
method_factory_.RevokeAll();
tab_contents_->render_view_host()->
- DragTargetDrop(ClientPoint(), ScreenPoint());
+ DragTargetDrop(ClientPoint(widget_), ScreenPoint(widget_));
// The second parameter is just an educated guess, but at least we will
// get the drag-end animation right sometimes.
@@ -381,20 +397,6 @@ class WebDragDest {
return TRUE;
}
- // Get the current location of the mouse cursor, relative to the screen.
- gfx::Point ScreenPoint() {
- int x, y;
- gdk_display_get_pointer(gtk_widget_get_display(widget_), NULL, &x, &y,
- NULL);
- return gfx::Point(x, y);
- }
-
- // Get the current location of the mouse cursor, relative to the render view.
- gfx::Point ClientPoint() {
- int x, y;
- gtk_widget_get_pointer(widget_, &x, &y);
- return gfx::Point(x, y);
- }
TabContents* tab_contents_;
// The render view.
@@ -435,7 +437,9 @@ TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents)
: TabContentsView(tab_contents),
floating_(gtk_floating_container_new()),
fixed_(gtk_fixed_new()),
- popup_view_(NULL) {
+ popup_view_(NULL),
+ drag_failed_(false),
+ drag_widget_(NULL) {
g_signal_connect(fixed_, "size-allocate",
G_CALLBACK(OnSizeAllocate), this);
g_signal_connect(floating_.get(), "set-floating-position",
@@ -446,6 +450,15 @@ TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents)
gtk_widget_show(floating_.get());
registrar_.Add(this, NotificationType::TAB_CONTENTS_CONNECTED,
Source<TabContents>(tab_contents));
+
+ // Renderer source DnD.
+ drag_widget_ = gtk_invisible_new();
+ g_signal_connect(drag_widget_, "drag-failed",
+ G_CALLBACK(OnDragFailedThunk), this);
+ g_signal_connect(drag_widget_, "drag-end", G_CALLBACK(OnDragEndThunk), this);
+ g_signal_connect(drag_widget_, "drag-data-get",
+ G_CALLBACK(OnDragDataGetThunk), this);
+ g_object_ref_sink(drag_widget_);
}
TabContentsViewGtk::~TabContentsViewGtk() {
@@ -522,14 +535,11 @@ RenderWidgetHostView* TabContentsViewGtk::CreateViewForWidget(
GDK_POINTER_MOTION_MASK);
g_signal_connect(content_view, "button-press-event",
G_CALLBACK(OnMouseDown), this);
+ InsertIntoContentArea(content_view);
- // Renderer DnD.
- g_signal_connect(content_view, "drag-end", G_CALLBACK(OnDragEnd), this);
- g_signal_connect(content_view, "drag-data-get", G_CALLBACK(OnDragDataGet),
- this);
+ // Renderer target DnD.
drag_dest_.reset(new WebDragDest(tab_contents(), content_view));
- InsertIntoContentArea(content_view);
return view;
}
@@ -543,7 +553,6 @@ gfx::NativeView TabContentsViewGtk::GetContentNativeView() const {
return tab_contents()->render_widget_host_view()->GetNativeView();
}
-
gfx::NativeWindow TabContentsViewGtk::GetTopLevelNativeWindow() const {
GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW);
return window ? GTK_WINDOW(window) : NULL;
@@ -612,7 +621,25 @@ void TabContentsViewGtk::GetContainerBounds(gfx::Rect* out) const {
}
void TabContentsViewGtk::OnContentsDestroy() {
- // TODO(estade): Windows uses this for some sort of plugin-related stuff.
+ // We don't want to try to handle drag events from this point on.
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragFailedThunk), this);
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragEndThunk), this);
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragDataGetThunk), this);
+
+ // 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_);
+ g_object_unref(drag_widget_);
+ drag_widget_ = NULL;
}
void TabContentsViewGtk::SetPageTitle(const std::wstring& title) {
@@ -722,6 +749,23 @@ void TabContentsViewGtk::Observe(NotificationType type,
}
}
+void TabContentsViewGtk::WillProcessEvent(GdkEvent* event) {
+ // No-op.
+}
+
+void TabContentsViewGtk::DidProcessEvent(GdkEvent* event) {
+ if (event->type != GDK_MOTION_NOTIFY)
+ return;
+
+ GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
+ gfx::Point client = ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceMovedTo(
+ client.x(), client.y(), event_motion->x_root, event_motion->y_root);
+ }
+}
+
void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
context_menu_.reset(new RenderViewContextMenuGtk(tab_contents(), params,
last_mouse_down_.time));
@@ -731,11 +775,6 @@ void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
// Render view DnD -------------------------------------------------------------
-void TabContentsViewGtk::DragEnded() {
- if (tab_contents()->render_view_host())
- tab_contents()->render_view_host()->DragSourceSystemDragEnded();
-}
-
void TabContentsViewGtk::StartDragging(const WebDropData& drop_data) {
DCHECK(GetContentNativeView());
@@ -754,8 +793,8 @@ void TabContentsViewGtk::StartDragging(const WebDropData& drop_data) {
if (targets_mask == 0) {
NOTIMPLEMENTED();
- DragEnded();
- return;
+ if (tab_contents()->render_view_host())
+ tab_contents()->render_view_host()->DragSourceSystemDragEnded();
}
drop_data_.reset(new WebDropData(drop_data));
@@ -768,6 +807,7 @@ void TabContentsViewGtk::StartDragging(const WebDropData& drop_data) {
0, GtkDndUtil::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
@@ -776,23 +816,22 @@ void TabContentsViewGtk::StartDragging(const WebDropData& drop_data) {
// 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.
- gtk_drag_begin(GetContentNativeView(), list, GDK_ACTION_COPY,
+ gtk_drag_begin(drag_widget_, list, GDK_ACTION_COPY,
1, // Drags are always initiated by the left button.
reinterpret_cast<GdkEvent*>(&last_mouse_down_));
+ MessageLoopForUI::current()->AddObserver(this);
// The drag adds a ref; let it own the list.
gtk_target_list_unref(list);
}
-// static
void TabContentsViewGtk::OnDragDataGet(
- GtkWidget* drag_widget,
GdkDragContext* context, GtkSelectionData* selection_data,
- guint target_type, guint time, TabContentsViewGtk* view) {
+ guint target_type, guint time) {
const int bits_per_byte = 8;
switch (target_type) {
case GtkDndUtil::TEXT_PLAIN: {
- std::string utf8_text = UTF16ToUTF8(view->drop_data_->plain_text);
+ std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
utf8_text.length());
break;
@@ -800,7 +839,7 @@ void TabContentsViewGtk::OnDragDataGet(
case GtkDndUtil::TEXT_URI_LIST: {
gchar* uri_array[2];
- uri_array[0] = strdup(view->drop_data_->url.spec().c_str());
+ uri_array[0] = strdup(drop_data_->url.spec().c_str());
uri_array[1] = NULL;
gtk_selection_data_set_uris(selection_data, uri_array);
free(uri_array[0]);
@@ -810,7 +849,7 @@ void TabContentsViewGtk::OnDragDataGet(
case GtkDndUtil::TEXT_HTML: {
// TODO(estade): change relative links to be absolute using
// |html_base_url|.
- std::string utf8_text = UTF16ToUTF8(view->drop_data_->text_html);
+ std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
gtk_selection_data_set(selection_data,
GtkDndUtil::GetAtomForTarget(GtkDndUtil::TEXT_HTML),
bits_per_byte,
@@ -821,8 +860,8 @@ void TabContentsViewGtk::OnDragDataGet(
case GtkDndUtil::CHROME_NAMED_URL: {
Pickle pickle;
- pickle.WriteString(UTF16ToUTF8(view->drop_data_->url_title));
- pickle.WriteString(view->drop_data_->url.spec());
+ pickle.WriteString(UTF16ToUTF8(drop_data_->url_title));
+ pickle.WriteString(drop_data_->url.spec());
gtk_selection_data_set(selection_data,
GtkDndUtil::GetAtomForTarget(GtkDndUtil::CHROME_NAMED_URL),
bits_per_byte,
@@ -833,10 +872,9 @@ void TabContentsViewGtk::OnDragDataGet(
case GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS: {
gtk_selection_data_set(selection_data,
- view->drag_file_mime_type_, bits_per_byte,
- reinterpret_cast<const guchar*>(
- view->drop_data_->file_contents.data()),
- view->drop_data_->file_contents.length());
+ drag_file_mime_type_, bits_per_byte,
+ reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
+ drop_data_->file_contents.length());
break;
}
@@ -845,11 +883,38 @@ void TabContentsViewGtk::OnDragDataGet(
}
}
-// static
-void TabContentsViewGtk::OnDragEnd(GtkWidget* widget,
- GdkDragContext* drag_context, TabContentsViewGtk* view) {
- view->DragEnded();
- view->drop_data_.reset();
+gboolean TabContentsViewGtk::OnDragFailed() {
+ drag_failed_ = true;
+
+ gfx::Point root = ScreenPoint(GetContentNativeView());
+ gfx::Point client = ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceCancelledAt(
+ client.x(), client.y(), root.x(), root.y());
+ }
+
+ // Let the native failure animation run.
+ return FALSE;
+}
+
+void TabContentsViewGtk::OnDragEnd() {
+ MessageLoopForUI::current()->RemoveObserver(this);
+
+ if (!drag_failed_) {
+ gfx::Point root = ScreenPoint(GetContentNativeView());
+ gfx::Point client = ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceEndedAt(
+ client.x(), client.y(), root.x(), root.y());
+ }
+ }
+
+ if (tab_contents()->render_view_host())
+ tab_contents()->render_view_host()->DragSourceSystemDragEnded();
+
+ drop_data_.reset();
}
// -----------------------------------------------------------------------------
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.h b/chrome/browser/tab_contents/tab_contents_view_gtk.h
index 029264d..1118cc3 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.h
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.h
@@ -9,6 +9,7 @@
#include <vector>
+#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/gtk/focus_store_gtk.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
@@ -25,7 +26,8 @@ class WebDragDest;
typedef struct _GtkFloatingContainer GtkFloatingContainer;
class TabContentsViewGtk : public TabContentsView,
- public NotificationObserver {
+ public NotificationObserver,
+ public MessageLoopForUI::Observer {
public:
// The corresponding TabContents is passed in the constructor, and manages our
// lifetime. This doesn't need to be the case, but is this way currently
@@ -77,6 +79,12 @@ class TabContentsViewGtk : public TabContentsView,
const NotificationSource& source,
const NotificationDetails& details);
+
+ protected:
+ // MessageLoop::Observer implementation:
+ virtual void WillProcessEvent(GdkEvent* event);
+ virtual void DidProcessEvent(GdkEvent* event);
+
private:
// Insert the given widget into the content area. Should only be used for
// web pages and the like (including interstitials and sad tab). Note that
@@ -84,8 +92,7 @@ class TabContentsViewGtk : public TabContentsView,
// should be taken that the correct one is hidden/shown.
void InsertIntoContentArea(GtkWidget* widget);
- // Tell webkit the drag is over.
- void DragEnded();
+ void CancelDragIfAny();
// We keep track of the timestamp of the latest mousedown event.
static gboolean OnMouseDown(GtkWidget* widget,
@@ -100,12 +107,30 @@ class TabContentsViewGtk : public TabContentsView,
GtkFloatingContainer* floating_container, GtkAllocation* allocation,
TabContentsViewGtk* tab_contents_view);
- // Webkit DnD.
- static void OnDragEnd(GtkWidget* widget, GdkDragContext* drag_context,
- TabContentsViewGtk* tab_contents_view);
- static void OnDragDataGet(GtkWidget* drag_widget,
- GdkDragContext* context, GtkSelectionData* selection_data,
- guint target_type, guint time, TabContentsViewGtk* view);
+ // Webkit source-side DnD.
+ static gboolean OnDragFailedThunk(GtkWidget* widget,
+ GdkDragContext* drag_context,
+ GtkDragResult result,
+ TabContentsViewGtk* view) {
+ return view->OnDragFailed();
+ }
+ gboolean OnDragFailed();
+ static void OnDragEndThunk(GtkWidget* widget,
+ GdkDragContext* drag_context,
+ TabContentsViewGtk* view) {
+ view->OnDragEnd();
+ }
+ void OnDragEnd();
+ static void OnDragDataGetThunk(GtkWidget* drag_widget,
+ GdkDragContext* context,
+ GtkSelectionData* selection_data,
+ guint target_type,
+ guint time,
+ TabContentsViewGtk* view) {
+ view->OnDragDataGet(context, selection_data, target_type, time);
+ }
+ void OnDragDataGet(GdkDragContext* context, GtkSelectionData* selection_data,
+ guint target_type, guint time);
// Contains |fixed_| as its GtkBin member and a possible floating widget from
// |popup_view_|.
@@ -139,13 +164,20 @@ class TabContentsViewGtk : public TabContentsView,
std::vector<ConstrainedWindowGtk*> constrained_windows_;
// The drop data for the current drag (for drags that originate in the render
- // view).
+ // view). Non-NULL iff there is a current drag.
scoped_ptr<WebDropData> drop_data_;
// The mime type for the file contents of the current drag (if any).
GdkAtom drag_file_mime_type_;
// The helper object that handles drag destination related interactions with
// GTK.
scoped_ptr<WebDragDest> drag_dest_;
+ // 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.
+ GtkWidget* drag_widget_;
DISALLOW_COPY_AND_ASSIGN(TabContentsViewGtk);
};