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