tab_drag_controller.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
11cf93952f404c0d786cd590799eb437ed6f3ae6eGordon Henriksen// Copyright (c) 2012 The Chromium Authors. All rights reserved.
28ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Use of this source code is governed by a BSD-style license that can be
38ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// found in the LICENSE file.
48ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
58ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
68ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
78ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include <math.h>
88ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include <set>
98ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
108ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "base/auto_reset.h"
118ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "base/callback.h"
128ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "base/command_line.h"
138ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "base/i18n/rtl.h"
14344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen#include "chrome/browser/chrome_notification_types.h"
158ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/profiles/profile.h"
164468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
174468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/browser_list.h"
188ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/browser_window.h"
194468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/media_utils.h"
205371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar#include "chrome/browser/ui/tabs/tab_strip_model.h"
218ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
228ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/views/frame/browser_view.h"
234468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
248ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
254468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/views/tabs/tab.h"
264468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "chrome/browser/ui/views/tabs/tab_strip.h"
278ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/browser/ui/views/tabs/window_finder.h"
288ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "chrome/common/chrome_switches.h"
298ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "content/public/browser/invalidate_type.h"
304468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/notification_details.h"
314468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/notification_service.h"
324468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/notification_source.h"
334468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/notification_types.h"
344468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/user_metrics.h"
354468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/web_contents.h"
364468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "content/public/browser/web_contents_view.h"
374468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "extensions/browser/extension_function_dispatcher.h"
384468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "ui/aura/env.h"
394468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "ui/base/resource/resource_bundle.h"
408ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/events/event_constants.h"
418ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/events/event_utils.h"
424468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "ui/gfx/geometry/point_conversions.h"
434468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "ui/gfx/screen.h"
444468440a2a92fecd57f002b1b9c0683d2b9c4aeaGordon Henriksen#include "ui/views/focus/view_storage.h"
458ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/views/widget/root_view.h"
468ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/views/widget/widget.h"
478ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
488ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#if defined(USE_ASH)
498ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ash/accelerators/accelerator_commands.h"
508ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ash/wm/coordinate_conversion.h"
515371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar#include "ash/wm/window_state.h"
52d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen#include "ui/aura/env.h"
538ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/aura/window.h"
548ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#include "ui/aura/window_event_dispatcher.h"
55a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#include "ui/events/gestures/gesture_recognizer.h"
56a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#endif
57a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
58a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#if defined(OS_WIN)
59a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#include "ui/aura/window.h"
60a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#include "ui/events/gestures/gesture_recognizer.h"
61a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen#endif
62a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
63a353ffa7e556bfd2864474911174da691117f691Gordon Henriksenusing base::UserMetricsAction;
64a353ffa7e556bfd2864474911174da691117f691Gordon Henriksenusing content::OpenURLParams;
65a353ffa7e556bfd2864474911174da691117f691Gordon Henriksenusing content::WebContents;
66a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
67a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen// If non-null there is a drag underway.
68a353ffa7e556bfd2864474911174da691117f691Gordon Henriksenstatic TabDragController* instance_ = NULL;
69a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
70a353ffa7e556bfd2864474911174da691117f691Gordon Henriksennamespace {
71a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
72a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen// Delay, in ms, during dragging before we bring a window to front.
73a353ffa7e556bfd2864474911174da691117f691Gordon Henriksenconst int kBringToFrontDelay = 750;
748ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
758ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Initial delay before moving tabs when the dragged tab is close to the edge of
768ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// the stacked tabs.
778ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenconst int kMoveAttachedInitialDelay = 600;
788ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
798ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Delay for moving tabs after the initial delay has passed.
8046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenconst int kMoveAttachedSubsequentDelay = 300;
81404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen
828ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenconst int kHorizontalMoveThreshold = 16;  // Pixels.
838ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
848ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Distance from the next/previous stacked before before we consider the tab
858ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// close enough to trigger moving.
8646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenconst int kStackedDistance = 36;
87404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen
888ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenvoid SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
898ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#if defined(USE_ASH)
908ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  ash::wm::GetWindowState(window)->set_window_position_managed(value);
918ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#endif
9246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
938ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
948ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Returns true if |tab_strip| browser window is docked.
958ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenbool IsDockedOrSnapped(const TabStrip* tab_strip) {
968ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#if defined(USE_ASH)
97957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen  DCHECK(tab_strip);
9846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  ash::wm::WindowState* window_state =
998ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      ash::wm::GetWindowState(tab_strip->GetWidget()->GetNativeWindow());
1008ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return window_state->IsDocked() || window_state->IsSnapped();
1018ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen#else
1028ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return false;
10346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen#endif
104404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen}
1058ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
1068ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
1078ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// of |bounds| is adjusted by |vertical_adjustment|.
1088ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenbool DoesRectContainVerticalPointExpanded(
10946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Rect& bounds,
110404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen    int vertical_adjustment,
1118ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    int y) {
1128ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  int upper_threshold = bounds.bottom() + vertical_adjustment;
1138ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  int lower_threshold = bounds.y() - vertical_adjustment;
1148ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return y >= lower_threshold && y <= upper_threshold;
115957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen}
11646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
117404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen// Adds |x_offset| to all the rectangles in |rects|.
1188ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenvoid OffsetX(int x_offset, std::vector<gfx::Rect>* rects) {
1198ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  if (x_offset == 0)
1208ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    return;
1218ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
1228ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  for (size_t i = 0; i < rects->size(); ++i)
1238ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    (*rects)[i].set_x((*rects)[i].x() + x_offset);
124957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen}
125957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen
12646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen// WidgetObserver implementation that resets the window position managed
1278ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// property on Show.
1288ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// We're forced to do this here since BrowserFrameAsh resets the 'window
1298ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// position managed' property during a show and we need the property set to
1308ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// false before WorkspaceLayoutManager sees the visibility change.
1318ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenclass WindowPositionManagedUpdater : public views::WidgetObserver {
132957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen public:
13346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  virtual void OnWidgetVisibilityChanged(views::Widget* widget,
1348ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen                                         bool visible) OVERRIDE {
1358ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    SetWindowPositionManaged(widget->GetNativeView(), false);
136404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen  }
1378ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen};
13857cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen
13957cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen// EscapeTracker installs itself as a pre-target handler on aura::Env and runs a
14057cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen// callback when it receives the escape key.
14157cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksenclass EscapeTracker : public ui::EventHandler {
14257cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen public:
14357cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  explicit EscapeTracker(const base::Closure& callback)
14457cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen      : escape_callback_(callback) {
145404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen    aura::Env::GetInstance()->AddPreTargetHandler(this);
14657cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  }
14757cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen
14857cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  virtual ~EscapeTracker() {
149ef989a275c1191f583178c6934f3594e7a9fd3a6Christopher Lamb    aura::Env::GetInstance()->RemovePreTargetHandler(this);
15057cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  }
15157cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen
15257cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen private:
15357cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  // ui::EventHandler:
15457cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen  virtual void OnKeyEvent(ui::KeyEvent* key) OVERRIDE {
15557cebeec7ba08b55f29f5bf98ad0a3a17e9d0c71Gordon Henriksen    if (key->type() == ui::ET_KEY_PRESSED &&
1568ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        key->key_code() == ui::VKEY_ESCAPE) {
1578ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      escape_callback_.Run();
1588ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    }
1598ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  }
160957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen
16146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  base::Closure escape_callback_;
1628ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
1638ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  DISALLOW_COPY_AND_ASSIGN(EscapeTracker);
1648ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen};
1658ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
1668ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}  // namespace
1678ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
168957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon HenriksenTabDragController::TabDragData::TabDragData()
16946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    : contents(NULL),
1708ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      original_delegate(NULL),
171957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen      source_model_index(-1),
17246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      attached_tab(NULL),
17346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      pinned(false) {
17446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
17546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
176957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon HenriksenTabDragController::TabDragData::~TabDragData() {
17746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
1781cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen
1791cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen///////////////////////////////////////////////////////////////////////////////
1801cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen// TabDragController, public:
1811cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen
1821cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen// static
1831cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksenconst int TabDragController::kTouchVerticalDetachMagnetism = 50;
1841cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen
1851cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen// static
1861cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksenconst int TabDragController::kVerticalDetachMagnetism = 15;
1871cf08fddc7413076dedad58dbb8d8d67e69a490fGordon Henriksen
1881cf08fddc7413076dedad58dbb8d8d67e69a490fGordon HenriksenTabDragController::TabDragController()
1898ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    : detach_into_browser_(true),
1908ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      event_source_(EVENT_SOURCE_MOUSE),
191344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      source_tabstrip_(NULL),
192344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      attached_tabstrip_(NULL),
193344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      screen_(NULL),
194344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
195344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      use_aura_capture_policy_(false),
196344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      offset_to_width_ratio_(0),
197e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      old_focused_view_id_(
198344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen          views::ViewStorage::GetInstance()->CreateStorageID()),
199344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      last_move_screen_loc_(0),
200344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      started_drag_(false),
201344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      active_(true),
202344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      source_tab_index_(std::numeric_limits<size_t>::max()),
203344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      initial_move_(true),
204344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      detach_behavior_(DETACHABLE),
205e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      move_behavior_(REORDER),
206344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      mouse_move_direction_(0),
207344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      is_dragging_window_(false),
208344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      is_dragging_new_browser_(false),
209344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      was_source_maximized_(false),
210344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      was_source_fullscreen_(false),
211344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      did_restore_window_(false),
212e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
213344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      waiting_for_run_loop_to_exit_(false),
214344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      tab_strip_to_attach_to_after_exit_(NULL),
215344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      move_loop_widget_(NULL),
21645d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar      is_mutating_(false),
21745d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar      attach_x_(-1),
21845d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar      attach_index_(-1),
21945d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar      weak_factory_(this) {
22045d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar  instance_ = this;
22145d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar}
22245d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar
223344be5fbecec9908bab611eafeae0549ba3be6d7Gordon HenriksenTabDragController::~TabDragController() {
224344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  views::ViewStorage::GetInstance()->RemoveView(old_focused_view_id_);
225344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
226e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  if (instance_ == this)
227344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    instance_ = NULL;
228957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen
229344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  if (move_loop_widget_) {
230344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    move_loop_widget_->RemoveObserver(this);
231344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    SetWindowPositionManaged(move_loop_widget_->GetNativeView(), true);
232e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  }
233e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen
234344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  if (source_tabstrip_ && detach_into_browser_)
235957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen    GetModel(source_tabstrip_)->RemoveObserver(this);
236344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
237e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen  // Reset the delegate of the dragged WebContents. This ends up doing nothing
238e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen  // if the drag was completed.
23945d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar  if (!detach_into_browser_)
240e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen    ResetDelegates();
241e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen
242e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen  if (event_source_ == EVENT_SOURCE_TOUCH) {
243e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen    TabStrip* capture_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
244e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen        attached_tabstrip_ : source_tabstrip_;
245e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen    capture_tabstrip->GetWidget()->ReleaseCapture();
246e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen  }
247e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen}
24845d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar
24945d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaarvoid TabDragController::Init(
25045d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar    TabStrip* source_tabstrip,
25145d6ac2cc13f7881687c2d7f03f9b9892fd85e6eErick Tryzelaar    Tab* source_tab,
252e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen    const std::vector<Tab*>& tabs,
253e62a8a353c3b21b551c00b9025800d3352e5349eGordon Henriksen    const gfx::Point& mouse_offset,
254344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    int source_tab_offset,
255e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    const ui::ListSelectionModel& initial_selection_model,
256e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    DetachBehavior detach_behavior,
257e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    MoveBehavior move_behavior,
258e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    EventSource event_source) {
259344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  DCHECK(!tabs.empty());
260dce5e74162e7ad16dc787057941a7b9147a70117Daniel Dunbar  DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
261344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  source_tabstrip_ = source_tabstrip;
262344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  was_source_maximized_ = source_tabstrip->GetWidget()->IsMaximized();
263e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  was_source_fullscreen_ = source_tabstrip->GetWidget()->IsFullscreen();
264344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  screen_ = gfx::Screen::GetScreenFor(
265957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen      source_tabstrip->GetWidget()->GetNativeView());
266344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  host_desktop_type_ = chrome::GetHostDesktopTypeForNativeView(
267dce5e74162e7ad16dc787057941a7b9147a70117Daniel Dunbar      source_tabstrip->GetWidget()->GetNativeView());
268344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen#if defined(OS_LINUX)
269344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  use_aura_capture_policy_ = true;
270e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen#else
271e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  use_aura_capture_policy_ =
272344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH);
273957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen#endif
2745371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  start_point_in_screen_ = gfx::Point(source_tab_offset, mouse_offset.y());
275890484984bd0d3a5f9d80808b0d251424c7ddf7aChris Lattner  views::View::ConvertPointToScreen(source_tab, &start_point_in_screen_);
276344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  event_source_ = event_source;
277344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  mouse_offset_ = mouse_offset;
2785371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  detach_behavior_ = detach_behavior;
279344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  move_behavior_ = move_behavior;
2805371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  last_point_in_screen_ = start_point_in_screen_;
281344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  last_move_screen_loc_ = start_point_in_screen_.x();
282344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  initial_tab_positions_ = source_tabstrip->GetTabXCoordinates();
283344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  if (detach_behavior == NOT_DETACHABLE)
284344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    detach_into_browser_ = false;
285344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
2865371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  if (detach_into_browser_)
2875371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    GetModel(source_tabstrip_)->AddObserver(this);
288344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
289344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  drag_data_.resize(tabs.size());
290344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  for (size_t i = 0; i < tabs.size(); ++i)
291344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    InitTabDragData(tabs[i], &(drag_data_[i]));
292344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  source_tab_index_ =
293e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
294344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
295344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  // Listen for Esc key presses.
296344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  escape_tracker_.reset(
297344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      new EscapeTracker(base::Bind(&TabDragController::EndDrag,
298344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen                                   weak_factory_.GetWeakPtr(),
299e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen                                   END_DRAG_CANCEL)));
300344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen
301344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  if (source_tab->width() > 0) {
3024647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    offset_to_width_ratio_ = static_cast<float>(
3034647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        source_tab->GetMirroredXInView(source_tab_offset)) /
3044647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        static_cast<float>(source_tab->width());
3054647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  }
3064647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  InitWindowCreatePoint();
3074647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  initial_selection_model_.Copy(initial_selection_model);
3084647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3094647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  // Gestures don't automatically do a capture. We don't allow multiple drags at
3104647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  // the same time, so we explicitly capture.
3114647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  if (event_source == EVENT_SOURCE_TOUCH)
3124647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    source_tabstrip_->GetWidget()->SetCapture(source_tabstrip_);
3134647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen}
3144647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3154647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen// static
3164647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksenbool TabDragController::IsAttachedTo(const TabStrip* tab_strip) {
3174647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  return (instance_ && instance_->active() &&
3184647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen          instance_->attached_tabstrip() == tab_strip);
3194647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen}
3204647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3214647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen// static
322957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksenbool TabDragController::IsActive() {
323e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  return instance_ && instance_->active();
3244647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen}
325e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen
3264647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksenvoid TabDragController::SetMoveBehavior(MoveBehavior behavior) {
3274647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  if (started_drag())
3284647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    return;
3294647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3304647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  move_behavior_ = behavior;
3314647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen}
3324647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3334647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksenvoid TabDragController::Drag(const gfx::Point& point_in_screen) {
3344647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  TRACE_EVENT1("views", "TabDragController::Drag",
3354647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen               "point_in_screen", point_in_screen.ToString());
3364647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3374647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  bring_to_front_timer_.Stop();
3384647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  move_stacked_timer_.Stop();
3394647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3404647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  if (waiting_for_run_loop_to_exit_)
3414647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    return;
342404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen
343404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen  if (!started_drag_) {
3444647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    if (!CanStartDrag(point_in_screen))
3454647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      return;  // User hasn't dragged far enough yet.
3464647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3474647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    // On windows SaveFocus() may trigger a capture lost, which destroys us.
3484647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    {
3494647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
3504647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      SaveFocus();
3514647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      if (!ref)
3524647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        return;
3534647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    }
3544647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    started_drag_ = true;
3554647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    Attach(source_tabstrip_, gfx::Point());
3564647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    if (detach_into_browser_ && static_cast<int>(drag_data_.size()) ==
3574647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        GetModel(source_tabstrip_)->count()) {
3584647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      if (was_source_maximized_ || was_source_fullscreen_) {
359957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen        did_restore_window_ = true;
3604647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        // When all tabs in a maximized browser are dragged the browser gets
3614647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        // restored during the drag and maximized back when the drag ends.
3624647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        views::Widget* widget = GetAttachedBrowserWidget();
3634647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
3644647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
3654647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
3664647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source_tabstrip_,
3674647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen                                                           point_in_screen,
3684647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen                                                           &drag_bounds));
3694647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        new_bounds.Offset(-widget->GetRestoredBounds().x() +
3704647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen                          point_in_screen.x() -
371957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen                          mouse_offset_.x(), 0);
372e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen        widget->SetVisibilityChangedAnimationsEnabled(false);
3734647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        widget->Restore();
3744647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        widget->SetBounds(new_bounds);
3754647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
3764647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen                                         point_in_screen,
3774647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen                                         &drag_bounds);
3784647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen        widget->SetVisibilityChangedAnimationsEnabled(true);
3794647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      }
3804647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      RunMoveLoop(GetWindowOffset(point_in_screen));
3814647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen      return;
3824647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen    }
3834647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  }
3844647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3854647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  ContinueDragging(point_in_screen);
386957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen}
3874647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
3884647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksenvoid TabDragController::EndDrag(EndDragReason reason) {
389404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen  TRACE_EVENT0("views", "TabDragController::EndDrag");
390e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen
391e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  // If we're dragging a window ignore capture lost since it'll ultimately
392e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  // trigger the move loop to end and we'll revert the drag when RunMoveLoop()
393e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  // finishes.
3944647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  if (reason == END_DRAG_CAPTURE_LOST && is_dragging_window_)
395e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    return;
3964647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen  EndDragImpl(reason != END_DRAG_COMPLETE && source_tabstrip_ ?
3974647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen              CANCELED : NORMAL);
398e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen}
3994647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksen
4004647569fe7706e76135a08ca0e5f90a447ccc5b4Gordon Henriksenvoid TabDragController::InitTabDragData(Tab* tab,
401e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen                                        TabDragData* drag_data) {
402e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen  TRACE_EVENT0("views", "TabDragController::InitTabDragData");
403957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen  drag_data->source_model_index =
404344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen      source_tabstrip_->GetModelIndexOfTab(tab);
405344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen  drag_data->contents = GetModel(source_tabstrip_)->GetWebContentsAt(
4068ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      drag_data->source_model_index);
4078ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
4088ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  registrar_.Add(
4098ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      this,
410e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
4118ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      content::Source<WebContents>(drag_data->contents));
4128ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4138ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  if (!detach_into_browser_) {
4148ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    drag_data->original_delegate = drag_data->contents->GetDelegate();
4158ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    drag_data->contents->SetDelegate(this);
4168ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  }
4178ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4188ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4198ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen///////////////////////////////////////////////////////////////////////////////
4208ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// TabDragController, PageNavigator implementation:
4218ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4228ef426baa36639458f6777309db25c1768dc9c8aGordon HenriksenWebContents* TabDragController::OpenURLFromTab(
4238ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    WebContents* source,
424404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen    const OpenURLParams& params) {
425404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen  if (source_tab_drag_data()->original_delegate) {
4268ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    OpenURLParams forward_params = params;
4278ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    if (params.disposition == CURRENT_TAB)
4288ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      forward_params.disposition = NEW_WINDOW;
4298ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4308ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    return source_tab_drag_data()->original_delegate->OpenURLFromTab(
4318ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        source, forward_params);
4328ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  }
4338ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return NULL;
4348ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4358ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4368ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen///////////////////////////////////////////////////////////////////////////////
4378ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen// TabDragController, content::WebContentsDelegate implementation:
438404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen
439404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksenvoid TabDragController::NavigationStateChanged(const WebContents* source,
4408ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen                                               unsigned changed_flags) {
4418ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  if (attached_tabstrip_ ||
4428ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      changed_flags == content::INVALIDATE_TYPE_PAGE_ACTIONS) {
4438ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    for (size_t i = 0; i < drag_data_.size(); ++i) {
4448ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      if (drag_data_[i].contents == source) {
4458ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        // Pass the NavigationStateChanged call to the original delegate so
4468ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        // that the title is updated. Do this only when we are attached as
4478ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        // otherwise the Tab isn't in the TabStrip (except for page action
4488ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        // updates).
4498ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        drag_data_[i].original_delegate->NavigationStateChanged(source,
4508ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen                                                                changed_flags);
4518ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        break;
4528ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      }
453e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen    }
4548ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  }
4558ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4568ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4578ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenvoid TabDragController::AddNewContents(WebContents* source,
4586d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen                                       WebContents* new_contents,
4598ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen                                       WindowOpenDisposition disposition,
4608ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen                                       const gfx::Rect& initial_pos,
4616d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen                                       bool user_gesture,
4626d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen                                       bool* was_blocked) {
4636d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen  DCHECK_NE(CURRENT_TAB, disposition);
4646d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen
4656d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksen  // Theoretically could be called while dragging if the page tries to
4668ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  // spawn a window. Route this message back to the browser in most cases.
4678ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  if (source_tab_drag_data()->original_delegate) {
4688ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    source_tab_drag_data()->original_delegate->AddNewContents(
4698ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        source, new_contents, disposition, initial_pos, user_gesture,
4708ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen        was_blocked);
4718ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  }
4728ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4738ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4748ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenbool TabDragController::ShouldSuppressDialogs() {
4758ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  // When a dialog is about to be shown we revert the drag. Otherwise a modal
4768ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  // dialog might appear and attempt to parent itself to a hidden tabcontents.
4778ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  EndDragImpl(CANCELED);
4788ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return false;
4798ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4808ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4818ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksencontent::JavaScriptDialogManager*
4828ef426baa36639458f6777309db25c1768dc9c8aGordon HenriksenTabDragController::GetJavaScriptDialogManager() {
4838ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  return GetJavaScriptDialogManagerInstance();
4848ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen}
4858ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
4868ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksenvoid TabDragController::RequestMediaAccessPermission(
487344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    content::WebContents* web_contents,
4888ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    const content::MediaStreamRequest& request,
489c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen    const content::MediaResponseCallback& callback) {
490c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen  ::RequestMediaAccessPermission(
491c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen      web_contents,
492c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen      Profile::FromBrowserContext(web_contents->GetBrowserContext()),
493c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen      request,
494c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen      callback);
495c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen}
496c84c16be9b29b4f805b92bfc2d93e2dfaa952f8fGordon Henriksen
4974733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen///////////////////////////////////////////////////////////////////////////////
4984733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen// TabDragController, content::NotificationObserver implementation:
4994733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
5005371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaarvoid TabDragController::Observe(
5014733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    int type,
5024733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    const content::NotificationSource& source,
5034733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    const content::NotificationDetails& details) {
5044733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
5054733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  WebContents* destroyed_web_contents =
5064733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      content::Source<WebContents>(source).ptr();
5074733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  for (size_t i = 0; i < drag_data_.size(); ++i) {
5084733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    if (drag_data_[i].contents == destroyed_web_contents) {
5094733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      // One of the tabs we're dragging has been destroyed. Cancel the drag.
5104733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      if (destroyed_web_contents->GetDelegate() == this)
5114733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        destroyed_web_contents->SetDelegate(NULL);
5124733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      drag_data_[i].contents = NULL;
5134733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      drag_data_[i].original_delegate = NULL;
5144733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      EndDragImpl(TAB_DESTROYED);
5154733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      return;
5164733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    }
5174733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  }
5184733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  // If we get here it means we got notification for a tab we don't know about.
5194733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  NOTREACHED();
5204733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen}
5214733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
5224733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksenvoid TabDragController::OnWidgetBoundsChanged(views::Widget* widget,
5234733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen                                              const gfx::Rect& new_bounds) {
5248ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  TRACE_EVENT1("views", "TabDragController::OnWidgetBoundsChanged",
5258ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen               "new_bounds", new_bounds.ToString());
52646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
52746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  Drag(GetCursorScreenPoint());
52846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
529957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen
5306d6203dff3560a2cc3ac8ec620ac3b105b0c7cc7Gordon Henriksenvoid TabDragController::TabStripEmpty() {
53146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  DCHECK(detach_into_browser_);
53246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  GetModel(source_tabstrip_)->RemoveObserver(this);
53346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // NULL out source_tabstrip_ so that we don't attempt to add back to it (in
534dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // the case of a revert).
535dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  source_tabstrip_ = NULL;
536dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen}
537dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen
538dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen///////////////////////////////////////////////////////////////////////////////
539dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen// TabDragController, private:
540dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen
541dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksenvoid TabDragController::InitWindowCreatePoint() {
542dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // window_create_point_ is only used in CompleteDrag() (through
543dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // GetWindowCreatePoint() to get the start point of the docked window) when
544dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // the attached_tabstrip_ is NULL and all the window's related bound
545dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // information are obtained from source_tabstrip_. So, we need to get the
546dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
54746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // the window_create_point_ is not in the correct coordinate system. Please
54846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // refer to http://crbug.com/6223 comment #15 for detailed information.
54946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  views::View* first_tab = source_tabstrip_->tab_at(0);
55046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
55146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  window_create_point_ = first_source_tab_point_;
55246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
55346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
55446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
55546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksengfx::Point TabDragController::GetWindowCreatePoint(
55646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Point& origin) const {
55746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // If the cursor is outside the monitor area, move it inside. For example,
55846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // dropping a tab onto the task bar on Windows produces this situation.
55946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  gfx::Rect work_area = screen_->GetDisplayNearestPoint(origin).work_area();
5605371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  gfx::Point create_point(origin);
56146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!work_area.IsEmpty()) {
56246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (create_point.x() < work_area.x())
56346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      create_point.set_x(work_area.x());
56446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    else if (create_point.x() > work_area.right())
56546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      create_point.set_x(work_area.right());
56646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (create_point.y() < work_area.y())
56746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      create_point.set_y(work_area.y());
56846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    else if (create_point.y() > work_area.bottom())
56946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      create_point.set_y(work_area.bottom());
57046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
57146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  return gfx::Point(create_point.x() - window_create_point_.x(),
57246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                    create_point.y() - window_create_point_.y());
57346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
5745371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar
57546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenvoid TabDragController::SaveFocus() {
57646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  DCHECK(source_tabstrip_);
57746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  views::View* focused_view =
57846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      source_tabstrip_->GetFocusManager()->GetFocusedView();
57946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (focused_view)
5801475142b93a618da72ec471e5ad7c1510c2feb37Gordon Henriksen    views::ViewStorage::GetInstance()->StoreView(old_focused_view_id_,
5811475142b93a618da72ec471e5ad7c1510c2feb37Gordon Henriksen                                                 focused_view);
5821475142b93a618da72ec471e5ad7c1510c2feb37Gordon Henriksen  source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
5835371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  // WARNING: we may have been deleted.
58480a75bfae980df96f969f1c05b0c4a80ce975240Gordon Henriksen}
5855eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen
58680a75bfae980df96f969f1c05b0c4a80ce975240Gordon Henriksenvoid TabDragController::RestoreFocus() {
58780a75bfae980df96f969f1c05b0c4a80ce975240Gordon Henriksen  if (attached_tabstrip_ != source_tabstrip_) {
58880a75bfae980df96f969f1c05b0c4a80ce975240Gordon Henriksen    if (is_dragging_new_browser_) {
5895eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen      content::WebContents* active_contents = source_dragged_contents();
5905eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen      if (active_contents && !active_contents->FocusLocationBarByDefault())
5915eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen        active_contents->GetView()->Focus();
5925eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen    }
5935eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen    return;
5945eca075b74d62c621b160aa216b4cd50829a2cc7Gordon Henriksen  }
5955371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  views::View* old_focused_view =
5964733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      views::ViewStorage::GetInstance()->RetrieveView(old_focused_view_id_);
5974733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  if (!old_focused_view)
5984733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    return;
5995371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  old_focused_view->GetFocusManager()->SetFocusedView(old_focused_view);
6004733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen}
6014733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6024733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksenbool TabDragController::CanStartDrag(const gfx::Point& point_in_screen) const {
6034733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  // Determine if the mouse has moved beyond a minimum elasticity distance in
6044733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  // any direction from the starting point.
6054733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  static const int kMinimumDragDistance = 10;
6064733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  int x_offset = abs(point_in_screen.x() - start_point_in_screen_.x());
6074733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  int y_offset = abs(point_in_screen.y() - start_point_in_screen_.y());
6084733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  return sqrt(pow(static_cast<float>(x_offset), 2) +
6094733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen              pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
6104733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen}
6114733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6124733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksenvoid TabDragController::ContinueDragging(const gfx::Point& point_in_screen) {
6134733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  TRACE_EVENT1("views", "TabDragController::ContinueDragging",
6144733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen               "point_in_screen", point_in_screen.ToString());
6154733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6164733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  DCHECK(!detach_into_browser_ || attached_tabstrip_);
6174733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6184733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  TabStrip* target_tabstrip = detach_behavior_ == DETACHABLE ?
6194733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      GetTargetTabStripForPoint(point_in_screen) : source_tabstrip_;
6204733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
6214733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6224733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  if (attached_tabstrip_) {
6234733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    int move_delta = point_in_screen.x() - last_point_in_screen_.x();
6244733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    if (move_delta > 0)
6254733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      mouse_move_direction_ |= kMovedMouseRight;
6264733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    else if (move_delta < 0)
6274733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      mouse_move_direction_ |= kMovedMouseLeft;
6284733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  }
6295371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  last_point_in_screen_ = point_in_screen;
6304733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6314733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  if (tab_strip_changed) {
6324733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    is_dragging_new_browser_ = false;
6334733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    did_restore_window_ = false;
6344733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    if (detach_into_browser_ &&
6354733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        DragBrowserToNewTabStrip(target_tabstrip, point_in_screen) ==
6364733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        DRAG_BROWSER_RESULT_STOP) {
6374733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      return;
6384733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    } else if (!detach_into_browser_) {
6394733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      if (attached_tabstrip_)
6404733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        Detach(RELEASE_CAPTURE);
6414733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      if (target_tabstrip)
642e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands        Attach(target_tabstrip, point_in_screen);
643e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands    }
644e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands  }
645e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands  if (is_dragging_window_) {
646e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands    static_cast<base::Timer*>(&bring_to_front_timer_)->Start(FROM_HERE,
647e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands        base::TimeDelta::FromMilliseconds(kBringToFrontDelay),
648e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands        base::Bind(&TabDragController::BringWindowUnderPointToFront,
6494733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen                   base::Unretained(this), point_in_screen));
6504733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  }
6514733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
6524733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  if (!is_dragging_window_ && attached_tabstrip_) {
6534733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    if (move_only()) {
6544733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      DragActiveTabStacked(point_in_screen);
6554733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    } else {
6564733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      MoveAttached(point_in_screen);
6574733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      if (tab_strip_changed) {
6584733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        // Move the corresponding window to the front. We do this after the
6594733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        // move as on windows activate triggers a synchronous paint.
6604733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen        attached_tabstrip_->GetWidget()->Activate();
6614733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      }
6624733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    }
6634733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen  }
66480a75bfae980df96f969f1c05b0c4a80ce975240Gordon Henriksen}
66546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
66646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon HenriksenTabDragController::DragBrowserResultType
66746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon HenriksenTabDragController::DragBrowserToNewTabStrip(
66846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    TabStrip* target_tabstrip,
66946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Point& point_in_screen) {
670957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen  TRACE_EVENT1("views", "TabDragController::DragBrowserToNewTabStrip",
67146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen               "point_in_screen", point_in_screen.ToString());
67246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
67346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!target_tabstrip) {
67446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    DetachIntoNewBrowserAndRunMoveLoop(point_in_screen);
67546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return DRAG_BROWSER_RESULT_STOP;
67646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
67746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (is_dragging_window_) {
6785371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // ReleaseCapture() is going to result in calling back to us (because it
67946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // results in a move). That'll cause all sorts of problems.  Reset the
68046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // observer so we don't get notified and process the event.
68146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (use_aura_capture_policy_) {
68246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      move_loop_widget_->RemoveObserver(this);
68346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      move_loop_widget_ = NULL;
68446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    }
68546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    views::Widget* browser_widget = GetAttachedBrowserWidget();
68646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // Need to release the drag controller before starting the move loop as it's
68746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // going to trigger capture lost, which cancels drag.
68846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    attached_tabstrip_->ReleaseDragController();
6892618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen    target_tabstrip->OwnDragController(this);
6902618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen    // Disable animations so that we don't see a close animation on aero.
69146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    browser_widget->SetVisibilityChangedAnimationsEnabled(false);
6925371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // For aura we can't release capture, otherwise it'll cancel a gesture.
6935371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // Instead we have to directly change capture.
69446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (use_aura_capture_policy_)
69546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_);
69646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    else
69746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      browser_widget->ReleaseCapture();
69846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen#if defined(OS_WIN)
69946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // The Gesture recognizer does not work well currently when capture changes
7005371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // while a touch gesture is in progress. So we need to manually transfer
70146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // gesture sequence and the GR's touch events queue to the new window. This
70246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // should really be done somewhere in capture change code and or inside the
70346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // GR. But we currently do not have a consistent way for doing it that would
70446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // work in all cases. Hence this hack.
70546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    ui::GestureRecognizer::Get()->TransferEventsTo(
70646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        browser_widget->GetNativeView(),
70746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        target_tabstrip->GetWidget()->GetNativeView());
7085371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar#endif
70946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
71046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // The window is going away. Since the drag is still on going we don't want
7114733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // that to effect the position of any windows.
7124733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    SetWindowPositionManaged(browser_widget->GetNativeView(), false);
7134733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
7145371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
7154733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // EndMoveLoop is going to snap the window back to its original location.
7164733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // Hide it so users don't see this. Hiding a window in Linux aura causes
7174733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // it to lose capture so skip it.
7184733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    browser_widget->Hide();
7194733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen#endif
7204733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    browser_widget->EndMoveLoop();
7214733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
7224733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // Ideally we would always swap the tabs now, but on non-ash it seems that
7234733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // running the move loop implicitly activates the window when done, leading
7244733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // to all sorts of flicker. So, on non-ash, instead we process the move
7254733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // after the loop completes. But on chromeos, we can do tab swapping now to
7264733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    // avoid the tab flashing issue(crbug.com/116329).
7274733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    if (use_aura_capture_policy_) {
7284733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      is_dragging_window_ = false;
7294733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      Detach(DONT_RELEASE_CAPTURE);
7304733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      Attach(target_tabstrip, point_in_screen);
7314733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      // Move the tabs into position.
7324733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      MoveAttached(point_in_screen);
7334733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      attached_tabstrip_->GetWidget()->Activate();
7344733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    } else {
7354733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen      tab_strip_to_attach_to_after_exit_ = target_tabstrip;
7364733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    }
7374733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen
7384733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    waiting_for_run_loop_to_exit_ = true;
73946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
74046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return DRAG_BROWSER_RESULT_STOP;
7411d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  }
7421d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  Detach(DONT_RELEASE_CAPTURE);
7431d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  Attach(target_tabstrip, point_in_screen);
7441d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  return DRAG_BROWSER_RESULT_CONTINUE;
7455371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar}
7461d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7471d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksenvoid TabDragController::DragActiveTabStacked(
7481d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    const gfx::Point& point_in_screen) {
7495371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  if (attached_tabstrip_->tab_count() !=
7501d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen      static_cast<int>(initial_tab_positions_.size()))
7511d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    return;  // TODO: should cancel drag if this happens.
7521d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7531d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  int delta = point_in_screen.x() - start_point_in_screen_.x();
7541d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  attached_tabstrip_->DragActiveTab(initial_tab_positions_, delta);
7551d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen}
7561d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7571d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksenvoid TabDragController::MoveAttachedToNextStackedIndex(
7581d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    const gfx::Point& point_in_screen) {
7591d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  int index = attached_tabstrip_->touch_layout_->active_index();
7601d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  if (index + 1 >= attached_tabstrip_->tab_count())
7611d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    return;
7621d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7631d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index + 1);
7641d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  StartMoveStackedTimerIfNecessary(point_in_screen,
7651d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen                                   kMoveAttachedSubsequentDelay);
7661d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen}
7671d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7681d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksenvoid TabDragController::MoveAttachedToPreviousStackedIndex(
7691d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    const gfx::Point& point_in_screen) {
7701d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  int index = attached_tabstrip_->touch_layout_->active_index();
7711d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  if (index <= attached_tabstrip_->GetMiniTabCount())
7721d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen    return;
7731d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
7741d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen  GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index - 1);
77546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  StartMoveStackedTimerIfNecessary(point_in_screen,
77646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                                   kMoveAttachedSubsequentDelay);
77746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
77846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
77946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenvoid TabDragController::MoveAttached(const gfx::Point& point_in_screen) {
780dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  DCHECK(attached_tabstrip_);
781dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  DCHECK(!is_dragging_window_);
7825371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar
783dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
784dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen
785dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // Determine the horizontal move threshold. This is dependent on the width
786dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // of tabs. The smaller the tabs compared to the standard size, the smaller
787dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // the threshold.
788dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  int threshold = kHorizontalMoveThreshold;
789dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  if (!attached_tabstrip_->touch_layout_.get()) {
7905371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    double unselected, selected;
791dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen    attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
792dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen    double ratio = unselected / Tab::GetStandardSize().width();
793dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen    threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
794dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  }
795dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  // else case: touch tabs never shrink.
796dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen
797dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  std::vector<Tab*> tabs(drag_data_.size());
798dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen  for (size_t i = 0; i < drag_data_.size(); ++i)
799dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen    tabs[i] = drag_data_[i].attached_tab;
800dc1ce7bdc6e32e7a4c4a110caa32834730183c1bGordon Henriksen
80146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  bool did_layout = false;
80246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // Update the model, moving the WebContents from one index to another. Do this
80346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // only if we have moved a minimum distance since the last reorder (to prevent
80446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // jitter) or if this the first move and the tabs are not consecutive.
805957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen  if ((abs(point_in_screen.x() - last_move_screen_loc_) > threshold ||
80646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        (initial_move_ && !AreTabsConsecutive()))) {
8075371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    TabStripModel* attached_model = GetModel(attached_tabstrip_);
80846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
80946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    int to_index = GetInsertionIndexForDraggedBounds(bounds);
81046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    bool do_move = true;
81146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // While dragging within a tabstrip the expectation is the insertion index
812957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen    // is based on the left edge of the tabs being dragged. OTOH when dragging
81346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // into a new tabstrip (attaching) the expectation is the insertion index is
8145371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // based on the cursor. This proves problematic as insertion may change the
81546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // size of the tabs, resulting in the index calculated before the insert
81646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // differing from the index calculated after the insert. To alleviate this
81746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // the index is chosen before insertion, and subsequently a new index is
81846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // only used once the mouse moves enough such that the index changes based
81946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // on the direction the mouse moved relative to |attach_x_| (smaller
82046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // x-coordinate should yield a smaller index or larger x-coordinate yields a
8215371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    // larger index).
82246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (attach_index_ != -1) {
82346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      gfx::Point tab_strip_point(point_in_screen);
82446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
82546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      const int new_x =
82646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen          attached_tabstrip_->GetMirroredXInView(tab_strip_point.x());
82746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      if (new_x < attach_x_)
82846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        to_index = std::min(to_index, attach_index_);
82946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      else
83046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        to_index = std::max(to_index, attach_index_);
83146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      if (to_index != attach_index_)
83246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        attach_index_ = -1;  // Once a valid move is detected, don't constrain.
83346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      else
8345371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar        do_move = false;
83546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    }
83646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (do_move) {
83746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      WebContents* last_contents = drag_data_[drag_data_.size() - 1].contents;
83846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      int index_of_last_item =
83946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen          attached_model->GetIndexOfWebContents(last_contents);
84046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      if (initial_move_) {
84146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        // TabStrip determines if the tabs needs to be animated based on model
8425371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar        // position. This means we need to invoke LayoutDraggedTabsAt before
84346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        // changing the model.
84446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        attached_tabstrip_->LayoutDraggedTabsAt(
84546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen            tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
84646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen            initial_move_);
84721491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen        did_layout = true;
84821491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      }
84921491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      attached_model->MoveSelectedTabsTo(to_index);
85021491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen
85121491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      // Move may do nothing in certain situations (such as when dragging pinned
85221491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      // tabs). Make sure the tabstrip actually changed before updating
8535371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar      // last_move_screen_loc_.
85421491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      if (index_of_last_item !=
8555371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar          attached_model->GetIndexOfWebContents(last_contents)) {
8565371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar        last_move_screen_loc_ = point_in_screen.x();
85721491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen      }
85821491edbf4027df4db559eb1a9aa8fbf3779cfabGordon Henriksen    }
85946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
86046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
86146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!did_layout) {
86246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    attached_tabstrip_->LayoutDraggedTabsAt(
86346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
86446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        initial_move_);
8655371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  }
86646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
86746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  StartMoveStackedTimerIfNecessary(point_in_screen, kMoveAttachedInitialDelay);
86846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
86946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  initial_move_ = false;
87046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
87146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
87246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenvoid TabDragController::StartMoveStackedTimerIfNecessary(
8735371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar    const gfx::Point& point_in_screen,
87446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    int delay_ms) {
87546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  DCHECK(attached_tabstrip_);
87646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
87746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
87846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!touch_layout)
87946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return;
88046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
8815371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
88246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
88346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  int index = touch_layout->active_index();
88446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (ShouldDragToNextStackedTab(bounds, index)) {
88546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    static_cast<base::Timer*>(&move_stacked_timer_)->Start(
88646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        FROM_HERE,
8875371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar        base::TimeDelta::FromMilliseconds(delay_ms),
88846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        base::Bind(&TabDragController::MoveAttachedToNextStackedIndex,
88946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                   base::Unretained(this), point_in_screen));
89046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  } else if (ShouldDragToPreviousStackedTab(bounds, index)) {
89146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    static_cast<base::Timer*>(&move_stacked_timer_)->Start(
89246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        FROM_HERE,
89346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        base::TimeDelta::FromMilliseconds(delay_ms),
89446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        base::Bind(&TabDragController::MoveAttachedToPreviousStackedIndex,
89546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                   base::Unretained(this), point_in_screen));
89646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
89746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
89846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
89946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon HenriksenTabDragController::DetachPosition TabDragController::GetDetachPosition(
90046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Point& point_in_screen) {
90146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  DCHECK(attached_tabstrip_);
90246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  gfx::Point attached_point(point_in_screen);
90346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  views::View::ConvertPointFromScreen(attached_tabstrip_, &attached_point);
90446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (attached_point.x() < 0)
90546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return DETACH_BEFORE;
90646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (attached_point.x() >= attached_tabstrip_->width())
90746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return DETACH_AFTER;
90846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  return DETACH_ABOVE_OR_BELOW;
90946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
91046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
91146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon HenriksenTabStrip* TabDragController::GetTargetTabStripForPoint(
91246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Point& point_in_screen) {
91346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  TRACE_EVENT1("views", "TabDragController::GetTargetTabStripForPoint",
91446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen               "point_in_screen", point_in_screen.ToString());
91546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
91646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (move_only() && attached_tabstrip_) {
91746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    DCHECK_EQ(DETACHABLE, detach_behavior_);
91846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // move_only() is intended for touch, in which case we only want to detach
91946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // if the touch point moves significantly in the vertical distance.
92046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    gfx::Rect tabstrip_bounds = GetViewScreenBounds(attached_tabstrip_);
92146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (DoesRectContainVerticalPointExpanded(tabstrip_bounds,
92246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                                             kTouchVerticalDetachMagnetism,
9232618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen                                             point_in_screen.y()))
9242618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen      return attached_tabstrip_;
92546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
92646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  gfx::NativeWindow local_window =
92746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      GetLocalProcessWindow(point_in_screen, is_dragging_window_);
92846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  TabStrip* tab_strip = GetTabStripForWindow(local_window);
9295371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  if (tab_strip && DoesTabStripContain(tab_strip, point_in_screen))
93046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return tab_strip;
9316b018979da52c9760363b8b74951bbe259044ea8Dan Gohman  return is_dragging_window_ ? attached_tabstrip_ : NULL;
93246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
9336b018979da52c9760363b8b74951bbe259044ea8Dan Gohman
93446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon HenriksenTabStrip* TabDragController::GetTabStripForWindow(gfx::NativeWindow window) {
93546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!window)
93646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return NULL;
93746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  BrowserView* browser_view =
93846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      BrowserView::GetBrowserViewForNativeWindow(window);
93946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // We don't allow drops on windows that don't have tabstrips.
94046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (!browser_view ||
94146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      !browser_view->browser()->SupportsWindowFeature(
94246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen          Browser::FEATURE_TABSTRIP))
94346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    return NULL;
94446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
94546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  TabStrip* other_tabstrip = browser_view->tabstrip();
94646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  TabStrip* tab_strip =
9472618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen      attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
9482618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  DCHECK(tab_strip);
94946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
95046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  return other_tabstrip->controller()->IsCompatibleWith(tab_strip) ?
95146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      other_tabstrip : NULL;
952957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen}
95346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
95446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenbool TabDragController::DoesTabStripContain(
95546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    TabStrip* tabstrip,
95646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    const gfx::Point& point_in_screen) const {
95746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // Make sure the specified screen point is actually within the bounds of the
95846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // specified tabstrip...
95946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
96046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  return point_in_screen.x() < tabstrip_bounds.right() &&
96146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      point_in_screen.x() >= tabstrip_bounds.x() &&
96246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      DoesRectContainVerticalPointExpanded(tabstrip_bounds,
96346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                                           kVerticalDetachMagnetism,
96446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                                           point_in_screen.y());
96546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen}
96646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
96746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksenvoid TabDragController::Attach(TabStrip* attached_tabstrip,
96846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                               const gfx::Point& point_in_screen) {
96946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  TRACE_EVENT1("views", "TabDragController::Attach",
97046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen               "point_in_screen", point_in_screen.ToString());
97146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
97246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  DCHECK(!attached_tabstrip_);  // We should already have detached by the time
97346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen                                // we get here.
97446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
97546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  attached_tabstrip_ = attached_tabstrip;
97646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
97746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  std::vector<Tab*> tabs =
97846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      GetTabsMatchingDraggedContents(attached_tabstrip_);
97946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
98046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (tabs.empty()) {
98146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // Transitioning from detached to attached to a new tabstrip. Add tabs to
98246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // the new model.
98346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
98446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
98546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
98646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    if (!detach_into_browser_) {
987404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen      // Remove ourselves as the delegate now that the dragged WebContents is
988404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen      // being inserted back into a Browser.
989404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen      for (size_t i = 0; i < drag_data_.size(); ++i) {
990404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen        drag_data_[i].contents->SetDelegate(NULL);
99146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        drag_data_[i].original_delegate = NULL;
99246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      }
99346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
994cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen      // Return the WebContents to normalcy.
995cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen      source_dragged_contents()->DecrementCapturerCount();
99646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    }
99746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
99846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // Inserting counts as a move. We don't want the tabs to jitter when the
99946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // user moves the tab immediately after attaching it.
10002618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen    last_move_screen_loc_ = point_in_screen.x();
100107cabf6102800aa701bc4d1bd282fafb63b8a416Gordon Henriksen
100246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // Figure out where to insert the tab based on the bounds of the dragged
1003cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen    // representation and the ideal bounds of the other Tabs already in the
1004cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen    // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
1005cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen    // changing due to animation).
1006cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen    gfx::Point tab_strip_point(point_in_screen);
100707cabf6102800aa701bc4d1bd282fafb63b8a416Gordon Henriksen    views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
100807cabf6102800aa701bc4d1bd282fafb63b8a416Gordon Henriksen    tab_strip_point.set_x(
100907cabf6102800aa701bc4d1bd282fafb63b8a416Gordon Henriksen        attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
1010e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands    tab_strip_point.Offset(0, -mouse_offset_.y());
1011e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands    gfx::Rect bounds = GetDraggedViewTabStripBounds(tab_strip_point);
1012e149e9960ba0726f4b084763f7ef95afa12d9a88Duncan Sands    int index = GetInsertionIndexForDraggedBounds(bounds);
1013cc0928ff22d9f3e8f2930874f6727db8c700ec35Gordon Henriksen    attach_index_ = index;
1014404a1942e43ca967700cc2608eb97b863add2677Gordon Henriksen    attach_x_ = tab_strip_point.x();
101546abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    base::AutoReset<bool> setter(&is_mutating_, true);
101646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    for (size_t i = 0; i < drag_data_.size(); ++i) {
1017957f9fe1cce0957bcde4a1093da83e17aaec6764Gordon Henriksen      int add_types = TabStripModel::ADD_NONE;
101846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      if (attached_tabstrip_->touch_layout_.get()) {
101946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        // StackedTabStripLayout positions relative to the active tab, if we
102046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen        // don't add the tab as active things bounce around.
10212618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen        DCHECK_EQ(1u, drag_data_.size());
10222618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen        add_types |= TabStripModel::ADD_ACTIVE;
1023e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      }
1024e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen      if (drag_data_[i].pinned)
1025e3b989d4a4ba47f77d5d38c35ff17e9673d9f87bGordon Henriksen        add_types |= TabStripModel::ADD_PINNED;
102646abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen      GetModel(attached_tabstrip_)->InsertWebContentsAt(
102746abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen          index + i, drag_data_[i].contents, add_types);
102846abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    }
102946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen
103046abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
10312618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  }
10322618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  DCHECK_EQ(tabs.size(), drag_data_.size());
10332618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  for (size_t i = 0; i < drag_data_.size(); ++i)
10342618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen    drag_data_[i].attached_tab = tabs[i];
10352618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen
10362618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  attached_tabstrip_->StartedDraggingTabs(tabs);
10372618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen
10382618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  ResetSelection(GetModel(attached_tabstrip_));
10392618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen
10402618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  // The size of the dragged tab may have changed. Adjust the x offset so that
10415371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  // ratio of mouse_offset_ to original width is maintained.
10425371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  std::vector<Tab*> tabs_to_source(tabs);
10435371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
10442618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen                       tabs_to_source.end());
10452618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
10462618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen      tabs[source_tab_index_]->width() +
10472618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen      static_cast<int>(offset_to_width_ratio_ *
10482618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen                       tabs[source_tab_index_]->width());
10492618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  mouse_offset_.set_x(new_x);
10502618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen
10512618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  // Transfer ownership of us to the new tabstrip as well as making sure the
105246abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // window has capture. This is important so that if activation changes the
105346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // drag isn't prematurely canceled.
105446abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  if (detach_into_browser_) {
10551ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen    attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
10561ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen    attached_tabstrip_->OwnDragController(this);
10571ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen  }
10585371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar
1059da1435f86ebc9886dd7704294e01d192d79e069cGordon Henriksen  // Redirect all mouse events to the TabStrip so that the tab that originated
1060da1435f86ebc9886dd7704294e01d192d79e069cGordon Henriksen  // the drag can safely be deleted.
10611ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen  if (detach_into_browser_ || attached_tabstrip_ == source_tabstrip_) {
10621ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen    static_cast<views::internal::RootView*>(
1063d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen        attached_tabstrip_->GetWidget()->GetRootView())->SetMouseHandler(
1064d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen            attached_tabstrip_);
1065d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  }
1066d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen}
1067d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
1068d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksenvoid TabDragController::Detach(ReleaseCapture release_capture) {
1069d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  TRACE_EVENT1("views", "TabDragController::Detach",
1070d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen               "release_capture", release_capture);
1071d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
1072d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  attach_index_ = -1;
1073d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
1074d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  // When the user detaches we assume they want to reorder.
1075d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  move_behavior_ = REORDER;
1076d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
10775371aa2a1c9a4eeecffdb9ab7b2175732e49475bErick Tryzelaar  // Release ownership of the drag controller and mouse capture. When we
1078d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  // reattach ownership is transfered.
1079d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  if (detach_into_browser_) {
1080d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen    attached_tabstrip_->ReleaseDragController();
1081d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen    if (release_capture == RELEASE_CAPTURE)
1082d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen      attached_tabstrip_->GetWidget()->ReleaseCapture();
1083d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  }
1084d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
1085d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen  mouse_move_direction_ = kMovedMouseLeft | kMovedMouseRight;
1086d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen
10878ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  // Prevent the WebContents HWND from being hidden by any of the model
10888ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  // operations performed during the drag.
10898ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  if (!detach_into_browser_)
10902618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen    source_dragged_contents()->IncrementCapturerCount(gfx::Size());
10912618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen
10922618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
10932618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  TabStripModel* attached_model = GetModel(attached_tabstrip_);
10942618a6c1122d5d2007787fb56156be44b21ab32aGordon Henriksen  std::vector<TabRendererData> tab_data;
10958ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen  for (size_t i = 0; i < drag_data_.size(); ++i) {
10968ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    tab_data.push_back(drag_data_[i].attached_tab->data());
10978ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    int index = attached_model->GetIndexOfWebContents(drag_data_[i].contents);
1098d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen    DCHECK_NE(-1, index);
10998ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
11008ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    // Hide the tab so that the user doesn't see it animate closed.
11018ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    drag_data_[i].attached_tab->SetVisible(false);
11028ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
11038ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    attached_model->DetachWebContentsAt(index);
1104a353ffa7e556bfd2864474911174da691117f691Gordon Henriksen
11058ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen    // Detaching resets the delegate, but we still want to be the delegate.
1106344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen    if (!detach_into_browser_)
11078ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen      drag_data_[i].contents->SetDelegate(this);
11088ef426baa36639458f6777309db25c1768dc9c8aGordon Henriksen
110946abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen    // Detaching may end up deleting the tab, drop references to it.
11104733be38930ae81716bba9ae75a8281bcb180634Gordon Henriksen    drag_data_[i].attached_tab = NULL;
111146abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  }
11121d21395f4ce152e7bf14d9ba6ea23549de6badd4Gordon Henriksen
111346abf91f7378fb7bb118d66fe6d69b5d3af1e9d5Gordon Henriksen  // If we've removed the last Tab from the TabStrip, hide the frame now.
11141ae6135fa37eb061499d079b9b33dc82dcc1283fGordon Henriksen  if (!attached_model->empty()) {
1115d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen    if (!selection_model_before_attach_.empty() &&
1116d78c0f5a7255e4347cbd82f7435c51401096652cGordon Henriksen        selection_model_before_attach_.active() >= 0 &&
1117344be5fbecec9908bab611eafeae0549ba3be6d7Gordon Henriksen        selection_model_before_attach_.active() < attached_model->count()) {
1118      // Restore the selection.
1119      attached_model->SetSelectionFromModel(selection_model_before_attach_);
1120    } else if (attached_tabstrip_ == source_tabstrip_ &&
1121               !initial_selection_model_.empty()) {
1122      RestoreInitialSelection();
1123    }
1124  }
1125
1126  attached_tabstrip_->DraggedTabsDetached();
1127  attached_tabstrip_ = NULL;
1128}
1129
1130void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
1131    const gfx::Point& point_in_screen) {
1132  if (GetModel(attached_tabstrip_)->count() ==
1133      static_cast<int>(drag_data_.size())) {
1134    // All the tabs in a browser are being dragged but all the tabs weren't
1135    // initially being dragged. For this to happen the user would have to
1136    // start dragging a set of tabs, the other tabs close, then detach.
1137    RunMoveLoop(GetWindowOffset(point_in_screen));
1138    return;
1139  }
1140
1141  const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
1142  std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
1143  OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
1144
1145  gfx::Vector2d drag_offset;
1146  Browser* browser = CreateBrowserForDrag(
1147      attached_tabstrip_, point_in_screen, &drag_offset, &drag_bounds);
1148#if defined(OS_WIN)
1149  gfx::NativeView attached_native_view =
1150    attached_tabstrip_->GetWidget()->GetNativeView();
1151#endif
1152  Detach(use_aura_capture_policy_ ? DONT_RELEASE_CAPTURE : RELEASE_CAPTURE);
1153  BrowserView* dragged_browser_view =
1154      BrowserView::GetBrowserViewForBrowser(browser);
1155  views::Widget* dragged_widget = dragged_browser_view->GetWidget();
1156#if defined(OS_WIN)
1157    // The Gesture recognizer does not work well currently when capture changes
1158    // while a touch gesture is in progress. So we need to manually transfer
1159    // gesture sequence and the GR's touch events queue to the new window. This
1160    // should really be done somewhere in capture change code and or inside the
1161    // GR. But we currently do not have a consistent way for doing it that would
1162    // work in all cases. Hence this hack.
1163    ui::GestureRecognizer::Get()->TransferEventsTo(
1164        attached_native_view,
1165        dragged_widget->GetNativeView());
1166#endif
1167  dragged_widget->SetVisibilityChangedAnimationsEnabled(false);
1168  Attach(dragged_browser_view->tabstrip(), gfx::Point());
1169  AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
1170                                   point_in_screen,
1171                                   &drag_bounds);
1172  WindowPositionManagedUpdater updater;
1173  dragged_widget->AddObserver(&updater);
1174  browser->window()->Show();
1175  dragged_widget->RemoveObserver(&updater);
1176  dragged_widget->SetVisibilityChangedAnimationsEnabled(true);
1177  // Activate may trigger a focus loss, destroying us.
1178  {
1179    base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
1180    browser->window()->Activate();
1181    if (!ref)
1182      return;
1183  }
1184  RunMoveLoop(drag_offset);
1185}
1186
1187void TabDragController::RunMoveLoop(const gfx::Vector2d& drag_offset) {
1188  // If the user drags the whole window we'll assume they are going to attach to
1189  // another window and therefore want to reorder.
1190  move_behavior_ = REORDER;
1191
1192  move_loop_widget_ = GetAttachedBrowserWidget();
1193  DCHECK(move_loop_widget_);
1194  move_loop_widget_->AddObserver(this);
1195  is_dragging_window_ = true;
1196  base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
1197  // Running the move loop releases mouse capture on non-ash, which triggers
1198  // destroying the drag loop. Release mouse capture ourself before this while
1199  // the DragController isn't owned by the TabStrip.
1200  if (host_desktop_type_ != chrome::HOST_DESKTOP_TYPE_ASH) {
1201    attached_tabstrip_->ReleaseDragController();
1202    attached_tabstrip_->GetWidget()->ReleaseCapture();
1203    attached_tabstrip_->OwnDragController(this);
1204  }
1205  const views::Widget::MoveLoopSource move_loop_source =
1206      event_source_ == EVENT_SOURCE_MOUSE ?
1207      views::Widget::MOVE_LOOP_SOURCE_MOUSE :
1208      views::Widget::MOVE_LOOP_SOURCE_TOUCH;
1209  const views::Widget::MoveLoopEscapeBehavior escape_behavior =
1210      is_dragging_new_browser_ ?
1211          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE :
1212          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
1213  views::Widget::MoveLoopResult result =
1214      move_loop_widget_->RunMoveLoop(
1215          drag_offset, move_loop_source, escape_behavior);
1216  content::NotificationService::current()->Notify(
1217      chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
1218      content::NotificationService::AllBrowserContextsAndSources(),
1219      content::NotificationService::NoDetails());
1220
1221  if (!ref)
1222    return;
1223  // Under chromeos we immediately set the |move_loop_widget_| to NULL.
1224  if (move_loop_widget_) {
1225    move_loop_widget_->RemoveObserver(this);
1226    move_loop_widget_ = NULL;
1227  }
1228  is_dragging_window_ = false;
1229  waiting_for_run_loop_to_exit_ = false;
1230  if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
1231    end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
1232    if (tab_strip_to_attach_to_after_exit_) {
1233      gfx::Point point_in_screen(GetCursorScreenPoint());
1234      Detach(DONT_RELEASE_CAPTURE);
1235      Attach(tab_strip_to_attach_to_after_exit_, point_in_screen);
1236      // Move the tabs into position.
1237      MoveAttached(point_in_screen);
1238      attached_tabstrip_->GetWidget()->Activate();
1239      // Activate may trigger a focus loss, destroying us.
1240      if (!ref)
1241        return;
1242      tab_strip_to_attach_to_after_exit_ = NULL;
1243    }
1244    DCHECK(attached_tabstrip_);
1245    attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
1246  } else if (active_) {
1247    EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ?
1248            END_DRAG_CANCEL : END_DRAG_COMPLETE);
1249  }
1250}
1251
1252int TabDragController::GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
1253                                             int start,
1254                                             int delta) const {
1255  for (int i = start, tab_count = attached_tabstrip_->tab_count();
1256       i >= 0 && i < tab_count; i += delta) {
1257    const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
1258    gfx::Rect left_half, right_half;
1259    ideal_bounds.SplitVertically(&left_half, &right_half);
1260    if (dragged_bounds.x() >= right_half.x() &&
1261        dragged_bounds.x() < right_half.right()) {
1262      return i + 1;
1263    } else if (dragged_bounds.x() >= left_half.x() &&
1264               dragged_bounds.x() < left_half.right()) {
1265      return i;
1266    }
1267  }
1268  return -1;
1269}
1270
1271int TabDragController::GetInsertionIndexForDraggedBounds(
1272    const gfx::Rect& dragged_bounds) const {
1273  int index = -1;
1274  if (attached_tabstrip_->touch_layout_.get()) {
1275    index = GetInsertionIndexForDraggedBoundsStacked(dragged_bounds);
1276    if (index != -1) {
1277      // Only move the tab to the left/right if the user actually moved the
1278      // mouse that way. This is necessary as tabs with stacked tabs
1279      // before/after them have multiple drag positions.
1280      int active_index = attached_tabstrip_->touch_layout_->active_index();
1281      if ((index < active_index &&
1282           (mouse_move_direction_ & kMovedMouseLeft) == 0) ||
1283          (index > active_index &&
1284           (mouse_move_direction_ & kMovedMouseRight) == 0)) {
1285        index = active_index;
1286      }
1287    }
1288  } else {
1289    index = GetInsertionIndexFrom(dragged_bounds, 0, 1);
1290  }
1291  if (index == -1) {
1292    int tab_count = attached_tabstrip_->tab_count();
1293    int right_tab_x = tab_count == 0 ? 0 :
1294        attached_tabstrip_->ideal_bounds(tab_count - 1).right();
1295    if (dragged_bounds.right() > right_tab_x) {
1296      index = GetModel(attached_tabstrip_)->count();
1297    } else {
1298      index = 0;
1299    }
1300  }
1301
1302  if (!drag_data_[0].attached_tab) {
1303    // If 'attached_tab' is NULL, it means we're in the process of attaching and
1304    // don't need to constrain the index.
1305    return index;
1306  }
1307
1308  int max_index = GetModel(attached_tabstrip_)->count() -
1309      static_cast<int>(drag_data_.size());
1310  return std::max(0, std::min(max_index, index));
1311}
1312
1313bool TabDragController::ShouldDragToNextStackedTab(
1314    const gfx::Rect& dragged_bounds,
1315    int index) const {
1316  if (index + 1 >= attached_tabstrip_->tab_count() ||
1317      !attached_tabstrip_->touch_layout_->IsStacked(index + 1) ||
1318      (mouse_move_direction_ & kMovedMouseRight) == 0)
1319    return false;
1320
1321  int active_x = attached_tabstrip_->ideal_bounds(index).x();
1322  int next_x = attached_tabstrip_->ideal_bounds(index + 1).x();
1323  int mid_x = std::min(next_x - kStackedDistance,
1324                       active_x + (next_x - active_x) / 4);
1325  return dragged_bounds.x() >= mid_x;
1326}
1327
1328bool TabDragController::ShouldDragToPreviousStackedTab(
1329    const gfx::Rect& dragged_bounds,
1330    int index) const {
1331  if (index - 1 < attached_tabstrip_->GetMiniTabCount() ||
1332      !attached_tabstrip_->touch_layout_->IsStacked(index - 1) ||
1333      (mouse_move_direction_ & kMovedMouseLeft) == 0)
1334    return false;
1335
1336  int active_x = attached_tabstrip_->ideal_bounds(index).x();
1337  int previous_x = attached_tabstrip_->ideal_bounds(index - 1).x();
1338  int mid_x = std::max(previous_x + kStackedDistance,
1339                       active_x - (active_x - previous_x) / 4);
1340  return dragged_bounds.x() <= mid_x;
1341}
1342
1343int TabDragController::GetInsertionIndexForDraggedBoundsStacked(
1344    const gfx::Rect& dragged_bounds) const {
1345  StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
1346  int active_index = touch_layout->active_index();
1347  // Search from the active index to the front of the tabstrip. Do this as tabs
1348  // overlap each other from the active index.
1349  int index = GetInsertionIndexFrom(dragged_bounds, active_index, -1);
1350  if (index != active_index)
1351    return index;
1352  if (index == -1)
1353    return GetInsertionIndexFrom(dragged_bounds, active_index + 1, 1);
1354
1355  // The position to drag to corresponds to the active tab. If the next/previous
1356  // tab is stacked, then shorten the distance used to determine insertion
1357  // bounds. We do this as GetInsertionIndexFrom() uses the bounds of the
1358  // tabs. When tabs are stacked the next/previous tab is on top of the tab.
1359  if (active_index + 1 < attached_tabstrip_->tab_count() &&
1360      touch_layout->IsStacked(active_index + 1)) {
1361    index = GetInsertionIndexFrom(dragged_bounds, active_index + 1, 1);
1362    if (index == -1 && ShouldDragToNextStackedTab(dragged_bounds, active_index))
1363      index = active_index + 1;
1364    else if (index == -1)
1365      index = active_index;
1366  } else if (ShouldDragToPreviousStackedTab(dragged_bounds, active_index)) {
1367    index = active_index - 1;
1368  }
1369  return index;
1370}
1371
1372gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
1373    const gfx::Point& tab_strip_point) {
1374  // attached_tab is NULL when inserting into a new tabstrip.
1375  if (source_tab_drag_data()->attached_tab) {
1376    return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
1377                     source_tab_drag_data()->attached_tab->width(),
1378                     source_tab_drag_data()->attached_tab->height());
1379  }
1380
1381  double sel_width, unselected_width;
1382  attached_tabstrip_->GetCurrentTabWidths(&sel_width, &unselected_width);
1383  return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
1384                   static_cast<int>(sel_width),
1385                   Tab::GetStandardSize().height());
1386}
1387
1388gfx::Point TabDragController::GetAttachedDragPoint(
1389    const gfx::Point& point_in_screen) {
1390  DCHECK(attached_tabstrip_);  // The tab must be attached.
1391
1392  gfx::Point tab_loc(point_in_screen);
1393  views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_loc);
1394  const int x =
1395      attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
1396
1397  // TODO: consider caching this.
1398  std::vector<Tab*> attached_tabs;
1399  for (size_t i = 0; i < drag_data_.size(); ++i)
1400    attached_tabs.push_back(drag_data_[i].attached_tab);
1401  const int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
1402  const int max_x = attached_tabstrip_->width() - size;
1403  return gfx::Point(std::min(std::max(x, 0), max_x), 0);
1404}
1405
1406std::vector<Tab*> TabDragController::GetTabsMatchingDraggedContents(
1407    TabStrip* tabstrip) {
1408  TabStripModel* model = GetModel(attached_tabstrip_);
1409  std::vector<Tab*> tabs;
1410  for (size_t i = 0; i < drag_data_.size(); ++i) {
1411    int model_index = model->GetIndexOfWebContents(drag_data_[i].contents);
1412    if (model_index == TabStripModel::kNoTab)
1413      return std::vector<Tab*>();
1414    tabs.push_back(tabstrip->tab_at(model_index));
1415  }
1416  return tabs;
1417}
1418
1419std::vector<gfx::Rect> TabDragController::CalculateBoundsForDraggedTabs() {
1420  std::vector<gfx::Rect> drag_bounds;
1421  std::vector<Tab*> attached_tabs;
1422  for (size_t i = 0; i < drag_data_.size(); ++i)
1423    attached_tabs.push_back(drag_data_[i].attached_tab);
1424  attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
1425                                                    &drag_bounds);
1426  return drag_bounds;
1427}
1428
1429void TabDragController::EndDragImpl(EndDragType type) {
1430  DCHECK(active_);
1431  active_ = false;
1432
1433  bring_to_front_timer_.Stop();
1434  move_stacked_timer_.Stop();
1435
1436  if (is_dragging_window_) {
1437    waiting_for_run_loop_to_exit_ = true;
1438
1439    if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1)) {
1440      SetWindowPositionManaged(GetAttachedBrowserWidget()->GetNativeView(),
1441                               true);
1442    }
1443
1444    // End the nested drag loop.
1445    GetAttachedBrowserWidget()->EndMoveLoop();
1446  }
1447
1448  if (type != TAB_DESTROYED) {
1449    // We only finish up the drag if we were actually dragging. If start_drag_
1450    // is false, the user just clicked and released and didn't move the mouse
1451    // enough to trigger a drag.
1452    if (started_drag_) {
1453      RestoreFocus();
1454      if (type == CANCELED)
1455        RevertDrag();
1456      else
1457        CompleteDrag();
1458    }
1459  } else if (drag_data_.size() > 1) {
1460    initial_selection_model_.Clear();
1461    RevertDrag();
1462  }  // else case the only tab we were dragging was deleted. Nothing to do.
1463
1464  if (!detach_into_browser_)
1465    ResetDelegates();
1466
1467  // Clear out drag data so we don't attempt to do anything with it.
1468  drag_data_.clear();
1469
1470  TabStrip* owning_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
1471      attached_tabstrip_ : source_tabstrip_;
1472  owning_tabstrip->DestroyDragController();
1473}
1474
1475void TabDragController::RevertDrag() {
1476  std::vector<Tab*> tabs;
1477  for (size_t i = 0; i < drag_data_.size(); ++i) {
1478    if (drag_data_[i].contents) {
1479      // Contents is NULL if a tab was destroyed while the drag was under way.
1480      tabs.push_back(drag_data_[i].attached_tab);
1481      RevertDragAt(i);
1482    }
1483  }
1484
1485  bool restore_frame = !detach_into_browser_ &&
1486                       attached_tabstrip_ != source_tabstrip_;
1487  if (attached_tabstrip_) {
1488    if (did_restore_window_)
1489      MaximizeAttachedWindow();
1490    if (attached_tabstrip_ == source_tabstrip_) {
1491      source_tabstrip_->StoppedDraggingTabs(
1492          tabs, initial_tab_positions_, move_behavior_ == MOVE_VISIBILE_TABS,
1493          false);
1494    } else {
1495      attached_tabstrip_->DraggedTabsDetached();
1496    }
1497  }
1498
1499  if (initial_selection_model_.empty())
1500    ResetSelection(GetModel(source_tabstrip_));
1501  else
1502    GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
1503
1504  // If we're not attached to any TabStrip, or attached to some other TabStrip,
1505  // we need to restore the bounds of the original TabStrip's frame, in case
1506  // it has been hidden.
1507  if (restore_frame && !restore_bounds_.IsEmpty())
1508    source_tabstrip_->GetWidget()->SetBounds(restore_bounds_);
1509
1510  if (detach_into_browser_ && source_tabstrip_)
1511    source_tabstrip_->GetWidget()->Activate();
1512
1513  // Return the WebContents to normalcy.  If the tab was attached to a
1514  // TabStrip before the revert, the decrement has already occurred.
1515  // If the tab was destroyed, don't attempt to dereference the
1516  // WebContents pointer.
1517  if (!detach_into_browser_ && !attached_tabstrip_ && source_dragged_contents())
1518    source_dragged_contents()->DecrementCapturerCount();
1519}
1520
1521void TabDragController::ResetSelection(TabStripModel* model) {
1522  DCHECK(model);
1523  ui::ListSelectionModel selection_model;
1524  bool has_one_valid_tab = false;
1525  for (size_t i = 0; i < drag_data_.size(); ++i) {
1526    // |contents| is NULL if a tab was deleted out from under us.
1527    if (drag_data_[i].contents) {
1528      int index = model->GetIndexOfWebContents(drag_data_[i].contents);
1529      DCHECK_NE(-1, index);
1530      selection_model.AddIndexToSelection(index);
1531      if (!has_one_valid_tab || i == source_tab_index_) {
1532        // Reset the active/lead to the first tab. If the source tab is still
1533        // valid we'll reset these again later on.
1534        selection_model.set_active(index);
1535        selection_model.set_anchor(index);
1536        has_one_valid_tab = true;
1537      }
1538    }
1539  }
1540  if (!has_one_valid_tab)
1541    return;
1542
1543  model->SetSelectionFromModel(selection_model);
1544}
1545
1546void TabDragController::RestoreInitialSelection() {
1547  // First time detaching from the source tabstrip. Reset selection model to
1548  // initial_selection_model_. Before resetting though we have to remove all
1549  // the tabs from initial_selection_model_ as it was created with the tabs
1550  // still there.
1551  ui::ListSelectionModel selection_model;
1552  selection_model.Copy(initial_selection_model_);
1553  for (DragData::const_reverse_iterator i(drag_data_.rbegin());
1554       i != drag_data_.rend(); ++i) {
1555    selection_model.DecrementFrom(i->source_model_index);
1556  }
1557  // We may have cleared out the selection model. Only reset it if it
1558  // contains something.
1559  if (selection_model.empty())
1560    return;
1561
1562  // The anchor/active may have been among the tabs that were dragged out. Force
1563  // the anchor/active to be valid.
1564  if (selection_model.anchor() == ui::ListSelectionModel::kUnselectedIndex)
1565    selection_model.set_anchor(selection_model.selected_indices()[0]);
1566  if (selection_model.active() == ui::ListSelectionModel::kUnselectedIndex)
1567    selection_model.set_active(selection_model.selected_indices()[0]);
1568  GetModel(source_tabstrip_)->SetSelectionFromModel(selection_model);
1569}
1570
1571void TabDragController::RevertDragAt(size_t drag_index) {
1572  DCHECK(started_drag_);
1573  DCHECK(source_tabstrip_);
1574
1575  base::AutoReset<bool> setter(&is_mutating_, true);
1576  TabDragData* data = &(drag_data_[drag_index]);
1577  if (attached_tabstrip_) {
1578    int index =
1579        GetModel(attached_tabstrip_)->GetIndexOfWebContents(data->contents);
1580    if (attached_tabstrip_ != source_tabstrip_) {
1581      // The Tab was inserted into another TabStrip. We need to put it back
1582      // into the original one.
1583      GetModel(attached_tabstrip_)->DetachWebContentsAt(index);
1584      // TODO(beng): (Cleanup) seems like we should use Attach() for this
1585      //             somehow.
1586      GetModel(source_tabstrip_)->InsertWebContentsAt(
1587          data->source_model_index, data->contents,
1588          (data->pinned ? TabStripModel::ADD_PINNED : 0));
1589    } else {
1590      // The Tab was moved within the TabStrip where the drag was initiated.
1591      // Move it back to the starting location.
1592      GetModel(source_tabstrip_)->MoveWebContentsAt(
1593          index, data->source_model_index, false);
1594    }
1595  } else {
1596    // The Tab was detached from the TabStrip where the drag began, and has not
1597    // been attached to any other TabStrip. We need to put it back into the
1598    // source TabStrip.
1599    GetModel(source_tabstrip_)->InsertWebContentsAt(
1600        data->source_model_index, data->contents,
1601        (data->pinned ? TabStripModel::ADD_PINNED : 0));
1602  }
1603}
1604
1605void TabDragController::CompleteDrag() {
1606  DCHECK(started_drag_);
1607
1608  if (attached_tabstrip_) {
1609    if (is_dragging_new_browser_ || did_restore_window_) {
1610      if (IsDockedOrSnapped(attached_tabstrip_)) {
1611        was_source_maximized_ = false;
1612        was_source_fullscreen_ = false;
1613      }
1614
1615      // If source window was maximized - maximize the new window as well.
1616      if (was_source_maximized_ || was_source_fullscreen_)
1617        MaximizeAttachedWindow();
1618    }
1619    attached_tabstrip_->StoppedDraggingTabs(
1620        GetTabsMatchingDraggedContents(attached_tabstrip_),
1621        initial_tab_positions_,
1622        move_behavior_ == MOVE_VISIBILE_TABS,
1623        true);
1624  } else {
1625    // Compel the model to construct a new window for the detached
1626    // WebContentses.
1627    views::Widget* widget = source_tabstrip_->GetWidget();
1628    gfx::Rect window_bounds(widget->GetRestoredBounds());
1629    window_bounds.set_origin(GetWindowCreatePoint(last_point_in_screen_));
1630
1631    base::AutoReset<bool> setter(&is_mutating_, true);
1632
1633    std::vector<TabStripModelDelegate::NewStripContents> contentses;
1634    for (size_t i = 0; i < drag_data_.size(); ++i) {
1635      TabStripModelDelegate::NewStripContents item;
1636      item.web_contents = drag_data_[i].contents;
1637      item.add_types = drag_data_[i].pinned ? TabStripModel::ADD_PINNED
1638                                            : TabStripModel::ADD_NONE;
1639      contentses.push_back(item);
1640    }
1641
1642    Browser* new_browser =
1643        GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
1644            contentses, window_bounds, widget->IsMaximized());
1645    ResetSelection(new_browser->tab_strip_model());
1646    new_browser->window()->Show();
1647
1648    // Return the WebContents to normalcy.
1649    if (!detach_into_browser_)
1650      source_dragged_contents()->DecrementCapturerCount();
1651  }
1652
1653  CleanUpHiddenFrame();
1654}
1655
1656void TabDragController::MaximizeAttachedWindow() {
1657  GetAttachedBrowserWidget()->Maximize();
1658#if defined(USE_ASH)
1659  if (was_source_fullscreen_ &&
1660      host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
1661    // In fullscreen mode it is only possible to get here if the source
1662    // was in "immersive fullscreen" mode, so toggle it back on.
1663    ash::accelerators::ToggleFullscreen();
1664  }
1665#endif
1666}
1667
1668void TabDragController::ResetDelegates() {
1669  DCHECK(!detach_into_browser_);
1670  for (size_t i = 0; i < drag_data_.size(); ++i) {
1671    if (drag_data_[i].contents &&
1672        drag_data_[i].contents->GetDelegate() == this) {
1673      drag_data_[i].contents->SetDelegate(
1674          drag_data_[i].original_delegate);
1675    }
1676  }
1677}
1678
1679gfx::Rect TabDragController::GetViewScreenBounds(
1680    views::View* view) const {
1681  gfx::Point view_topleft;
1682  views::View::ConvertPointToScreen(view, &view_topleft);
1683  gfx::Rect view_screen_bounds = view->GetLocalBounds();
1684  view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
1685  return view_screen_bounds;
1686}
1687
1688void TabDragController::CleanUpHiddenFrame() {
1689  // If the model we started dragging from is now empty, we must ask the
1690  // delegate to close the frame.
1691  if (!detach_into_browser_ && GetModel(source_tabstrip_)->empty())
1692    GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession();
1693}
1694
1695void TabDragController::BringWindowUnderPointToFront(
1696    const gfx::Point& point_in_screen) {
1697  aura::Window* window = GetLocalProcessWindow(point_in_screen, true);
1698
1699  // Only bring browser windows to front - only windows with a TabStrip can
1700  // be tab drag targets.
1701  if (!GetTabStripForWindow(window))
1702    return;
1703
1704  if (window) {
1705    views::Widget* widget_window = views::Widget::GetWidgetForNativeView(
1706        window);
1707    if (!widget_window)
1708      return;
1709
1710#if defined(USE_ASH)
1711    if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
1712      // TODO(varkha): The code below ensures that the phantom drag widget
1713      // is shown on top of browser windows. The code should be moved to ash/
1714      // and the phantom should be able to assert its top-most state on its own.
1715      // One strategy would be for DragWindowController to
1716      // be able to observe stacking changes to the phantom drag widget's
1717      // siblings in order to keep it on top. One way is to implement a
1718      // notification that is sent to a window parent's observers when a
1719      // stacking order is changed among the children of that same parent.
1720      // Note that OnWindowStackingChanged is sent only to the child that is the
1721      // argument of one of the Window::StackChildX calls and not to all its
1722      // siblings affected by the stacking change.
1723      aura::Window* browser_window = widget_window->GetNativeView();
1724      // Find a topmost non-popup window and stack the recipient browser above
1725      // it in order to avoid stacking the browser window on top of the phantom
1726      // drag widget created by DragWindowController in a second display.
1727      for (aura::Window::Windows::const_reverse_iterator it =
1728           browser_window->parent()->children().rbegin();
1729           it != browser_window->parent()->children().rend(); ++it) {
1730        // If the iteration reached the recipient browser window then it is
1731        // already topmost and it is safe to return with no stacking change.
1732        if (*it == browser_window)
1733          return;
1734        if ((*it)->type() != ui::wm::WINDOW_TYPE_POPUP) {
1735          widget_window->StackAbove(*it);
1736          break;
1737        }
1738      }
1739    } else {
1740      widget_window->StackAtTop();
1741    }
1742#else
1743    widget_window->StackAtTop();
1744#endif
1745
1746    // The previous call made the window appear on top of the dragged window,
1747    // move the dragged window to the front.
1748    if (is_dragging_window_)
1749      attached_tabstrip_->GetWidget()->StackAtTop();
1750  }
1751}
1752
1753TabStripModel* TabDragController::GetModel(
1754    TabStrip* tabstrip) const {
1755  return static_cast<BrowserTabStripController*>(tabstrip->controller())->
1756      model();
1757}
1758
1759views::Widget* TabDragController::GetAttachedBrowserWidget() {
1760  return attached_tabstrip_->GetWidget();
1761}
1762
1763bool TabDragController::AreTabsConsecutive() {
1764  for (size_t i = 1; i < drag_data_.size(); ++i) {
1765    if (drag_data_[i - 1].source_model_index + 1 !=
1766        drag_data_[i].source_model_index) {
1767      return false;
1768    }
1769  }
1770  return true;
1771}
1772
1773gfx::Rect TabDragController::CalculateDraggedBrowserBounds(
1774    TabStrip* source,
1775    const gfx::Point& point_in_screen,
1776    std::vector<gfx::Rect>* drag_bounds) {
1777  gfx::Point center(0, source->height() / 2);
1778  views::View::ConvertPointToWidget(source, &center);
1779  gfx::Rect new_bounds(source->GetWidget()->GetRestoredBounds());
1780  if (source->GetWidget()->IsMaximized()) {
1781    // If the restore bounds is really small, we don't want to honor it
1782    // (dragging a really small window looks wrong), instead make sure the new
1783    // window is at least 50% the size of the old.
1784    const gfx::Size max_size(
1785        source->GetWidget()->GetWindowBoundsInScreen().size());
1786    new_bounds.set_width(
1787        std::max(max_size.width() / 2, new_bounds.width()));
1788    new_bounds.set_height(
1789        std::max(max_size.height() / 2, new_bounds.height()));
1790  }
1791  new_bounds.set_y(point_in_screen.y() - center.y());
1792  switch (GetDetachPosition(point_in_screen)) {
1793    case DETACH_BEFORE:
1794      new_bounds.set_x(point_in_screen.x() - center.x());
1795      new_bounds.Offset(-mouse_offset_.x(), 0);
1796      break;
1797    case DETACH_AFTER: {
1798      gfx::Point right_edge(source->width(), 0);
1799      views::View::ConvertPointToWidget(source, &right_edge);
1800      new_bounds.set_x(point_in_screen.x() - right_edge.x());
1801      new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
1802      OffsetX(-(*drag_bounds)[0].x(), drag_bounds);
1803      break;
1804    }
1805    default:
1806      break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
1807  }
1808
1809  // To account for the extra vertical on restored windows that is absent on
1810  // maximized windows, add an additional vertical offset extracted from the tab
1811  // strip.
1812  if (source->GetWidget()->IsMaximized())
1813    new_bounds.Offset(0, -source->button_v_offset());
1814  return new_bounds;
1815}
1816
1817void TabDragController::AdjustBrowserAndTabBoundsForDrag(
1818    int last_tabstrip_width,
1819    const gfx::Point& point_in_screen,
1820    std::vector<gfx::Rect>* drag_bounds) {
1821  attached_tabstrip_->InvalidateLayout();
1822  attached_tabstrip_->DoLayout();
1823  const int dragged_tabstrip_width = attached_tabstrip_->tab_area_width();
1824
1825  // If the new tabstrip is smaller than the old resize the tabs.
1826  if (dragged_tabstrip_width < last_tabstrip_width) {
1827    const float leading_ratio =
1828        drag_bounds->front().x() / static_cast<float>(last_tabstrip_width);
1829    *drag_bounds = CalculateBoundsForDraggedTabs();
1830
1831    if (drag_bounds->back().right() < dragged_tabstrip_width) {
1832      const int delta_x =
1833          std::min(static_cast<int>(leading_ratio * dragged_tabstrip_width),
1834                   dragged_tabstrip_width -
1835                       (drag_bounds->back().right() -
1836                        drag_bounds->front().x()));
1837      OffsetX(delta_x, drag_bounds);
1838    }
1839
1840    // Reposition the restored window such that the tab that was dragged remains
1841    // under the mouse cursor.
1842    gfx::Point offset(
1843        static_cast<int>((*drag_bounds)[source_tab_index_].width() *
1844                         offset_to_width_ratio_) +
1845        (*drag_bounds)[source_tab_index_].x(), 0);
1846    views::View::ConvertPointToWidget(attached_tabstrip_, &offset);
1847    gfx::Rect bounds = GetAttachedBrowserWidget()->GetWindowBoundsInScreen();
1848    bounds.set_x(point_in_screen.x() - offset.x());
1849    GetAttachedBrowserWidget()->SetBounds(bounds);
1850  }
1851  attached_tabstrip_->SetTabBoundsForDrag(*drag_bounds);
1852}
1853
1854Browser* TabDragController::CreateBrowserForDrag(
1855    TabStrip* source,
1856    const gfx::Point& point_in_screen,
1857    gfx::Vector2d* drag_offset,
1858    std::vector<gfx::Rect>* drag_bounds) {
1859  gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source,
1860                                                     point_in_screen,
1861                                                     drag_bounds));
1862  *drag_offset = point_in_screen - new_bounds.origin();
1863
1864  Profile* profile =
1865      Profile::FromBrowserContext(drag_data_[0].contents->GetBrowserContext());
1866  Browser::CreateParams create_params(Browser::TYPE_TABBED,
1867                                      profile,
1868                                      host_desktop_type_);
1869  create_params.initial_bounds = new_bounds;
1870  Browser* browser = new Browser(create_params);
1871  is_dragging_new_browser_ = true;
1872  SetWindowPositionManaged(browser->window()->GetNativeWindow(), false);
1873  // If the window is created maximized then the bounds we supplied are ignored.
1874  // We need to reset them again so they are honored.
1875  browser->window()->SetBounds(new_bounds);
1876
1877  return browser;
1878}
1879
1880gfx::Point TabDragController::GetCursorScreenPoint() {
1881#if defined(USE_ASH)
1882  if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH &&
1883      event_source_ == EVENT_SOURCE_TOUCH &&
1884      aura::Env::GetInstance()->is_touch_down()) {
1885    views::Widget* widget = GetAttachedBrowserWidget();
1886    DCHECK(widget);
1887    aura::Window* widget_window = widget->GetNativeWindow();
1888    DCHECK(widget_window->GetRootWindow());
1889    gfx::PointF touch_point_f;
1890    bool got_touch_point = ui::GestureRecognizer::Get()->
1891        GetLastTouchPointForTarget(widget_window, &touch_point_f);
1892    // TODO(tdresser): Switch to using gfx::PointF. See crbug.com/337824.
1893    gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f);
1894    DCHECK(got_touch_point);
1895    ash::wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point);
1896    return touch_point;
1897  }
1898#endif
1899  return screen_->GetCursorScreenPoint();
1900}
1901
1902gfx::Vector2d TabDragController::GetWindowOffset(
1903    const gfx::Point& point_in_screen) {
1904  TabStrip* owning_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
1905      attached_tabstrip_ : source_tabstrip_;
1906  views::View* toplevel_view = owning_tabstrip->GetWidget()->GetContentsView();
1907
1908  gfx::Point point = point_in_screen;
1909  views::View::ConvertPointFromScreen(toplevel_view, &point);
1910  return point.OffsetFromOrigin();
1911}
1912
1913gfx::NativeWindow TabDragController::GetLocalProcessWindow(
1914    const gfx::Point& screen_point,
1915    bool exclude_dragged_view) {
1916  std::set<aura::Window*> exclude;
1917  if (exclude_dragged_view) {
1918    aura::Window* dragged_window =
1919        attached_tabstrip_->GetWidget()->GetNativeView();
1920    if (dragged_window)
1921      exclude.insert(dragged_window);
1922  }
1923#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1924  // Exclude windows which are pending deletion via Browser::TabStripEmpty().
1925  // These windows can be returned in the Linux Aura port because the browser
1926  // window which was used for dragging is not hidden once all of its tabs are
1927  // attached to another browser window in DragBrowserToNewTabStrip().
1928  // TODO(pkotwicz): Fix this properly (crbug.com/358482)
1929  BrowserList* browser_list = BrowserList::GetInstance(
1930      chrome::HOST_DESKTOP_TYPE_NATIVE);
1931  for (BrowserList::const_iterator it = browser_list->begin();
1932       it != browser_list->end(); ++it) {
1933    if ((*it)->tab_strip_model()->empty())
1934      exclude.insert((*it)->window()->GetNativeWindow());
1935  }
1936#endif
1937  return GetLocalProcessWindowAtPoint(host_desktop_type_,
1938                                      screen_point,
1939                                      exclude);
1940
1941}
1942