Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11.cc
index 4ea28c6..b2d4eed 100644 (file)
@@ -9,18 +9,18 @@
 #include "base/event_types.h"
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_pump_dispatcher.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/clipboard/clipboard.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
 #include "ui/base/x/selection_utils.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/events/event.h"
+#include "ui/events/platform/platform_event_source.h"
 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
+#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
 #include "ui/wm/public/drag_drop_client.h"
 #include "ui/wm/public/drag_drop_delegate.h"
 
@@ -64,7 +64,6 @@ const char* kAtomsToCache[] = {
   "XdndStatus",
   "XdndTypeList",
   ui::Clipboard::kMimeTypeText,
-  "_NET_WM_WINDOW_TYPE_MENU",
   NULL
 };
 
@@ -72,71 +71,27 @@ const char* kAtomsToCache[] = {
 // mouse button before ending the move loop.
 const int kEndMoveLoopTimeoutMs = 1000;
 
+// The time to wait since sending the last XdndPosition message before
+// reprocessing the most recent mouse move event in case that the window
+// stacking order has changed and |source_current_window_| needs to be updated.
+const int kRepeatMouseMoveTimeoutMs = 350;
+
 static base::LazyInstance<
     std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
         g_live_client_map = LAZY_INSTANCE_INITIALIZER;
 
-// Helper class to FindWindowFor which looks for a drag target under the
-// cursor.
-class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
- public:
-  DragTargetWindowFinder(XID ignored_icon_window,
-                         Atom menu_type_atom,
-                         gfx::Point screen_loc)
-      : ignored_icon_window_(ignored_icon_window),
-        output_window_(None),
-        menu_type_atom_(menu_type_atom),
-        screen_loc_(screen_loc) {
-    ui::EnumerateTopLevelWindows(this);
-  }
-
-  virtual ~DragTargetWindowFinder() {}
-
-  XID window() const { return output_window_; }
-
- protected:
-  virtual bool ShouldStopIterating(XID window) OVERRIDE {
-    if (window == ignored_icon_window_)
-      return false;
-
-    if (!ui::IsWindowVisible(window))
-      return false;
-
-    if (!ui::WindowContainsPoint(window, screen_loc_))
-      return false;
-
-    int value = 0;
-    if (ui::PropertyExists(window, "WM_STATE") ||
-        (ui::GetIntProperty(window, "_NET_WM_WINDOW_TYPE", &value) &&
-         static_cast<Atom>(value) == menu_type_atom_)) {
-      output_window_ = window;
-      return true;
-    }
-
-    return false;
-  }
-
- private:
-  XID ignored_icon_window_;
-  XID output_window_;
-  const Atom menu_type_atom_;
-  gfx::Point screen_loc_;
-
-  DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
-};
-
 // Returns the topmost X11 window at |screen_point| if it is advertising that
 // is supports the Xdnd protocol. Will return the window under the pointer as
 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
 // |dest_window|.
 void FindWindowFor(const gfx::Point& screen_point,
-                   ::Window* mouse_window, ::Window* dest_window,
-                   Atom menu_type_atom) {
-  DragTargetWindowFinder finder(None, menu_type_atom, screen_point);
-  *mouse_window = finder.window();
+                   ::Window* mouse_window,
+                   ::Window* dest_window) {
+  views::X11TopmostWindowFinder finder;
+  *mouse_window = finder.FindWindowAt(screen_point);
   *dest_window = None;
 
-  if (finder.window() == None)
+  if (*mouse_window == None)
     return;
 
   // Figure out which window we should test as XdndAware. If mouse_window has
@@ -159,8 +114,8 @@ namespace views {
 DesktopDragDropClientAuraX11*
 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
 
-class DesktopDragDropClientAuraX11::X11DragContext :
-    public base::MessagePumpDispatcher {
+class DesktopDragDropClientAuraX11::X11DragContext
+    : public ui::PlatformEventDispatcher {
  public:
   X11DragContext(ui::X11AtomCache* atom_cache,
                  ::Window local_window,
@@ -200,8 +155,9 @@ class DesktopDragDropClientAuraX11::X11DragContext :
   // |drag_operation|.
   void MaskOpeartion(::Atom xdnd_operation, int* drag_operation) const;
 
-  // Overridden from MessagePumpDispatcher:
-  virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;
+  // ui::PlatformEventDispatcher:
+  virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
+  virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
 
   // The atom cache owned by our parent.
   ui::X11AtomCache* atom_cache_;
@@ -271,8 +227,7 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
   if (!client) {
     // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
     // created by some other process. Listen for messages on it.
-    base::MessagePumpX11::Current()->AddDispatcherForWindow(
-        this, source_window_);
+    ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
     XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
 
     // We must perform a full sync here because we could be racing
@@ -290,13 +245,8 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
 }
 
 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
-  DesktopDragDropClientAuraX11* client =
-      DesktopDragDropClientAuraX11::GetForWindow(source_window_);
-  if (!client) {
-    // Unsubscribe from message events.
-    base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
-        source_window_);
-  }
+  // Unsubscribe from message events.
+  ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
 }
 
 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
@@ -402,13 +352,19 @@ void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion(
     *drag_operation |= ui::DragDropTypes::DRAG_LINK;
 }
 
-uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
-    const base::NativeEvent& event) {
+bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
+    const ui::PlatformEvent& event) {
+  return event->xany.window == source_window_;
+}
+
+uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
+    const ui::PlatformEvent& event) {
   if (event->type == PropertyNotify &&
       event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
     ReadActions();
+    return ui::POST_DISPATCH_STOP_PROPAGATION;
   }
-  return POST_DISPATCH_NONE;
+  return ui::POST_DISPATCH_NONE;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -424,11 +380,13 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
       xwindow_(xwindow),
       atom_cache_(xdisplay_, kAtomsToCache),
       target_window_(NULL),
+      waiting_on_status_(false),
+      status_received_since_enter_(false),
       source_provider_(NULL),
       source_current_window_(None),
       source_state_(SOURCE_STATE_OTHER),
       drag_operation_(0),
-      resulting_operation_(0),
+      negotiated_operation_(ui::DragDropTypes::DRAG_NONE),
       grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
       copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
       move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
@@ -517,18 +475,36 @@ void DesktopDragDropClientAuraX11::OnXdndStatus(
 
   unsigned long source_window = event.data.l[0];
 
-  waiting_on_status_.erase(source_window);
   if (source_window != source_current_window_)
     return;
 
-  int drag_operation = ui::DragDropTypes::DRAG_NONE;
+  if (source_state_ != SOURCE_STATE_PENDING_DROP &&
+      source_state_ != SOURCE_STATE_OTHER) {
+    return;
+  }
+
+  waiting_on_status_ = false;
+  status_received_since_enter_ = true;
+
   if (event.data.l[1] & 1) {
     ::Atom atom_operation = event.data.l[4];
-    negotiated_operation_[source_window] = atom_operation;
-    drag_operation = AtomToDragOperation(atom_operation);
+    negotiated_operation_ = AtomToDragOperation(atom_operation);
+  } else {
+    negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
   }
 
-  switch (drag_operation) {
+  if (source_state_ == SOURCE_STATE_PENDING_DROP) {
+    // We were waiting on the status message so we could send the XdndDrop.
+    if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
+      move_loop_.EndMoveLoop();
+      return;
+    }
+    source_state_ = SOURCE_STATE_DROPPED;
+    SendXdndDrop(source_window);
+    return;
+  }
+
+  switch (negotiated_operation_) {
     case ui::DragDropTypes::DRAG_COPY:
       move_loop_.UpdateCursor(copy_grab_cursor_);
       break;
@@ -546,31 +522,24 @@ void DesktopDragDropClientAuraX11::OnXdndStatus(
   // the spec) the other side must handle further position messages within
   // it. GTK+ doesn't bother with this, so neither should we.
 
-  if (source_state_ == SOURCE_STATE_PENDING_DROP) {
-    // We were waiting on the status message so we could send the XdndDrop.
-    source_state_ = SOURCE_STATE_DROPPED;
-    SendXdndDrop(source_window);
-    return;
-  }
-
-  NextPositionMap::iterator it = next_position_message_.find(source_window);
-  if (source_state_ == SOURCE_STATE_OTHER &&
-      it != next_position_message_.end()) {
+  if (next_position_message_.get()) {
     // We were waiting on the status message so we could send off the next
     // position message we queued up.
-    gfx::Point p = it->second.first;
-    unsigned long time = it->second.second;
-    next_position_message_.erase(it);
+    gfx::Point p = next_position_message_->first;
+    unsigned long event_time = next_position_message_->second;
+    next_position_message_.reset();
 
-    SendXdndPosition(source_window, p, time);
+    SendXdndPosition(source_window, p, event_time);
   }
 }
 
 void DesktopDragDropClientAuraX11::OnXdndFinished(
     const XClientMessageEvent& event) {
   DVLOG(1) << "XdndFinished";
-  resulting_operation_ = AtomToDragOperation(
-      negotiated_operation_[event.data.l[0]]);
+
+  // Clear |source_current_window_| to avoid sending XdndLeave upon ending the
+  // move loop.
+  source_current_window_ = None;
   move_loop_.EndMoveLoop();
 }
 
@@ -631,10 +600,12 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop(
   source_current_window_ = None;
   DCHECK(!g_current_drag_drop_client);
   g_current_drag_drop_client = this;
-  waiting_on_status_.clear();
+  waiting_on_status_ = false;
+  next_position_message_.reset();
+  status_received_since_enter_ = false;
   source_state_ = SOURCE_STATE_OTHER;
   drag_operation_ = operation;
-  resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
+  negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
 
   const ui::OSExchangeData::Provider* provider = &data.provider();
   source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
@@ -675,7 +646,7 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop(
     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
 
-    return resulting_operation_;
+    return negotiated_operation_;
   }
   return ui::DragDropTypes::DRAG_NONE;
 }
@@ -704,40 +675,13 @@ void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
 }
 
 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
-  gfx::Point screen_point(event->x_root, event->y_root);
-
-  if (source_state_ != SOURCE_STATE_OTHER)
-    return;
-
-  // Find the current window the cursor is over.
-  ::Window mouse_window = None;
-  ::Window dest_window = None;
-  FindWindowFor(screen_point, &mouse_window, &dest_window,
-                atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
-
-  if (source_current_window_ != dest_window) {
-    if (source_current_window_ != None)
-      SendXdndLeave(source_current_window_);
-
-    source_current_window_ = dest_window;
-
-    if (source_current_window_ != None) {
-      negotiated_operation_.erase(source_current_window_);
-      SendXdndEnter(source_current_window_);
-    }
-  }
-
-  if (source_current_window_ != None) {
-    if (ContainsKey(waiting_on_status_, dest_window)) {
-      next_position_message_[dest_window] =
-          std::make_pair(screen_point, event->time);
-    } else {
-      SendXdndPosition(dest_window, screen_point, event->time);
-    }
-  }
+  repeat_mouse_move_timer_.Stop();
+  ProcessMouseMove(gfx::Point(event->x_root, event->y_root), event->time);
 }
 
 void DesktopDragDropClientAuraX11::OnMouseReleased() {
+  repeat_mouse_move_timer_.Stop();
+
   if (source_state_ != SOURCE_STATE_OTHER) {
     // The user has previously released the mouse and is clicking in
     // frustration.
@@ -746,20 +690,23 @@ void DesktopDragDropClientAuraX11::OnMouseReleased() {
   }
 
   if (source_current_window_ != None) {
-    if (ContainsKey(waiting_on_status_, source_current_window_)) {
-      // If we are waiting for an XdndStatus message, we need to wait for it to
-      // complete.
-      source_state_ = SOURCE_STATE_PENDING_DROP;
+    if (waiting_on_status_) {
+      if (status_received_since_enter_) {
+        // If we are waiting for an XdndStatus message, we need to wait for it
+        // to complete.
+        source_state_ = SOURCE_STATE_PENDING_DROP;
+
+        // Start timer to end the move loop if the target takes too long to send
+        // the XdndStatus and XdndFinished messages.
+        StartEndMoveLoopTimer();
+        return;
+      }
 
-      // Start timer to end the move loop if the target takes too long to send
-      // the XdndStatus and XdndFinished messages.
-      StartEndMoveLoopTimer();
+      move_loop_.EndMoveLoop();
       return;
     }
 
-    std::map< ::Window, ::Atom>::iterator it =
-        negotiated_operation_.find(source_current_window_);
-    if (it != negotiated_operation_.end() && it->second != None) {
+    if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) {
       // We have negotiated an action with the other end.
       source_state_ = SOURCE_STATE_DROPPED;
       SendXdndDrop(source_current_window_);
@@ -769,22 +716,56 @@ void DesktopDragDropClientAuraX11::OnMouseReleased() {
       StartEndMoveLoopTimer();
       return;
     }
-
-    SendXdndLeave(source_current_window_);
-    source_current_window_ = None;
   }
 
   move_loop_.EndMoveLoop();
 }
 
 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
-  if (source_current_window_ != None)
+  if (source_current_window_ != None) {
     SendXdndLeave(source_current_window_);
+    source_current_window_ = None;
+  }
   target_current_context_.reset();
-  source_state_ = SOURCE_STATE_OTHER;
+  repeat_mouse_move_timer_.Stop();
   end_move_loop_timer_.Stop();
 }
 
+void DesktopDragDropClientAuraX11::ProcessMouseMove(
+    const gfx::Point& screen_point,
+    unsigned long event_time) {
+  if (source_state_ != SOURCE_STATE_OTHER)
+    return;
+
+  // Find the current window the cursor is over.
+  ::Window mouse_window = None;
+  ::Window dest_window = None;
+  FindWindowFor(screen_point, &mouse_window, &dest_window);
+
+  if (source_current_window_ != dest_window) {
+    if (source_current_window_ != None)
+      SendXdndLeave(source_current_window_);
+
+    source_current_window_ = dest_window;
+    waiting_on_status_ = false;
+    next_position_message_.reset();
+    status_received_since_enter_ = false;
+    negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
+
+    if (source_current_window_ != None)
+      SendXdndEnter(source_current_window_);
+  }
+
+  if (source_current_window_ != None) {
+    if (waiting_on_status_) {
+      next_position_message_.reset(
+          new std::pair<gfx::Point, unsigned long>(screen_point, event_time));
+    } else {
+      SendXdndPosition(dest_window, screen_point, event_time);
+    }
+  }
+}
+
 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
   end_move_loop_timer_.Start(FROM_HERE,
                              base::TimeDelta::FromMilliseconds(
@@ -862,7 +843,8 @@ void DesktopDragDropClientAuraX11::NotifyDragLeave() {
   return None;
 }
 
-int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
+ui::DragDropTypes::DragOperation
+DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
     return ui::DragDropTypes::DRAG_COPY;
   if (atom == atom_cache_.GetAtom(kXdndActionMove))
@@ -949,12 +931,6 @@ void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
 }
 
 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
-  // If we're sending a leave message, don't wait for status messages anymore.
-  waiting_on_status_.erase(dest_window);
-  NextPositionMap::iterator it = next_position_message_.find(dest_window);
-  if (it != next_position_message_.end())
-    next_position_message_.erase(it);
-
   XEvent xev;
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
@@ -971,8 +947,8 @@ void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
 void DesktopDragDropClientAuraX11::SendXdndPosition(
     ::Window dest_window,
     const gfx::Point& screen_point,
-    unsigned long time) {
-  waiting_on_status_.insert(dest_window);
+    unsigned long event_time) {
+  waiting_on_status_ = true;
 
   XEvent xev;
   xev.xclient.type = ClientMessage;
@@ -982,9 +958,20 @@ void DesktopDragDropClientAuraX11::SendXdndPosition(
   xev.xclient.data.l[0] = xwindow_;
   xev.xclient.data.l[1] = 0;
   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
-  xev.xclient.data.l[3] = time;
+  xev.xclient.data.l[3] = event_time;
   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
   SendXClientEvent(dest_window, &xev);
+
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
+  // the Xdnd protocol both recommend that drag events should be sent
+  // periodically.
+  repeat_mouse_move_timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
+      base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
+                 base::Unretained(this),
+                 screen_point,
+                 event_time));
 }
 
 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {