Dragging event has some complex code stacking.
There is a seperate protocl for drag-and-drop a.k.a DnD.
Wayland Protocol book also has a seperated page for [Drag & drop] but it looks like empty somehow...?
Anyway, we use wayland drag-and-drop protocol for dragging tabs, windows and items.
## Browser UI Views
They are two usages of drag-and-drop.
### Tab drag
Used when you drag tab or whole window.
[TabDragController::Drag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/tabs/tab_drag_controller.cc;l=567;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) dispatches the dragging event.
While the tab is dragged, they are handled as [TabSlotView](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/tabs/tab_slot_view.h;l=14;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588). The target tabs are collected from [GetViewsMatchingDraggedContents](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/tabs/tab_drag_controller.cc;l=1741;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) and passed to [TabDragContextImpl::StartedDragging](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/tabs/tab_strip.cc;l=533;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588).
The evene is passed through the below path:
[BrowserTabStripController::OnStartDragging](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc;l=484;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
-> [BrowserFrame::SetTabDragKind](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/frame/browser_frame.cc;l=408;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
-> [DesktopBrowserFrameLacros::TabDraggingKindChanged](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/frame/desktop_browser_frame_lacros.cc;l=38;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
-> [BrowserDesktopWindowTreeHostLacros::TabDraggingKindChanged](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/frame/browser_desktop_window_tree_host_lacros.cc;l=166;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
-> [WaylandToplevelWindow::StartWindowDraggingSessionIfNeeded](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_toplevel_window.cc;l=764;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
-> [WaylandWindowDragController::StartDragSession](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=120;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588)
Their 3 types of [TabDragKind](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/ui/views/frame/browser_frame.h;l=49-59;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588), `kNone` representing no drag is active, `kTab` representing one or more but not all tabs within a window are being draged, and `kAllTabs` representing all of the tabs in a window are being dragged. `kAllTabs` implies that it's dragging the window.
Both of them are handled in the same protocol.
### Item drag
Used when you drag an item such as an image to for example a download box or folder.
[DragDownloadItem](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chrome/browser/download/drag_download_item_aura.cc;l=26;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) is one of the usage.
It goes to [WaylandWindow::StartDrag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window.cc;l=255;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) and then [WaylandDataDragController::StartSession](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc;l=119;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588).
This time, unlike tab dragging, we set mime type using [WaylandExchangeDataProvider](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_exchange_data_provider.h;l=16;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) and construct a different mime type depending on the content to drag.
## Wayland Platform Implementation
Let's see how it is handled on ozone/wayland.
It's controlled by [WaylandWindowDragController](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.h) on wayland.
Tab drag is conreolled by state machine:
```cpp=
enum class State {
kIdle, // No DnD session nor drag loop running.
kAttached, // DnD session ongoing but no drag loop running.
kDetached, // Drag loop running. ie: blocked in a Drag() call.
kDropped, // Drop event was just received.
kCancelled, // Drag cancel event was just received.
kAttaching, // About to transition back to |kAttached|.
};
```
Initially, `state_` is set as `kIdle`.
On dragging [StartDragSession](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=120;drc=b254fd126e05f71848bd58aba7c46e649794807f) is called as explained in above section, and the state is updated to `kAttached`.
Then we create [WaylandDataSource](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_source.h;l=39;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) from [WaylandDataDeviceManager](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_device_manager.h;l=19;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) and set Drag-and-Drop action to [kDndActionWindowDrag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=64;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588). FYI: DnD is a short word for drag-and-drop.
While we are dragging, the window uses custom mime type `chromium/x-window` defined by [kMimeTypeChromiumWindow](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=61;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588).
Then [StartDrag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_device.cc;l=36;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) from data device. [WaylandDataDevice](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_device.h) class provides access to inter-client data transfer mechanisms which wraps wl_data_device protocol such as `wl_data_device_start_drag`.
[StartDrag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_data_device.cc;l=36;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) calls `wl_data_device_start_drag` protocol. (For window drag controller, [DrawIcon](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=207;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) is done by buffer manager.)
Server side impl in Exo is in [wl_data_device_manager.cc](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/wayland/wl_data_device_manager.cc).
Let's look at the drag event triggered from mouse input from here.
`wl_data_device_start_drag` goes to [Seat::StartDrag](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:components/exo/seat.cc;l=122;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588).
It obtains `cursor_location` inside Ash from [Env::GetLastPointerPoint](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/aura/env.cc;l=157;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) and then creates [DragDropOperation](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:components/exo/drag_drop_operation.cc;l=157;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) with `cursor_location` as drag start point.
[DragDropOperation ctor](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:components/exo/drag_drop_operation.cc;l=169;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) is a main part. It [ScheduleStartDragDropOperation](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:components/exo/drag_drop_operation.cc;l=356;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) which post [StartDragDropOperation](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:components/exo/drag_drop_operation.cc;l=374;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) task to sequenced task runner.
## Extended drag source
[ExtendedDragSource](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc;l=76;drc=636048a55ca7e2e35d5a15ba10d64b03dfe8c588) may be supported on new enough version.
It can use `zcr_extended_drag_source_v1` protocol and the server side impl is in [zcr_extended_drag.cc](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/wayland/zcr_extended_drag.cc) and the protocol is designed in [extended-drag-unstable-v1.xml](https://source.chromium.org/chromium/chromium/src/+/main:third_party/wayland-protocols/unstable/extended-drag/extended-drag-unstable-v1.xml).
This extends the existing Wayland drag-and-drop protocol such as making toplevel shell surfaces draggable or snappable.
It is required for Chromium-like tab tragging UX where the user is able to drag a tab or any other kind of UI piece out of its original window into a new surface.