Index: services/ui/ws/current_drag_operation.cc |
diff --git a/services/ui/ws/current_drag_operation.cc b/services/ui/ws/current_drag_operation.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1e163d342d9d9a676464547cab6a12558b706e45 |
--- /dev/null |
+++ b/services/ui/ws/current_drag_operation.cc |
@@ -0,0 +1,247 @@ |
+// Copyright 2016 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 "services/ui/ws/current_drag_operation.h" |
+ |
+#include "base/logging.h" |
+#include "services/ui/ws/current_drag_operation_source.h" |
+#include "services/ui/ws/drag_target_connection.h" |
+#include "services/ui/ws/event_dispatcher.h" |
+#include "services/ui/ws/server_window.h" |
+ |
+namespace ui { |
+namespace ws { |
+ |
+struct CurrentDragOperation::Operation { |
+ DragEventType type; |
+ uint32_t key_state; |
+ gfx::Point position; |
sky
2016/09/06 21:11:27
Document what this is relative to. Screen?
|
+}; |
+ |
+CurrentDragOperation::CurrentDragOperation( |
+ CurrentDragOperationSource* source, |
+ ServerWindow* window, |
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, |
+ uint32_t drag_operations) |
+ : source_(source), |
+ drag_operations_(drag_operations), |
+ source_window_(window), |
+ mime_data_(std::move(mime_data)), |
+ weak_factory_(this) { |
+ source_window_->AddObserver(this); |
+} |
+ |
+CurrentDragOperation::~CurrentDragOperation() { |
+ if (source_window_) |
+ source_window_->RemoveObserver(this); |
+} |
+ |
+void CurrentDragOperation::DispatchCancel() { |
+ MessageDragCompleted(false); |
+} |
+ |
+void CurrentDragOperation::DispatchLocatedEvent(const ui::LocatedEvent& event, |
+ ServerWindow* current_target) { |
+ uint32_t key_state = event.flags(); |
+ gfx::Point position = event.location(); |
+ |
+ if (waiting_for_final_drop_response_) { |
+ // If we're waiting on a target window to respond to the final drag drop |
sky
2016/09/06 21:11:27
If you early return here, does it mean the event i
|
+ // call, don't process any more mouse events. |
+ return; |
+ } |
+ |
+ if (current_target && !current_target->can_accept_drags()) { |
+ // If the current window over point doesn't accept drops, than don't send |
+ // it events. |
+ current_target = nullptr; |
sky
2016/09/06 21:11:27
Shouldn't this bubble
|
+ } |
+ |
+ if (current_target && current_target == current_target_window_ && |
+ event.type() != ET_POINTER_UP) { |
sky
2016/09/06 21:11:27
Shouldn't you only care about POINTER_UP of the po
|
+ // We're continuing a drag inside the bounds of |
+ // |current_target_window_|. Send a continue message. |
+ auto& window_ops = current_window_state_[current_target]; |
+ if (window_ops.size() > 1 && window_ops.back().type == TYPE_OVER) { |
+ // If we have a queued DragOver which isn't in progress, then just |
+ // modify it's data. |
+ auto& back = window_ops.back(); |
+ back.key_state = key_state; |
+ back.position = position; |
+ } else { |
+ QueueEvent(current_target, TYPE_OVER, key_state, position); |
+ } |
+ } else if (current_target != current_target_window_) { |
+ if (current_target_window_) { |
+ auto& window_ops = current_window_state_[current_target_window_]; |
+ if (window_ops.size() > 1 && window_ops.back().type == TYPE_OVER) { |
+ // If we have queued DragOver events, we want to replace them. |
+ auto& back = window_ops.back(); |
+ back.type = TYPE_LEAVE; |
+ back.key_state = key_state; |
+ back.position = position; |
+ } else { |
+ QueueEvent(current_target_window_, TYPE_LEAVE, key_state, position); |
+ } |
+ } |
+ |
+ if (current_target) |
+ QueueEvent(current_target, TYPE_ENTER, key_state, position); |
+ |
+ current_target_window_ = current_target; |
+ } |
+ |
+ if (event.type() == ET_POINTER_UP) { |
+ if (current_target) { |
+ QueueEvent(current_target, TYPE_DROP, key_state, position); |
+ waiting_for_final_drop_response_ = true; |
+ } else { |
+ // The pointer was released over no window or a window that doesn't |
+ // accept drags. |
+ MessageDragCompleted(false); |
+ } |
+ } |
+} |
+ |
+void CurrentDragOperation::MessageDragCompleted(bool success) { |
+ for (ServerWindow* window : called_on_drag_start_) { |
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(window); |
+ if (connection) |
+ connection->PerformOnDragFinish(window); |
+ } |
+ called_on_drag_start_.clear(); |
+ |
+ source_->OnDragOver(success); |
+ // |this| may be invalid now. |
+} |
+ |
+size_t CurrentDragOperation::GetSizeOfQueueForWindow(ServerWindow* window) { |
+ auto it = current_window_state_.find(window); |
+ if (it == current_window_state_.end()) |
+ return 0u; |
+ return it->second.size(); |
+} |
+ |
+void CurrentDragOperation::QueueEvent(ServerWindow* window, |
+ DragEventType type, |
+ uint32_t key_state, |
+ gfx::Point position) { |
+ // If this window doesn't have the mime data, send it a PerformOnDragStart() |
+ // message. |
+ if (window != source_window_ && |
+ !base::ContainsKey(called_on_drag_start_, window)) { |
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(window); |
+ connection->PerformOnDragStart(window, mime_data_.Clone()); |
+ called_on_drag_start_.insert(window); |
+ } |
+ |
+ auto& window_ops = current_window_state_[window]; |
+ if (window_ops.empty() && window != source_window_) |
+ window->AddObserver(this); |
+ window_ops.push_back({type, key_state, position}); |
+ if (window_ops.size() == 1) { |
+ // The first item in the queue is the item we're waiting for a response |
+ // on. If we're the only item, send it. |
+ DispatchFrontOfWindowQueue(window, &window_ops); |
+ } |
+} |
+ |
+void CurrentDragOperation::RemoveQueueFront(ServerWindow* window) { |
+ auto& window_ops = current_window_state_[window]; |
+ if (window_ops.size() == 1 && window != source_window_) { |
+ // This is the last event in queue; stop watching the queue. |
+ window->RemoveObserver(this); |
+ } |
+ |
+ DCHECK(!window_ops.empty()); |
+ window_ops.pop_front(); |
+ |
+ if (!window_ops.empty()) |
+ DispatchFrontOfWindowQueue(window, &window_ops); |
+} |
+ |
+void CurrentDragOperation::DispatchFrontOfWindowQueue( |
+ ServerWindow* target, |
+ std::deque<Operation>* queue) { |
+ DragTargetConnection* connection = source_->GetDragTargetWithRoot(target); |
+ |
+ DCHECK(!queue->empty()); |
+ const Operation& op = queue->front(); |
+ switch (op.type) { |
+ case TYPE_ENTER: { |
+ connection->PerformOnDragEnter( |
+ target, op.key_state, op.position, drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ case TYPE_OVER: { |
+ connection->PerformOnDragOver( |
+ target, op.key_state, op.position, drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragStatusCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ case TYPE_LEAVE: { |
+ connection->PerformOnDragLeave(target); |
+ RemoveQueueFront(target); |
+ break; |
+ } |
+ case TYPE_DROP: { |
+ connection->PerformOnDragDrop( |
+ target, op.key_state, op.position, drag_operations_, |
+ base::Bind(&CurrentDragOperation::OnDragDropCompleted, |
+ weak_factory_.GetWeakPtr(), target->id())); |
+ break; |
+ } |
+ } |
+} |
+ |
+void CurrentDragOperation::OnDragStatusCompleted(const WindowId& id, |
+ uint32_t bitmask) { |
+ ServerWindow* window = source_->GetWindowById(id); |
+ if (!window) { |
+ // The window has been deleted and its queue is empty. |
+ return; |
+ } |
+ |
+ // We must remove the completed item. |
+ RemoveQueueFront(window); |
+ |
+ // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We |
+ // should use this data to change the cursor. |
+} |
+ |
+void CurrentDragOperation::OnDragDropCompleted(const WindowId& id, |
+ uint32_t bitmask) { |
+ ServerWindow* window = source_->GetWindowById(id); |
+ if (!window) { |
+ // The window has been deleted after we sent the drop message. It's really |
+ // hard to recover from this so just signal to the source that our drag |
+ // failed. |
+ MessageDragCompleted(false); |
+ return; |
+ } |
+ |
+ // TODO(erg): What happens if there are events behind the drag drop!? |
+ RemoveQueueFront(window); |
+ MessageDragCompleted(bitmask != 0u); |
+} |
+ |
+void CurrentDragOperation::OnWindowDestroying(ServerWindow* window) { |
+ if (current_target_window_ == window) |
+ current_target_window_ = nullptr; |
+ |
+ current_window_state_.erase(window); |
+ called_on_drag_start_.erase(window); |
+ |
+ if (source_window_ == window) { |
+ source_window_ = nullptr; |
+ // Our source window is being deleted, fail the drag. |
+ MessageDragCompleted(false); |
+ } |
+} |
+ |
+} // namespace ws |
+} // namespace ui |