From 824f8f4009af78a9dd1251284afa2346e026f049 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 8 Jul 2014 22:37:06 +0000 Subject: [PATCH] Linux: Add drag&drop support (issue #1258). - Requires proper handling of the "XdndProxy" property on the top-level window. This currently works in cefsimple but not cefclient (perhaps due to https://bugzilla.gnome.org/show_bug.cgi?id=653264). git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1759 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- libcef/browser/browser_host_impl_linux.cc | 6 +- libcef/browser/window_x11.cc | 113 ++++++++++++++++------ patch/patch.cfg | 6 ++ patch/patches/ui_dragdrop_355390.patch | 14 +++ patch/patches/views_widget_180.patch | 52 +++++++++- 5 files changed, 154 insertions(+), 37 deletions(-) create mode 100644 patch/patches/ui_dragdrop_355390.patch diff --git a/libcef/browser/browser_host_impl_linux.cc b/libcef/browser/browser_host_impl_linux.cc index f3135ab19..ab4d140dc 100644 --- a/libcef/browser/browser_host_impl_linux.cc +++ b/libcef/browser/browser_host_impl_linux.cc @@ -417,8 +417,10 @@ void CefBrowserHostImpl::PlatformTranslateMouseEvent( GetBrowser(), result.x, result.y, result.globalX, result.globalY); - } else { - // TODO(linux): Convert global{X,Y} to screen coordinates. + } else if (window_x11_) { + const gfx::Point& origin = window_x11_->bounds().origin(); + result.globalX += origin.x(); + result.globalY += origin.y(); } // modifiers diff --git a/libcef/browser/window_x11.cc b/libcef/browser/window_x11.cc index cb25d7690..ef62f7878 100644 --- a/libcef/browser/window_x11.cc +++ b/libcef/browser/window_x11.cc @@ -12,14 +12,23 @@ #include "ui/base/x/x11_util.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" +#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" namespace { +const char kWMDeleteWindow[] = "WM_DELETE_WINDOW"; +const char kWMProtocols[] = "WM_PROTOCOLS"; +const char kNetWMPing[] = "_NET_WM_PING"; +const char kNetWMPid[] = "_NET_WM_PID"; +const char kXdndProxy[] = "XdndProxy"; + const char* kAtomsToCache[] = { - "WM_DELETE_WINDOW", - "WM_PROTOCOLS", - "_NET_WM_PING", - "_NET_WM_PID", + kWMDeleteWindow, + kWMProtocols, + kNetWMPing, + kNetWMPid, + kXdndProxy, NULL }; @@ -90,8 +99,8 @@ CefWindowX11::CefWindowX11(CefRefPtr browser, // should listen for activation events and anything else that GTK+ listens // for, and do something useful. ::Atom protocols[2]; - protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); - protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); + protocols[0] = atom_cache_.GetAtom(kWMDeleteWindow); + protocols[1] = atom_cache_.GetAtom(kNetWMPing); XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with @@ -105,7 +114,7 @@ CefWindowX11::CefWindowX11(CefRefPtr browser, long pid = getpid(); XChangeProperty(xdisplay_, xwindow_, - atom_cache_.GetAtom("_NET_WM_PID"), + atom_cache_.GetAtom(kNetWMPid), XA_CARDINAL, 32, PropModeReplace, @@ -125,9 +134,9 @@ void CefWindowX11::Close() { XEvent ev = {0}; ev.xclient.type = ClientMessage; ev.xclient.window = xwindow_; - ev.xclient.message_type = atom_cache_.GetAtom("WM_PROTOCOLS"); + ev.xclient.message_type = atom_cache_.GetAtom(kWMProtocols); ev.xclient.format = 32; - ev.xclient.data.l[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); + ev.xclient.data.l[0] = atom_cache_.GetAtom(kWMDeleteWindow); ev.xclient.data.l[1] = CurrentTime; XSendEvent(xdisplay_, xwindow_, False, NoEventMask, &ev); } @@ -245,37 +254,77 @@ uint32_t CefWindowX11::DispatchEvent(const ui::PlatformEvent& event) { changes.width = bounds.width(); changes.height = bounds.height(); XConfigureWindow(xdisplay_, child, CWHeight | CWWidth, &changes); + + // Explicitly set the screen bounds so that WindowTreeHost::*Screen() + // methods return the correct results. + views::DesktopWindowTreeHostX11* window_tree_host = + views::DesktopWindowTreeHostX11::GetHostForXID(child); + if (window_tree_host) + window_tree_host->set_screen_bounds(bounds); + + // Find the top-most window containing the browser window. + views::X11TopmostWindowFinder finder; + ::Window topmost_window = finder.FindWindowAt(bounds.origin()); + DCHECK(topmost_window); + + // Configure the drag&drop proxy property for the top-most window so + // that all drag&drop-related messages will be sent to the child + // DesktopWindowTreeHostX11. The proxy property is referenced by + // DesktopDragDropClientAuraX11::FindWindowFor. + ::Window proxy_target = gfx::kNullAcceleratedWidget; + ui::GetXIDProperty(topmost_window, kXdndProxy, &proxy_target); + if (proxy_target != child) { + // Set the proxy target for the top-most window. + XChangeProperty(xdisplay_, + topmost_window, + atom_cache_.GetAtom(kXdndProxy), + XA_WINDOW, + 32, + PropModeReplace, + reinterpret_cast(&child), 1); + // Do the same for the proxy target per the spec. + XChangeProperty(xdisplay_, + child, + atom_cache_.GetAtom(kXdndProxy), + XA_WINDOW, + 32, + PropModeReplace, + reinterpret_cast(&child), 1); + } } } break; } case ClientMessage: { - Atom message_type = static_cast(xev->xclient.data.l[0]); - if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { - // We have received a close message from the window manager. - if (browser_ && browser_->destruction_state() <= - CefBrowserHostImpl::DESTRUCTION_STATE_PENDING) { - if (browser_->destruction_state() == - CefBrowserHostImpl::DESTRUCTION_STATE_NONE) { - // Request that the browser close. - browser_->CloseBrowser(false); + Atom message_type = xev->xclient.message_type; + if (message_type == atom_cache_.GetAtom(kWMProtocols)) { + Atom protocol = static_cast(xev->xclient.data.l[0]); + if (protocol == atom_cache_.GetAtom(kWMDeleteWindow)) { + // We have received a close message from the window manager. + if (browser_ && browser_->destruction_state() <= + CefBrowserHostImpl::DESTRUCTION_STATE_PENDING) { + if (browser_->destruction_state() == + CefBrowserHostImpl::DESTRUCTION_STATE_NONE) { + // Request that the browser close. + browser_->CloseBrowser(false); + } + + // Cancel the close. + } else { + // Allow the close. + XDestroyWindow(xdisplay_, xwindow_); } + } else if (protocol == atom_cache_.GetAtom(kNetWMPing)) { + XEvent reply_event = *xev; + reply_event.xclient.window = parent_xwindow_; - // Cancel the close. - } else { - // Allow the close. - XDestroyWindow(xdisplay_, xwindow_); + XSendEvent(xdisplay_, + reply_event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &reply_event); + XFlush(xdisplay_); } - } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { - XEvent reply_event = *xev; - reply_event.xclient.window = parent_xwindow_; - - XSendEvent(xdisplay_, - reply_event.xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &reply_event); - XFlush(xdisplay_); } break; } diff --git a/patch/patch.cfg b/patch/patch.cfg index 8ba9e8474..804cce488 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -112,6 +112,12 @@ patches = [ 'name': 'webkit_platform_mac_328814', 'path': '../third_party/WebKit/Source/platform/mac/', }, + { + # Fix drag&drop of combined text and URL data on Linux/Aura. + # https://codereview.chromium.org/208313009 + 'name': 'ui_dragdrop_355390', + 'path': '../ui/base/dragdrop/', + }, { # Disable scollbar bounce and overlay on OS X. # http://code.google.com/p/chromiumembedded/issues/detail?id=364 diff --git a/patch/patches/ui_dragdrop_355390.patch b/patch/patches/ui_dragdrop_355390.patch new file mode 100644 index 000000000..1f8ad08b2 --- /dev/null +++ b/patch/patches/ui_dragdrop_355390.patch @@ -0,0 +1,14 @@ +Index: os_exchange_data_provider_aurax11.cc +=================================================================== +--- os_exchange_data_provider_aurax11.cc (revision 280796) ++++ os_exchange_data_provider_aurax11.cc (working copy) +@@ -155,7 +155,8 @@ + format_map_.Insert(atom_cache_.GetAtom(kMimeTypeMozillaURL), mem); + + // Set a string fallback as well. +- SetString(spec); ++ if (!HasString()) ++ SetString(spec); + + // Return early if this drag already contains file contents (this implies + // that file contents must be populated before URLs). Nautilus (and possibly diff --git a/patch/patches/views_widget_180.patch b/patch/patches/views_widget_180.patch index ff0b760b9..259f14f77 100644 --- a/patch/patches/views_widget_180.patch +++ b/patch/patches/views_widget_180.patch @@ -80,7 +80,34 @@ Index: desktop_aura/desktop_window_tree_host_x11.cc xwindow_ = None; desktop_native_widget_aura_->OnHostClosed(); -@@ -1042,9 +1044,13 @@ +@@ -443,6 +445,8 @@ + } + + gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const { ++ if (!screen_bounds_.IsEmpty()) ++ return screen_bounds_; + return bounds_; + } + +@@ -455,6 +459,8 @@ + // Attempts to calculate the rect by asking the NonClientFrameView what it + // thought its GetBoundsForClientView() were broke combobox drop down + // placement. ++ if (!screen_bounds_.IsEmpty()) ++ return screen_bounds_; + return bounds_; + } + +@@ -899,6 +905,8 @@ + } + + gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const { ++ if (!screen_bounds_.IsEmpty()) ++ return screen_bounds_.origin(); + return bounds_.origin(); + } + +@@ -1042,9 +1050,13 @@ } } @@ -95,7 +122,7 @@ Index: desktop_aura/desktop_window_tree_host_x11.cc bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(), 0, // border width -@@ -1600,6 +1606,10 @@ +@@ -1600,6 +1612,10 @@ } break; } @@ -110,7 +137,26 @@ Index: desktop_aura/desktop_window_tree_host_x11.h =================================================================== --- desktop_aura/desktop_window_tree_host_x11.h (revision 280796) +++ desktop_aura/desktop_window_tree_host_x11.h (working copy) -@@ -331,6 +331,9 @@ +@@ -84,6 +84,8 @@ + // Deallocates the internal list of open windows. + static void CleanUpWindowList(); + ++ void set_screen_bounds(const gfx::Rect& bounds) { screen_bounds_ = bounds; } ++ + protected: + // Overridden from DesktopWindowTreeHost: + virtual void Init(aura::Window* content_window, +@@ -250,6 +252,9 @@ + // The bounds of |xwindow_|. + gfx::Rect bounds_; + ++ // Override the screen bounds when the host is a child window. ++ gfx::Rect screen_bounds_; ++ + // Whenever the bounds are set, we keep the previous set of bounds around so + // we can have a better chance of getting the real |restored_bounds_|. Window + // managers tend to send a Configure message with the maximized bounds, and +@@ -331,6 +336,9 @@ // the frame when |xwindow_| gains focus or handles a mouse button event. bool urgency_hint_set_;