Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / tab_drag_controller.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
6
7 #include <math.h>
8 #include <set>
9
10 #include "base/auto_reset.h"
11 #include "base/callback.h"
12 #include "base/i18n/rtl.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
19 #include "chrome/browser/ui/views/frame/browser_view.h"
20 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
21 #include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
22 #include "chrome/browser/ui/views/tabs/tab.h"
23 #include "chrome/browser/ui/views/tabs/tab_strip.h"
24 #include "chrome/browser/ui/views/tabs/window_finder.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/user_metrics.h"
30 #include "content/public/browser/web_contents.h"
31 #include "extensions/browser/extension_function_dispatcher.h"
32 #include "ui/events/event_constants.h"
33 #include "ui/events/gestures/gesture_recognizer.h"
34 #include "ui/gfx/geometry/point_conversions.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/views/event_monitor.h"
37 #include "ui/views/focus/view_storage.h"
38 #include "ui/views/widget/root_view.h"
39 #include "ui/views/widget/widget.h"
40
41 #if defined(USE_ASH)
42 #include "ash/accelerators/accelerator_commands.h"
43 #include "ash/shell.h"
44 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
45 #include "ash/wm/window_state.h"
46 #include "ui/wm/core/coordinate_conversion.h"
47 #endif
48
49 #if defined(USE_AURA)
50 #include "ui/aura/env.h"
51 #include "ui/aura/window.h"
52 #include "ui/wm/core/window_modality_controller.h"
53 #endif
54
55 using base::UserMetricsAction;
56 using content::OpenURLParams;
57 using content::WebContents;
58
59 // If non-null there is a drag underway.
60 static TabDragController* instance_ = NULL;
61
62 namespace {
63
64 // Delay, in ms, during dragging before we bring a window to front.
65 const int kBringToFrontDelay = 750;
66
67 // Initial delay before moving tabs when the dragged tab is close to the edge of
68 // the stacked tabs.
69 const int kMoveAttachedInitialDelay = 600;
70
71 // Delay for moving tabs after the initial delay has passed.
72 const int kMoveAttachedSubsequentDelay = 300;
73
74 const int kHorizontalMoveThreshold = 16;  // Pixels.
75
76 // Distance from the next/previous stacked before before we consider the tab
77 // close enough to trigger moving.
78 const int kStackedDistance = 36;
79
80 #if defined(USE_ASH)
81 void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
82   ash::wm::GetWindowState(window)->set_window_position_managed(value);
83 }
84
85 // Returns true if |tab_strip| browser window is docked.
86 bool IsDockedOrSnapped(const TabStrip* tab_strip) {
87   DCHECK(tab_strip);
88   ash::wm::WindowState* window_state =
89       ash::wm::GetWindowState(tab_strip->GetWidget()->GetNativeWindow());
90   return window_state->IsDocked() || window_state->IsSnapped();
91 }
92 #else
93 void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
94 }
95
96 bool IsDockedOrSnapped(const TabStrip* tab_strip) {
97   return false;
98 }
99 #endif
100
101 #if defined(USE_AURA)
102 gfx::NativeWindow GetModalTransient(gfx::NativeWindow window) {
103   return wm::GetModalTransient(window);
104 }
105 #else
106 gfx::NativeWindow GetModalTransient(gfx::NativeWindow window) {
107   NOTIMPLEMENTED();
108   return NULL;
109 }
110 #endif
111
112 // Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
113 // of |bounds| is adjusted by |vertical_adjustment|.
114 bool DoesRectContainVerticalPointExpanded(
115     const gfx::Rect& bounds,
116     int vertical_adjustment,
117     int y) {
118   int upper_threshold = bounds.bottom() + vertical_adjustment;
119   int lower_threshold = bounds.y() - vertical_adjustment;
120   return y >= lower_threshold && y <= upper_threshold;
121 }
122
123 // Adds |x_offset| to all the rectangles in |rects|.
124 void OffsetX(int x_offset, std::vector<gfx::Rect>* rects) {
125   if (x_offset == 0)
126     return;
127
128   for (size_t i = 0; i < rects->size(); ++i)
129     (*rects)[i].set_x((*rects)[i].x() + x_offset);
130 }
131
132 // WidgetObserver implementation that resets the window position managed
133 // property on Show.
134 // We're forced to do this here since BrowserFrameAsh resets the 'window
135 // position managed' property during a show and we need the property set to
136 // false before WorkspaceLayoutManager sees the visibility change.
137 class WindowPositionManagedUpdater : public views::WidgetObserver {
138  public:
139   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override {
140     SetWindowPositionManaged(widget->GetNativeWindow(), false);
141   }
142 };
143
144 // EscapeTracker installs an event monitor and runs a callback when it receives
145 // the escape key.
146 class EscapeTracker : public ui::EventHandler {
147  public:
148   explicit EscapeTracker(const base::Closure& callback)
149       : escape_callback_(callback),
150         event_monitor_(views::EventMonitor::Create(this)) {
151   }
152
153  private:
154   // ui::EventHandler:
155   void OnKeyEvent(ui::KeyEvent* key) override {
156     if (key->type() == ui::ET_KEY_PRESSED &&
157         key->key_code() == ui::VKEY_ESCAPE) {
158       escape_callback_.Run();
159     }
160   }
161
162   base::Closure escape_callback_;
163   scoped_ptr<views::EventMonitor> event_monitor_;
164
165   DISALLOW_COPY_AND_ASSIGN(EscapeTracker);
166 };
167
168 }  // namespace
169
170 TabDragController::TabDragData::TabDragData()
171     : contents(NULL),
172       source_model_index(-1),
173       attached_tab(NULL),
174       pinned(false) {
175 }
176
177 TabDragController::TabDragData::~TabDragData() {
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////
181 // TabDragController, public:
182
183 // static
184 const int TabDragController::kTouchVerticalDetachMagnetism = 50;
185
186 // static
187 const int TabDragController::kVerticalDetachMagnetism = 15;
188
189 TabDragController::TabDragController()
190     : event_source_(EVENT_SOURCE_MOUSE),
191       source_tabstrip_(NULL),
192       attached_tabstrip_(NULL),
193       screen_(NULL),
194       host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
195       can_release_capture_(true),
196       offset_to_width_ratio_(0),
197       old_focused_view_id_(
198           views::ViewStorage::GetInstance()->CreateStorageID()),
199       last_move_screen_loc_(0),
200       started_drag_(false),
201       active_(true),
202       source_tab_index_(std::numeric_limits<size_t>::max()),
203       initial_move_(true),
204       detach_behavior_(DETACHABLE),
205       move_behavior_(REORDER),
206       mouse_move_direction_(0),
207       is_dragging_window_(false),
208       is_dragging_new_browser_(false),
209       was_source_maximized_(false),
210       was_source_fullscreen_(false),
211       did_restore_window_(false),
212       end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
213       waiting_for_run_loop_to_exit_(false),
214       tab_strip_to_attach_to_after_exit_(NULL),
215       move_loop_widget_(NULL),
216       is_mutating_(false),
217       attach_x_(-1),
218       attach_index_(-1),
219       weak_factory_(this) {
220   instance_ = this;
221 }
222
223 TabDragController::~TabDragController() {
224   views::ViewStorage::GetInstance()->RemoveView(old_focused_view_id_);
225
226   if (instance_ == this)
227     instance_ = NULL;
228
229   if (move_loop_widget_) {
230     move_loop_widget_->RemoveObserver(this);
231     SetWindowPositionManaged(move_loop_widget_->GetNativeWindow(), true);
232   }
233
234   if (source_tabstrip_)
235     GetModel(source_tabstrip_)->RemoveObserver(this);
236
237   if (event_source_ == EVENT_SOURCE_TOUCH) {
238     TabStrip* capture_tabstrip = attached_tabstrip_ ?
239         attached_tabstrip_ : source_tabstrip_;
240     capture_tabstrip->GetWidget()->ReleaseCapture();
241   }
242 }
243
244 void TabDragController::Init(
245     TabStrip* source_tabstrip,
246     Tab* source_tab,
247     const std::vector<Tab*>& tabs,
248     const gfx::Point& mouse_offset,
249     int source_tab_offset,
250     const ui::ListSelectionModel& initial_selection_model,
251     MoveBehavior move_behavior,
252     EventSource event_source) {
253   DCHECK(!tabs.empty());
254   DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
255   source_tabstrip_ = source_tabstrip;
256   was_source_maximized_ = source_tabstrip->GetWidget()->IsMaximized();
257   was_source_fullscreen_ = source_tabstrip->GetWidget()->IsFullscreen();
258   screen_ = gfx::Screen::GetScreenFor(
259       source_tabstrip->GetWidget()->GetNativeView());
260   host_desktop_type_ = chrome::GetHostDesktopTypeForNativeView(
261       source_tabstrip->GetWidget()->GetNativeView());
262   // Do not release capture when transferring capture between widgets on:
263   // - Desktop Linux
264   //     Mouse capture is not synchronous on desktop Linux. Chrome makes
265   //     transferring capture between widgets without releasing capture appear
266   //     synchronous on desktop Linux, so use that.
267   // - Ash
268   //     Releasing capture on Ash cancels gestures so avoid it.
269 #if defined(OS_LINUX)
270   can_release_capture_ = false;
271 #else
272   can_release_capture_ =
273       (host_desktop_type_ != chrome::HOST_DESKTOP_TYPE_ASH);
274 #endif
275   start_point_in_screen_ = gfx::Point(source_tab_offset, mouse_offset.y());
276   views::View::ConvertPointToScreen(source_tab, &start_point_in_screen_);
277   event_source_ = event_source;
278   mouse_offset_ = mouse_offset;
279   move_behavior_ = move_behavior;
280   last_point_in_screen_ = start_point_in_screen_;
281   last_move_screen_loc_ = start_point_in_screen_.x();
282   initial_tab_positions_ = source_tabstrip->GetTabXCoordinates();
283
284   GetModel(source_tabstrip_)->AddObserver(this);
285
286   drag_data_.resize(tabs.size());
287   for (size_t i = 0; i < tabs.size(); ++i)
288     InitTabDragData(tabs[i], &(drag_data_[i]));
289   source_tab_index_ =
290       std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
291
292   // Listen for Esc key presses.
293   escape_tracker_.reset(
294       new EscapeTracker(base::Bind(&TabDragController::EndDrag,
295                                    weak_factory_.GetWeakPtr(),
296                                    END_DRAG_CANCEL)));
297
298   if (source_tab->width() > 0) {
299     offset_to_width_ratio_ = static_cast<float>(
300         source_tab->GetMirroredXInView(source_tab_offset)) /
301         static_cast<float>(source_tab->width());
302   }
303   InitWindowCreatePoint();
304   initial_selection_model_.Copy(initial_selection_model);
305
306   // Gestures don't automatically do a capture. We don't allow multiple drags at
307   // the same time, so we explicitly capture.
308   if (event_source == EVENT_SOURCE_TOUCH)
309     source_tabstrip_->GetWidget()->SetCapture(source_tabstrip_);
310
311 #if defined(USE_ASH)
312   if (ash::Shell::HasInstance() &&
313       ash::Shell::GetInstance()->maximize_mode_controller()->
314           IsMaximizeModeWindowManagerEnabled()) {
315     detach_behavior_ = NOT_DETACHABLE;
316   }
317 #endif
318 }
319
320 // static
321 bool TabDragController::IsAttachedTo(const TabStrip* tab_strip) {
322   return (instance_ && instance_->active() &&
323           instance_->attached_tabstrip() == tab_strip);
324 }
325
326 // static
327 bool TabDragController::IsActive() {
328   return instance_ && instance_->active();
329 }
330
331 void TabDragController::SetMoveBehavior(MoveBehavior behavior) {
332   if (started_drag())
333     return;
334
335   move_behavior_ = behavior;
336 }
337
338 void TabDragController::Drag(const gfx::Point& point_in_screen) {
339   TRACE_EVENT1("views", "TabDragController::Drag",
340                "point_in_screen", point_in_screen.ToString());
341
342   bring_to_front_timer_.Stop();
343   move_stacked_timer_.Stop();
344
345   if (waiting_for_run_loop_to_exit_)
346     return;
347
348   if (!started_drag_) {
349     if (!CanStartDrag(point_in_screen))
350       return;  // User hasn't dragged far enough yet.
351
352     // On windows SaveFocus() may trigger a capture lost, which destroys us.
353     {
354       base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
355       SaveFocus();
356       if (!ref)
357         return;
358     }
359     started_drag_ = true;
360     Attach(source_tabstrip_, gfx::Point());
361     if (static_cast<int>(drag_data_.size()) ==
362         GetModel(source_tabstrip_)->count()) {
363       if (was_source_maximized_ || was_source_fullscreen_) {
364         did_restore_window_ = true;
365         // When all tabs in a maximized browser are dragged the browser gets
366         // restored during the drag and maximized back when the drag ends.
367         views::Widget* widget = GetAttachedBrowserWidget();
368         const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
369         std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
370         OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
371         gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source_tabstrip_,
372                                                            point_in_screen,
373                                                            &drag_bounds));
374         new_bounds.Offset(-widget->GetRestoredBounds().x() +
375                           point_in_screen.x() -
376                           mouse_offset_.x(), 0);
377         widget->SetVisibilityChangedAnimationsEnabled(false);
378         widget->Restore();
379         widget->SetBounds(new_bounds);
380         AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
381                                          point_in_screen,
382                                          &drag_bounds);
383         widget->SetVisibilityChangedAnimationsEnabled(true);
384       }
385       RunMoveLoop(GetWindowOffset(point_in_screen));
386       return;
387     }
388   }
389
390   ContinueDragging(point_in_screen);
391 }
392
393 void TabDragController::EndDrag(EndDragReason reason) {
394   TRACE_EVENT0("views", "TabDragController::EndDrag");
395
396   // If we're dragging a window ignore capture lost since it'll ultimately
397   // trigger the move loop to end and we'll revert the drag when RunMoveLoop()
398   // finishes.
399   if (reason == END_DRAG_CAPTURE_LOST && is_dragging_window_)
400     return;
401   EndDragImpl(reason != END_DRAG_COMPLETE && source_tabstrip_ ?
402               CANCELED : NORMAL);
403 }
404
405 void TabDragController::InitTabDragData(Tab* tab,
406                                         TabDragData* drag_data) {
407   TRACE_EVENT0("views", "TabDragController::InitTabDragData");
408   drag_data->source_model_index =
409       source_tabstrip_->GetModelIndexOfTab(tab);
410   drag_data->contents = GetModel(source_tabstrip_)->GetWebContentsAt(
411       drag_data->source_model_index);
412   drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
413   registrar_.Add(
414       this,
415       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
416       content::Source<WebContents>(drag_data->contents));
417 }
418
419 ///////////////////////////////////////////////////////////////////////////////
420 // TabDragController, content::NotificationObserver implementation:
421
422 void TabDragController::Observe(
423     int type,
424     const content::NotificationSource& source,
425     const content::NotificationDetails& details) {
426   DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
427   WebContents* destroyed_web_contents =
428       content::Source<WebContents>(source).ptr();
429   for (size_t i = 0; i < drag_data_.size(); ++i) {
430     if (drag_data_[i].contents == destroyed_web_contents) {
431       // One of the tabs we're dragging has been destroyed. Cancel the drag.
432       drag_data_[i].contents = NULL;
433       EndDragImpl(TAB_DESTROYED);
434       return;
435     }
436   }
437   // If we get here it means we got notification for a tab we don't know about.
438   NOTREACHED();
439 }
440
441 void TabDragController::OnWidgetBoundsChanged(views::Widget* widget,
442                                               const gfx::Rect& new_bounds) {
443   TRACE_EVENT1("views", "TabDragController::OnWidgetBoundsChanged",
444                "new_bounds", new_bounds.ToString());
445
446   Drag(GetCursorScreenPoint());
447 }
448
449 void TabDragController::TabStripEmpty() {
450   GetModel(source_tabstrip_)->RemoveObserver(this);
451   // NULL out source_tabstrip_ so that we don't attempt to add back to it (in
452   // the case of a revert).
453   source_tabstrip_ = NULL;
454 }
455
456 ///////////////////////////////////////////////////////////////////////////////
457 // TabDragController, private:
458
459 void TabDragController::InitWindowCreatePoint() {
460   // window_create_point_ is only used in CompleteDrag() (through
461   // GetWindowCreatePoint() to get the start point of the docked window) when
462   // the attached_tabstrip_ is NULL and all the window's related bound
463   // information are obtained from source_tabstrip_. So, we need to get the
464   // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
465   // the window_create_point_ is not in the correct coordinate system. Please
466   // refer to http://crbug.com/6223 comment #15 for detailed information.
467   views::View* first_tab = source_tabstrip_->tab_at(0);
468   views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
469   window_create_point_ = first_source_tab_point_;
470   window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
471 }
472
473 gfx::Point TabDragController::GetWindowCreatePoint(
474     const gfx::Point& origin) const {
475   // If the cursor is outside the monitor area, move it inside. For example,
476   // dropping a tab onto the task bar on Windows produces this situation.
477   gfx::Rect work_area = screen_->GetDisplayNearestPoint(origin).work_area();
478   gfx::Point create_point(origin);
479   if (!work_area.IsEmpty()) {
480     if (create_point.x() < work_area.x())
481       create_point.set_x(work_area.x());
482     else if (create_point.x() > work_area.right())
483       create_point.set_x(work_area.right());
484     if (create_point.y() < work_area.y())
485       create_point.set_y(work_area.y());
486     else if (create_point.y() > work_area.bottom())
487       create_point.set_y(work_area.bottom());
488   }
489   return gfx::Point(create_point.x() - window_create_point_.x(),
490                     create_point.y() - window_create_point_.y());
491 }
492
493 void TabDragController::SaveFocus() {
494   DCHECK(source_tabstrip_);
495   views::View* focused_view =
496       source_tabstrip_->GetFocusManager()->GetFocusedView();
497   if (focused_view)
498     views::ViewStorage::GetInstance()->StoreView(old_focused_view_id_,
499                                                  focused_view);
500   source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
501   // WARNING: we may have been deleted.
502 }
503
504 void TabDragController::RestoreFocus() {
505   if (attached_tabstrip_ != source_tabstrip_) {
506     if (is_dragging_new_browser_) {
507       content::WebContents* active_contents = source_dragged_contents();
508       if (active_contents && !active_contents->FocusLocationBarByDefault())
509         active_contents->Focus();
510     }
511     return;
512   }
513   views::View* old_focused_view =
514       views::ViewStorage::GetInstance()->RetrieveView(old_focused_view_id_);
515   if (!old_focused_view)
516     return;
517   old_focused_view->GetFocusManager()->SetFocusedView(old_focused_view);
518 }
519
520 bool TabDragController::CanStartDrag(const gfx::Point& point_in_screen) const {
521   // Determine if the mouse has moved beyond a minimum elasticity distance in
522   // any direction from the starting point.
523   static const int kMinimumDragDistance = 10;
524   int x_offset = abs(point_in_screen.x() - start_point_in_screen_.x());
525   int y_offset = abs(point_in_screen.y() - start_point_in_screen_.y());
526   return sqrt(pow(static_cast<float>(x_offset), 2) +
527               pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
528 }
529
530 void TabDragController::ContinueDragging(const gfx::Point& point_in_screen) {
531   TRACE_EVENT1("views", "TabDragController::ContinueDragging",
532                "point_in_screen", point_in_screen.ToString());
533
534   DCHECK(attached_tabstrip_);
535
536   TabStrip* target_tabstrip = detach_behavior_ == DETACHABLE ?
537       GetTargetTabStripForPoint(point_in_screen) : source_tabstrip_;
538   bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
539
540   if (attached_tabstrip_) {
541     int move_delta = point_in_screen.x() - last_point_in_screen_.x();
542     if (move_delta > 0)
543       mouse_move_direction_ |= kMovedMouseRight;
544     else if (move_delta < 0)
545       mouse_move_direction_ |= kMovedMouseLeft;
546   }
547   last_point_in_screen_ = point_in_screen;
548
549   if (tab_strip_changed) {
550     is_dragging_new_browser_ = false;
551     did_restore_window_ = false;
552     if (DragBrowserToNewTabStrip(target_tabstrip, point_in_screen) ==
553         DRAG_BROWSER_RESULT_STOP) {
554       return;
555     }
556   }
557   if (is_dragging_window_) {
558     static_cast<base::Timer*>(&bring_to_front_timer_)->Start(FROM_HERE,
559         base::TimeDelta::FromMilliseconds(kBringToFrontDelay),
560         base::Bind(&TabDragController::BringWindowUnderPointToFront,
561                    base::Unretained(this), point_in_screen));
562   }
563
564   if (!is_dragging_window_ && attached_tabstrip_) {
565     if (move_only()) {
566       DragActiveTabStacked(point_in_screen);
567     } else {
568       MoveAttached(point_in_screen);
569       if (tab_strip_changed) {
570         // Move the corresponding window to the front. We do this after the
571         // move as on windows activate triggers a synchronous paint.
572         attached_tabstrip_->GetWidget()->Activate();
573       }
574     }
575   }
576 }
577
578 TabDragController::DragBrowserResultType
579 TabDragController::DragBrowserToNewTabStrip(
580     TabStrip* target_tabstrip,
581     const gfx::Point& point_in_screen) {
582   TRACE_EVENT1("views", "TabDragController::DragBrowserToNewTabStrip",
583                "point_in_screen", point_in_screen.ToString());
584
585   if (!target_tabstrip) {
586     DetachIntoNewBrowserAndRunMoveLoop(point_in_screen);
587     return DRAG_BROWSER_RESULT_STOP;
588   }
589   if (is_dragging_window_) {
590     // ReleaseCapture() is going to result in calling back to us (because it
591     // results in a move). That'll cause all sorts of problems.  Reset the
592     // observer so we don't get notified and process the event.
593     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
594       move_loop_widget_->RemoveObserver(this);
595       move_loop_widget_ = NULL;
596     }
597     views::Widget* browser_widget = GetAttachedBrowserWidget();
598     // Need to release the drag controller before starting the move loop as it's
599     // going to trigger capture lost, which cancels drag.
600     attached_tabstrip_->ReleaseDragController();
601     target_tabstrip->OwnDragController(this);
602     // Disable animations so that we don't see a close animation on aero.
603     browser_widget->SetVisibilityChangedAnimationsEnabled(false);
604     if (can_release_capture_)
605       browser_widget->ReleaseCapture();
606     else
607       target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_);
608 #if defined(OS_WIN)
609     // The Gesture recognizer does not work well currently when capture changes
610     // while a touch gesture is in progress. So we need to manually transfer
611     // gesture sequence and the GR's touch events queue to the new window. This
612     // should really be done somewhere in capture change code and or inside the
613     // GR. But we currently do not have a consistent way for doing it that would
614     // work in all cases. Hence this hack.
615     ui::GestureRecognizer::Get()->TransferEventsTo(
616         browser_widget->GetNativeView(),
617         target_tabstrip->GetWidget()->GetNativeView());
618 #endif
619
620     // The window is going away. Since the drag is still on going we don't want
621     // that to effect the position of any windows.
622     SetWindowPositionManaged(browser_widget->GetNativeWindow(), false);
623
624 #if !defined(OS_LINUX) || defined(OS_CHROMEOS)
625     // EndMoveLoop is going to snap the window back to its original location.
626     // Hide it so users don't see this. Hiding a window in Linux aura causes
627     // it to lose capture so skip it.
628     browser_widget->Hide();
629 #endif
630     browser_widget->EndMoveLoop();
631
632     // Ideally we would always swap the tabs now, but on non-ash Windows, it
633     // seems that running the move loop implicitly activates the window when
634     // done, leading to all sorts of flicker. So, on non-ash Windows, instead
635     // we process the move after the loop completes. But on chromeos, we can
636     // do tab swapping now to avoid the tab flashing issue
637     // (crbug.com/116329).
638     if (can_release_capture_) {
639       tab_strip_to_attach_to_after_exit_ = target_tabstrip;
640     } else {
641       is_dragging_window_ = false;
642       Detach(DONT_RELEASE_CAPTURE);
643       Attach(target_tabstrip, point_in_screen);
644       // Move the tabs into position.
645       MoveAttached(point_in_screen);
646       attached_tabstrip_->GetWidget()->Activate();
647     }
648
649     waiting_for_run_loop_to_exit_ = true;
650     end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
651     return DRAG_BROWSER_RESULT_STOP;
652   }
653   Detach(DONT_RELEASE_CAPTURE);
654   Attach(target_tabstrip, point_in_screen);
655   return DRAG_BROWSER_RESULT_CONTINUE;
656 }
657
658 void TabDragController::DragActiveTabStacked(
659     const gfx::Point& point_in_screen) {
660   if (attached_tabstrip_->tab_count() !=
661       static_cast<int>(initial_tab_positions_.size()))
662     return;  // TODO: should cancel drag if this happens.
663
664   int delta = point_in_screen.x() - start_point_in_screen_.x();
665   attached_tabstrip_->DragActiveTab(initial_tab_positions_, delta);
666 }
667
668 void TabDragController::MoveAttachedToNextStackedIndex(
669     const gfx::Point& point_in_screen) {
670   int index = attached_tabstrip_->touch_layout_->active_index();
671   if (index + 1 >= attached_tabstrip_->tab_count())
672     return;
673
674   GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index + 1);
675   StartMoveStackedTimerIfNecessary(point_in_screen,
676                                    kMoveAttachedSubsequentDelay);
677 }
678
679 void TabDragController::MoveAttachedToPreviousStackedIndex(
680     const gfx::Point& point_in_screen) {
681   int index = attached_tabstrip_->touch_layout_->active_index();
682   if (index <= attached_tabstrip_->GetMiniTabCount())
683     return;
684
685   GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index - 1);
686   StartMoveStackedTimerIfNecessary(point_in_screen,
687                                    kMoveAttachedSubsequentDelay);
688 }
689
690 void TabDragController::MoveAttached(const gfx::Point& point_in_screen) {
691   DCHECK(attached_tabstrip_);
692   DCHECK(!is_dragging_window_);
693
694   gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
695
696   // Determine the horizontal move threshold. This is dependent on the width
697   // of tabs. The smaller the tabs compared to the standard size, the smaller
698   // the threshold.
699   int threshold = kHorizontalMoveThreshold;
700   if (!attached_tabstrip_->touch_layout_.get()) {
701     double unselected, selected;
702     attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
703     double ratio = unselected / Tab::GetStandardSize().width();
704     threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
705   }
706   // else case: touch tabs never shrink.
707
708   std::vector<Tab*> tabs(drag_data_.size());
709   for (size_t i = 0; i < drag_data_.size(); ++i)
710     tabs[i] = drag_data_[i].attached_tab;
711
712   bool did_layout = false;
713   // Update the model, moving the WebContents from one index to another. Do this
714   // only if we have moved a minimum distance since the last reorder (to prevent
715   // jitter) or if this the first move and the tabs are not consecutive.
716   if ((abs(point_in_screen.x() - last_move_screen_loc_) > threshold ||
717         (initial_move_ && !AreTabsConsecutive()))) {
718     TabStripModel* attached_model = GetModel(attached_tabstrip_);
719     int to_index = GetInsertionIndexForDraggedBounds(
720         GetDraggedViewTabStripBounds(dragged_view_point));
721     bool do_move = true;
722     // While dragging within a tabstrip the expectation is the insertion index
723     // is based on the left edge of the tabs being dragged. OTOH when dragging
724     // into a new tabstrip (attaching) the expectation is the insertion index is
725     // based on the cursor. This proves problematic as insertion may change the
726     // size of the tabs, resulting in the index calculated before the insert
727     // differing from the index calculated after the insert. To alleviate this
728     // the index is chosen before insertion, and subsequently a new index is
729     // only used once the mouse moves enough such that the index changes based
730     // on the direction the mouse moved relative to |attach_x_| (smaller
731     // x-coordinate should yield a smaller index or larger x-coordinate yields a
732     // larger index).
733     if (attach_index_ != -1) {
734       gfx::Point tab_strip_point(point_in_screen);
735       views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
736       const int new_x =
737           attached_tabstrip_->GetMirroredXInView(tab_strip_point.x());
738       if (new_x < attach_x_)
739         to_index = std::min(to_index, attach_index_);
740       else
741         to_index = std::max(to_index, attach_index_);
742       if (to_index != attach_index_)
743         attach_index_ = -1;  // Once a valid move is detected, don't constrain.
744       else
745         do_move = false;
746     }
747     if (do_move) {
748       WebContents* last_contents = drag_data_[drag_data_.size() - 1].contents;
749       int index_of_last_item =
750           attached_model->GetIndexOfWebContents(last_contents);
751       if (initial_move_) {
752         // TabStrip determines if the tabs needs to be animated based on model
753         // position. This means we need to invoke LayoutDraggedTabsAt before
754         // changing the model.
755         attached_tabstrip_->LayoutDraggedTabsAt(
756             tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
757             initial_move_);
758         did_layout = true;
759       }
760       attached_model->MoveSelectedTabsTo(to_index);
761
762       // Move may do nothing in certain situations (such as when dragging pinned
763       // tabs). Make sure the tabstrip actually changed before updating
764       // last_move_screen_loc_.
765       if (index_of_last_item !=
766           attached_model->GetIndexOfWebContents(last_contents)) {
767         last_move_screen_loc_ = point_in_screen.x();
768       }
769     }
770   }
771
772   if (!did_layout) {
773     attached_tabstrip_->LayoutDraggedTabsAt(
774         tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
775         initial_move_);
776   }
777
778   StartMoveStackedTimerIfNecessary(point_in_screen, kMoveAttachedInitialDelay);
779
780   initial_move_ = false;
781 }
782
783 void TabDragController::StartMoveStackedTimerIfNecessary(
784     const gfx::Point& point_in_screen,
785     int delay_ms) {
786   DCHECK(attached_tabstrip_);
787
788   StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
789   if (!touch_layout)
790     return;
791
792   gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
793   gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
794   int index = touch_layout->active_index();
795   if (ShouldDragToNextStackedTab(bounds, index)) {
796     static_cast<base::Timer*>(&move_stacked_timer_)->Start(
797         FROM_HERE,
798         base::TimeDelta::FromMilliseconds(delay_ms),
799         base::Bind(&TabDragController::MoveAttachedToNextStackedIndex,
800                    base::Unretained(this), point_in_screen));
801   } else if (ShouldDragToPreviousStackedTab(bounds, index)) {
802     static_cast<base::Timer*>(&move_stacked_timer_)->Start(
803         FROM_HERE,
804         base::TimeDelta::FromMilliseconds(delay_ms),
805         base::Bind(&TabDragController::MoveAttachedToPreviousStackedIndex,
806                    base::Unretained(this), point_in_screen));
807   }
808 }
809
810 TabDragController::DetachPosition TabDragController::GetDetachPosition(
811     const gfx::Point& point_in_screen) {
812   DCHECK(attached_tabstrip_);
813   gfx::Point attached_point(point_in_screen);
814   views::View::ConvertPointFromScreen(attached_tabstrip_, &attached_point);
815   if (attached_point.x() < 0)
816     return DETACH_BEFORE;
817   if (attached_point.x() >= attached_tabstrip_->width())
818     return DETACH_AFTER;
819   return DETACH_ABOVE_OR_BELOW;
820 }
821
822 TabStrip* TabDragController::GetTargetTabStripForPoint(
823     const gfx::Point& point_in_screen) {
824   TRACE_EVENT1("views", "TabDragController::GetTargetTabStripForPoint",
825                "point_in_screen", point_in_screen.ToString());
826
827   if (move_only() && attached_tabstrip_) {
828     // move_only() is intended for touch, in which case we only want to detach
829     // if the touch point moves significantly in the vertical distance.
830     gfx::Rect tabstrip_bounds = GetViewScreenBounds(attached_tabstrip_);
831     if (DoesRectContainVerticalPointExpanded(tabstrip_bounds,
832                                              kTouchVerticalDetachMagnetism,
833                                              point_in_screen.y()))
834       return attached_tabstrip_;
835   }
836   gfx::NativeWindow local_window =
837       GetLocalProcessWindow(point_in_screen, is_dragging_window_);
838   // Do not allow dragging into a window with a modal dialog, it causes a weird
839   // behavior.  See crbug.com/336691
840   if (!GetModalTransient(local_window)) {
841     TabStrip* tab_strip = GetTabStripForWindow(local_window);
842     if (tab_strip && DoesTabStripContain(tab_strip, point_in_screen))
843       return tab_strip;
844   }
845
846   return is_dragging_window_ ? attached_tabstrip_ : NULL;
847 }
848
849 TabStrip* TabDragController::GetTabStripForWindow(gfx::NativeWindow window) {
850   if (!window)
851     return NULL;
852   BrowserView* browser_view =
853       BrowserView::GetBrowserViewForNativeWindow(window);
854   // We don't allow drops on windows that don't have tabstrips.
855   if (!browser_view ||
856       !browser_view->browser()->SupportsWindowFeature(
857           Browser::FEATURE_TABSTRIP))
858     return NULL;
859
860   TabStrip* other_tabstrip = browser_view->tabstrip();
861   TabStrip* tab_strip =
862       attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
863   DCHECK(tab_strip);
864
865   return other_tabstrip->controller()->IsCompatibleWith(tab_strip) ?
866       other_tabstrip : NULL;
867 }
868
869 bool TabDragController::DoesTabStripContain(
870     TabStrip* tabstrip,
871     const gfx::Point& point_in_screen) const {
872   // Make sure the specified screen point is actually within the bounds of the
873   // specified tabstrip...
874   gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
875   return point_in_screen.x() < tabstrip_bounds.right() &&
876       point_in_screen.x() >= tabstrip_bounds.x() &&
877       DoesRectContainVerticalPointExpanded(tabstrip_bounds,
878                                            kVerticalDetachMagnetism,
879                                            point_in_screen.y());
880 }
881
882 void TabDragController::Attach(TabStrip* attached_tabstrip,
883                                const gfx::Point& point_in_screen) {
884   TRACE_EVENT1("views", "TabDragController::Attach",
885                "point_in_screen", point_in_screen.ToString());
886
887   DCHECK(!attached_tabstrip_);  // We should already have detached by the time
888                                 // we get here.
889
890   attached_tabstrip_ = attached_tabstrip;
891
892   std::vector<Tab*> tabs =
893       GetTabsMatchingDraggedContents(attached_tabstrip_);
894
895   if (tabs.empty()) {
896     // Transitioning from detached to attached to a new tabstrip. Add tabs to
897     // the new model.
898
899     selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
900
901     // Inserting counts as a move. We don't want the tabs to jitter when the
902     // user moves the tab immediately after attaching it.
903     last_move_screen_loc_ = point_in_screen.x();
904
905     // Figure out where to insert the tab based on the bounds of the dragged
906     // representation and the ideal bounds of the other Tabs already in the
907     // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
908     // changing due to animation).
909     gfx::Point tab_strip_point(point_in_screen);
910     views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
911     tab_strip_point.set_x(
912         attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
913     tab_strip_point.Offset(0, -mouse_offset_.y());
914     int index = GetInsertionIndexForDraggedBounds(
915         GetDraggedViewTabStripBounds(tab_strip_point));
916     attach_index_ = index;
917     attach_x_ = tab_strip_point.x();
918     base::AutoReset<bool> setter(&is_mutating_, true);
919     for (size_t i = 0; i < drag_data_.size(); ++i) {
920       int add_types = TabStripModel::ADD_NONE;
921       if (attached_tabstrip_->touch_layout_.get()) {
922         // StackedTabStripLayout positions relative to the active tab, if we
923         // don't add the tab as active things bounce around.
924         DCHECK_EQ(1u, drag_data_.size());
925         add_types |= TabStripModel::ADD_ACTIVE;
926       }
927       if (drag_data_[i].pinned)
928         add_types |= TabStripModel::ADD_PINNED;
929       GetModel(attached_tabstrip_)->InsertWebContentsAt(
930           index + i, drag_data_[i].contents, add_types);
931     }
932
933     tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
934   }
935   DCHECK_EQ(tabs.size(), drag_data_.size());
936   for (size_t i = 0; i < drag_data_.size(); ++i)
937     drag_data_[i].attached_tab = tabs[i];
938
939   attached_tabstrip_->StartedDraggingTabs(tabs);
940
941   ResetSelection(GetModel(attached_tabstrip_));
942
943   // The size of the dragged tab may have changed. Adjust the x offset so that
944   // ratio of mouse_offset_ to original width is maintained.
945   std::vector<Tab*> tabs_to_source(tabs);
946   tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
947                        tabs_to_source.end());
948   int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
949       tabs[source_tab_index_]->width() +
950       static_cast<int>(offset_to_width_ratio_ *
951                        tabs[source_tab_index_]->width());
952   mouse_offset_.set_x(new_x);
953
954   // Transfer ownership of us to the new tabstrip as well as making sure the
955   // window has capture. This is important so that if activation changes the
956   // drag isn't prematurely canceled.
957   attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
958   attached_tabstrip_->OwnDragController(this);
959 }
960
961 void TabDragController::Detach(ReleaseCapture release_capture) {
962   TRACE_EVENT1("views", "TabDragController::Detach",
963                "release_capture", release_capture);
964
965   attach_index_ = -1;
966
967   // When the user detaches we assume they want to reorder.
968   move_behavior_ = REORDER;
969
970   // Release ownership of the drag controller and mouse capture. When we
971   // reattach ownership is transfered.
972   attached_tabstrip_->ReleaseDragController();
973   if (release_capture == RELEASE_CAPTURE)
974     attached_tabstrip_->GetWidget()->ReleaseCapture();
975
976   mouse_move_direction_ = kMovedMouseLeft | kMovedMouseRight;
977
978   std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
979   TabStripModel* attached_model = GetModel(attached_tabstrip_);
980   std::vector<TabRendererData> tab_data;
981   for (size_t i = 0; i < drag_data_.size(); ++i) {
982     tab_data.push_back(drag_data_[i].attached_tab->data());
983     int index = attached_model->GetIndexOfWebContents(drag_data_[i].contents);
984     DCHECK_NE(-1, index);
985
986     // Hide the tab so that the user doesn't see it animate closed.
987     drag_data_[i].attached_tab->SetVisible(false);
988     drag_data_[i].attached_tab->set_detached();
989
990     attached_model->DetachWebContentsAt(index);
991
992     // Detaching may end up deleting the tab, drop references to it.
993     drag_data_[i].attached_tab = NULL;
994   }
995
996   // If we've removed the last Tab from the TabStrip, hide the frame now.
997   if (!attached_model->empty()) {
998     if (!selection_model_before_attach_.empty() &&
999         selection_model_before_attach_.active() >= 0 &&
1000         selection_model_before_attach_.active() < attached_model->count()) {
1001       // Restore the selection.
1002       attached_model->SetSelectionFromModel(selection_model_before_attach_);
1003     } else if (attached_tabstrip_ == source_tabstrip_ &&
1004                !initial_selection_model_.empty()) {
1005       RestoreInitialSelection();
1006     }
1007   }
1008
1009   attached_tabstrip_->DraggedTabsDetached();
1010   attached_tabstrip_ = NULL;
1011 }
1012
1013 void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
1014     const gfx::Point& point_in_screen) {
1015   if (GetModel(attached_tabstrip_)->count() ==
1016       static_cast<int>(drag_data_.size())) {
1017     // All the tabs in a browser are being dragged but all the tabs weren't
1018     // initially being dragged. For this to happen the user would have to
1019     // start dragging a set of tabs, the other tabs close, then detach.
1020     RunMoveLoop(GetWindowOffset(point_in_screen));
1021     return;
1022   }
1023
1024   const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
1025   std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
1026   OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
1027
1028   gfx::Vector2d drag_offset;
1029   Browser* browser = CreateBrowserForDrag(
1030       attached_tabstrip_, point_in_screen, &drag_offset, &drag_bounds);
1031 #if defined(OS_WIN)
1032   gfx::NativeView attached_native_view =
1033     attached_tabstrip_->GetWidget()->GetNativeView();
1034 #endif
1035   Detach(can_release_capture_ ? RELEASE_CAPTURE : DONT_RELEASE_CAPTURE);
1036   BrowserView* dragged_browser_view =
1037       BrowserView::GetBrowserViewForBrowser(browser);
1038   views::Widget* dragged_widget = dragged_browser_view->GetWidget();
1039 #if defined(OS_WIN)
1040     // The Gesture recognizer does not work well currently when capture changes
1041     // while a touch gesture is in progress. So we need to manually transfer
1042     // gesture sequence and the GR's touch events queue to the new window. This
1043     // should really be done somewhere in capture change code and or inside the
1044     // GR. But we currently do not have a consistent way for doing it that would
1045     // work in all cases. Hence this hack.
1046     ui::GestureRecognizer::Get()->TransferEventsTo(
1047         attached_native_view,
1048         dragged_widget->GetNativeView());
1049 #endif
1050   dragged_widget->SetVisibilityChangedAnimationsEnabled(false);
1051   Attach(dragged_browser_view->tabstrip(), gfx::Point());
1052   AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
1053                                    point_in_screen,
1054                                    &drag_bounds);
1055   WindowPositionManagedUpdater updater;
1056   dragged_widget->AddObserver(&updater);
1057   browser->window()->Show();
1058   dragged_widget->RemoveObserver(&updater);
1059   dragged_widget->SetVisibilityChangedAnimationsEnabled(true);
1060   // Activate may trigger a focus loss, destroying us.
1061   {
1062     base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
1063     browser->window()->Activate();
1064     if (!ref)
1065       return;
1066   }
1067   RunMoveLoop(drag_offset);
1068 }
1069
1070 void TabDragController::RunMoveLoop(const gfx::Vector2d& drag_offset) {
1071   // If the user drags the whole window we'll assume they are going to attach to
1072   // another window and therefore want to reorder.
1073   move_behavior_ = REORDER;
1074
1075   move_loop_widget_ = GetAttachedBrowserWidget();
1076   DCHECK(move_loop_widget_);
1077   move_loop_widget_->AddObserver(this);
1078   is_dragging_window_ = true;
1079   base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
1080   if (can_release_capture_) {
1081     // Running the move loop releases mouse capture, which triggers destroying
1082     // the drag loop. Release mouse capture now while the DragController is not
1083     // owned by the TabStrip.
1084     attached_tabstrip_->ReleaseDragController();
1085     attached_tabstrip_->GetWidget()->ReleaseCapture();
1086     attached_tabstrip_->OwnDragController(this);
1087   }
1088   const views::Widget::MoveLoopSource move_loop_source =
1089       event_source_ == EVENT_SOURCE_MOUSE ?
1090       views::Widget::MOVE_LOOP_SOURCE_MOUSE :
1091       views::Widget::MOVE_LOOP_SOURCE_TOUCH;
1092   const views::Widget::MoveLoopEscapeBehavior escape_behavior =
1093       is_dragging_new_browser_ ?
1094           views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE :
1095           views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
1096   views::Widget::MoveLoopResult result =
1097       move_loop_widget_->RunMoveLoop(
1098           drag_offset, move_loop_source, escape_behavior);
1099   content::NotificationService::current()->Notify(
1100       chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
1101       content::NotificationService::AllBrowserContextsAndSources(),
1102       content::NotificationService::NoDetails());
1103
1104   if (!ref)
1105     return;
1106   if (move_loop_widget_) {
1107     move_loop_widget_->RemoveObserver(this);
1108     move_loop_widget_ = NULL;
1109   }
1110   is_dragging_window_ = false;
1111   waiting_for_run_loop_to_exit_ = false;
1112   if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
1113     end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
1114     if (tab_strip_to_attach_to_after_exit_) {
1115       gfx::Point point_in_screen(GetCursorScreenPoint());
1116       Detach(DONT_RELEASE_CAPTURE);
1117       Attach(tab_strip_to_attach_to_after_exit_, point_in_screen);
1118       // Move the tabs into position.
1119       MoveAttached(point_in_screen);
1120       attached_tabstrip_->GetWidget()->Activate();
1121       // Activate may trigger a focus loss, destroying us.
1122       if (!ref)
1123         return;
1124       tab_strip_to_attach_to_after_exit_ = NULL;
1125     }
1126     DCHECK(attached_tabstrip_);
1127     attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
1128   } else if (active_) {
1129     EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ?
1130             END_DRAG_CANCEL : END_DRAG_COMPLETE);
1131   }
1132 }
1133
1134 int TabDragController::GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
1135                                              int start) const {
1136   const int last_tab = attached_tabstrip_->tab_count() - 1;
1137   // Make the actual "drag insertion point" be just after the leading edge of
1138   // the first dragged tab.  This is closer to where the user thinks of the tab
1139   // as "starting" than just dragged_bounds.x(), especially with narrow tabs.
1140   const int dragged_x = dragged_bounds.x() + Tab::leading_width_for_drag();
1141   if (start < 0 || start > last_tab ||
1142       dragged_x < attached_tabstrip_->ideal_bounds(start).x())
1143     return -1;
1144
1145   for (int i = start; i <= last_tab; ++i) {
1146     const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
1147     if (dragged_x < (ideal_bounds.x() + (ideal_bounds.width() / 2)))
1148       return i;
1149   }
1150
1151   return (dragged_x < attached_tabstrip_->ideal_bounds(last_tab).right()) ?
1152       (last_tab + 1) : -1;
1153 }
1154
1155 int TabDragController::GetInsertionIndexFromReversed(
1156     const gfx::Rect& dragged_bounds,
1157     int start) const {
1158   // Make the actual "drag insertion point" be just after the leading edge of
1159   // the first dragged tab.  This is closer to where the user thinks of the tab
1160   // as "starting" than just dragged_bounds.x(), especially with narrow tabs.
1161   const int dragged_x = dragged_bounds.x() + Tab::leading_width_for_drag();
1162   if (start < 0 || start >= attached_tabstrip_->tab_count() ||
1163       dragged_x >= attached_tabstrip_->ideal_bounds(start).right())
1164     return -1;
1165
1166   for (int i = start; i >= 0; --i) {
1167     const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
1168     if (dragged_x >= (ideal_bounds.x() + (ideal_bounds.width() / 2)))
1169       return i + 1;
1170   }
1171
1172   return (dragged_x >= attached_tabstrip_->ideal_bounds(0).x()) ? 0 : -1;
1173 }
1174
1175 int TabDragController::GetInsertionIndexForDraggedBounds(
1176     const gfx::Rect& dragged_bounds) const {
1177   // If the strip has no tabs, the only position to insert at is 0.
1178   const int tab_count = attached_tabstrip_->tab_count();
1179   if (!tab_count)
1180     return 0;
1181
1182   int index = -1;
1183   if (attached_tabstrip_->touch_layout_.get()) {
1184     index = GetInsertionIndexForDraggedBoundsStacked(dragged_bounds);
1185     if (index != -1) {
1186       // Only move the tab to the left/right if the user actually moved the
1187       // mouse that way. This is necessary as tabs with stacked tabs
1188       // before/after them have multiple drag positions.
1189       int active_index = attached_tabstrip_->touch_layout_->active_index();
1190       if ((index < active_index &&
1191            (mouse_move_direction_ & kMovedMouseLeft) == 0) ||
1192           (index > active_index &&
1193            (mouse_move_direction_ & kMovedMouseRight) == 0)) {
1194         index = active_index;
1195       }
1196     }
1197   } else {
1198     index = GetInsertionIndexFrom(dragged_bounds, 0);
1199   }
1200   if (index == -1) {
1201     const int last_tab_right =
1202         attached_tabstrip_->ideal_bounds(tab_count - 1).right();
1203     index = (dragged_bounds.right() > last_tab_right) ? tab_count : 0;
1204   }
1205
1206   const Tab* last_visible_tab = attached_tabstrip_->GetLastVisibleTab();
1207   int last_insertion_point = last_visible_tab ?
1208       (attached_tabstrip_->GetModelIndexOfTab(last_visible_tab) + 1) : 0;
1209   if (drag_data_[0].attached_tab) {
1210     // We're not in the process of attaching, so clamp the insertion point to
1211     // keep it within the visible region.
1212     last_insertion_point = std::max(
1213         0, last_insertion_point - static_cast<int>(drag_data_.size()));
1214   }
1215
1216   // Ensure the first dragged tab always stays in the visible index range.
1217   return std::min(index, last_insertion_point);
1218 }
1219
1220 bool TabDragController::ShouldDragToNextStackedTab(
1221     const gfx::Rect& dragged_bounds,
1222     int index) const {
1223   if (index + 1 >= attached_tabstrip_->tab_count() ||
1224       !attached_tabstrip_->touch_layout_->IsStacked(index + 1) ||
1225       (mouse_move_direction_ & kMovedMouseRight) == 0)
1226     return false;
1227
1228   int active_x = attached_tabstrip_->ideal_bounds(index).x();
1229   int next_x = attached_tabstrip_->ideal_bounds(index + 1).x();
1230   int mid_x = std::min(next_x - kStackedDistance,
1231                        active_x + (next_x - active_x) / 4);
1232   // TODO(pkasting): Should this add Tab::leading_width_for_drag() as
1233   // GetInsertionIndexFrom() does?
1234   return dragged_bounds.x() >= mid_x;
1235 }
1236
1237 bool TabDragController::ShouldDragToPreviousStackedTab(
1238     const gfx::Rect& dragged_bounds,
1239     int index) const {
1240   if (index - 1 < attached_tabstrip_->GetMiniTabCount() ||
1241       !attached_tabstrip_->touch_layout_->IsStacked(index - 1) ||
1242       (mouse_move_direction_ & kMovedMouseLeft) == 0)
1243     return false;
1244
1245   int active_x = attached_tabstrip_->ideal_bounds(index).x();
1246   int previous_x = attached_tabstrip_->ideal_bounds(index - 1).x();
1247   int mid_x = std::max(previous_x + kStackedDistance,
1248                        active_x - (active_x - previous_x) / 4);
1249   // TODO(pkasting): Should this add Tab::leading_width_for_drag() as
1250   // GetInsertionIndexFrom() does?
1251   return dragged_bounds.x() <= mid_x;
1252 }
1253
1254 int TabDragController::GetInsertionIndexForDraggedBoundsStacked(
1255     const gfx::Rect& dragged_bounds) const {
1256   StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
1257   int active_index = touch_layout->active_index();
1258   // Search from the active index to the front of the tabstrip. Do this as tabs
1259   // overlap each other from the active index.
1260   int index = GetInsertionIndexFromReversed(dragged_bounds, active_index);
1261   if (index != active_index)
1262     return index;
1263   if (index == -1)
1264     return GetInsertionIndexFrom(dragged_bounds, active_index + 1);
1265
1266   // The position to drag to corresponds to the active tab. If the next/previous
1267   // tab is stacked, then shorten the distance used to determine insertion
1268   // bounds. We do this as GetInsertionIndexFrom() uses the bounds of the
1269   // tabs. When tabs are stacked the next/previous tab is on top of the tab.
1270   if (active_index + 1 < attached_tabstrip_->tab_count() &&
1271       touch_layout->IsStacked(active_index + 1)) {
1272     index = GetInsertionIndexFrom(dragged_bounds, active_index + 1);
1273     if (index == -1 && ShouldDragToNextStackedTab(dragged_bounds, active_index))
1274       index = active_index + 1;
1275     else if (index == -1)
1276       index = active_index;
1277   } else if (ShouldDragToPreviousStackedTab(dragged_bounds, active_index)) {
1278     index = active_index - 1;
1279   }
1280   return index;
1281 }
1282
1283 gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
1284     const gfx::Point& tab_strip_point) {
1285   // attached_tab is NULL when inserting into a new tabstrip.
1286   if (source_tab_drag_data()->attached_tab) {
1287     return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
1288                      source_tab_drag_data()->attached_tab->width(),
1289                      source_tab_drag_data()->attached_tab->height());
1290   }
1291
1292   double sel_width, unselected_width;
1293   attached_tabstrip_->GetCurrentTabWidths(&sel_width, &unselected_width);
1294   return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
1295                    static_cast<int>(sel_width),
1296                    Tab::GetStandardSize().height());
1297 }
1298
1299 gfx::Point TabDragController::GetAttachedDragPoint(
1300     const gfx::Point& point_in_screen) {
1301   DCHECK(attached_tabstrip_);  // The tab must be attached.
1302
1303   gfx::Point tab_loc(point_in_screen);
1304   views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_loc);
1305   const int x =
1306       attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
1307
1308   // TODO: consider caching this.
1309   std::vector<Tab*> attached_tabs;
1310   for (size_t i = 0; i < drag_data_.size(); ++i)
1311     attached_tabs.push_back(drag_data_[i].attached_tab);
1312   const int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
1313   const int max_x = attached_tabstrip_->width() - size;
1314   return gfx::Point(std::min(std::max(x, 0), max_x), 0);
1315 }
1316
1317 std::vector<Tab*> TabDragController::GetTabsMatchingDraggedContents(
1318     TabStrip* tabstrip) {
1319   TabStripModel* model = GetModel(attached_tabstrip_);
1320   std::vector<Tab*> tabs;
1321   for (size_t i = 0; i < drag_data_.size(); ++i) {
1322     int model_index = model->GetIndexOfWebContents(drag_data_[i].contents);
1323     if (model_index == TabStripModel::kNoTab)
1324       return std::vector<Tab*>();
1325     tabs.push_back(tabstrip->tab_at(model_index));
1326   }
1327   return tabs;
1328 }
1329
1330 std::vector<gfx::Rect> TabDragController::CalculateBoundsForDraggedTabs() {
1331   std::vector<gfx::Rect> drag_bounds;
1332   std::vector<Tab*> attached_tabs;
1333   for (size_t i = 0; i < drag_data_.size(); ++i)
1334     attached_tabs.push_back(drag_data_[i].attached_tab);
1335   attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
1336                                                     &drag_bounds);
1337   return drag_bounds;
1338 }
1339
1340 void TabDragController::EndDragImpl(EndDragType type) {
1341   DCHECK(active_);
1342   active_ = false;
1343
1344   bring_to_front_timer_.Stop();
1345   move_stacked_timer_.Stop();
1346
1347   if (is_dragging_window_) {
1348     waiting_for_run_loop_to_exit_ = true;
1349
1350     if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1)) {
1351       SetWindowPositionManaged(GetAttachedBrowserWidget()->GetNativeWindow(),
1352                                true);
1353     }
1354
1355     // End the nested drag loop.
1356     GetAttachedBrowserWidget()->EndMoveLoop();
1357   }
1358
1359   if (type != TAB_DESTROYED) {
1360     // We only finish up the drag if we were actually dragging. If start_drag_
1361     // is false, the user just clicked and released and didn't move the mouse
1362     // enough to trigger a drag.
1363     if (started_drag_) {
1364       RestoreFocus();
1365       if (type == CANCELED)
1366         RevertDrag();
1367       else
1368         CompleteDrag();
1369     }
1370   } else if (drag_data_.size() > 1) {
1371     initial_selection_model_.Clear();
1372     if (started_drag_)
1373       RevertDrag();
1374   }  // else case the only tab we were dragging was deleted. Nothing to do.
1375
1376   // Clear out drag data so we don't attempt to do anything with it.
1377   drag_data_.clear();
1378
1379   TabStrip* owning_tabstrip = attached_tabstrip_ ?
1380       attached_tabstrip_ : source_tabstrip_;
1381   owning_tabstrip->DestroyDragController();
1382 }
1383
1384 void TabDragController::RevertDrag() {
1385   std::vector<Tab*> tabs;
1386   for (size_t i = 0; i < drag_data_.size(); ++i) {
1387     if (drag_data_[i].contents) {
1388       // Contents is NULL if a tab was destroyed while the drag was under way.
1389       tabs.push_back(drag_data_[i].attached_tab);
1390       RevertDragAt(i);
1391     }
1392   }
1393
1394   if (attached_tabstrip_) {
1395     if (did_restore_window_)
1396       MaximizeAttachedWindow();
1397     if (attached_tabstrip_ == source_tabstrip_) {
1398       source_tabstrip_->StoppedDraggingTabs(
1399           tabs, initial_tab_positions_, move_behavior_ == MOVE_VISIBILE_TABS,
1400           false);
1401     } else {
1402       attached_tabstrip_->DraggedTabsDetached();
1403     }
1404   }
1405
1406   if (initial_selection_model_.empty())
1407     ResetSelection(GetModel(source_tabstrip_));
1408   else
1409     GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
1410
1411   if (source_tabstrip_)
1412     source_tabstrip_->GetWidget()->Activate();
1413 }
1414
1415 void TabDragController::ResetSelection(TabStripModel* model) {
1416   DCHECK(model);
1417   ui::ListSelectionModel selection_model;
1418   bool has_one_valid_tab = false;
1419   for (size_t i = 0; i < drag_data_.size(); ++i) {
1420     // |contents| is NULL if a tab was deleted out from under us.
1421     if (drag_data_[i].contents) {
1422       int index = model->GetIndexOfWebContents(drag_data_[i].contents);
1423       DCHECK_NE(-1, index);
1424       selection_model.AddIndexToSelection(index);
1425       if (!has_one_valid_tab || i == source_tab_index_) {
1426         // Reset the active/lead to the first tab. If the source tab is still
1427         // valid we'll reset these again later on.
1428         selection_model.set_active(index);
1429         selection_model.set_anchor(index);
1430         has_one_valid_tab = true;
1431       }
1432     }
1433   }
1434   if (!has_one_valid_tab)
1435     return;
1436
1437   model->SetSelectionFromModel(selection_model);
1438 }
1439
1440 void TabDragController::RestoreInitialSelection() {
1441   // First time detaching from the source tabstrip. Reset selection model to
1442   // initial_selection_model_. Before resetting though we have to remove all
1443   // the tabs from initial_selection_model_ as it was created with the tabs
1444   // still there.
1445   ui::ListSelectionModel selection_model;
1446   selection_model.Copy(initial_selection_model_);
1447   for (DragData::const_reverse_iterator i(drag_data_.rbegin());
1448        i != drag_data_.rend(); ++i) {
1449     selection_model.DecrementFrom(i->source_model_index);
1450   }
1451   // We may have cleared out the selection model. Only reset it if it
1452   // contains something.
1453   if (selection_model.empty())
1454     return;
1455
1456   // The anchor/active may have been among the tabs that were dragged out. Force
1457   // the anchor/active to be valid.
1458   if (selection_model.anchor() == ui::ListSelectionModel::kUnselectedIndex)
1459     selection_model.set_anchor(selection_model.selected_indices()[0]);
1460   if (selection_model.active() == ui::ListSelectionModel::kUnselectedIndex)
1461     selection_model.set_active(selection_model.selected_indices()[0]);
1462   GetModel(source_tabstrip_)->SetSelectionFromModel(selection_model);
1463 }
1464
1465 void TabDragController::RevertDragAt(size_t drag_index) {
1466   DCHECK(started_drag_);
1467   DCHECK(source_tabstrip_);
1468
1469   base::AutoReset<bool> setter(&is_mutating_, true);
1470   TabDragData* data = &(drag_data_[drag_index]);
1471   if (attached_tabstrip_) {
1472     int index =
1473         GetModel(attached_tabstrip_)->GetIndexOfWebContents(data->contents);
1474     if (attached_tabstrip_ != source_tabstrip_) {
1475       // The Tab was inserted into another TabStrip. We need to put it back
1476       // into the original one.
1477       GetModel(attached_tabstrip_)->DetachWebContentsAt(index);
1478       // TODO(beng): (Cleanup) seems like we should use Attach() for this
1479       //             somehow.
1480       GetModel(source_tabstrip_)->InsertWebContentsAt(
1481           data->source_model_index, data->contents,
1482           (data->pinned ? TabStripModel::ADD_PINNED : 0));
1483     } else {
1484       // The Tab was moved within the TabStrip where the drag was initiated.
1485       // Move it back to the starting location.
1486       GetModel(source_tabstrip_)->MoveWebContentsAt(
1487           index, data->source_model_index, false);
1488     }
1489   } else {
1490     // The Tab was detached from the TabStrip where the drag began, and has not
1491     // been attached to any other TabStrip. We need to put it back into the
1492     // source TabStrip.
1493     GetModel(source_tabstrip_)->InsertWebContentsAt(
1494         data->source_model_index, data->contents,
1495         (data->pinned ? TabStripModel::ADD_PINNED : 0));
1496   }
1497 }
1498
1499 void TabDragController::CompleteDrag() {
1500   DCHECK(started_drag_);
1501
1502   if (attached_tabstrip_) {
1503     if (is_dragging_new_browser_ || did_restore_window_) {
1504       if (IsDockedOrSnapped(attached_tabstrip_)) {
1505         was_source_maximized_ = false;
1506         was_source_fullscreen_ = false;
1507       }
1508
1509       // If source window was maximized - maximize the new window as well.
1510       if (was_source_maximized_ || was_source_fullscreen_)
1511         MaximizeAttachedWindow();
1512     }
1513     attached_tabstrip_->StoppedDraggingTabs(
1514         GetTabsMatchingDraggedContents(attached_tabstrip_),
1515         initial_tab_positions_,
1516         move_behavior_ == MOVE_VISIBILE_TABS,
1517         true);
1518   } else {
1519     // Compel the model to construct a new window for the detached
1520     // WebContentses.
1521     views::Widget* widget = source_tabstrip_->GetWidget();
1522     gfx::Rect window_bounds(widget->GetRestoredBounds());
1523     window_bounds.set_origin(GetWindowCreatePoint(last_point_in_screen_));
1524
1525     base::AutoReset<bool> setter(&is_mutating_, true);
1526
1527     std::vector<TabStripModelDelegate::NewStripContents> contentses;
1528     for (size_t i = 0; i < drag_data_.size(); ++i) {
1529       TabStripModelDelegate::NewStripContents item;
1530       item.web_contents = drag_data_[i].contents;
1531       item.add_types = drag_data_[i].pinned ? TabStripModel::ADD_PINNED
1532                                             : TabStripModel::ADD_NONE;
1533       contentses.push_back(item);
1534     }
1535
1536     Browser* new_browser =
1537         GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
1538             contentses, window_bounds, widget->IsMaximized());
1539     ResetSelection(new_browser->tab_strip_model());
1540     new_browser->window()->Show();
1541   }
1542 }
1543
1544 void TabDragController::MaximizeAttachedWindow() {
1545   GetAttachedBrowserWidget()->Maximize();
1546 #if defined(USE_ASH)
1547   if (was_source_fullscreen_ &&
1548       host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
1549     // In fullscreen mode it is only possible to get here if the source
1550     // was in "immersive fullscreen" mode, so toggle it back on.
1551     ash::accelerators::ToggleFullscreen();
1552   }
1553 #endif
1554 }
1555
1556 gfx::Rect TabDragController::GetViewScreenBounds(
1557     views::View* view) const {
1558   gfx::Point view_topleft;
1559   views::View::ConvertPointToScreen(view, &view_topleft);
1560   gfx::Rect view_screen_bounds = view->GetLocalBounds();
1561   view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
1562   return view_screen_bounds;
1563 }
1564
1565 void TabDragController::BringWindowUnderPointToFront(
1566     const gfx::Point& point_in_screen) {
1567   gfx::NativeWindow window = GetLocalProcessWindow(point_in_screen, true);
1568
1569   // Only bring browser windows to front - only windows with a TabStrip can
1570   // be tab drag targets.
1571   if (!GetTabStripForWindow(window))
1572     return;
1573
1574   if (window) {
1575     views::Widget* widget_window = views::Widget::GetWidgetForNativeWindow(
1576         window);
1577     if (!widget_window)
1578       return;
1579
1580 #if defined(USE_ASH)
1581     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
1582       // TODO(varkha): The code below ensures that the phantom drag widget
1583       // is shown on top of browser windows. The code should be moved to ash/
1584       // and the phantom should be able to assert its top-most state on its own.
1585       // One strategy would be for DragWindowController to
1586       // be able to observe stacking changes to the phantom drag widget's
1587       // siblings in order to keep it on top. One way is to implement a
1588       // notification that is sent to a window parent's observers when a
1589       // stacking order is changed among the children of that same parent.
1590       // Note that OnWindowStackingChanged is sent only to the child that is the
1591       // argument of one of the Window::StackChildX calls and not to all its
1592       // siblings affected by the stacking change.
1593       aura::Window* browser_window = widget_window->GetNativeView();
1594       // Find a topmost non-popup window and stack the recipient browser above
1595       // it in order to avoid stacking the browser window on top of the phantom
1596       // drag widget created by DragWindowController in a second display.
1597       for (aura::Window::Windows::const_reverse_iterator it =
1598            browser_window->parent()->children().rbegin();
1599            it != browser_window->parent()->children().rend(); ++it) {
1600         // If the iteration reached the recipient browser window then it is
1601         // already topmost and it is safe to return with no stacking change.
1602         if (*it == browser_window)
1603           return;
1604         if ((*it)->type() != ui::wm::WINDOW_TYPE_POPUP) {
1605           widget_window->StackAbove(*it);
1606           break;
1607         }
1608       }
1609     } else {
1610       widget_window->StackAtTop();
1611     }
1612 #else
1613     widget_window->StackAtTop();
1614 #endif
1615
1616     // The previous call made the window appear on top of the dragged window,
1617     // move the dragged window to the front.
1618     if (is_dragging_window_)
1619       attached_tabstrip_->GetWidget()->StackAtTop();
1620   }
1621 }
1622
1623 TabStripModel* TabDragController::GetModel(
1624     TabStrip* tabstrip) const {
1625   return static_cast<BrowserTabStripController*>(tabstrip->controller())->
1626       model();
1627 }
1628
1629 views::Widget* TabDragController::GetAttachedBrowserWidget() {
1630   return attached_tabstrip_->GetWidget();
1631 }
1632
1633 bool TabDragController::AreTabsConsecutive() {
1634   for (size_t i = 1; i < drag_data_.size(); ++i) {
1635     if (drag_data_[i - 1].source_model_index + 1 !=
1636         drag_data_[i].source_model_index) {
1637       return false;
1638     }
1639   }
1640   return true;
1641 }
1642
1643 gfx::Rect TabDragController::CalculateDraggedBrowserBounds(
1644     TabStrip* source,
1645     const gfx::Point& point_in_screen,
1646     std::vector<gfx::Rect>* drag_bounds) {
1647   gfx::Point center(0, source->height() / 2);
1648   views::View::ConvertPointToWidget(source, &center);
1649   gfx::Rect new_bounds(source->GetWidget()->GetRestoredBounds());
1650   if (source->GetWidget()->IsMaximized()) {
1651     // If the restore bounds is really small, we don't want to honor it
1652     // (dragging a really small window looks wrong), instead make sure the new
1653     // window is at least 50% the size of the old.
1654     const gfx::Size max_size(
1655         source->GetWidget()->GetWindowBoundsInScreen().size());
1656     new_bounds.set_width(
1657         std::max(max_size.width() / 2, new_bounds.width()));
1658     new_bounds.set_height(
1659         std::max(max_size.height() / 2, new_bounds.height()));
1660   }
1661   new_bounds.set_y(point_in_screen.y() - center.y());
1662   switch (GetDetachPosition(point_in_screen)) {
1663     case DETACH_BEFORE:
1664       new_bounds.set_x(point_in_screen.x() - center.x());
1665       new_bounds.Offset(-mouse_offset_.x(), 0);
1666       break;
1667     case DETACH_AFTER: {
1668       gfx::Point right_edge(source->width(), 0);
1669       views::View::ConvertPointToWidget(source, &right_edge);
1670       new_bounds.set_x(point_in_screen.x() - right_edge.x());
1671       new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
1672       OffsetX(-(*drag_bounds)[0].x(), drag_bounds);
1673       break;
1674     }
1675     default:
1676       break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
1677   }
1678
1679   // To account for the extra vertical on restored windows that is absent on
1680   // maximized windows, add an additional vertical offset extracted from the tab
1681   // strip.
1682   if (source->GetWidget()->IsMaximized())
1683     new_bounds.Offset(0, -source->kNewTabButtonVerticalOffset);
1684   return new_bounds;
1685 }
1686
1687 void TabDragController::AdjustBrowserAndTabBoundsForDrag(
1688     int last_tabstrip_width,
1689     const gfx::Point& point_in_screen,
1690     std::vector<gfx::Rect>* drag_bounds) {
1691   attached_tabstrip_->InvalidateLayout();
1692   attached_tabstrip_->DoLayout();
1693   const int dragged_tabstrip_width = attached_tabstrip_->tab_area_width();
1694
1695   // If the new tabstrip is smaller than the old resize the tabs.
1696   if (dragged_tabstrip_width < last_tabstrip_width) {
1697     const float leading_ratio =
1698         drag_bounds->front().x() / static_cast<float>(last_tabstrip_width);
1699     *drag_bounds = CalculateBoundsForDraggedTabs();
1700
1701     if (drag_bounds->back().right() < dragged_tabstrip_width) {
1702       const int delta_x =
1703           std::min(static_cast<int>(leading_ratio * dragged_tabstrip_width),
1704                    dragged_tabstrip_width -
1705                        (drag_bounds->back().right() -
1706                         drag_bounds->front().x()));
1707       OffsetX(delta_x, drag_bounds);
1708     }
1709
1710     // Reposition the restored window such that the tab that was dragged remains
1711     // under the mouse cursor.
1712     gfx::Point offset(
1713         static_cast<int>((*drag_bounds)[source_tab_index_].width() *
1714                          offset_to_width_ratio_) +
1715         (*drag_bounds)[source_tab_index_].x(), 0);
1716     views::View::ConvertPointToWidget(attached_tabstrip_, &offset);
1717     gfx::Rect bounds = GetAttachedBrowserWidget()->GetWindowBoundsInScreen();
1718     bounds.set_x(point_in_screen.x() - offset.x());
1719     GetAttachedBrowserWidget()->SetBounds(bounds);
1720   }
1721   attached_tabstrip_->SetTabBoundsForDrag(*drag_bounds);
1722 }
1723
1724 Browser* TabDragController::CreateBrowserForDrag(
1725     TabStrip* source,
1726     const gfx::Point& point_in_screen,
1727     gfx::Vector2d* drag_offset,
1728     std::vector<gfx::Rect>* drag_bounds) {
1729   gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source,
1730                                                      point_in_screen,
1731                                                      drag_bounds));
1732   *drag_offset = point_in_screen - new_bounds.origin();
1733
1734   Profile* profile =
1735       Profile::FromBrowserContext(drag_data_[0].contents->GetBrowserContext());
1736   Browser::CreateParams create_params(Browser::TYPE_TABBED,
1737                                       profile,
1738                                       host_desktop_type_);
1739   create_params.initial_bounds = new_bounds;
1740   Browser* browser = new Browser(create_params);
1741   is_dragging_new_browser_ = true;
1742   SetWindowPositionManaged(browser->window()->GetNativeWindow(), false);
1743   // If the window is created maximized then the bounds we supplied are ignored.
1744   // We need to reset them again so they are honored.
1745   browser->window()->SetBounds(new_bounds);
1746
1747   return browser;
1748 }
1749
1750 gfx::Point TabDragController::GetCursorScreenPoint() {
1751 #if defined(USE_ASH)
1752   if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH &&
1753       event_source_ == EVENT_SOURCE_TOUCH &&
1754       aura::Env::GetInstance()->is_touch_down()) {
1755     views::Widget* widget = GetAttachedBrowserWidget();
1756     DCHECK(widget);
1757     aura::Window* widget_window = widget->GetNativeWindow();
1758     DCHECK(widget_window->GetRootWindow());
1759     gfx::PointF touch_point_f;
1760     bool got_touch_point = ui::GestureRecognizer::Get()->
1761         GetLastTouchPointForTarget(widget_window, &touch_point_f);
1762     // TODO(tdresser): Switch to using gfx::PointF. See crbug.com/337824.
1763     gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f);
1764     DCHECK(got_touch_point);
1765     wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point);
1766     return touch_point;
1767   }
1768 #endif
1769
1770   return screen_->GetCursorScreenPoint();
1771 }
1772
1773 gfx::Vector2d TabDragController::GetWindowOffset(
1774     const gfx::Point& point_in_screen) {
1775   TabStrip* owning_tabstrip = attached_tabstrip_ ?
1776       attached_tabstrip_ : source_tabstrip_;
1777   views::View* toplevel_view = owning_tabstrip->GetWidget()->GetContentsView();
1778
1779   gfx::Point point = point_in_screen;
1780   views::View::ConvertPointFromScreen(toplevel_view, &point);
1781   return point.OffsetFromOrigin();
1782 }
1783
1784 gfx::NativeWindow TabDragController::GetLocalProcessWindow(
1785     const gfx::Point& screen_point,
1786     bool exclude_dragged_view) {
1787   std::set<gfx::NativeWindow> exclude;
1788   if (exclude_dragged_view) {
1789     gfx::NativeWindow dragged_window =
1790         attached_tabstrip_->GetWidget()->GetNativeWindow();
1791     if (dragged_window)
1792       exclude.insert(dragged_window);
1793   }
1794 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1795   // Exclude windows which are pending deletion via Browser::TabStripEmpty().
1796   // These windows can be returned in the Linux Aura port because the browser
1797   // window which was used for dragging is not hidden once all of its tabs are
1798   // attached to another browser window in DragBrowserToNewTabStrip().
1799   // TODO(pkotwicz): Fix this properly (crbug.com/358482)
1800   BrowserList* browser_list = BrowserList::GetInstance(
1801       chrome::HOST_DESKTOP_TYPE_NATIVE);
1802   for (BrowserList::const_iterator it = browser_list->begin();
1803        it != browser_list->end(); ++it) {
1804     if ((*it)->tab_strip_model()->empty())
1805       exclude.insert((*it)->window()->GetNativeWindow());
1806   }
1807 #endif
1808   return GetLocalProcessWindowAtPoint(host_desktop_type_,
1809                                       screen_point,
1810                                       exclude);
1811
1812 }