chromium/chrome/browser/ui/views/tabs/tab_drag_controller.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"

#include <algorithm>
#include <limits>
#include <optional>
#include <set>
#include <utility>

#include "base/auto_reset.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_auto_reset.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/tabs/features.h"
#include "chrome/browser/ui/tabs/organization/metrics.h"
#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
#include "chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h"
#include "chrome/browser/ui/tabs/tab_group.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/chrome_widget_sublevel.h"
#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_slot_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_layout_helper.h"
#include "chrome/browser/ui/views/tabs/tab_strip_scroll_session.h"
#include "chrome/browser/ui/views/tabs/tab_style_views.h"
#include "chrome/browser/ui/views/tabs/window_finder.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/ui/web_applications/web_app_tabbed_utils.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#include "components/tab_groups/tab_group_id.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/os_exchange_data_provider_factory.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/presentation_time_recorder.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/drag_utils.h"
#include "ui/views/event_monitor.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/base/window_state_type.h"  // nogncheck
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/public/cpp/window_properties.h"  // nogncheck
#endif

#if BUILDFLAG(IS_MAC)
#include "base/debug/dump_without_crashing.h"
#include "base/strings/string_number_conversions.h"
#include "components/crash/core/common/crash_key.h"
#include "components/remote_cocoa/browser/window.h"
#endif

#if BUILDFLAG(IS_LINUX)
#include "ui/aura/client/drag_drop_client.h"
#endif

#if defined(USE_AURA)
#include "ui/aura/env.h"                            // nogncheck
#include "ui/aura/window.h"                         // nogncheck
#include "ui/wm/core/window_modality_controller.h"  // nogncheck
#endif

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif

OpenURLParams;
WebContents;

// If non-null there is a drag underway.
static TabDragController* g_tab_drag_controller =;

namespace {

// A dragged window is forced to be a bit smaller than maximized bounds during a
// drag. This prevents the dragged browser widget from getting maximized at
// creation and makes it easier to drag tabs out of a restored window that had
// maximized size.
constexpr int kMaximizedWindowInset =;  // DIPs.

// Some platforms, such as Lacros and Desktop Linux with Wayland, disallow
// client applications to manipulate absolute screen positions, by design.
// Preventing, for example, clients from programmatically positioning toplevel
// windows using absolute coordinates. By default, this class assumes that the
// underlying platform supports it, unless indicated by the Ozone platform
// properties.
bool PlatformProvidesAbsoluteWindowPositions() {}

constexpr char kTabDraggingPresentationTimeHistogram[] =;
constexpr char kTabDraggingPresentationTimeMaxHistogram[] =;
constexpr char kDragAmongTabsPresentationTimeHistogram[] =;
constexpr char kDragToNewBrowserPresentationTimeHistogram[] =;

#if BUILDFLAG(IS_CHROMEOS)

// Returns the aura::Window which stores the window properties for tab-dragging.
aura::Window* GetWindowForTabDraggingProperties(const TabDragContext* context) {
  return context ? context->GetWidget()->GetNativeWindow() : nullptr;
}

// Returns true if |context| browser window is snapped.
bool IsSnapped(const TabDragContext* context) {
  DCHECK(context);
  chromeos::WindowStateType type =
      GetWindowForTabDraggingProperties(context)->GetProperty(
          chromeos::kWindowStateTypeKey);
  return type == chromeos::WindowStateType::kPrimarySnapped ||
         type == chromeos::WindowStateType::kSecondarySnapped;
}

#else

bool IsSnapped(const TabDragContext* context) {}

#endif  // BUILDFLAG(IS_CHROMEOS)

// TODO(crbug.com/41482188): This should take a weak ref and return a Liveness,
// because setting capture may cause the drag to end and the drag controller to
// be destroyed.
void SetCapture(TabDragContext* context) {}

gfx::Rect GetTabstripScreenBounds(const TabDragContext* context) {}

// Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
// of |bounds| is adjusted by |vertical_adjustment|.
bool DoesRectContainVerticalPointExpanded(const gfx::Rect& bounds,
                                          int vertical_adjustment,
                                          int y) {}

// Adds |x_offset| to all the rectangles in |rects|.
void OffsetX(int x_offset, std::vector<gfx::Rect>* rects) {}

bool IsWindowDragUsingSystemDragDropAllowed() {}

void UpdateSystemDnDDragImage(TabDragContext* attached_context,
                              const gfx::ImageSkia& image) {}

}  // namespace

// EventTracker installs an event monitor and ends the drag when it receives any
// key event, or a mouse release event during a system DnD session.
class EventTracker : public ui::EventObserver {};

class TabDragController::SourceTabStripEmptinessTracker
    : public TabStripModelObserver {};

class TabDragController::DraggedTabsClosedTracker
    : public TabStripModelObserver {};

TabDragController::TabDragData::TabDragData()
    :{}

TabDragController::TabDragData::~TabDragData() = default;

TabDragController::TabDragData::TabDragData(TabDragData&&) = default;

///////////////////////////////////////////////////////////////////////////////
// TabDragController, public:

// static
const int TabDragController::kTouchVerticalDetachMagnetism =;

// static
const int TabDragController::kVerticalDetachMagnetism =;

TabDragController::TabDragController()
    :{}

TabDragController::~TabDragController() {}

TabDragController::Liveness TabDragController::Init(
    TabDragContext* source_context,
    TabSlotView* source_view,
    const std::vector<raw_ptr<TabSlotView, VectorExperimental>>& dragging_views,
    const gfx::Point& mouse_offset,
    int source_view_offset,
    ui::ListSelectionModel initial_selection_model,
    ui::mojom::DragEventSource event_source) {}

// static
bool TabDragController::IsAttachedTo(const TabDragContextBase* context) {}

// static
bool TabDragController::IsActive() {}

// static
bool TabDragController::IsSystemDragAndDropSessionRunning() {}

// static
void TabDragController::OnSystemDragAndDropUpdated(
    const ui::DropTargetEvent& event) {}

// static
void TabDragController::OnSystemDragAndDropExited() {}

// static
void TabDragController::OnSystemDragAndDropEnded() {}

// static
TabDragContext* TabDragController::GetSourceContext() {}

void TabDragController::TabWasAdded() {}

void TabDragController::OnTabWillBeRemoved(content::WebContents* contents) {}

bool TabDragController::CanRemoveTabDuringDrag(
    content::WebContents* contents) const {}

bool TabDragController::CanRestoreFullscreenWindowDuringDrag() const {}

void TabDragController::Drag(const gfx::Point& point_in_screen) {}

void TabDragController::EndDrag(EndDragReason reason) {}

void TabDragController::SetDragLoopDoneCallbackForTesting(
    base::OnceClosure callback) {}

void TabDragController::InitDragData(TabSlotView* view,
                                     TabDragData* drag_data) {}

void TabDragController::OnWidgetBoundsChanged(views::Widget* widget,
                                              const gfx::Rect& new_bounds) {}

void TabDragController::OnWidgetDestroyed(views::Widget* widget) {}

void TabDragController::OnSourceTabStripEmpty() {}

void TabDragController::OnActiveStripWebContentsRemoved(
    content::WebContents* contents) {}

void TabDragController::OnActiveStripWebContentsReplaced(
    content::WebContents* previous,
    content::WebContents* next) {}

///////////////////////////////////////////////////////////////////////////////
// TabDragController, private:

void TabDragController::InitWindowCreatePoint() {}

gfx::Point TabDragController::GetWindowCreatePoint(
    const gfx::Point& origin) const {}

void TabDragController::SaveFocus() {}

void TabDragController::RestoreFocus() {}

bool TabDragController::CanStartDrag(const gfx::Point& point_in_screen) const {}

TabDragController::Liveness TabDragController::ContinueDragging(
    const gfx::Point& point_in_screen) {}

TabDragController::DragBrowserResultType
TabDragController::DragBrowserToNewTabStrip(TabDragContext* target_context,
                                            const gfx::Point& point_in_screen) {}

bool TabDragController::ShouldDragWindowUsingSystemDnD() {}

gfx::ImageSkia TabDragController::GetDragImageForSystemDnD() {}

TabDragController::Liveness TabDragController::StartSystemDnDSessionIfNecessary(
    TabDragContext* context,
    const gfx::Point& point_in_screen) {}

gfx::Rect TabDragController::GetEnclosingRectForDraggedTabs() {}

gfx::Point TabDragController::GetLastPointInScreen() {}

bool TabDragController::IsDraggingTabState() {}

views::View* TabDragController::GetAttachedContext() {}

views::ScrollView* TabDragController::GetScrollView() {}

void TabDragController::MoveAttached(const gfx::Point& point_in_screen,
                                     bool just_attached) {}

TabDragController::DetachPosition TabDragController::GetDetachPosition(
    const gfx::Point& point_in_screen) {}

void TabDragController::DetachAndAttachToNewContext(
    ReleaseCapture release_capture,
    TabDragContext* target_context,
    const gfx::Point& point_in_screen,
    bool set_capture) {}

TabDragController::Liveness TabDragController::GetTargetTabStripForPoint(
    const gfx::Point& point_in_screen,
    TabDragContext** context) {}

bool TabDragController::DoesTabStripContain(
    TabDragContext* context,
    const gfx::Point& point_in_screen) const {}

void TabDragController::Attach(TabDragContext* attached_context,
                               const gfx::Point& point_in_screen,
                               std::unique_ptr<TabDragController> controller,
                               bool set_capture) {}

std::unique_ptr<TabDragController> TabDragController::Detach(
    ReleaseCapture release_capture) {}

void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
    const gfx::Point& point_in_screen) {}

void TabDragController::RunMoveLoop(const gfx::Vector2d& drag_offset) {}

gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
    const gfx::Point& tab_strip_point) {}

gfx::Point TabDragController::GetAttachedDragPoint(
    const gfx::Point& point_in_screen) {}

std::vector<raw_ptr<TabSlotView, VectorExperimental>>
TabDragController::GetViewsMatchingDraggedContents(TabDragContext* context) {}

void TabDragController::EndDragImpl(EndDragType type) {}

void TabDragController::RevertDrag() {}

void TabDragController::ResetSelection(TabStripModel* model) {}

void TabDragController::RestoreInitialSelection() {}

void TabDragController::RevertHeaderDrag(tab_groups::TabGroupId group_id) {}

void TabDragController::RevertDragAt(size_t drag_index) {}

void TabDragController::CompleteDrag() {}

void TabDragController::MaximizeAttachedWindow() {}

void TabDragController::BringWindowUnderPointToFront(
    const gfx::Point& point_in_screen) {}

bool TabDragController::IsDraggingTab(content::WebContents* contents) const {}

views::Widget* TabDragController::GetAttachedBrowserWidget() {}

bool TabDragController::AreTabsConsecutive() {}

gfx::Rect TabDragController::CalculateDraggedBrowserBounds(
    TabDragContext* source,
    const gfx::Point& point_in_screen,
    std::vector<gfx::Rect>* drag_bounds) {}

gfx::Rect TabDragController::CalculateNonMaximizedDraggedBrowserBounds(
    views::Widget* widget,
    const gfx::Point& point_in_screen) {}

void TabDragController::AdjustBrowserAndTabBoundsForDrag(
    int previous_tab_area_width,
    const gfx::Point& point_in_screen,
    gfx::Vector2d* drag_offset,
    std::vector<gfx::Rect>* drag_bounds) {}

std::optional<webapps::AppId> TabDragController::GetControllingAppForDrag(
    Browser* browser) {}

Browser* TabDragController::CreateBrowserForDrag(
    TabDragContext* source,
    const gfx::Point& point_in_screen,
    gfx::Vector2d* drag_offset,
    std::vector<gfx::Rect>* drag_bounds) {}

gfx::Point TabDragController::GetCursorScreenPoint() {}

gfx::Vector2d TabDragController::GetWindowOffset(
    const gfx::Point& point_in_screen) {}

TabDragController::Liveness TabDragController::GetLocalProcessWindow(
    const gfx::Point& screen_point,
    bool exclude_dragged_view,
    gfx::NativeWindow* window) {}

void TabDragController::SetTabDraggingInfo() {}

void TabDragController::ClearTabDraggingInfo() {}

std::optional<tab_groups::TabGroupId>
TabDragController::CalculateGroupForDraggedTabs(int to_index) {}

bool TabDragController::CanAttachTo(gfx::NativeWindow window) {}

int TabDragController::GetOutOfBoundsYCoordinate() const {}

void TabDragController::NotifyEventIfTabAddedToGroup() {}

void TabDragController::MaybePauseTrackingSavedTabGroup() {}

void TabDragController::MaybeResumeTrackingSavedTabGroup() {}