Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "services/ui/ws/drag_controller.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "services/ui/ws/drag_source.h" | |
| 9 #include "services/ui/ws/drag_target_connection.h" | |
| 10 #include "services/ui/ws/event_dispatcher.h" | |
| 11 #include "services/ui/ws/server_window.h" | |
| 12 | |
| 13 namespace ui { | |
| 14 namespace ws { | |
| 15 | |
| 16 struct DragController::Operation { | |
| 17 OperationType type; | |
| 18 uint32_t event_flags; | |
| 19 gfx::Point screen_position; | |
| 20 }; | |
| 21 | |
| 22 struct DragController::WindowState { | |
|
sky
2016/09/15 18:10:47
Much easier to understand, and better documented.
| |
| 23 // Set to true once we've observed | |
|
sky
2016/09/15 18:10:47
Did you forget to finish the sentence?
| |
| 24 bool observed; | |
| 25 | |
| 26 // If we're waiting for a response, this is the type of message. TYPE_NONE | |
| 27 // means there's no outstanding | |
| 28 OperationType waiting_on_reply; | |
| 29 | |
| 30 // The operation that we'll send off if |waiting_on_reply| isn't TYPE_NONE. | |
| 31 Operation queued_operation; | |
| 32 }; | |
| 33 | |
| 34 DragController::DragController( | |
| 35 DragSource* source, | |
| 36 ServerWindow* source_window, | |
| 37 DragTargetConnection* source_connection, | |
| 38 int32_t drag_pointer, | |
| 39 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, | |
| 40 uint32_t drag_operations) | |
| 41 : source_(source), | |
| 42 drag_operations_(drag_operations), | |
| 43 drag_pointer_id_(drag_pointer), | |
| 44 source_window_(source_window), | |
| 45 source_connection_(source_connection), | |
| 46 mime_data_(std::move(mime_data)), | |
| 47 weak_factory_(this) { | |
| 48 EnsureWindowObserved(source_window_); | |
| 49 } | |
| 50 | |
| 51 DragController::~DragController() { | |
| 52 for (auto& pair : window_state_) { | |
| 53 if (pair.second.observed) | |
| 54 pair.first->RemoveObserver(this); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 void DragController::Cancel() { | |
| 59 MessageDragCompleted(false); | |
| 60 // |this| may be deleted now. | |
| 61 } | |
| 62 | |
| 63 bool DragController::DispatchPointerEvent(const ui::PointerEvent& event, | |
| 64 ServerWindow* current_target) { | |
| 65 uint32_t event_flags = | |
| 66 event.flags() & | |
| 67 (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN); | |
| 68 gfx::Point screen_position = event.location(); | |
| 69 | |
| 70 if (waiting_for_final_drop_response_) { | |
| 71 // If we're waiting on a target window to respond to the final drag drop | |
| 72 // call, don't process any more pointer events. | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 if (event.pointer_id() != drag_pointer_id_) | |
| 77 return false; | |
| 78 | |
| 79 // If |current_target| doesn't accept drags, walk its hierarchy up until we | |
| 80 // find one that does (or set to nullptr at the top of the tree). | |
| 81 while (current_target && !current_target->can_accept_drops()) | |
| 82 current_target = current_target->parent(); | |
| 83 | |
| 84 if (current_target) { | |
| 85 // If we're non-null, we're about to use |current_target| in some | |
| 86 // way. Ensure that we receive notifications that this window has gone | |
| 87 // away. | |
| 88 EnsureWindowObserved(current_target); | |
| 89 } | |
| 90 | |
| 91 if (current_target && current_target == current_target_window_ && | |
| 92 event.type() != ET_POINTER_UP) { | |
| 93 QueueOperation(current_target, OperationType::OVER, event_flags, | |
| 94 screen_position); | |
| 95 } else if (current_target != current_target_window_) { | |
| 96 if (current_target_window_) { | |
| 97 QueueOperation(current_target_window_, OperationType::LEAVE, event_flags, | |
| 98 screen_position); | |
| 99 } | |
| 100 | |
| 101 if (current_target) { | |
| 102 // TODO(erg): If we have a queued LEAVE operation, does this turn into a | |
| 103 // noop? | |
| 104 QueueOperation(current_target, OperationType::ENTER, event_flags, | |
| 105 screen_position); | |
| 106 } | |
| 107 | |
| 108 SetCurrentTargetWindow(current_target); | |
| 109 } | |
| 110 | |
| 111 if (event.type() == ET_POINTER_UP) { | |
| 112 if (current_target) { | |
| 113 QueueOperation(current_target, OperationType::DROP, event_flags, | |
| 114 screen_position); | |
| 115 waiting_for_final_drop_response_ = true; | |
| 116 } else { | |
| 117 // The pointer was released over no window or a window that doesn't | |
| 118 // accept drags. | |
| 119 MessageDragCompleted(false); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 return true; | |
| 124 } | |
| 125 | |
| 126 void DragController::OnWillDestroyDragTargetConnection( | |
| 127 DragTargetConnection* connection) { | |
| 128 called_on_drag_mime_types_.erase(connection); | |
| 129 } | |
| 130 | |
| 131 void DragController::MessageDragCompleted(bool success) { | |
| 132 for (DragTargetConnection* connection : called_on_drag_mime_types_) | |
| 133 connection->PerformOnDragDropDone(); | |
| 134 called_on_drag_mime_types_.clear(); | |
| 135 | |
| 136 source_->OnDragCompleted(success); | |
| 137 // |this| may be deleted now. | |
| 138 } | |
| 139 | |
| 140 size_t DragController::GetSizeOfQueueForWindow(ServerWindow* window) { | |
|
sky
2016/09/15 18:10:47
It doesn't seem as though you are using this anymo
Elliot Glaysher
2016/09/15 19:14:33
It's used in the unit tests via the private testin
| |
| 141 auto it = window_state_.find(window); | |
| 142 if (it == window_state_.end()) | |
| 143 return 0u; | |
| 144 if (it->second.waiting_on_reply == OperationType::NONE) | |
| 145 return 0u; | |
| 146 if (it->second.queued_operation.type == OperationType::NONE) | |
| 147 return 1u; | |
| 148 return 2u; | |
| 149 } | |
| 150 | |
| 151 void DragController::SetCurrentTargetWindow(ServerWindow* current_target) { | |
| 152 current_target_window_ = current_target; | |
| 153 } | |
| 154 | |
| 155 void DragController::EnsureWindowObserved(ServerWindow* window) { | |
| 156 if (!window) | |
| 157 return; | |
| 158 | |
| 159 WindowState& state = window_state_[window]; | |
| 160 if (!state.observed) { | |
| 161 state.observed = true; | |
| 162 window->AddObserver(this); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 void DragController::QueueOperation(ServerWindow* window, | |
| 167 OperationType type, | |
| 168 uint32_t event_flags, | |
| 169 gfx::Point screen_position) { | |
| 170 // If this window doesn't have the mime data, send it. | |
| 171 DragTargetConnection* connection = source_->GetDragTargetForWindow(window); | |
| 172 if (connection != source_connection_ && | |
| 173 !base::ContainsKey(called_on_drag_mime_types_, connection)) { | |
| 174 connection->PerformOnDragDropStart(mime_data_.Clone()); | |
| 175 called_on_drag_mime_types_.insert(connection); | |
| 176 } | |
| 177 | |
| 178 WindowState& state = window_state_[window]; | |
| 179 // Set the queued operation to the incoming. | |
| 180 state.queued_operation = {type, event_flags, screen_position}; | |
| 181 | |
| 182 if (state.waiting_on_reply == OperationType::NONE) { | |
| 183 // Send the operation immediately. | |
| 184 DispatchOperation(window, &state); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 void DragController::DispatchOperation(ServerWindow* target, | |
| 189 WindowState* state) { | |
| 190 DragTargetConnection* connection = source_->GetDragTargetForWindow(target); | |
| 191 | |
| 192 DCHECK_EQ(OperationType::NONE, state->waiting_on_reply); | |
| 193 Operation& op = state->queued_operation; | |
| 194 switch (op.type) { | |
| 195 case OperationType::NONE: { | |
|
sky
2016/09/15 18:10:47
It is possible to get here with an operation of NO
Elliot Glaysher
2016/09/15 19:14:33
Added a NOTREACHED() to emphasize that this is to
| |
| 196 break; | |
| 197 } | |
| 198 case OperationType::ENTER: { | |
| 199 connection->PerformOnDragEnter( | |
| 200 target, op.event_flags, op.screen_position, drag_operations_, | |
| 201 base::Bind(&DragController::OnDragStatusCompleted, | |
| 202 weak_factory_.GetWeakPtr(), target->id())); | |
| 203 state->waiting_on_reply = OperationType::ENTER; | |
| 204 break; | |
| 205 } | |
| 206 case OperationType::OVER: { | |
| 207 connection->PerformOnDragOver( | |
| 208 target, op.event_flags, op.screen_position, drag_operations_, | |
| 209 base::Bind(&DragController::OnDragStatusCompleted, | |
| 210 weak_factory_.GetWeakPtr(), target->id())); | |
| 211 state->waiting_on_reply = OperationType::OVER; | |
| 212 break; | |
| 213 } | |
| 214 case OperationType::LEAVE: { | |
| 215 connection->PerformOnDragLeave(target); | |
| 216 state->waiting_on_reply = OperationType::NONE; | |
| 217 break; | |
| 218 } | |
| 219 case OperationType::DROP: { | |
| 220 connection->PerformOnCompleteDrop( | |
| 221 target, op.event_flags, op.screen_position, drag_operations_, | |
| 222 base::Bind(&DragController::OnDragDropCompleted, | |
| 223 weak_factory_.GetWeakPtr(), target->id())); | |
| 224 state->waiting_on_reply = OperationType::DROP; | |
| 225 break; | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 state->queued_operation = {OperationType::NONE, 0, gfx::Point()}; | |
| 230 } | |
| 231 | |
| 232 void DragController::OnRespondToOperation(ServerWindow* window) { | |
| 233 WindowState& state = window_state_[window]; | |
| 234 DCHECK_NE(OperationType::NONE, state.waiting_on_reply); | |
| 235 state.waiting_on_reply = OperationType::NONE; | |
| 236 if (state.queued_operation.type != OperationType::NONE) | |
| 237 DispatchOperation(window, &state); | |
| 238 } | |
| 239 | |
| 240 void DragController::OnDragStatusCompleted(const WindowId& id, | |
| 241 uint32_t bitmask) { | |
| 242 ServerWindow* window = source_->GetWindowById(id); | |
| 243 if (!window) { | |
| 244 // The window has been deleted and its queue is empty. | |
| 245 return; | |
| 246 } | |
| 247 | |
| 248 // We must remove the completed item. | |
| 249 OnRespondToOperation(window); | |
| 250 | |
| 251 // TODO(erg): |bitmask| is the allowed drag actions at the mouse location. We | |
| 252 // should use this data to change the cursor. | |
| 253 } | |
| 254 | |
| 255 void DragController::OnDragDropCompleted(const WindowId& id, uint32_t bitmask) { | |
| 256 ServerWindow* window = source_->GetWindowById(id); | |
| 257 if (!window) { | |
| 258 // The window has been deleted after we sent the drop message. It's really | |
| 259 // hard to recover from this so just signal to the source that our drag | |
| 260 // failed. | |
| 261 MessageDragCompleted(false); | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 OnRespondToOperation(window); | |
| 266 MessageDragCompleted(bitmask != 0u); | |
| 267 } | |
| 268 | |
| 269 void DragController::OnWindowDestroying(ServerWindow* window) { | |
| 270 auto it = window_state_.find(window); | |
| 271 if (it != window_state_.end()) { | |
| 272 window->RemoveObserver(this); | |
| 273 window_state_.erase(it); | |
| 274 } | |
| 275 | |
| 276 if (current_target_window_ == window) | |
| 277 current_target_window_ = nullptr; | |
| 278 | |
| 279 if (source_window_ == window) { | |
| 280 source_window_ = nullptr; | |
| 281 // Our source window is being deleted, fail the drag. | |
| 282 MessageDragCompleted(false); | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 } // namespace ws | |
| 287 } // namespace ui | |
| OLD | NEW |