15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <math.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/auto_reset.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
15e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "chrome/browser/ui/browser_list.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/frame/browser_view.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/window_finder.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_details.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/user_metrics.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/browser/extension_function_dispatcher.h"
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "ui/aura/env.h"
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/aura/window.h"
34d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event_constants.h"
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/events/gestures/gesture_recognizer.h"
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/geometry/point_conversions.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/views/focus/view_storage.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "ui/wm/core/coordinate_conversion.h"
42a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#include "ui/wm/core/window_modality_controller.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(USE_ASH)
4503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ash/accelerators/accelerator_commands.h"
4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ash/shell.h"
4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ash/wm/maximize_mode/maximize_mode_controller.h"
4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ash/wm/window_state.h"
4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif
5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::UserMetricsAction;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::OpenURLParams;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// If non-null there is a drag underway.
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static TabDragController* instance_ = NULL;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Delay, in ms, during dragging before we bring a window to front.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBringToFrontDelay = 750;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Initial delay before moving tabs when the dragged tab is close to the edge of
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the stacked tabs.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMoveAttachedInitialDelay = 600;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Delay for moving tabs after the initial delay has passed.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMoveAttachedSubsequentDelay = 300;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kHorizontalMoveThreshold = 16;  // Pixels.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Distance from the next/previous stacked before before we consider the tab
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// close enough to trigger moving.
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kStackedDistance = 36;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(USE_ASH)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
7868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  ash::wm::GetWindowState(window)->set_window_position_managed(value);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Returns true if |tab_strip| browser window is docked.
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool IsDockedOrSnapped(const TabStrip* tab_strip) {
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK(tab_strip);
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ash::wm::WindowState* window_state =
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ash::wm::GetWindowState(tab_strip->GetWidget()->GetNativeWindow());
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return window_state->IsDocked() || window_state->IsSnapped();
871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
8803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#else
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)bool IsDockedOrSnapped(const TabStrip* tab_strip) {
9303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return false;
9403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
9503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of |bounds| is adjusted by |vertical_adjustment|.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DoesRectContainVerticalPointExpanded(
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& bounds,
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int vertical_adjustment,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int y) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int upper_threshold = bounds.bottom() + vertical_adjustment;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lower_threshold = bounds.y() - vertical_adjustment;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return y >= lower_threshold && y <= upper_threshold;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Adds |x_offset| to all the rectangles in |rects|.
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void OffsetX(int x_offset, std::vector<gfx::Rect>* rects) {
1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (x_offset == 0)
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (size_t i = 0; i < rects->size(); ++i)
1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    (*rects)[i].set_x((*rects)[i].x() + x_offset);
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// WidgetObserver implementation that resets the window position managed
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// property on Show.
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// We're forced to do this here since BrowserFrameAsh resets the 'window
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// position managed' property during a show and we need the property set to
1214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// false before WorkspaceLayoutManager sees the visibility change.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class WindowPositionManagedUpdater : public views::WidgetObserver {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnWidgetVisibilityChanged(views::Widget* widget,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         bool visible) OVERRIDE {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetWindowPositionManaged(widget->GetNativeView(), false);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
130c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// EscapeTracker installs itself as a pre-target handler on aura::Env and runs a
131c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// callback when it receives the escape key.
132c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochclass EscapeTracker : public ui::EventHandler {
133c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch public:
134c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  explicit EscapeTracker(const base::Closure& callback)
135c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      : escape_callback_(callback) {
136c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    aura::Env::GetInstance()->AddPreTargetHandler(this);
137c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
138c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
139c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual ~EscapeTracker() {
140c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    aura::Env::GetInstance()->RemovePreTargetHandler(this);
141c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
142c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
143c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch private:
144c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // ui::EventHandler:
145c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  virtual void OnKeyEvent(ui::KeyEvent* key) OVERRIDE {
146c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (key->type() == ui::ET_KEY_PRESSED &&
147c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        key->key_code() == ui::VKEY_ESCAPE) {
148c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      escape_callback_.Run();
149c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
150c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
151c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
152c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::Closure escape_callback_;
153c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
154c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DISALLOW_COPY_AND_ASSIGN(EscapeTracker);
155c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch};
156c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::TabDragData::TabDragData()
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : contents(NULL),
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_model_index(-1),
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tab(NULL),
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pinned(false) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::TabDragData::~TabDragData() {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabDragController, public:
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int TabDragController::kTouchVerticalDetachMagnetism = 50;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int TabDragController::kVerticalDetachMagnetism = 15;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::TabDragController()
179116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : event_source_(EVENT_SOURCE_MOUSE),
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_tabstrip_(NULL),
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_(NULL),
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      screen_(NULL),
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      can_release_capture_(true),
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offset_to_width_ratio_(0),
18658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      old_focused_view_id_(
18758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          views::ViewStorage::GetInstance()->CreateStorageID()),
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_move_screen_loc_(0),
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      started_drag_(false),
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      active_(true),
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_tab_index_(std::numeric_limits<size_t>::max()),
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      initial_move_(true),
19303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      detach_behavior_(DETACHABLE),
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      move_behavior_(REORDER),
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_move_direction_(0),
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_dragging_window_(false),
19768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      is_dragging_new_browser_(false),
19868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      was_source_maximized_(false),
19968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      was_source_fullscreen_(false),
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      did_restore_window_(false),
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waiting_for_run_loop_to_exit_(false),
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_strip_to_attach_to_after_exit_(NULL),
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      move_loop_widget_(NULL),
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      is_mutating_(false),
2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      attach_x_(-1),
2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      attach_index_(-1),
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      weak_factory_(this) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance_ = this;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::~TabDragController() {
21358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  views::ViewStorage::GetInstance()->RemoveView(old_focused_view_id_);
21458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (instance_ == this)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    instance_ = NULL;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (move_loop_widget_) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_loop_widget_->RemoveObserver(this);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetWindowPositionManaged(move_loop_widget_->GetNativeView(), true);
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
223116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (source_tabstrip_)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetModel(source_tabstrip_)->RemoveObserver(this);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (event_source_ == EVENT_SOURCE_TOUCH) {
227116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    TabStrip* capture_tabstrip = attached_tabstrip_ ?
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        attached_tabstrip_ : source_tabstrip_;
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    capture_tabstrip->GetWidget()->ReleaseCapture();
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::Init(
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* source_tabstrip,
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Tab* source_tab,
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<Tab*>& tabs,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& mouse_offset,
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int source_tab_offset,
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::ListSelectionModel& initial_selection_model,
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    MoveBehavior move_behavior,
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EventSource event_source) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!tabs.empty());
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source_tabstrip_ = source_tabstrip;
24568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  was_source_maximized_ = source_tabstrip->GetWidget()->IsMaximized();
24668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  was_source_fullscreen_ = source_tabstrip->GetWidget()->IsFullscreen();
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  screen_ = gfx::Screen::GetScreenFor(
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_tabstrip->GetWidget()->GetNativeView());
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  host_desktop_type_ = chrome::GetHostDesktopTypeForNativeView(
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_tabstrip->GetWidget()->GetNativeView());
2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Do not release capture when transferring capture between widgets on:
2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // - Desktop Linux
2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  //     Mouse capture is not synchronous on desktop Linux. Chrome makes
2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  //     transferring capture between widgets without releasing capture appear
2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  //     synchronous on desktop Linux, so use that.
2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // - Ash
2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  //     Releasing capture on Ash cancels gestures so avoid it.
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#if defined(OS_LINUX)
2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  can_release_capture_ = false;
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#else
2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  can_release_capture_ =
2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      (host_desktop_type_ != chrome::HOST_DESKTOP_TYPE_ASH);
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#endif
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  start_point_in_screen_ = gfx::Point(source_tab_offset, mouse_offset.y());
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToScreen(source_tab, &start_point_in_screen_);
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event_source_ = event_source;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mouse_offset_ = mouse_offset;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_behavior_ = move_behavior;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_point_in_screen_ = start_point_in_screen_;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_move_screen_loc_ = start_point_in_screen_.x();
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  initial_tab_positions_ = source_tabstrip->GetTabXCoordinates();
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
273116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  GetModel(source_tabstrip_)->AddObserver(this);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_data_.resize(tabs.size());
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InitTabDragData(tabs[i], &(drag_data_[i]));
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source_tab_index_ =
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for Esc key presses.
282c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  escape_tracker_.reset(
283c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      new EscapeTracker(base::Bind(&TabDragController::EndDrag,
284c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                   weak_factory_.GetWeakPtr(),
285c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                   END_DRAG_CANCEL)));
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source_tab->width() > 0) {
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    offset_to_width_ratio_ = static_cast<float>(
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        source_tab->GetMirroredXInView(source_tab_offset)) /
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<float>(source_tab->width());
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitWindowCreatePoint();
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  initial_selection_model_.Copy(initial_selection_model);
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Gestures don't automatically do a capture. We don't allow multiple drags at
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the same time, so we explicitly capture.
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (event_source == EVENT_SOURCE_TOUCH)
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    source_tabstrip_->GetWidget()->SetCapture(source_tabstrip_);
29903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
30003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(USE_ASH)
30103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (ash::Shell::HasInstance() &&
30203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ash::Shell::GetInstance()->maximize_mode_controller()->
30303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          IsMaximizeModeWindowManagerEnabled()) {
30403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    detach_behavior_ = NOT_DETACHABLE;
30503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
30603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
3101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool TabDragController::IsAttachedTo(const TabStrip* tab_strip) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (instance_ && instance_->active() &&
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          instance_->attached_tabstrip() == tab_strip);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::IsActive() {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return instance_ && instance_->active();
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::SetMoveBehavior(MoveBehavior behavior) {
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (started_drag())
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_behavior_ = behavior;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::Drag(const gfx::Point& point_in_screen) {
328f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::Drag",
329f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "point_in_screen", point_in_screen.ToString());
330f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bring_to_front_timer_.Stop();
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_stacked_timer_.Stop();
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (waiting_for_run_loop_to_exit_)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!started_drag_) {
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!CanStartDrag(point_in_screen))
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;  // User hasn't dragged far enough yet.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // On windows SaveFocus() may trigger a capture lost, which destroys us.
34258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    {
343d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
34458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      SaveFocus();
345d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (!ref)
34658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        return;
34758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    started_drag_ = true;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Attach(source_tabstrip_, gfx::Point());
350116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (static_cast<int>(drag_data_.size()) ==
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetModel(source_tabstrip_)->count()) {
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (was_source_maximized_ || was_source_fullscreen_) {
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        did_restore_window_ = true;
354a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // When all tabs in a maximized browser are dragged the browser gets
355a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // restored during the drag and maximized back when the drag ends.
356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        views::Widget* widget = GetAttachedBrowserWidget();
357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
359a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
360a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source_tabstrip_,
361a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                           point_in_screen,
362a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                           &drag_bounds));
363a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        new_bounds.Offset(-widget->GetRestoredBounds().x() +
364a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          point_in_screen.x() -
365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          mouse_offset_.x(), 0);
366a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        widget->SetVisibilityChangedAnimationsEnabled(false);
367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        widget->Restore();
368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        widget->SetBounds(new_bounds);
369a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
370a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                         point_in_screen,
371a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                         &drag_bounds);
372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        widget->SetVisibilityChangedAnimationsEnabled(true);
373a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RunMoveLoop(GetWindowOffset(point_in_screen));
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ContinueDragging(point_in_screen);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::EndDrag(EndDragReason reason) {
383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT0("views", "TabDragController::EndDrag");
384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're dragging a window ignore capture lost since it'll ultimately
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // trigger the move loop to end and we'll revert the drag when RunMoveLoop()
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // finishes.
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reason == END_DRAG_CAPTURE_LOST && is_dragging_window_)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndDragImpl(reason != END_DRAG_COMPLETE && source_tabstrip_ ?
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              CANCELED : NORMAL);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabDragController::InitTabDragData(Tab* tab,
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        TabDragData* drag_data) {
396f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT0("views", "TabDragController::InitTabDragData");
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_data->source_model_index =
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      source_tabstrip_->GetModelIndexOfTab(tab);
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  drag_data->contents = GetModel(source_tabstrip_)->GetWebContentsAt(
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_data->source_model_index);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::Source<WebContents>(drag_data->contents));
406d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
407d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabDragController, content::NotificationObserver implementation:
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::Observe(
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WebContents* destroyed_web_contents =
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<WebContents>(source).ptr();
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i) {
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (drag_data_[i].contents == destroyed_web_contents) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // One of the tabs we're dragging has been destroyed. Cancel the drag.
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_data_[i].contents = NULL;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndDragImpl(TAB_DESTROYED);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we get here it means we got notification for a tab we don't know about.
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabDragController::OnWidgetBoundsChanged(views::Widget* widget,
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                              const gfx::Rect& new_bounds) {
432f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::OnWidgetBoundsChanged",
433f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "new_bounds", new_bounds.ToString());
434f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Drag(GetCursorScreenPoint());
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::TabStripEmpty() {
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetModel(source_tabstrip_)->RemoveObserver(this);
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NULL out source_tabstrip_ so that we don't attempt to add back to it (in
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the case of a revert).
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source_tabstrip_ = NULL;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabDragController, private:
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::InitWindowCreatePoint() {
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // window_create_point_ is only used in CompleteDrag() (through
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GetWindowCreatePoint() to get the start point of the docked window) when
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the attached_tabstrip_ is NULL and all the window's related bound
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // information are obtained from source_tabstrip_. So, we need to get the
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the window_create_point_ is not in the correct coordinate system. Please
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // refer to http://crbug.com/6223 comment #15 for detailed information.
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View* first_tab = source_tabstrip_->tab_at(0);
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_create_point_ = first_source_tab_point_;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Point TabDragController::GetWindowCreatePoint(
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& origin) const {
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the cursor is outside the monitor area, move it inside. For example,
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dropping a tab onto the task bar on Windows produces this situation.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect work_area = screen_->GetDisplayNearestPoint(origin).work_area();
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point create_point(origin);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!work_area.IsEmpty()) {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (create_point.x() < work_area.x())
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create_point.set_x(work_area.x());
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (create_point.x() > work_area.right())
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create_point.set_x(work_area.right());
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (create_point.y() < work_area.y())
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create_point.set_y(work_area.y());
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (create_point.y() > work_area.bottom())
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create_point.set_y(work_area.bottom());
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Point(create_point.x() - window_create_point_.x(),
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    create_point.y() - window_create_point_.y());
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::SaveFocus() {
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(source_tabstrip_);
48458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  views::View* focused_view =
48558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      source_tabstrip_->GetFocusManager()->GetFocusedView();
48658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (focused_view)
48758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    views::ViewStorage::GetInstance()->StoreView(old_focused_view_id_,
48858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                                 focused_view);
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
49058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // WARNING: we may have been deleted.
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::RestoreFocus() {
494f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (attached_tabstrip_ != source_tabstrip_) {
495f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (is_dragging_new_browser_) {
496f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      content::WebContents* active_contents = source_dragged_contents();
497f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (active_contents && !active_contents->FocusLocationBarByDefault())
498010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        active_contents->Focus();
499f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
50058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
501f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
50258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  views::View* old_focused_view =
5035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      views::ViewStorage::GetInstance()->RetrieveView(old_focused_view_id_);
50458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!old_focused_view)
50558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
50658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  old_focused_view->GetFocusManager()->SetFocusedView(old_focused_view);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::CanStartDrag(const gfx::Point& point_in_screen) const {
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Determine if the mouse has moved beyond a minimum elasticity distance in
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // any direction from the starting point.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kMinimumDragDistance = 10;
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x_offset = abs(point_in_screen.x() - start_point_in_screen_.x());
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int y_offset = abs(point_in_screen.y() - start_point_in_screen_.y());
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return sqrt(pow(static_cast<float>(x_offset), 2) +
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::ContinueDragging(const gfx::Point& point_in_screen) {
520f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::ContinueDragging",
521f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "point_in_screen", point_in_screen.ToString());
522f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
523116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(attached_tabstrip_);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  TabStrip* target_tabstrip = detach_behavior_ == DETACHABLE ?
52603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      GetTargetTabStripForPoint(point_in_screen) : source_tabstrip_;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_) {
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int move_delta = point_in_screen.x() - last_point_in_screen_.x();
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (move_delta > 0)
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_move_direction_ |= kMovedMouseRight;
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (move_delta < 0)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_move_direction_ |= kMovedMouseLeft;
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_point_in_screen_ = point_in_screen;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_strip_changed) {
53968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    is_dragging_new_browser_ = false;
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    did_restore_window_ = false;
541116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (DragBrowserToNewTabStrip(target_tabstrip, point_in_screen) ==
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DRAG_BROWSER_RESULT_STOP) {
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (is_dragging_window_) {
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<base::Timer*>(&bring_to_front_timer_)->Start(FROM_HERE,
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(kBringToFrontDelay),
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&TabDragController::BringWindowUnderPointToFront,
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::Unretained(this), point_in_screen));
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!is_dragging_window_ && attached_tabstrip_) {
5545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (move_only()) {
5555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DragActiveTabStacked(point_in_screen);
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      MoveAttached(point_in_screen);
5585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (tab_strip_changed) {
5595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // Move the corresponding window to the front. We do this after the
5605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // move as on windows activate triggers a synchronous paint.
5615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        attached_tabstrip_->GetWidget()->Activate();
5625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::DragBrowserResultType
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::DragBrowserToNewTabStrip(
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* target_tabstrip,
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
571f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::DragBrowserToNewTabStrip",
572f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "point_in_screen", point_in_screen.ToString());
573f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!target_tabstrip) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DetachIntoNewBrowserAndRunMoveLoop(point_in_screen);
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return DRAG_BROWSER_RESULT_STOP;
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_dragging_window_) {
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ReleaseCapture() is going to result in calling back to us (because it
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // results in a move). That'll cause all sorts of problems.  Reset the
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // observer so we don't get notified and process the event.
5821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      move_loop_widget_->RemoveObserver(this);
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      move_loop_widget_ = NULL;
5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget* browser_widget = GetAttachedBrowserWidget();
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Need to release the drag controller before starting the move loop as it's
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // going to trigger capture lost, which cancels drag.
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabstrip_->ReleaseDragController();
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    target_tabstrip->OwnDragController(this);
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Disable animations so that we don't see a close animation on aero.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_widget->SetVisibilityChangedAnimationsEnabled(false);
5931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (can_release_capture_)
5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser_widget->ReleaseCapture();
5951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    else
5961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_);
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_WIN)
5988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // The Gesture recognizer does not work well currently when capture changes
5998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // while a touch gesture is in progress. So we need to manually transfer
6008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // gesture sequence and the GR's touch events queue to the new window. This
6018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // should really be done somewhere in capture change code and or inside the
6028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // GR. But we currently do not have a consistent way for doing it that would
6038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // work in all cases. Hence this hack.
6048bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    ui::GestureRecognizer::Get()->TransferEventsTo(
6058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        browser_widget->GetNativeView(),
6068bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        target_tabstrip->GetWidget()->GetNativeView());
6078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#endif
6088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The window is going away. Since the drag is still on going we don't want
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // that to effect the position of any windows.
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetWindowPositionManaged(browser_widget->GetNativeView(), false);
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
613a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // EndMoveLoop is going to snap the window back to its original location.
615a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Hide it so users don't see this. Hiding a window in Linux aura causes
616a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // it to lose capture so skip it.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_widget->Hide();
618a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#endif
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_widget->EndMoveLoop();
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Ideally we would always swap the tabs now, but on non-ash Windows, it
6221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // seems that running the move loop implicitly activates the window when
6231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // done, leading to all sorts of flicker. So, on non-ash Windows, instead
6241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // we process the move after the loop completes. But on chromeos, we can
6251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // do tab swapping now to avoid the tab flashing issue
6261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // (crbug.com/116329).
6271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (can_release_capture_) {
6281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      tab_strip_to_attach_to_after_exit_ = target_tabstrip;
6291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else {
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      is_dragging_window_ = false;
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Detach(DONT_RELEASE_CAPTURE);
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Attach(target_tabstrip, point_in_screen);
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Move the tabs into position.
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MoveAttached(point_in_screen);
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      attached_tabstrip_->GetWidget()->Activate();
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waiting_for_run_loop_to_exit_ = true;
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return DRAG_BROWSER_RESULT_STOP;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Detach(DONT_RELEASE_CAPTURE);
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Attach(target_tabstrip, point_in_screen);
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DRAG_BROWSER_RESULT_CONTINUE;
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::DragActiveTabStacked(
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_->tab_count() !=
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(initial_tab_positions_.size()))
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // TODO: should cancel drag if this happens.
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int delta = point_in_screen.x() - start_point_in_screen_.x();
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_->DragActiveTab(initial_tab_positions_, delta);
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::MoveAttachedToNextStackedIndex(
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index = attached_tabstrip_->touch_layout_->active_index();
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index + 1 >= attached_tabstrip_->tab_count())
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index + 1);
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartMoveStackedTimerIfNecessary(point_in_screen,
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   kMoveAttachedSubsequentDelay);
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::MoveAttachedToPreviousStackedIndex(
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index = attached_tabstrip_->touch_layout_->active_index();
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index <= attached_tabstrip_->GetMiniTabCount())
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index - 1);
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartMoveStackedTimerIfNecessary(point_in_screen,
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   kMoveAttachedSubsequentDelay);
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::MoveAttached(const gfx::Point& point_in_screen) {
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(attached_tabstrip_);
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!is_dragging_window_);
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Determine the horizontal move threshold. This is dependent on the width
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of tabs. The smaller the tabs compared to the standard size, the smaller
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the threshold.
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int threshold = kHorizontalMoveThreshold;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!attached_tabstrip_->touch_layout_.get()) {
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double unselected, selected;
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double ratio = unselected / Tab::GetStandardSize().width();
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // else case: touch tabs never shrink.
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs(drag_data_.size());
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs[i] = drag_data_[i].attached_tab;
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool did_layout = false;
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the model, moving the WebContents from one index to another. Do this
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // only if we have moved a minimum distance since the last reorder (to prevent
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // jitter) or if this the first move and the tabs are not consecutive.
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((abs(point_in_screen.x() - last_move_screen_loc_) > threshold ||
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (initial_move_ && !AreTabsConsecutive()))) {
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStripModel* attached_model = GetModel(attached_tabstrip_);
708f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int to_index = GetInsertionIndexForDraggedBounds(
709f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        GetDraggedViewTabStripBounds(dragged_view_point));
7104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    bool do_move = true;
7114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // While dragging within a tabstrip the expectation is the insertion index
7124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // is based on the left edge of the tabs being dragged. OTOH when dragging
7134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // into a new tabstrip (attaching) the expectation is the insertion index is
7144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // based on the cursor. This proves problematic as insertion may change the
7154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // size of the tabs, resulting in the index calculated before the insert
7164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // differing from the index calculated after the insert. To alleviate this
7174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // the index is chosen before insertion, and subsequently a new index is
7184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // only used once the mouse moves enough such that the index changes based
7194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // on the direction the mouse moved relative to |attach_x_| (smaller
7204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // x-coordinate should yield a smaller index or larger x-coordinate yields a
7214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // larger index).
7224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (attach_index_ != -1) {
7234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      gfx::Point tab_strip_point(point_in_screen);
724a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
7254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      const int new_x =
7264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          attached_tabstrip_->GetMirroredXInView(tab_strip_point.x());
7274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (new_x < attach_x_)
7284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        to_index = std::min(to_index, attach_index_);
7294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      else
7304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        to_index = std::max(to_index, attach_index_);
7314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (to_index != attach_index_)
7324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        attach_index_ = -1;  // Once a valid move is detected, don't constrain.
7334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      else
7344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        do_move = false;
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (do_move) {
7374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      WebContents* last_contents = drag_data_[drag_data_.size() - 1].contents;
7384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      int index_of_last_item =
7394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          attached_model->GetIndexOfWebContents(last_contents);
7404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (initial_move_) {
7414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // TabStrip determines if the tabs needs to be animated based on model
7424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // position. This means we need to invoke LayoutDraggedTabsAt before
7434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // changing the model.
7444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        attached_tabstrip_->LayoutDraggedTabsAt(
7454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
7464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            initial_move_);
7474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        did_layout = true;
7484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      }
7494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      attached_model->MoveSelectedTabsTo(to_index);
7504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
7514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Move may do nothing in certain situations (such as when dragging pinned
7524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // tabs). Make sure the tabstrip actually changed before updating
7534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // last_move_screen_loc_.
7544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (index_of_last_item !=
7554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          attached_model->GetIndexOfWebContents(last_contents)) {
7564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        last_move_screen_loc_ = point_in_screen.x();
7574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      }
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!did_layout) {
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabstrip_->LayoutDraggedTabsAt(
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        initial_move_);
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartMoveStackedTimerIfNecessary(point_in_screen, kMoveAttachedInitialDelay);
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  initial_move_ = false;
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::StartMoveStackedTimerIfNecessary(
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen,
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int delay_ms) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(attached_tabstrip_);
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!touch_layout)
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index = touch_layout->active_index();
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldDragToNextStackedTab(bounds, index)) {
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<base::Timer*>(&move_stacked_timer_)->Start(
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(delay_ms),
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&TabDragController::MoveAttachedToNextStackedIndex,
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::Unretained(this), point_in_screen));
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (ShouldDragToPreviousStackedTab(bounds, index)) {
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<base::Timer*>(&move_stacked_timer_)->Start(
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(delay_ms),
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&TabDragController::MoveAttachedToPreviousStackedIndex,
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::Unretained(this), point_in_screen));
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController::DetachPosition TabDragController::GetDetachPosition(
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(attached_tabstrip_);
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point attached_point(point_in_screen);
803a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  views::View::ConvertPointFromScreen(attached_tabstrip_, &attached_point);
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_point.x() < 0)
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return DETACH_BEFORE;
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_point.x() >= attached_tabstrip_->width())
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return DETACH_AFTER;
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return DETACH_ABOVE_OR_BELOW;
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip* TabDragController::GetTargetTabStripForPoint(
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
813f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::GetTargetTabStripForPoint",
814f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "point_in_screen", point_in_screen.ToString());
815f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (move_only() && attached_tabstrip_) {
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // move_only() is intended for touch, in which case we only want to detach
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // if the touch point moves significantly in the vertical distance.
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect tabstrip_bounds = GetViewScreenBounds(attached_tabstrip_);
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (DoesRectContainVerticalPointExpanded(tabstrip_bounds,
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             kTouchVerticalDetachMagnetism,
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             point_in_screen.y()))
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return attached_tabstrip_;
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow local_window =
826a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      GetLocalProcessWindow(point_in_screen, is_dragging_window_);
827a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  // Do not allow dragging into a window with a modal dialog, it causes a weird
828a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  // behavior.  See crbug.com/336691
829a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  if (!wm::GetModalTransient(local_window)) {
830a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    TabStrip* tab_strip = GetTabStripForWindow(local_window);
831a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    if (tab_strip && DoesTabStripContain(tab_strip, point_in_screen))
832a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      return tab_strip;
833a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  }
834a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return is_dragging_window_ ? attached_tabstrip_ : NULL;
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip* TabDragController::GetTabStripForWindow(gfx::NativeWindow window) {
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!window)
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserView* browser_view =
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserView::GetBrowserViewForNativeWindow(window);
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't allow drops on windows that don't have tabstrips.
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!browser_view ||
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !browser_view->browser()->SupportsWindowFeature(
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Browser::FEATURE_TABSTRIP))
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStrip* other_tabstrip = browser_view->tabstrip();
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStrip* tab_strip =
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(tab_strip);
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return other_tabstrip->controller()->IsCompatibleWith(tab_strip) ?
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      other_tabstrip : NULL;
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::DoesTabStripContain(
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* tabstrip,
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) const {
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure the specified screen point is actually within the bounds of the
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // specified tabstrip...
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return point_in_screen.x() < tabstrip_bounds.right() &&
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      point_in_screen.x() >= tabstrip_bounds.x() &&
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DoesRectContainVerticalPointExpanded(tabstrip_bounds,
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           kVerticalDetachMagnetism,
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           point_in_screen.y());
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::Attach(TabStrip* attached_tabstrip,
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               const gfx::Point& point_in_screen) {
873f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::Attach",
874f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "point_in_screen", point_in_screen.ToString());
875f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!attached_tabstrip_);  // We should already have detached by the time
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                // we get here.
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_ = attached_tabstrip;
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs =
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetTabsMatchingDraggedContents(attached_tabstrip_);
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tabs.empty()) {
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Transitioning from detached to attached to a new tabstrip. Add tabs to
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the new model.
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Inserting counts as a move. We don't want the tabs to jitter when the
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // user moves the tab immediately after attaching it.
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    last_move_screen_loc_ = point_in_screen.x();
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Figure out where to insert the tab based on the bounds of the dragged
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // representation and the ideal bounds of the other Tabs already in the
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // changing due to animation).
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point tab_strip_point(point_in_screen);
899a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_strip_point.set_x(
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
9024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    tab_strip_point.Offset(0, -mouse_offset_.y());
903f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int index = GetInsertionIndexForDraggedBounds(
904f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        GetDraggedViewTabStripBounds(tab_strip_point));
9054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    attach_index_ = index;
9064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    attach_x_ = tab_strip_point.x();
9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::AutoReset<bool> setter(&is_mutating_, true);
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < drag_data_.size(); ++i) {
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int add_types = TabStripModel::ADD_NONE;
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (attached_tabstrip_->touch_layout_.get()) {
9112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // StackedTabStripLayout positions relative to the active tab, if we
9122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // don't add the tab as active things bounce around.
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK_EQ(1u, drag_data_.size());
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        add_types |= TabStripModel::ADD_ACTIVE;
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (drag_data_[i].pinned)
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        add_types |= TabStripModel::ADD_PINNED;
9182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetModel(attached_tabstrip_)->InsertWebContentsAt(
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          index + i, drag_data_[i].contents, add_types);
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(tabs.size(), drag_data_.size());
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i)
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_data_[i].attached_tab = tabs[i];
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_->StartedDraggingTabs(tabs);
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResetSelection(GetModel(attached_tabstrip_));
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The size of the dragged tab may have changed. Adjust the x offset so that
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ratio of mouse_offset_ to original width is maintained.
9342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs_to_source(tabs);
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       tabs_to_source.end());
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tabs[source_tab_index_]->width() +
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(offset_to_width_ratio_ *
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       tabs[source_tab_index_]->width());
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mouse_offset_.set_x(new_x);
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Transfer ownership of us to the new tabstrip as well as making sure the
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // window has capture. This is important so that if activation changes the
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // drag isn't prematurely canceled.
946116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
947116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  attached_tabstrip_->OwnDragController(this);
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::Detach(ReleaseCapture release_capture) {
951f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  TRACE_EVENT1("views", "TabDragController::Detach",
952f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               "release_capture", release_capture);
953f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
9544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  attach_index_ = -1;
9554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the user detaches we assume they want to reorder.
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_behavior_ = REORDER;
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Release ownership of the drag controller and mouse capture. When we
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // reattach ownership is transfered.
961116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  attached_tabstrip_->ReleaseDragController();
962116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (release_capture == RELEASE_CAPTURE)
963116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    attached_tabstrip_->GetWidget()->ReleaseCapture();
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mouse_move_direction_ = kMovedMouseLeft | kMovedMouseRight;
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStripModel* attached_model = GetModel(attached_tabstrip_);
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<TabRendererData> tab_data;
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i) {
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_data.push_back(drag_data_[i].attached_tab->data());
9722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = attached_model->GetIndexOfWebContents(drag_data_[i].contents);
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_NE(-1, index);
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Hide the tab so that the user doesn't see it animate closed.
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_data_[i].attached_tab->SetVisible(false);
977116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    drag_data_[i].attached_tab->set_detached();
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    attached_model->DetachWebContentsAt(index);
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Detaching may end up deleting the tab, drop references to it.
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_data_[i].attached_tab = NULL;
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we've removed the last Tab from the TabStrip, hide the frame now.
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!attached_model->empty()) {
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!selection_model_before_attach_.empty() &&
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        selection_model_before_attach_.active() >= 0 &&
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        selection_model_before_attach_.active() < attached_model->count()) {
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Restore the selection.
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_model->SetSelectionFromModel(selection_model_before_attach_);
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (attached_tabstrip_ == source_tabstrip_ &&
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               !initial_selection_model_.empty()) {
9945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      RestoreInitialSelection();
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_->DraggedTabsDetached();
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_ = NULL;
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetModel(attached_tabstrip_)->count() ==
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(drag_data_.size())) {
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // All the tabs in a browser are being dragged but all the tabs weren't
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // initially being dragged. For this to happen the user would have to
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // start dragging a set of tabs, the other tabs close, then detach.
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RunMoveLoop(GetWindowOffset(point_in_screen));
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
10144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
10154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
10167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Vector2d drag_offset;
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Browser* browser = CreateBrowserForDrag(
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_, point_in_screen, &drag_offset, &drag_bounds);
10205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_WIN)
10211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  gfx::NativeView attached_native_view =
10221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    attached_tabstrip_->GetWidget()->GetNativeView();
10231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#endif
10241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  Detach(can_release_capture_ ? RELEASE_CAPTURE : DONT_RELEASE_CAPTURE);
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserView* dragged_browser_view =
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserView::GetBrowserViewForBrowser(browser);
1027d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  views::Widget* dragged_widget = dragged_browser_view->GetWidget();
10285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_WIN)
10291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // The Gesture recognizer does not work well currently when capture changes
10301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // while a touch gesture is in progress. So we need to manually transfer
10311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // gesture sequence and the GR's touch events queue to the new window. This
10321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // should really be done somewhere in capture change code and or inside the
10331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // GR. But we currently do not have a consistent way for doing it that would
10341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // work in all cases. Hence this hack.
10351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    ui::GestureRecognizer::Get()->TransferEventsTo(
10361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        attached_native_view,
10371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        dragged_widget->GetNativeView());
10381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#endif
1039d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  dragged_widget->SetVisibilityChangedAnimationsEnabled(false);
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Attach(dragged_browser_view->tabstrip(), gfx::Point());
1041a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
1042a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   point_in_screen,
1043a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   &drag_bounds);
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WindowPositionManagedUpdater updater;
1045d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  dragged_widget->AddObserver(&updater);
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser->window()->Show();
1047d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  dragged_widget->RemoveObserver(&updater);
1048d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  dragged_widget->SetVisibilityChangedAnimationsEnabled(true);
1049d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Activate may trigger a focus loss, destroying us.
1050d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  {
1051d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
1052d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    browser->window()->Activate();
1053d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!ref)
1054d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return;
1055d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RunMoveLoop(drag_offset);
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::RunMoveLoop(const gfx::Vector2d& drag_offset) {
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the user drags the whole window we'll assume they are going to attach to
106190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // another window and therefore want to reorder.
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_behavior_ = REORDER;
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_loop_widget_ = GetAttachedBrowserWidget();
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(move_loop_widget_);
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_loop_widget_->AddObserver(this);
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_dragging_window_ = true;
1068d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
10691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (can_release_capture_) {
10701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Running the move loop releases mouse capture, which triggers destroying
10711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // the drag loop. Release mouse capture now while the DragController is not
10721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // owned by the TabStrip.
10732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    attached_tabstrip_->ReleaseDragController();
10742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    attached_tabstrip_->GetWidget()->ReleaseCapture();
10752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    attached_tabstrip_->OwnDragController(this);
10762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
10772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const views::Widget::MoveLoopSource move_loop_source =
10782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event_source_ == EVENT_SOURCE_MOUSE ?
10792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      views::Widget::MOVE_LOOP_SOURCE_MOUSE :
10802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      views::Widget::MOVE_LOOP_SOURCE_TOUCH;
108168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  const views::Widget::MoveLoopEscapeBehavior escape_behavior =
108268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      is_dragging_new_browser_ ?
108368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE :
108468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget::MoveLoopResult result =
108668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      move_loop_widget_->RunMoveLoop(
108768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          drag_offset, move_loop_source, escape_behavior);
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NotificationService::AllBrowserContextsAndSources(),
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NotificationService::NoDetails());
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1093d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!ref)
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (move_loop_widget_) {
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_loop_widget_->RemoveObserver(this);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_loop_widget_ = NULL;
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_dragging_window_ = false;
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  waiting_for_run_loop_to_exit_ = false;
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_strip_to_attach_to_after_exit_) {
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Point point_in_screen(GetCursorScreenPoint());
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Detach(DONT_RELEASE_CAPTURE);
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Attach(tab_strip_to_attach_to_after_exit_, point_in_screen);
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Move the tabs into position.
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MoveAttached(point_in_screen);
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_->GetWidget()->Activate();
1110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // Activate may trigger a focus loss, destroying us.
1111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (!ref)
1112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return;
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_strip_to_attach_to_after_exit_ = NULL;
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(attached_tabstrip_);
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (active_) {
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ?
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            END_DRAG_CANCEL : END_DRAG_COMPLETE);
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabDragController::GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
1124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                             int start) const {
1125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const int last_tab = attached_tabstrip_->tab_count() - 1;
1126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Make the actual "drag insertion point" be just after the leading edge of
1127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // the first dragged tab.  This is closer to where the user thinks of the tab
1128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // as "starting" than just dragged_bounds.x(), especially with narrow tabs.
1129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const int dragged_x = dragged_bounds.x() + Tab::leading_width_for_drag();
1130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (start < 0 || start > last_tab ||
1131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      dragged_x < attached_tabstrip_->ideal_bounds(start).x())
1132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return -1;
1133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (int i = start; i <= last_tab; ++i) {
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
1136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (dragged_x < (ideal_bounds.x() + (ideal_bounds.width() / 2)))
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return i;
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return (dragged_x < attached_tabstrip_->ideal_bounds(last_tab).right()) ?
1141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      (last_tab + 1) : -1;
1142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
1143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int TabDragController::GetInsertionIndexFromReversed(
1145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const gfx::Rect& dragged_bounds,
1146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int start) const {
1147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Make the actual "drag insertion point" be just after the leading edge of
1148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // the first dragged tab.  This is closer to where the user thinks of the tab
1149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // as "starting" than just dragged_bounds.x(), especially with narrow tabs.
1150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const int dragged_x = dragged_bounds.x() + Tab::leading_width_for_drag();
1151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (start < 0 || start >= attached_tabstrip_->tab_count() ||
1152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      dragged_x >= attached_tabstrip_->ideal_bounds(start).right())
1153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return -1;
1154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (int i = start; i >= 0; --i) {
1156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
1157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (dragged_x >= (ideal_bounds.x() + (ideal_bounds.width() / 2)))
1158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return i + 1;
1159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return (dragged_x >= attached_tabstrip_->ideal_bounds(0).x()) ? 0 : -1;
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabDragController::GetInsertionIndexForDraggedBounds(
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& dragged_bounds) const {
1166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // If the strip has no tabs, the only position to insert at is 0.
1167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const int tab_count = attached_tabstrip_->tab_count();
1168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!tab_count)
1169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return 0;
1170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index = -1;
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_->touch_layout_.get()) {
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = GetInsertionIndexForDraggedBoundsStacked(dragged_bounds);
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (index != -1) {
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only move the tab to the left/right if the user actually moved the
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // mouse that way. This is necessary as tabs with stacked tabs
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // before/after them have multiple drag positions.
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int active_index = attached_tabstrip_->touch_layout_->active_index();
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((index < active_index &&
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (mouse_move_direction_ & kMovedMouseLeft) == 0) ||
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (index > active_index &&
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (mouse_move_direction_ & kMovedMouseRight) == 0)) {
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        index = active_index;
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    index = GetInsertionIndexFrom(dragged_bounds, 0);
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index == -1) {
1190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const int last_tab_right =
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        attached_tabstrip_->ideal_bounds(tab_count - 1).right();
1192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    index = (dragged_bounds.right() > last_tab_right) ? tab_count : 0;
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const Tab* last_visible_tab = attached_tabstrip_->GetLastVisibleTab();
1196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int last_insertion_point = last_visible_tab ?
1197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      (attached_tabstrip_->GetModelIndexOfTab(last_visible_tab) + 1) : 0;
1198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (drag_data_[0].attached_tab) {
1199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // We're not in the process of attaching, so clamp the insertion point to
1200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // keep it within the visible region.
1201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    last_insertion_point = std::max(
1202f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        0, last_insertion_point - static_cast<int>(drag_data_.size()));
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Ensure the first dragged tab always stays in the visible index range.
1206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return std::min(index, last_insertion_point);
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::ShouldDragToNextStackedTab(
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& dragged_bounds,
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index) const {
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index + 1 >= attached_tabstrip_->tab_count() ||
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !attached_tabstrip_->touch_layout_->IsStacked(index + 1) ||
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (mouse_move_direction_ & kMovedMouseRight) == 0)
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_x = attached_tabstrip_->ideal_bounds(index).x();
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int next_x = attached_tabstrip_->ideal_bounds(index + 1).x();
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mid_x = std::min(next_x - kStackedDistance,
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       active_x + (next_x - active_x) / 4);
1221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // TODO(pkasting): Should this add Tab::leading_width_for_drag() as
1222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // GetInsertionIndexFrom() does?
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dragged_bounds.x() >= mid_x;
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::ShouldDragToPreviousStackedTab(
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& dragged_bounds,
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index) const {
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index - 1 < attached_tabstrip_->GetMiniTabCount() ||
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !attached_tabstrip_->touch_layout_->IsStacked(index - 1) ||
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (mouse_move_direction_ & kMovedMouseLeft) == 0)
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_x = attached_tabstrip_->ideal_bounds(index).x();
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int previous_x = attached_tabstrip_->ideal_bounds(index - 1).x();
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mid_x = std::max(previous_x + kStackedDistance,
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       active_x - (active_x - previous_x) / 4);
1238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // TODO(pkasting): Should this add Tab::leading_width_for_drag() as
1239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // GetInsertionIndexFrom() does?
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dragged_bounds.x() <= mid_x;
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabDragController::GetInsertionIndexForDraggedBoundsStacked(
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& dragged_bounds) const {
12452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_index = touch_layout->active_index();
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Search from the active index to the front of the tabstrip. Do this as tabs
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // overlap each other from the active index.
1249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int index = GetInsertionIndexFromReversed(dragged_bounds, active_index);
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index != active_index)
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return index;
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index == -1)
1253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return GetInsertionIndexFrom(dragged_bounds, active_index + 1);
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The position to drag to corresponds to the active tab. If the next/previous
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tab is stacked, then shorten the distance used to determine insertion
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bounds. We do this as GetInsertionIndexFrom() uses the bounds of the
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tabs. When tabs are stacked the next/previous tab is on top of the tab.
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (active_index + 1 < attached_tabstrip_->tab_count() &&
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      touch_layout->IsStacked(active_index + 1)) {
1261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    index = GetInsertionIndexFrom(dragged_bounds, active_index + 1);
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (index == -1 && ShouldDragToNextStackedTab(dragged_bounds, active_index))
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = active_index + 1;
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (index == -1)
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = active_index;
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (ShouldDragToPreviousStackedTab(dragged_bounds, active_index)) {
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = active_index - 1;
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return index;
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& tab_strip_point) {
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // attached_tab is NULL when inserting into a new tabstrip.
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source_tab_drag_data()->attached_tab) {
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     source_tab_drag_data()->attached_tab->width(),
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     source_tab_drag_data()->attached_tab->height());
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double sel_width, unselected_width;
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_->GetCurrentTabWidths(&sel_width, &unselected_width);
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   static_cast<int>(sel_width),
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   Tab::GetStandardSize().height());
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Point TabDragController::GetAttachedDragPoint(
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(attached_tabstrip_);  // The tab must be attached.
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point tab_loc(point_in_screen);
1293a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_loc);
12944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const int x =
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO: consider caching this.
12982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> attached_tabs;
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i)
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabs.push_back(drag_data_[i].attached_tab);
13014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
13024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const int max_x = attached_tabstrip_->width() - size;
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Point(std::min(std::max(x, 0), max_x), 0);
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<Tab*> TabDragController::GetTabsMatchingDraggedContents(
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* tabstrip) {
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStripModel* model = GetModel(attached_tabstrip_);
13092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs;
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i) {
13112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int model_index = model->GetIndexOfWebContents(drag_data_[i].contents);
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (model_index == TabStripModel::kNoTab)
13132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return std::vector<Tab*>();
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs.push_back(tabstrip->tab_at(model_index));
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tabs;
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)std::vector<gfx::Rect> TabDragController::CalculateBoundsForDraggedTabs() {
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<gfx::Rect> drag_bounds;
13212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> attached_tabs;
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i)
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attached_tabs.push_back(drag_data_[i].attached_tab);
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    &drag_bounds);
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drag_bounds;
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::EndDragImpl(EndDragType type) {
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(active_);
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  active_ = false;
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bring_to_front_timer_.Stop();
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  move_stacked_timer_.Stop();
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_dragging_window_) {
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waiting_for_run_loop_to_exit_ = true;
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1)) {
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetWindowPositionManaged(GetAttachedBrowserWidget()->GetNativeView(),
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               true);
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // End the nested drag loop.
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetAttachedBrowserWidget()->EndMoveLoop();
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type != TAB_DESTROYED) {
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We only finish up the drag if we were actually dragging. If start_drag_
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // is false, the user just clicked and released and didn't move the mouse
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // enough to trigger a drag.
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (started_drag_) {
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RestoreFocus();
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (type == CANCELED)
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        RevertDrag();
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CompleteDrag();
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (drag_data_.size() > 1) {
1360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    initial_selection_model_.Clear();
13611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (started_drag_)
13621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      RevertDrag();
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }  // else case the only tab we were dragging was deleted. Nothing to do.
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clear out drag data so we don't attempt to do anything with it.
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_data_.clear();
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1368116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  TabStrip* owning_tabstrip = attached_tabstrip_ ?
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_ : source_tabstrip_;
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  owning_tabstrip->DestroyDragController();
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::RevertDrag() {
13742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs;
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i) {
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (drag_data_[i].contents) {
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Contents is NULL if a tab was destroyed while the drag was under way.
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tabs.push_back(drag_data_[i].attached_tab);
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RevertDragAt(i);
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_) {
13845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (did_restore_window_)
13855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      MaximizeAttachedWindow();
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (attached_tabstrip_ == source_tabstrip_) {
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      source_tabstrip_->StoppedDraggingTabs(
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tabs, initial_tab_positions_, move_behavior_ == MOVE_VISIBILE_TABS,
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          false);
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_->DraggedTabsDetached();
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (initial_selection_model_.empty())
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResetSelection(GetModel(source_tabstrip_));
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1400116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (source_tabstrip_)
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    source_tabstrip_->GetWidget()->Activate();
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::ResetSelection(TabStripModel* model) {
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(model);
14062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ui::ListSelectionModel selection_model;
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool has_one_valid_tab = false;
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < drag_data_.size(); ++i) {
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |contents| is NULL if a tab was deleted out from under us.
14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (drag_data_[i].contents) {
14112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int index = model->GetIndexOfWebContents(drag_data_[i].contents);
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK_NE(-1, index);
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      selection_model.AddIndexToSelection(index);
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!has_one_valid_tab || i == source_tab_index_) {
14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Reset the active/lead to the first tab. If the source tab is still
14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // valid we'll reset these again later on.
14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        selection_model.set_active(index);
14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        selection_model.set_anchor(index);
14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        has_one_valid_tab = true;
14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!has_one_valid_tab)
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model->SetSelectionFromModel(selection_model);
14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void TabDragController::RestoreInitialSelection() {
14305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // First time detaching from the source tabstrip. Reset selection model to
14315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // initial_selection_model_. Before resetting though we have to remove all
14325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the tabs from initial_selection_model_ as it was created with the tabs
14335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // still there.
14345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ui::ListSelectionModel selection_model;
14355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  selection_model.Copy(initial_selection_model_);
14365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (DragData::const_reverse_iterator i(drag_data_.rbegin());
14375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       i != drag_data_.rend(); ++i) {
14385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    selection_model.DecrementFrom(i->source_model_index);
14395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
14405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // We may have cleared out the selection model. Only reset it if it
14415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // contains something.
14425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (selection_model.empty())
14435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
14445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
14455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The anchor/active may have been among the tabs that were dragged out. Force
14465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the anchor/active to be valid.
14475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (selection_model.anchor() == ui::ListSelectionModel::kUnselectedIndex)
14485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    selection_model.set_anchor(selection_model.selected_indices()[0]);
14495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (selection_model.active() == ui::ListSelectionModel::kUnselectedIndex)
14505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    selection_model.set_active(selection_model.selected_indices()[0]);
14515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  GetModel(source_tabstrip_)->SetSelectionFromModel(selection_model);
14525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
14535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
14545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::RevertDragAt(size_t drag_index) {
14555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(started_drag_);
14565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(source_tabstrip_);
14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::AutoReset<bool> setter(&is_mutating_, true);
14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabDragData* data = &(drag_data_[drag_index]);
14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_) {
14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index =
14622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetModel(attached_tabstrip_)->GetIndexOfWebContents(data->contents);
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (attached_tabstrip_ != source_tabstrip_) {
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The Tab was inserted into another TabStrip. We need to put it back
14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // into the original one.
14662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetModel(attached_tabstrip_)->DetachWebContentsAt(index);
14675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(beng): (Cleanup) seems like we should use Attach() for this
14685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      //             somehow.
14692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetModel(source_tabstrip_)->InsertWebContentsAt(
14705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          data->source_model_index, data->contents,
14715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (data->pinned ? TabStripModel::ADD_PINNED : 0));
14725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
14735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The Tab was moved within the TabStrip where the drag was initiated.
14745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Move it back to the starting location.
14752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetModel(source_tabstrip_)->MoveWebContentsAt(
14765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          index, data->source_model_index, false);
14775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
14795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The Tab was detached from the TabStrip where the drag began, and has not
14805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // been attached to any other TabStrip. We need to put it back into the
14815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // source TabStrip.
14822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GetModel(source_tabstrip_)->InsertWebContentsAt(
14835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        data->source_model_index, data->contents,
14845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (data->pinned ? TabStripModel::ADD_PINNED : 0));
14855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::CompleteDrag() {
14895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(started_drag_);
14905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (attached_tabstrip_) {
14925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (is_dragging_new_browser_ || did_restore_window_) {
1493a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (IsDockedOrSnapped(attached_tabstrip_)) {
14941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        was_source_maximized_ = false;
14951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        was_source_fullscreen_ = false;
14961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      }
14975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
149868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      // If source window was maximized - maximize the new window as well.
1499a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (was_source_maximized_ || was_source_fullscreen_)
15005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        MaximizeAttachedWindow();
150168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    }
15024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    attached_tabstrip_->StoppedDraggingTabs(
15034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        GetTabsMatchingDraggedContents(attached_tabstrip_),
15044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        initial_tab_positions_,
15054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        move_behavior_ == MOVE_VISIBILE_TABS,
15064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        true);
15075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
15085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Compel the model to construct a new window for the detached
15095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // WebContentses.
15105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget* widget = source_tabstrip_->GetWidget();
15115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect window_bounds(widget->GetRestoredBounds());
15125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window_bounds.set_origin(GetWindowCreatePoint(last_point_in_screen_));
15135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::AutoReset<bool> setter(&is_mutating_, true);
15155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<TabStripModelDelegate::NewStripContents> contentses;
15175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < drag_data_.size(); ++i) {
15185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabStripModelDelegate::NewStripContents item;
15192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      item.web_contents = drag_data_[i].contents;
15205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      item.add_types = drag_data_[i].pinned ? TabStripModel::ADD_PINNED
15215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            : TabStripModel::ADD_NONE;
15225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      contentses.push_back(item);
15234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
15245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Browser* new_browser =
15265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
1527a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            contentses, window_bounds, widget->IsMaximized());
15285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResetSelection(new_browser->tab_strip_model());
15295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_browser->window()->Show();
15305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void TabDragController::MaximizeAttachedWindow() {
15345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  GetAttachedBrowserWidget()->Maximize();
153503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(USE_ASH)
15365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (was_source_fullscreen_ &&
15375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
15385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // In fullscreen mode it is only possible to get here if the source
15395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // was in "immersive fullscreen" mode, so toggle it back on.
15405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ash::accelerators::ToggleFullscreen();
15415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
154203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif
15435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
15445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
15455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Rect TabDragController::GetViewScreenBounds(
15465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View* view) const {
15475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point view_topleft;
15485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToScreen(view, &view_topleft);
15495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect view_screen_bounds = view->GetLocalBounds();
15505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
15515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return view_screen_bounds;
15525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabDragController::BringWindowUnderPointToFront(
15555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
1556a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  aura::Window* window = GetLocalProcessWindow(point_in_screen, true);
1557a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1558a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Only bring browser windows to front - only windows with a TabStrip can
1559a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // be tab drag targets.
1560a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!GetTabStripForWindow(window))
1561a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
1562a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
15635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (window) {
15645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::Widget* widget_window = views::Widget::GetWidgetForNativeView(
15655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        window);
15663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (!widget_window)
15675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
15685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1569d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
1570d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // TODO(varkha): The code below ensures that the phantom drag widget
1571d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // is shown on top of browser windows. The code should be moved to ash/
1572d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // and the phantom should be able to assert its top-most state on its own.
1573d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // One strategy would be for DragWindowController to
1574d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // be able to observe stacking changes to the phantom drag widget's
1575d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // siblings in order to keep it on top. One way is to implement a
1576d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // notification that is sent to a window parent's observers when a
1577d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // stacking order is changed among the children of that same parent.
1578d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Note that OnWindowStackingChanged is sent only to the child that is the
1579d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // argument of one of the Window::StackChildX calls and not to all its
1580d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // siblings affected by the stacking change.
1581d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      aura::Window* browser_window = widget_window->GetNativeView();
1582d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Find a topmost non-popup window and stack the recipient browser above
1583d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // it in order to avoid stacking the browser window on top of the phantom
1584d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // drag widget created by DragWindowController in a second display.
1585d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      for (aura::Window::Windows::const_reverse_iterator it =
1586d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)           browser_window->parent()->children().rbegin();
1587d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)           it != browser_window->parent()->children().rend(); ++it) {
1588d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // If the iteration reached the recipient browser window then it is
1589d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // already topmost and it is safe to return with no stacking change.
1590d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (*it == browser_window)
1591d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          return;
15925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if ((*it)->type() != ui::wm::WINDOW_TYPE_POPUP) {
1593d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          widget_window->StackAbove(*it);
1594d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          break;
1595d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
15963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      }
1597d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    } else {
1598d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      widget_window->StackAtTop();
15993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    }
16003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
16015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The previous call made the window appear on top of the dragged window,
16025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // move the dragged window to the front.
16035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (is_dragging_window_)
16045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_->GetWidget()->StackAtTop();
16055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStripModel* TabDragController::GetModel(
16095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* tabstrip) const {
16105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<BrowserTabStripController*>(tabstrip->controller())->
16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      model();
16125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)views::Widget* TabDragController::GetAttachedBrowserWidget() {
16155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return attached_tabstrip_->GetWidget();
16165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabDragController::AreTabsConsecutive() {
16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 1; i < drag_data_.size(); ++i) {
16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (drag_data_[i - 1].source_model_index + 1 !=
16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        drag_data_[i].source_model_index) {
16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1628a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)gfx::Rect TabDragController::CalculateDraggedBrowserBounds(
16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStrip* source,
16305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen,
16315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<gfx::Rect>* drag_bounds) {
16325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point center(0, source->height() / 2);
16335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToWidget(source, &center);
16347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  gfx::Rect new_bounds(source->GetWidget()->GetRestoredBounds());
163568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (source->GetWidget()->IsMaximized()) {
163668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // If the restore bounds is really small, we don't want to honor it
163768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // (dragging a really small window looks wrong), instead make sure the new
163868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // window is at least 50% the size of the old.
163968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const gfx::Size max_size(
164068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        source->GetWidget()->GetWindowBoundsInScreen().size());
164168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    new_bounds.set_width(
164268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)        std::max(max_size.width() / 2, new_bounds.width()));
164368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    new_bounds.set_height(
1644a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        std::max(max_size.height() / 2, new_bounds.height()));
164568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_bounds.set_y(point_in_screen.y() - center.y());
16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetDetachPosition(point_in_screen)) {
16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DETACH_BEFORE:
16495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(point_in_screen.x() - center.x());
16505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.Offset(-mouse_offset_.x(), 0);
16515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case DETACH_AFTER: {
16535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Point right_edge(source->width(), 0);
16545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      views::View::ConvertPointToWidget(source, &right_edge);
16555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(point_in_screen.x() - right_edge.x());
16565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
16574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      OffsetX(-(*drag_bounds)[0].x(), drag_bounds);
16585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // To account for the extra vertical on restored windows that is absent on
16654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // maximized windows, add an additional vertical offset extracted from the tab
16664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // strip.
16674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (source->GetWidget()->IsMaximized())
1668f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    new_bounds.Offset(0, -source->kNewTabButtonVerticalOffset);
1669a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return new_bounds;
1670a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
1671a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1672a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void TabDragController::AdjustBrowserAndTabBoundsForDrag(
1673a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    int last_tabstrip_width,
1674a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const gfx::Point& point_in_screen,
1675a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::vector<gfx::Rect>* drag_bounds) {
1676a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  attached_tabstrip_->InvalidateLayout();
1677a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  attached_tabstrip_->DoLayout();
1678a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const int dragged_tabstrip_width = attached_tabstrip_->tab_area_width();
1679a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1680a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // If the new tabstrip is smaller than the old resize the tabs.
1681a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (dragged_tabstrip_width < last_tabstrip_width) {
1682a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const float leading_ratio =
1683a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        drag_bounds->front().x() / static_cast<float>(last_tabstrip_width);
1684a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *drag_bounds = CalculateBoundsForDraggedTabs();
1685a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1686a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (drag_bounds->back().right() < dragged_tabstrip_width) {
1687a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      const int delta_x =
1688a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          std::min(static_cast<int>(leading_ratio * dragged_tabstrip_width),
1689a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                   dragged_tabstrip_width -
1690a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       (drag_bounds->back().right() -
1691a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        drag_bounds->front().x()));
1692a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      OffsetX(delta_x, drag_bounds);
1693a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
16944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1695a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Reposition the restored window such that the tab that was dragged remains
1696a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // under the mouse cursor.
1697a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    gfx::Point offset(
1698a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        static_cast<int>((*drag_bounds)[source_tab_index_].width() *
1699a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                         offset_to_width_ratio_) +
1700a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        (*drag_bounds)[source_tab_index_].x(), 0);
1701a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    views::View::ConvertPointToWidget(attached_tabstrip_, &offset);
1702a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    gfx::Rect bounds = GetAttachedBrowserWidget()->GetWindowBoundsInScreen();
1703a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    bounds.set_x(point_in_screen.x() - offset.x());
1704a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    GetAttachedBrowserWidget()->SetBounds(bounds);
1705a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
1706a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  attached_tabstrip_->SetTabBoundsForDrag(*drag_bounds);
1707a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
1708a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1709a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)Browser* TabDragController::CreateBrowserForDrag(
1710a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    TabStrip* source,
1711a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const gfx::Point& point_in_screen,
1712a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    gfx::Vector2d* drag_offset,
1713a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::vector<gfx::Rect>* drag_bounds) {
1714a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source,
1715a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                     point_in_screen,
1716a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                     drag_bounds));
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *drag_offset = point_in_screen - new_bounds.origin();
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Profile* profile =
17202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Profile::FromBrowserContext(drag_data_[0].contents->GetBrowserContext());
17212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Browser::CreateParams create_params(Browser::TYPE_TABBED,
17222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      profile,
17232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      host_desktop_type_);
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  create_params.initial_bounds = new_bounds;
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Browser* browser = new Browser(create_params);
172668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  is_dragging_new_browser_ = true;
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetWindowPositionManaged(browser->window()->GetNativeWindow(), false);
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the window is created maximized then the bounds we supplied are ignored.
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We need to reset them again so they are honored.
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser->window()->SetBounds(new_bounds);
173190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return browser;
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Point TabDragController::GetCursorScreenPoint() {
17362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH &&
17372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event_source_ == EVENT_SOURCE_TOUCH &&
17382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      aura::Env::GetInstance()->is_touch_down()) {
17392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    views::Widget* widget = GetAttachedBrowserWidget();
17402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(widget);
17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    aura::Window* widget_window = widget->GetNativeWindow();
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(widget_window->GetRootWindow());
17435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    gfx::PointF touch_point_f;
17448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool got_touch_point = ui::GestureRecognizer::Get()->
17455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        GetLastTouchPointForTarget(widget_window, &touch_point_f);
17465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // TODO(tdresser): Switch to using gfx::PointF. See crbug.com/337824.
17475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f);
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(got_touch_point);
17496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point);
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return touch_point;
17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1752f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return screen_->GetCursorScreenPoint();
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Vector2d TabDragController::GetWindowOffset(
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_screen) {
1758116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  TabStrip* owning_tabstrip = attached_tabstrip_ ?
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      attached_tabstrip_ : source_tabstrip_;
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View* toplevel_view = owning_tabstrip->GetWidget()->GetContentsView();
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point point = point_in_screen;
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointFromScreen(toplevel_view, &point);
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return point.OffsetFromOrigin();
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1766a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1767a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)gfx::NativeWindow TabDragController::GetLocalProcessWindow(
1768a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const gfx::Point& screen_point,
1769a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bool exclude_dragged_view) {
1770a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::set<aura::Window*> exclude;
1771a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (exclude_dragged_view) {
1772a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    aura::Window* dragged_window =
1773a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        attached_tabstrip_->GetWidget()->GetNativeView();
1774a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (dragged_window)
1775a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      exclude.insert(dragged_window);
1776a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
1777e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1778e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Exclude windows which are pending deletion via Browser::TabStripEmpty().
1779e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // These windows can be returned in the Linux Aura port because the browser
1780e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // window which was used for dragging is not hidden once all of its tabs are
1781e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // attached to another browser window in DragBrowserToNewTabStrip().
1782e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // TODO(pkotwicz): Fix this properly (crbug.com/358482)
1783e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  BrowserList* browser_list = BrowserList::GetInstance(
1784e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      chrome::HOST_DESKTOP_TYPE_NATIVE);
1785e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  for (BrowserList::const_iterator it = browser_list->begin();
1786e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch       it != browser_list->end(); ++it) {
1787e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if ((*it)->tab_strip_model()->empty())
1788e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      exclude.insert((*it)->window()->GetNativeWindow());
1789e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  }
1790e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#endif
1791a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return GetLocalProcessWindowAtPoint(host_desktop_type_,
1792a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      screen_point,
1793a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      exclude);
1794a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1795a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
1796