diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 21:49:26 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 21:49:26 +0000 |
commit | 7a4f563b87e5319d2b7892da301b4aed36160f34 (patch) | |
tree | 22ed0b456fb089316b18afe8950a340703be0d8a /chrome/browser/tab_contents | |
parent | 9ec8a8ce8b2cb0c29387ddccce7fb0fb356e0b2c (diff) | |
download | chromium_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.cc | 165 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_gtk.h | 52 |
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); }; |