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/command_line.h"
     13 #include "base/i18n/rtl.h"
     14 #include "chrome/browser/chrome_notification_types.h"
     15 #include "chrome/browser/extensions/extension_function_dispatcher.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
     18 #include "chrome/browser/ui/browser_window.h"
     19 #include "chrome/browser/ui/media_utils.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
     22 #include "chrome/browser/ui/views/frame/browser_view.h"
     23 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
     24 #include "chrome/browser/ui/views/tabs/dragged_tab_view.h"
     25 #include "chrome/browser/ui/views/tabs/native_view_photobooth.h"
     26 #include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
     27 #include "chrome/browser/ui/views/tabs/tab.h"
     28 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     29 #include "chrome/common/chrome_switches.h"
     30 #include "content/public/browser/invalidate_type.h"
     31 #include "content/public/browser/notification_details.h"
     32 #include "content/public/browser/notification_service.h"
     33 #include "content/public/browser/notification_source.h"
     34 #include "content/public/browser/notification_types.h"
     35 #include "content/public/browser/user_metrics.h"
     36 #include "content/public/browser/web_contents.h"
     37 #include "content/public/browser/web_contents_view.h"
     38 #include "grit/theme_resources.h"
     39 #include "ui/base/resource/resource_bundle.h"
     40 #include "ui/events/event_constants.h"
     41 #include "ui/events/event_utils.h"
     42 #include "ui/gfx/animation/animation.h"
     43 #include "ui/gfx/animation/animation_delegate.h"
     44 #include "ui/gfx/animation/slide_animation.h"
     45 #include "ui/gfx/canvas.h"
     46 #include "ui/gfx/image/image_skia.h"
     47 #include "ui/gfx/screen.h"
     48 #include "ui/views/focus/view_storage.h"
     49 #include "ui/views/widget/root_view.h"
     50 #include "ui/views/widget/widget.h"
     51 
     52 #if defined(USE_ASH)
     53 #include "ash/accelerators/accelerator_commands.h"
     54 #include "ash/wm/coordinate_conversion.h"
     55 #include "ash/wm/window_state.h"
     56 #include "ui/aura/env.h"
     57 #include "ui/aura/root_window.h"
     58 #include "ui/aura/window.h"
     59 #include "ui/events/gestures/gesture_recognizer.h"
     60 #endif
     61 
     62 #if defined(OS_WIN) && defined(USE_AURA)
     63 #include "ui/aura/window.h"
     64 #include "ui/events/gestures/gesture_recognizer.h"
     65 #endif
     66 
     67 using content::OpenURLParams;
     68 using content::UserMetricsAction;
     69 using content::WebContents;
     70 
     71 static const int kHorizontalMoveThreshold = 16;  // Pixels.
     72 
     73 // Distance from the next/previous stacked before before we consider the tab
     74 // close enough to trigger moving.
     75 static const int kStackedDistance = 36;
     76 
     77 // If non-null there is a drag underway.
     78 static TabDragController* instance_ = NULL;
     79 
     80 namespace {
     81 
     82 // Delay, in ms, during dragging before we bring a window to front.
     83 const int kBringToFrontDelay = 750;
     84 
     85 // Initial delay before moving tabs when the dragged tab is close to the edge of
     86 // the stacked tabs.
     87 const int kMoveAttachedInitialDelay = 600;
     88 
     89 // Delay for moving tabs after the initial delay has passed.
     90 const int kMoveAttachedSubsequentDelay = 300;
     91 
     92 // Radius of the rect drawn by DockView.
     93 const int kRoundedRectRadius = 4;
     94 
     95 // Spacing between tab icons when DockView is showing a docking location that
     96 // contains more than one tab.
     97 const int kTabSpacing = 4;
     98 
     99 // DockView is the view responsible for giving a visual indicator of where a
    100 // dock is going to occur.
    101 
    102 class DockView : public views::View {
    103  public:
    104   explicit DockView(DockInfo::Type type) : type_(type) {}
    105 
    106   virtual gfx::Size GetPreferredSize() OVERRIDE {
    107     return gfx::Size(DockInfo::popup_width(), DockInfo::popup_height());
    108   }
    109 
    110   virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE {
    111     // Fill the background rect.
    112     SkPaint paint;
    113     paint.setColor(SkColorSetRGB(108, 108, 108));
    114     paint.setStyle(SkPaint::kFill_Style);
    115     canvas->DrawRoundRect(GetLocalBounds(), kRoundedRectRadius, paint);
    116 
    117     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    118 
    119     gfx::ImageSkia* high_icon = rb.GetImageSkiaNamed(IDR_DOCK_HIGH);
    120     gfx::ImageSkia* wide_icon = rb.GetImageSkiaNamed(IDR_DOCK_WIDE);
    121 
    122     canvas->Save();
    123     bool rtl_ui = base::i18n::IsRTL();
    124     if (rtl_ui) {
    125       // Flip canvas to draw the mirrored tab images for RTL UI.
    126       canvas->Translate(gfx::Vector2d(width(), 0));
    127       canvas->Scale(-1, 1);
    128     }
    129     int x_of_active_tab = width() / 2 + kTabSpacing / 2;
    130     int x_of_inactive_tab = width() / 2 - high_icon->width() - kTabSpacing / 2;
    131     switch (type_) {
    132       case DockInfo::LEFT_OF_WINDOW:
    133       case DockInfo::LEFT_HALF:
    134         if (!rtl_ui)
    135           std::swap(x_of_active_tab, x_of_inactive_tab);
    136         canvas->DrawImageInt(*high_icon, x_of_active_tab,
    137                              (height() - high_icon->height()) / 2);
    138         if (type_ == DockInfo::LEFT_OF_WINDOW) {
    139           DrawImageWithAlpha(canvas, *high_icon, x_of_inactive_tab,
    140                              (height() - high_icon->height()) / 2);
    141         }
    142         break;
    143 
    144 
    145       case DockInfo::RIGHT_OF_WINDOW:
    146       case DockInfo::RIGHT_HALF:
    147         if (rtl_ui)
    148           std::swap(x_of_active_tab, x_of_inactive_tab);
    149         canvas->DrawImageInt(*high_icon, x_of_active_tab,
    150                              (height() - high_icon->height()) / 2);
    151         if (type_ == DockInfo::RIGHT_OF_WINDOW) {
    152          DrawImageWithAlpha(canvas, *high_icon, x_of_inactive_tab,
    153                            (height() - high_icon->height()) / 2);
    154         }
    155         break;
    156 
    157       case DockInfo::TOP_OF_WINDOW:
    158         canvas->DrawImageInt(*wide_icon, (width() - wide_icon->width()) / 2,
    159                              height() / 2 - high_icon->height());
    160         break;
    161 
    162       case DockInfo::MAXIMIZE: {
    163         gfx::ImageSkia* max_icon = rb.GetImageSkiaNamed(IDR_DOCK_MAX);
    164         canvas->DrawImageInt(*max_icon, (width() - max_icon->width()) / 2,
    165                              (height() - max_icon->height()) / 2);
    166         break;
    167       }
    168 
    169       case DockInfo::BOTTOM_HALF:
    170       case DockInfo::BOTTOM_OF_WINDOW:
    171         canvas->DrawImageInt(*wide_icon, (width() - wide_icon->width()) / 2,
    172                              height() / 2 + kTabSpacing / 2);
    173         if (type_ == DockInfo::BOTTOM_OF_WINDOW) {
    174           DrawImageWithAlpha(canvas, *wide_icon,
    175               (width() - wide_icon->width()) / 2,
    176               height() / 2 - kTabSpacing / 2 - wide_icon->height());
    177         }
    178         break;
    179 
    180       default:
    181         NOTREACHED();
    182         break;
    183     }
    184     canvas->Restore();
    185   }
    186 
    187  private:
    188   void DrawImageWithAlpha(gfx::Canvas* canvas, const gfx::ImageSkia& image,
    189                           int x, int y) {
    190     SkPaint paint;
    191     paint.setAlpha(128);
    192     canvas->DrawImageInt(image, x, y, paint);
    193   }
    194 
    195   DockInfo::Type type_;
    196 
    197   DISALLOW_COPY_AND_ASSIGN(DockView);
    198 };
    199 
    200 void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
    201 #if defined(USE_ASH)
    202   ash::wm::GetWindowState(window)->set_window_position_managed(value);
    203 #endif
    204 }
    205 
    206 // Returns true if |tab_strip| browser window is docked.
    207 bool IsDockedOrSnapped(const TabStrip* tab_strip) {
    208 #if defined(USE_ASH)
    209   DCHECK(tab_strip);
    210   ash::wm::WindowState* window_state =
    211       ash::wm::GetWindowState(tab_strip->GetWidget()->GetNativeWindow());
    212   return window_state->IsDocked() ||
    213       window_state->window_show_type() == ash::wm::SHOW_TYPE_LEFT_SNAPPED ||
    214       window_state->window_show_type() == ash::wm::SHOW_TYPE_RIGHT_SNAPPED;
    215 #endif
    216   return false;
    217 }
    218 
    219 // Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
    220 // of |bounds| is adjusted by |vertical_adjustment|.
    221 bool DoesRectContainVerticalPointExpanded(
    222     const gfx::Rect& bounds,
    223     int vertical_adjustment,
    224     int y) {
    225   int upper_threshold = bounds.bottom() + vertical_adjustment;
    226   int lower_threshold = bounds.y() - vertical_adjustment;
    227   return y >= lower_threshold && y <= upper_threshold;
    228 }
    229 
    230 // Adds |x_offset| to all the rectangles in |rects|.
    231 void OffsetX(int x_offset, std::vector<gfx::Rect>* rects) {
    232   if (x_offset == 0)
    233     return;
    234 
    235   for (size_t i = 0; i < rects->size(); ++i)
    236     (*rects)[i].set_x((*rects)[i].x() + x_offset);
    237 }
    238 
    239 // WidgetObserver implementation that resets the window position managed
    240 // property on Show.
    241 // We're forced to do this here since BrowserFrameAsh resets the 'window
    242 // position managed' property during a show and we need the property set to
    243 // false before WorkspaceLayoutManager sees the visibility change.
    244 class WindowPositionManagedUpdater : public views::WidgetObserver {
    245  public:
    246   virtual void OnWidgetVisibilityChanged(views::Widget* widget,
    247                                          bool visible) OVERRIDE {
    248     SetWindowPositionManaged(widget->GetNativeView(), false);
    249   }
    250 };
    251 
    252 }  // namespace
    253 
    254 ///////////////////////////////////////////////////////////////////////////////
    255 // DockDisplayer
    256 
    257 // DockDisplayer is responsible for giving the user a visual indication of a
    258 // possible dock position (as represented by DockInfo). DockDisplayer shows
    259 // a window with a DockView in it. Two animations are used that correspond to
    260 // the state of DockInfo::in_enable_area.
    261 class TabDragController::DockDisplayer : public gfx::AnimationDelegate {
    262  public:
    263   DockDisplayer(TabDragController* controller, const DockInfo& info)
    264       : controller_(controller),
    265         popup_(NULL),
    266         popup_view_(NULL),
    267         animation_(this),
    268         hidden_(false),
    269         in_enable_area_(info.in_enable_area()) {
    270     popup_ = new views::Widget;
    271     views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
    272     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    273     params.keep_on_top = true;
    274     params.bounds = info.GetPopupRect();
    275     popup_->Init(params);
    276     popup_->SetContentsView(new DockView(info.type()));
    277     popup_->SetOpacity(0x00);
    278     if (info.in_enable_area())
    279       animation_.Reset(1);
    280     else
    281       animation_.Show();
    282     popup_->Show();
    283     popup_view_ = popup_->GetNativeView();
    284   }
    285 
    286   virtual ~DockDisplayer() {
    287     if (controller_)
    288       controller_->DockDisplayerDestroyed(this);
    289   }
    290 
    291   // Updates the state based on |in_enable_area|.
    292   void UpdateInEnabledArea(bool in_enable_area) {
    293     if (in_enable_area != in_enable_area_) {
    294       in_enable_area_ = in_enable_area;
    295       UpdateLayeredAlpha();
    296     }
    297   }
    298 
    299   // Resets the reference to the hosting TabDragController. This is
    300   // invoked when the TabDragController is destroyed.
    301   void clear_controller() { controller_ = NULL; }
    302 
    303   // NativeView of the window we create.
    304   gfx::NativeView popup_view() { return popup_view_; }
    305 
    306   // Starts the hide animation. When the window is closed the
    307   // TabDragController is notified by way of the DockDisplayerDestroyed
    308   // method
    309   void Hide() {
    310     if (hidden_)
    311       return;
    312 
    313     if (!popup_) {
    314       delete this;
    315       return;
    316     }
    317     hidden_ = true;
    318     animation_.Hide();
    319   }
    320 
    321   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
    322     UpdateLayeredAlpha();
    323   }
    324 
    325   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
    326     if (!hidden_)
    327       return;
    328     popup_->Close();
    329     delete this;
    330   }
    331 
    332  private:
    333   void UpdateLayeredAlpha() {
    334     double scale = in_enable_area_ ? 1 : .5;
    335     popup_->SetOpacity(static_cast<unsigned char>(animation_.GetCurrentValue() *
    336         scale * 255.0));
    337   }
    338 
    339   // TabDragController that created us.
    340   TabDragController* controller_;
    341 
    342   // Window we're showing.
    343   views::Widget* popup_;
    344 
    345   // NativeView of |popup_|. We cache this to avoid the possibility of
    346   // invoking a method on popup_ after we close it.
    347   gfx::NativeView popup_view_;
    348 
    349   // Animation for when first made visible.
    350   gfx::SlideAnimation animation_;
    351 
    352   // Have we been hidden?
    353   bool hidden_;
    354 
    355   // Value of DockInfo::in_enable_area.
    356   bool in_enable_area_;
    357 };
    358 
    359 TabDragController::TabDragData::TabDragData()
    360     : contents(NULL),
    361       original_delegate(NULL),
    362       source_model_index(-1),
    363       attached_tab(NULL),
    364       pinned(false) {
    365 }
    366 
    367 TabDragController::TabDragData::~TabDragData() {
    368 }
    369 
    370 ///////////////////////////////////////////////////////////////////////////////
    371 // TabDragController, public:
    372 
    373 // static
    374 const int TabDragController::kTouchVerticalDetachMagnetism = 50;
    375 
    376 // static
    377 const int TabDragController::kVerticalDetachMagnetism = 15;
    378 
    379 TabDragController::TabDragController()
    380     : detach_into_browser_(ShouldDetachIntoNewBrowser()),
    381       event_source_(EVENT_SOURCE_MOUSE),
    382       source_tabstrip_(NULL),
    383       attached_tabstrip_(NULL),
    384       screen_(NULL),
    385       host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
    386       offset_to_width_ratio_(0),
    387       old_focused_view_id_(
    388           views::ViewStorage::GetInstance()->CreateStorageID()),
    389       last_move_screen_loc_(0),
    390       started_drag_(false),
    391       active_(true),
    392       source_tab_index_(std::numeric_limits<size_t>::max()),
    393       initial_move_(true),
    394       detach_behavior_(DETACHABLE),
    395       move_behavior_(REORDER),
    396       mouse_move_direction_(0),
    397       is_dragging_window_(false),
    398       is_dragging_new_browser_(false),
    399       was_source_maximized_(false),
    400       was_source_fullscreen_(false),
    401       end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
    402       waiting_for_run_loop_to_exit_(false),
    403       tab_strip_to_attach_to_after_exit_(NULL),
    404       move_loop_widget_(NULL),
    405       is_mutating_(false),
    406       attach_x_(-1),
    407       attach_index_(-1),
    408       weak_factory_(this) {
    409   instance_ = this;
    410 }
    411 
    412 TabDragController::~TabDragController() {
    413   views::ViewStorage::GetInstance()->RemoveView(old_focused_view_id_);
    414 
    415   if (instance_ == this)
    416     instance_ = NULL;
    417 
    418   if (move_loop_widget_) {
    419     move_loop_widget_->RemoveObserver(this);
    420     SetWindowPositionManaged(move_loop_widget_->GetNativeView(), true);
    421   }
    422 
    423   if (source_tabstrip_ && detach_into_browser_)
    424     GetModel(source_tabstrip_)->RemoveObserver(this);
    425 
    426   base::MessageLoopForUI::current()->RemoveObserver(this);
    427 
    428   // Need to delete the view here manually _before_ we reset the dragged
    429   // contents to NULL, otherwise if the view is animating to its destination
    430   // bounds, it won't be able to clean up properly since its cleanup routine
    431   // uses GetIndexForDraggedContents, which will be invalid.
    432   view_.reset(NULL);
    433 
    434   // Reset the delegate of the dragged WebContents. This ends up doing nothing
    435   // if the drag was completed.
    436   if (!detach_into_browser_)
    437     ResetDelegates();
    438 }
    439 
    440 void TabDragController::Init(
    441     TabStrip* source_tabstrip,
    442     Tab* source_tab,
    443     const std::vector<Tab*>& tabs,
    444     const gfx::Point& mouse_offset,
    445     int source_tab_offset,
    446     const ui::ListSelectionModel& initial_selection_model,
    447     DetachBehavior detach_behavior,
    448     MoveBehavior move_behavior,
    449     EventSource event_source) {
    450   DCHECK(!tabs.empty());
    451   DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
    452   source_tabstrip_ = source_tabstrip;
    453   was_source_maximized_ = source_tabstrip->GetWidget()->IsMaximized();
    454   was_source_fullscreen_ = source_tabstrip->GetWidget()->IsFullscreen();
    455   screen_ = gfx::Screen::GetScreenFor(
    456       source_tabstrip->GetWidget()->GetNativeView());
    457   host_desktop_type_ = chrome::GetHostDesktopTypeForNativeView(
    458       source_tabstrip->GetWidget()->GetNativeView());
    459   start_point_in_screen_ = gfx::Point(source_tab_offset, mouse_offset.y());
    460   views::View::ConvertPointToScreen(source_tab, &start_point_in_screen_);
    461   event_source_ = event_source;
    462   mouse_offset_ = mouse_offset;
    463   detach_behavior_ = detach_behavior;
    464   move_behavior_ = move_behavior;
    465   last_point_in_screen_ = start_point_in_screen_;
    466   last_move_screen_loc_ = start_point_in_screen_.x();
    467   initial_tab_positions_ = source_tabstrip->GetTabXCoordinates();
    468   if (detach_behavior == NOT_DETACHABLE)
    469     detach_into_browser_ = false;
    470 
    471   if (detach_into_browser_)
    472     GetModel(source_tabstrip_)->AddObserver(this);
    473 
    474   drag_data_.resize(tabs.size());
    475   for (size_t i = 0; i < tabs.size(); ++i)
    476     InitTabDragData(tabs[i], &(drag_data_[i]));
    477   source_tab_index_ =
    478       std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
    479 
    480   // Listen for Esc key presses.
    481   base::MessageLoopForUI::current()->AddObserver(this);
    482 
    483   if (source_tab->width() > 0) {
    484     offset_to_width_ratio_ = static_cast<float>(
    485         source_tab->GetMirroredXInView(source_tab_offset)) /
    486         static_cast<float>(source_tab->width());
    487   }
    488   InitWindowCreatePoint();
    489   initial_selection_model_.Copy(initial_selection_model);
    490 }
    491 
    492 // static
    493 bool TabDragController::IsAttachedTo(const TabStrip* tab_strip) {
    494   return (instance_ && instance_->active() &&
    495           instance_->attached_tabstrip() == tab_strip);
    496 }
    497 
    498 // static
    499 bool TabDragController::IsActive() {
    500   return instance_ && instance_->active();
    501 }
    502 
    503 // static
    504 bool TabDragController::ShouldDetachIntoNewBrowser() {
    505 #if defined(USE_AURA)
    506   return true;
    507 #else
    508   return CommandLine::ForCurrentProcess()->HasSwitch(
    509       switches::kTabBrowserDragging);
    510 #endif
    511 }
    512 
    513 void TabDragController::SetMoveBehavior(MoveBehavior behavior) {
    514   if (started_drag())
    515     return;
    516 
    517   move_behavior_ = behavior;
    518 }
    519 
    520 void TabDragController::Drag(const gfx::Point& point_in_screen) {
    521   TRACE_EVENT1("views", "TabDragController::Drag",
    522                "point_in_screen", point_in_screen.ToString());
    523 
    524   bring_to_front_timer_.Stop();
    525   move_stacked_timer_.Stop();
    526 
    527   if (waiting_for_run_loop_to_exit_)
    528     return;
    529 
    530   if (!started_drag_) {
    531     if (!CanStartDrag(point_in_screen))
    532       return;  // User hasn't dragged far enough yet.
    533 
    534     // On windows SaveFocus() may trigger a capture lost, which destroys us.
    535     {
    536       base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
    537       SaveFocus();
    538       if (!ref)
    539         return;
    540     }
    541     started_drag_ = true;
    542     Attach(source_tabstrip_, gfx::Point());
    543     if (detach_into_browser_ && static_cast<int>(drag_data_.size()) ==
    544         GetModel(source_tabstrip_)->count()) {
    545 #if defined(USE_ASH)
    546       if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH &&
    547           (was_source_maximized_ || was_source_fullscreen_)) {
    548         // When all tabs in a maximized browser are dragged the browser gets
    549         // restored during the drag and maximized back when the drag ends.
    550         views::Widget* widget = GetAttachedBrowserWidget();
    551         const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
    552         std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
    553         OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
    554         gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source_tabstrip_,
    555                                                            point_in_screen,
    556                                                            &drag_bounds));
    557         new_bounds.Offset(-widget->GetRestoredBounds().x() +
    558                           point_in_screen.x() -
    559                           mouse_offset_.x(), 0);
    560         widget->SetVisibilityChangedAnimationsEnabled(false);
    561         widget->Restore();
    562         widget->SetBounds(new_bounds);
    563         AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
    564                                          point_in_screen,
    565                                          &drag_bounds);
    566         widget->SetVisibilityChangedAnimationsEnabled(true);
    567         is_dragging_new_browser_ = true;
    568       }
    569 #endif
    570       RunMoveLoop(GetWindowOffset(point_in_screen));
    571       return;
    572     }
    573   }
    574 
    575   ContinueDragging(point_in_screen);
    576 }
    577 
    578 void TabDragController::EndDrag(EndDragReason reason) {
    579   TRACE_EVENT0("views", "TabDragController::EndDrag");
    580 
    581   // If we're dragging a window ignore capture lost since it'll ultimately
    582   // trigger the move loop to end and we'll revert the drag when RunMoveLoop()
    583   // finishes.
    584   if (reason == END_DRAG_CAPTURE_LOST && is_dragging_window_)
    585     return;
    586   EndDragImpl(reason != END_DRAG_COMPLETE && source_tabstrip_ ?
    587               CANCELED : NORMAL);
    588 }
    589 
    590 void TabDragController::InitTabDragData(Tab* tab,
    591                                         TabDragData* drag_data) {
    592   TRACE_EVENT0("views", "TabDragController::InitTabDragData");
    593   drag_data->source_model_index =
    594       source_tabstrip_->GetModelIndexOfTab(tab);
    595   drag_data->contents = GetModel(source_tabstrip_)->GetWebContentsAt(
    596       drag_data->source_model_index);
    597   drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
    598   registrar_.Add(
    599       this,
    600       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    601       content::Source<WebContents>(drag_data->contents));
    602 
    603   if (!detach_into_browser_) {
    604     drag_data->original_delegate = drag_data->contents->GetDelegate();
    605     drag_data->contents->SetDelegate(this);
    606   }
    607 }
    608 
    609 ///////////////////////////////////////////////////////////////////////////////
    610 // TabDragController, PageNavigator implementation:
    611 
    612 WebContents* TabDragController::OpenURLFromTab(
    613     WebContents* source,
    614     const OpenURLParams& params) {
    615   if (source_tab_drag_data()->original_delegate) {
    616     OpenURLParams forward_params = params;
    617     if (params.disposition == CURRENT_TAB)
    618       forward_params.disposition = NEW_WINDOW;
    619 
    620     return source_tab_drag_data()->original_delegate->OpenURLFromTab(
    621         source, forward_params);
    622   }
    623   return NULL;
    624 }
    625 
    626 ///////////////////////////////////////////////////////////////////////////////
    627 // TabDragController, content::WebContentsDelegate implementation:
    628 
    629 void TabDragController::NavigationStateChanged(const WebContents* source,
    630                                                unsigned changed_flags) {
    631   if (attached_tabstrip_ ||
    632       changed_flags == content::INVALIDATE_TYPE_PAGE_ACTIONS) {
    633     for (size_t i = 0; i < drag_data_.size(); ++i) {
    634       if (drag_data_[i].contents == source) {
    635         // Pass the NavigationStateChanged call to the original delegate so
    636         // that the title is updated. Do this only when we are attached as
    637         // otherwise the Tab isn't in the TabStrip (except for page action
    638         // updates).
    639         drag_data_[i].original_delegate->NavigationStateChanged(source,
    640                                                                 changed_flags);
    641         break;
    642       }
    643     }
    644   }
    645   if (view_.get())
    646     view_->Update();
    647 }
    648 
    649 void TabDragController::AddNewContents(WebContents* source,
    650                                        WebContents* new_contents,
    651                                        WindowOpenDisposition disposition,
    652                                        const gfx::Rect& initial_pos,
    653                                        bool user_gesture,
    654                                        bool* was_blocked) {
    655   DCHECK_NE(CURRENT_TAB, disposition);
    656 
    657   // Theoretically could be called while dragging if the page tries to
    658   // spawn a window. Route this message back to the browser in most cases.
    659   if (source_tab_drag_data()->original_delegate) {
    660     source_tab_drag_data()->original_delegate->AddNewContents(
    661         source, new_contents, disposition, initial_pos, user_gesture,
    662         was_blocked);
    663   }
    664 }
    665 
    666 void TabDragController::LoadingStateChanged(WebContents* source) {
    667   // It would be nice to respond to this message by changing the
    668   // screen shot in the dragged tab.
    669   if (view_.get())
    670     view_->Update();
    671 }
    672 
    673 bool TabDragController::ShouldSuppressDialogs() {
    674   // When a dialog is about to be shown we revert the drag. Otherwise a modal
    675   // dialog might appear and attempt to parent itself to a hidden tabcontents.
    676   EndDragImpl(CANCELED);
    677   return false;
    678 }
    679 
    680 content::JavaScriptDialogManager*
    681 TabDragController::GetJavaScriptDialogManager() {
    682   return GetJavaScriptDialogManagerInstance();
    683 }
    684 
    685 void TabDragController::RequestMediaAccessPermission(
    686     content::WebContents* web_contents,
    687     const content::MediaStreamRequest& request,
    688     const content::MediaResponseCallback& callback) {
    689   ::RequestMediaAccessPermission(
    690       web_contents,
    691       Profile::FromBrowserContext(web_contents->GetBrowserContext()),
    692       request,
    693       callback);
    694 }
    695 
    696 ///////////////////////////////////////////////////////////////////////////////
    697 // TabDragController, content::NotificationObserver implementation:
    698 
    699 void TabDragController::Observe(
    700     int type,
    701     const content::NotificationSource& source,
    702     const content::NotificationDetails& details) {
    703   DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
    704   WebContents* destroyed_web_contents =
    705       content::Source<WebContents>(source).ptr();
    706   for (size_t i = 0; i < drag_data_.size(); ++i) {
    707     if (drag_data_[i].contents == destroyed_web_contents) {
    708       // One of the tabs we're dragging has been destroyed. Cancel the drag.
    709       if (destroyed_web_contents->GetDelegate() == this)
    710         destroyed_web_contents->SetDelegate(NULL);
    711       drag_data_[i].contents = NULL;
    712       drag_data_[i].original_delegate = NULL;
    713       EndDragImpl(TAB_DESTROYED);
    714       return;
    715     }
    716   }
    717   // If we get here it means we got notification for a tab we don't know about.
    718   NOTREACHED();
    719 }
    720 
    721 ///////////////////////////////////////////////////////////////////////////////
    722 // TabDragController, MessageLoop::Observer implementation:
    723 
    724 base::EventStatus TabDragController::WillProcessEvent(
    725     const base::NativeEvent& event) {
    726   return base::EVENT_CONTINUE;
    727 }
    728 
    729 void TabDragController::DidProcessEvent(const base::NativeEvent& event) {
    730   // If the user presses ESC during a drag, we need to abort and revert things
    731   // to the way they were. This is the most reliable way to do this since no
    732   // single view or window reliably receives events throughout all the various
    733   // kinds of tab dragging.
    734   if (ui::EventTypeFromNative(event) == ui::ET_KEY_PRESSED &&
    735       ui::KeyboardCodeFromNative(event) == ui::VKEY_ESCAPE) {
    736     EndDrag(END_DRAG_CANCEL);
    737   }
    738 }
    739 
    740 void TabDragController::OnWidgetBoundsChanged(views::Widget* widget,
    741                                               const gfx::Rect& new_bounds) {
    742   TRACE_EVENT1("views", "TabDragController::OnWidgetBoundsChanged",
    743                "new_bounds", new_bounds.ToString());
    744 
    745   Drag(GetCursorScreenPoint());
    746 }
    747 
    748 void TabDragController::TabStripEmpty() {
    749   DCHECK(detach_into_browser_);
    750   GetModel(source_tabstrip_)->RemoveObserver(this);
    751   // NULL out source_tabstrip_ so that we don't attempt to add back to it (in
    752   // the case of a revert).
    753   source_tabstrip_ = NULL;
    754 }
    755 
    756 ///////////////////////////////////////////////////////////////////////////////
    757 // TabDragController, private:
    758 
    759 void TabDragController::InitWindowCreatePoint() {
    760   // window_create_point_ is only used in CompleteDrag() (through
    761   // GetWindowCreatePoint() to get the start point of the docked window) when
    762   // the attached_tabstrip_ is NULL and all the window's related bound
    763   // information are obtained from source_tabstrip_. So, we need to get the
    764   // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
    765   // the window_create_point_ is not in the correct coordinate system. Please
    766   // refer to http://crbug.com/6223 comment #15 for detailed information.
    767   views::View* first_tab = source_tabstrip_->tab_at(0);
    768   views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
    769   window_create_point_ = first_source_tab_point_;
    770   window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
    771 }
    772 
    773 gfx::Point TabDragController::GetWindowCreatePoint(
    774     const gfx::Point& origin) const {
    775   if (dock_info_.type() != DockInfo::NONE && dock_info_.in_enable_area()) {
    776     // If we're going to dock, we need to return the exact coordinate,
    777     // otherwise we may attempt to maximize on the wrong monitor.
    778     return origin;
    779   }
    780 
    781   // If the cursor is outside the monitor area, move it inside. For example,
    782   // dropping a tab onto the task bar on Windows produces this situation.
    783   gfx::Rect work_area = screen_->GetDisplayNearestPoint(origin).work_area();
    784   gfx::Point create_point(origin);
    785   if (!work_area.IsEmpty()) {
    786     if (create_point.x() < work_area.x())
    787       create_point.set_x(work_area.x());
    788     else if (create_point.x() > work_area.right())
    789       create_point.set_x(work_area.right());
    790     if (create_point.y() < work_area.y())
    791       create_point.set_y(work_area.y());
    792     else if (create_point.y() > work_area.bottom())
    793       create_point.set_y(work_area.bottom());
    794   }
    795   return gfx::Point(create_point.x() - window_create_point_.x(),
    796                     create_point.y() - window_create_point_.y());
    797 }
    798 
    799 void TabDragController::UpdateDockInfo(const gfx::Point& point_in_screen) {
    800   TRACE_EVENT1("views", "TabDragController::UpdateDockInfo",
    801                "point_in_screen", point_in_screen.ToString());
    802 
    803   // Update the DockInfo for the current mouse coordinates.
    804   DockInfo dock_info = GetDockInfoAtPoint(point_in_screen);
    805   if (!dock_info.equals(dock_info_)) {
    806     // DockInfo for current position differs.
    807     if (dock_info_.type() != DockInfo::NONE &&
    808         !dock_controllers_.empty()) {
    809       // Hide old visual indicator.
    810       dock_controllers_.back()->Hide();
    811     }
    812     dock_info_ = dock_info;
    813     if (dock_info_.type() != DockInfo::NONE) {
    814       // Show new docking position.
    815       DockDisplayer* controller = new DockDisplayer(this, dock_info_);
    816       if (controller->popup_view()) {
    817         dock_controllers_.push_back(controller);
    818         dock_windows_.insert(controller->popup_view());
    819       } else {
    820         delete controller;
    821       }
    822     }
    823   } else if (dock_info_.type() != DockInfo::NONE &&
    824              !dock_controllers_.empty()) {
    825     // Current dock position is the same as last, update the controller's
    826     // in_enable_area state as it may have changed.
    827     dock_controllers_.back()->UpdateInEnabledArea(dock_info_.in_enable_area());
    828   }
    829 }
    830 
    831 void TabDragController::SaveFocus() {
    832   DCHECK(source_tabstrip_);
    833   views::View* focused_view =
    834       source_tabstrip_->GetFocusManager()->GetFocusedView();
    835   if (focused_view)
    836     views::ViewStorage::GetInstance()->StoreView(old_focused_view_id_,
    837                                                  focused_view);
    838   source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
    839   // WARNING: we may have been deleted.
    840 }
    841 
    842 void TabDragController::RestoreFocus() {
    843   if (attached_tabstrip_ != source_tabstrip_) {
    844     if (is_dragging_new_browser_) {
    845       content::WebContents* active_contents = source_dragged_contents();
    846       if (active_contents && !active_contents->FocusLocationBarByDefault())
    847         active_contents->GetView()->Focus();
    848     }
    849     return;
    850   }
    851   views::View* old_focused_view =
    852       views::ViewStorage::GetInstance()->RetrieveView(
    853       old_focused_view_id_);
    854   if (!old_focused_view)
    855     return;
    856   old_focused_view->GetFocusManager()->SetFocusedView(old_focused_view);
    857 }
    858 
    859 bool TabDragController::CanStartDrag(const gfx::Point& point_in_screen) const {
    860   // Determine if the mouse has moved beyond a minimum elasticity distance in
    861   // any direction from the starting point.
    862   static const int kMinimumDragDistance = 10;
    863   int x_offset = abs(point_in_screen.x() - start_point_in_screen_.x());
    864   int y_offset = abs(point_in_screen.y() - start_point_in_screen_.y());
    865   return sqrt(pow(static_cast<float>(x_offset), 2) +
    866               pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
    867 }
    868 
    869 void TabDragController::ContinueDragging(const gfx::Point& point_in_screen) {
    870   TRACE_EVENT1("views", "TabDragController::ContinueDragging",
    871                "point_in_screen", point_in_screen.ToString());
    872 
    873   DCHECK(!detach_into_browser_ || attached_tabstrip_);
    874 
    875   TabStrip* target_tabstrip = detach_behavior_ == DETACHABLE ?
    876       GetTargetTabStripForPoint(point_in_screen) : source_tabstrip_;
    877   bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
    878 
    879   if (attached_tabstrip_) {
    880     int move_delta = point_in_screen.x() - last_point_in_screen_.x();
    881     if (move_delta > 0)
    882       mouse_move_direction_ |= kMovedMouseRight;
    883     else if (move_delta < 0)
    884       mouse_move_direction_ |= kMovedMouseLeft;
    885   }
    886   last_point_in_screen_ = point_in_screen;
    887 
    888   if (tab_strip_changed) {
    889     is_dragging_new_browser_ = false;
    890     if (detach_into_browser_ &&
    891         DragBrowserToNewTabStrip(target_tabstrip, point_in_screen) ==
    892         DRAG_BROWSER_RESULT_STOP) {
    893       return;
    894     } else if (!detach_into_browser_) {
    895       if (attached_tabstrip_)
    896         Detach(RELEASE_CAPTURE);
    897       if (target_tabstrip)
    898         Attach(target_tabstrip, point_in_screen);
    899     }
    900   }
    901   if (view_.get() || is_dragging_window_) {
    902     static_cast<base::Timer*>(&bring_to_front_timer_)->Start(FROM_HERE,
    903         base::TimeDelta::FromMilliseconds(kBringToFrontDelay),
    904         base::Bind(&TabDragController::BringWindowUnderPointToFront,
    905                    base::Unretained(this), point_in_screen));
    906   }
    907 
    908   UpdateDockInfo(point_in_screen);
    909 
    910   if (!is_dragging_window_) {
    911     if (attached_tabstrip_) {
    912       if (move_only()) {
    913         DragActiveTabStacked(point_in_screen);
    914       } else {
    915         MoveAttached(point_in_screen);
    916         if (tab_strip_changed) {
    917           // Move the corresponding window to the front. We do this after the
    918           // move as on windows activate triggers a synchronous paint.
    919           attached_tabstrip_->GetWidget()->Activate();
    920         }
    921       }
    922     } else {
    923       MoveDetached(point_in_screen);
    924     }
    925   }
    926 }
    927 
    928 TabDragController::DragBrowserResultType
    929 TabDragController::DragBrowserToNewTabStrip(
    930     TabStrip* target_tabstrip,
    931     const gfx::Point& point_in_screen) {
    932   TRACE_EVENT1("views", "TabDragController::DragBrowserToNewTabStrip",
    933                "point_in_screen", point_in_screen.ToString());
    934 
    935   if (!target_tabstrip) {
    936     DetachIntoNewBrowserAndRunMoveLoop(point_in_screen);
    937     return DRAG_BROWSER_RESULT_STOP;
    938   }
    939   if (is_dragging_window_) {
    940     // ReleaseCapture() is going to result in calling back to us (because it
    941     // results in a move). That'll cause all sorts of problems.  Reset the
    942     // observer so we don't get notified and process the event.
    943     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
    944       move_loop_widget_->RemoveObserver(this);
    945       move_loop_widget_ = NULL;
    946     }
    947     views::Widget* browser_widget = GetAttachedBrowserWidget();
    948     // Need to release the drag controller before starting the move loop as it's
    949     // going to trigger capture lost, which cancels drag.
    950     attached_tabstrip_->ReleaseDragController();
    951     target_tabstrip->OwnDragController(this);
    952     // Disable animations so that we don't see a close animation on aero.
    953     browser_widget->SetVisibilityChangedAnimationsEnabled(false);
    954     // For aura we can't release capture, otherwise it'll cancel a gesture.
    955     // Instead we have to directly change capture.
    956     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH)
    957       target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_);
    958     else
    959       browser_widget->ReleaseCapture();
    960 #if defined(OS_WIN) && defined(USE_AURA)
    961     // The Gesture recognizer does not work well currently when capture changes
    962     // while a touch gesture is in progress. So we need to manually transfer
    963     // gesture sequence and the GR's touch events queue to the new window. This
    964     // should really be done somewhere in capture change code and or inside the
    965     // GR. But we currently do not have a consistent way for doing it that would
    966     // work in all cases. Hence this hack.
    967     ui::GestureRecognizer::Get()->TransferEventsTo(
    968         browser_widget->GetNativeView(),
    969         target_tabstrip->GetWidget()->GetNativeView());
    970 #endif
    971 
    972     // The window is going away. Since the drag is still on going we don't want
    973     // that to effect the position of any windows.
    974     SetWindowPositionManaged(browser_widget->GetNativeView(), false);
    975 
    976     // EndMoveLoop is going to snap the window back to its original location.
    977     // Hide it so users don't see this.
    978     browser_widget->Hide();
    979     browser_widget->EndMoveLoop();
    980 
    981     // Ideally we would always swap the tabs now, but on non-ash it seems that
    982     // running the move loop implicitly activates the window when done, leading
    983     // to all sorts of flicker. So, on non-ash, instead we process the move
    984     // after the loop completes. But on chromeos, we can do tab swapping now to
    985     // avoid the tab flashing issue(crbug.com/116329).
    986     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
    987       is_dragging_window_ = false;
    988       Detach(DONT_RELEASE_CAPTURE);
    989       Attach(target_tabstrip, point_in_screen);
    990       // Move the tabs into position.
    991       MoveAttached(point_in_screen);
    992       attached_tabstrip_->GetWidget()->Activate();
    993     } else {
    994       tab_strip_to_attach_to_after_exit_ = target_tabstrip;
    995     }
    996 
    997     waiting_for_run_loop_to_exit_ = true;
    998     end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
    999     return DRAG_BROWSER_RESULT_STOP;
   1000   }
   1001   Detach(DONT_RELEASE_CAPTURE);
   1002   Attach(target_tabstrip, point_in_screen);
   1003   return DRAG_BROWSER_RESULT_CONTINUE;
   1004 }
   1005 
   1006 void TabDragController::DragActiveTabStacked(
   1007     const gfx::Point& point_in_screen) {
   1008   if (attached_tabstrip_->tab_count() !=
   1009       static_cast<int>(initial_tab_positions_.size()))
   1010     return;  // TODO: should cancel drag if this happens.
   1011 
   1012   int delta = point_in_screen.x() - start_point_in_screen_.x();
   1013   attached_tabstrip_->DragActiveTab(initial_tab_positions_, delta);
   1014 }
   1015 
   1016 void TabDragController::MoveAttachedToNextStackedIndex(
   1017     const gfx::Point& point_in_screen) {
   1018   int index = attached_tabstrip_->touch_layout_->active_index();
   1019   if (index + 1 >= attached_tabstrip_->tab_count())
   1020     return;
   1021 
   1022   GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index + 1);
   1023   StartMoveStackedTimerIfNecessary(point_in_screen,
   1024                                    kMoveAttachedSubsequentDelay);
   1025 }
   1026 
   1027 void TabDragController::MoveAttachedToPreviousStackedIndex(
   1028     const gfx::Point& point_in_screen) {
   1029   int index = attached_tabstrip_->touch_layout_->active_index();
   1030   if (index <= attached_tabstrip_->GetMiniTabCount())
   1031     return;
   1032 
   1033   GetModel(attached_tabstrip_)->MoveSelectedTabsTo(index - 1);
   1034   StartMoveStackedTimerIfNecessary(point_in_screen,
   1035                                    kMoveAttachedSubsequentDelay);
   1036 }
   1037 
   1038 void TabDragController::MoveAttached(const gfx::Point& point_in_screen) {
   1039   DCHECK(attached_tabstrip_);
   1040   DCHECK(!view_.get());
   1041   DCHECK(!is_dragging_window_);
   1042 
   1043   gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
   1044 
   1045   // Determine the horizontal move threshold. This is dependent on the width
   1046   // of tabs. The smaller the tabs compared to the standard size, the smaller
   1047   // the threshold.
   1048   int threshold = kHorizontalMoveThreshold;
   1049   if (!attached_tabstrip_->touch_layout_.get()) {
   1050     double unselected, selected;
   1051     attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
   1052     double ratio = unselected / Tab::GetStandardSize().width();
   1053     threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
   1054   }
   1055   // else case: touch tabs never shrink.
   1056 
   1057   std::vector<Tab*> tabs(drag_data_.size());
   1058   for (size_t i = 0; i < drag_data_.size(); ++i)
   1059     tabs[i] = drag_data_[i].attached_tab;
   1060 
   1061   bool did_layout = false;
   1062   // Update the model, moving the WebContents from one index to another. Do this
   1063   // only if we have moved a minimum distance since the last reorder (to prevent
   1064   // jitter) or if this the first move and the tabs are not consecutive.
   1065   if ((abs(point_in_screen.x() - last_move_screen_loc_) > threshold ||
   1066         (initial_move_ && !AreTabsConsecutive()))) {
   1067     TabStripModel* attached_model = GetModel(attached_tabstrip_);
   1068     gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
   1069     int to_index = GetInsertionIndexForDraggedBounds(bounds);
   1070     bool do_move = true;
   1071     // While dragging within a tabstrip the expectation is the insertion index
   1072     // is based on the left edge of the tabs being dragged. OTOH when dragging
   1073     // into a new tabstrip (attaching) the expectation is the insertion index is
   1074     // based on the cursor. This proves problematic as insertion may change the
   1075     // size of the tabs, resulting in the index calculated before the insert
   1076     // differing from the index calculated after the insert. To alleviate this
   1077     // the index is chosen before insertion, and subsequently a new index is
   1078     // only used once the mouse moves enough such that the index changes based
   1079     // on the direction the mouse moved relative to |attach_x_| (smaller
   1080     // x-coordinate should yield a smaller index or larger x-coordinate yields a
   1081     // larger index).
   1082     if (attach_index_ != -1) {
   1083       gfx::Point tab_strip_point(point_in_screen);
   1084       views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
   1085       const int new_x =
   1086           attached_tabstrip_->GetMirroredXInView(tab_strip_point.x());
   1087       if (new_x < attach_x_)
   1088         to_index = std::min(to_index, attach_index_);
   1089       else
   1090         to_index = std::max(to_index, attach_index_);
   1091       if (to_index != attach_index_)
   1092         attach_index_ = -1;  // Once a valid move is detected, don't constrain.
   1093       else
   1094         do_move = false;
   1095     }
   1096     if (do_move) {
   1097       WebContents* last_contents = drag_data_[drag_data_.size() - 1].contents;
   1098       int index_of_last_item =
   1099           attached_model->GetIndexOfWebContents(last_contents);
   1100       if (initial_move_) {
   1101         // TabStrip determines if the tabs needs to be animated based on model
   1102         // position. This means we need to invoke LayoutDraggedTabsAt before
   1103         // changing the model.
   1104         attached_tabstrip_->LayoutDraggedTabsAt(
   1105             tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
   1106             initial_move_);
   1107         did_layout = true;
   1108       }
   1109       attached_model->MoveSelectedTabsTo(to_index);
   1110 
   1111       // Move may do nothing in certain situations (such as when dragging pinned
   1112       // tabs). Make sure the tabstrip actually changed before updating
   1113       // last_move_screen_loc_.
   1114       if (index_of_last_item !=
   1115           attached_model->GetIndexOfWebContents(last_contents)) {
   1116         last_move_screen_loc_ = point_in_screen.x();
   1117       }
   1118     }
   1119   }
   1120 
   1121   if (!did_layout) {
   1122     attached_tabstrip_->LayoutDraggedTabsAt(
   1123         tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
   1124         initial_move_);
   1125   }
   1126 
   1127   StartMoveStackedTimerIfNecessary(point_in_screen, kMoveAttachedInitialDelay);
   1128 
   1129   initial_move_ = false;
   1130 }
   1131 
   1132 void TabDragController::MoveDetached(const gfx::Point& point_in_screen) {
   1133   DCHECK(!attached_tabstrip_);
   1134   DCHECK(view_.get());
   1135   DCHECK(!is_dragging_window_);
   1136 
   1137   // Move the View. There are no changes to the model if we're detached.
   1138   view_->MoveTo(point_in_screen);
   1139 }
   1140 
   1141 void TabDragController::StartMoveStackedTimerIfNecessary(
   1142     const gfx::Point& point_in_screen,
   1143     int delay_ms) {
   1144   DCHECK(attached_tabstrip_);
   1145 
   1146   StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
   1147   if (!touch_layout)
   1148     return;
   1149 
   1150   gfx::Point dragged_view_point = GetAttachedDragPoint(point_in_screen);
   1151   gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
   1152   int index = touch_layout->active_index();
   1153   if (ShouldDragToNextStackedTab(bounds, index)) {
   1154     static_cast<base::Timer*>(&move_stacked_timer_)->Start(
   1155         FROM_HERE,
   1156         base::TimeDelta::FromMilliseconds(delay_ms),
   1157         base::Bind(&TabDragController::MoveAttachedToNextStackedIndex,
   1158                    base::Unretained(this), point_in_screen));
   1159   } else if (ShouldDragToPreviousStackedTab(bounds, index)) {
   1160     static_cast<base::Timer*>(&move_stacked_timer_)->Start(
   1161         FROM_HERE,
   1162         base::TimeDelta::FromMilliseconds(delay_ms),
   1163         base::Bind(&TabDragController::MoveAttachedToPreviousStackedIndex,
   1164                    base::Unretained(this), point_in_screen));
   1165   }
   1166 }
   1167 
   1168 TabDragController::DetachPosition TabDragController::GetDetachPosition(
   1169     const gfx::Point& point_in_screen) {
   1170   DCHECK(attached_tabstrip_);
   1171   gfx::Point attached_point(point_in_screen);
   1172   views::View::ConvertPointFromScreen(attached_tabstrip_, &attached_point);
   1173   if (attached_point.x() < 0)
   1174     return DETACH_BEFORE;
   1175   if (attached_point.x() >= attached_tabstrip_->width())
   1176     return DETACH_AFTER;
   1177   return DETACH_ABOVE_OR_BELOW;
   1178 }
   1179 
   1180 DockInfo TabDragController::GetDockInfoAtPoint(
   1181     const gfx::Point& point_in_screen) {
   1182   // TODO: add support for dock info when |detach_into_browser_| is true.
   1183   if (attached_tabstrip_ || detach_into_browser_) {
   1184     // If the mouse is over a tab strip, don't offer a dock position.
   1185     return DockInfo();
   1186   }
   1187 
   1188   if (dock_info_.IsValidForPoint(point_in_screen)) {
   1189     // It's possible any given screen coordinate has multiple docking
   1190     // positions. Check the current info first to avoid having the docking
   1191     // position bounce around.
   1192     return dock_info_;
   1193   }
   1194 
   1195   gfx::NativeView dragged_view = view_->GetWidget()->GetNativeView();
   1196   dock_windows_.insert(dragged_view);
   1197   DockInfo info = DockInfo::GetDockInfoAtPoint(
   1198       host_desktop_type_,
   1199       point_in_screen,
   1200       dock_windows_);
   1201   dock_windows_.erase(dragged_view);
   1202   return info;
   1203 }
   1204 
   1205 TabStrip* TabDragController::GetTargetTabStripForPoint(
   1206     const gfx::Point& point_in_screen) {
   1207   TRACE_EVENT1("views", "TabDragController::GetTargetTabStripForPoint",
   1208                "point_in_screen", point_in_screen.ToString());
   1209 
   1210   if (move_only() && attached_tabstrip_) {
   1211     DCHECK_EQ(DETACHABLE, detach_behavior_);
   1212     // move_only() is intended for touch, in which case we only want to detach
   1213     // if the touch point moves significantly in the vertical distance.
   1214     gfx::Rect tabstrip_bounds = GetViewScreenBounds(attached_tabstrip_);
   1215     if (DoesRectContainVerticalPointExpanded(tabstrip_bounds,
   1216                                              kTouchVerticalDetachMagnetism,
   1217                                              point_in_screen.y()))
   1218       return attached_tabstrip_;
   1219   }
   1220   gfx::NativeView dragged_view = NULL;
   1221   if (view_.get())
   1222     dragged_view = view_->GetWidget()->GetNativeView();
   1223   else if (is_dragging_window_)
   1224     dragged_view = attached_tabstrip_->GetWidget()->GetNativeView();
   1225   if (dragged_view)
   1226     dock_windows_.insert(dragged_view);
   1227   gfx::NativeWindow local_window =
   1228       DockInfo::GetLocalProcessWindowAtPoint(
   1229           host_desktop_type_,
   1230           point_in_screen,
   1231           dock_windows_);
   1232   if (dragged_view)
   1233     dock_windows_.erase(dragged_view);
   1234   TabStrip* tab_strip = GetTabStripForWindow(local_window);
   1235   if (tab_strip && DoesTabStripContain(tab_strip, point_in_screen))
   1236     return tab_strip;
   1237   return is_dragging_window_ ? attached_tabstrip_ : NULL;
   1238 }
   1239 
   1240 TabStrip* TabDragController::GetTabStripForWindow(gfx::NativeWindow window) {
   1241   if (!window)
   1242     return NULL;
   1243   BrowserView* browser_view =
   1244       BrowserView::GetBrowserViewForNativeWindow(window);
   1245   // We don't allow drops on windows that don't have tabstrips.
   1246   if (!browser_view ||
   1247       !browser_view->browser()->SupportsWindowFeature(
   1248           Browser::FEATURE_TABSTRIP))
   1249     return NULL;
   1250 
   1251   TabStrip* other_tabstrip = browser_view->tabstrip();
   1252   TabStrip* tab_strip =
   1253       attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
   1254   DCHECK(tab_strip);
   1255 
   1256   return other_tabstrip->controller()->IsCompatibleWith(tab_strip) ?
   1257       other_tabstrip : NULL;
   1258 }
   1259 
   1260 bool TabDragController::DoesTabStripContain(
   1261     TabStrip* tabstrip,
   1262     const gfx::Point& point_in_screen) const {
   1263   // Make sure the specified screen point is actually within the bounds of the
   1264   // specified tabstrip...
   1265   gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
   1266   return point_in_screen.x() < tabstrip_bounds.right() &&
   1267       point_in_screen.x() >= tabstrip_bounds.x() &&
   1268       DoesRectContainVerticalPointExpanded(tabstrip_bounds,
   1269                                            kVerticalDetachMagnetism,
   1270                                            point_in_screen.y());
   1271 }
   1272 
   1273 void TabDragController::Attach(TabStrip* attached_tabstrip,
   1274                                const gfx::Point& point_in_screen) {
   1275   TRACE_EVENT1("views", "TabDragController::Attach",
   1276                "point_in_screen", point_in_screen.ToString());
   1277 
   1278   DCHECK(!attached_tabstrip_);  // We should already have detached by the time
   1279                                 // we get here.
   1280 
   1281   attached_tabstrip_ = attached_tabstrip;
   1282 
   1283   // And we don't need the dragged view.
   1284   view_.reset();
   1285 
   1286   std::vector<Tab*> tabs =
   1287       GetTabsMatchingDraggedContents(attached_tabstrip_);
   1288 
   1289   if (tabs.empty()) {
   1290     // Transitioning from detached to attached to a new tabstrip. Add tabs to
   1291     // the new model.
   1292 
   1293     selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
   1294 
   1295     if (!detach_into_browser_) {
   1296       // Remove ourselves as the delegate now that the dragged WebContents is
   1297       // being inserted back into a Browser.
   1298       for (size_t i = 0; i < drag_data_.size(); ++i) {
   1299         drag_data_[i].contents->SetDelegate(NULL);
   1300         drag_data_[i].original_delegate = NULL;
   1301       }
   1302 
   1303       // Return the WebContents to normalcy.
   1304       source_dragged_contents()->DecrementCapturerCount();
   1305     }
   1306 
   1307     // Inserting counts as a move. We don't want the tabs to jitter when the
   1308     // user moves the tab immediately after attaching it.
   1309     last_move_screen_loc_ = point_in_screen.x();
   1310 
   1311     // Figure out where to insert the tab based on the bounds of the dragged
   1312     // representation and the ideal bounds of the other Tabs already in the
   1313     // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
   1314     // changing due to animation).
   1315     gfx::Point tab_strip_point(point_in_screen);
   1316     views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_strip_point);
   1317     tab_strip_point.set_x(
   1318         attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
   1319     tab_strip_point.Offset(0, -mouse_offset_.y());
   1320     gfx::Rect bounds = GetDraggedViewTabStripBounds(tab_strip_point);
   1321     int index = GetInsertionIndexForDraggedBounds(bounds);
   1322     attach_index_ = index;
   1323     attach_x_ = tab_strip_point.x();
   1324     base::AutoReset<bool> setter(&is_mutating_, true);
   1325     for (size_t i = 0; i < drag_data_.size(); ++i) {
   1326       int add_types = TabStripModel::ADD_NONE;
   1327       if (attached_tabstrip_->touch_layout_.get()) {
   1328         // StackedTabStripLayout positions relative to the active tab, if we
   1329         // don't add the tab as active things bounce around.
   1330         DCHECK_EQ(1u, drag_data_.size());
   1331         add_types |= TabStripModel::ADD_ACTIVE;
   1332       }
   1333       if (drag_data_[i].pinned)
   1334         add_types |= TabStripModel::ADD_PINNED;
   1335       GetModel(attached_tabstrip_)->InsertWebContentsAt(
   1336           index + i, drag_data_[i].contents, add_types);
   1337     }
   1338 
   1339     tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
   1340   }
   1341   DCHECK_EQ(tabs.size(), drag_data_.size());
   1342   for (size_t i = 0; i < drag_data_.size(); ++i)
   1343     drag_data_[i].attached_tab = tabs[i];
   1344 
   1345   attached_tabstrip_->StartedDraggingTabs(tabs);
   1346 
   1347   ResetSelection(GetModel(attached_tabstrip_));
   1348 
   1349   // The size of the dragged tab may have changed. Adjust the x offset so that
   1350   // ratio of mouse_offset_ to original width is maintained.
   1351   std::vector<Tab*> tabs_to_source(tabs);
   1352   tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
   1353                        tabs_to_source.end());
   1354   int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
   1355       tabs[source_tab_index_]->width() +
   1356       static_cast<int>(offset_to_width_ratio_ *
   1357                        tabs[source_tab_index_]->width());
   1358   mouse_offset_.set_x(new_x);
   1359 
   1360   // Transfer ownership of us to the new tabstrip as well as making sure the
   1361   // window has capture. This is important so that if activation changes the
   1362   // drag isn't prematurely canceled.
   1363   if (detach_into_browser_) {
   1364     attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
   1365     attached_tabstrip_->OwnDragController(this);
   1366   }
   1367 
   1368   // Redirect all mouse events to the TabStrip so that the tab that originated
   1369   // the drag can safely be deleted.
   1370   if (detach_into_browser_ || attached_tabstrip_ == source_tabstrip_) {
   1371     static_cast<views::internal::RootView*>(
   1372         attached_tabstrip_->GetWidget()->GetRootView())->SetMouseHandler(
   1373             attached_tabstrip_);
   1374   }
   1375 }
   1376 
   1377 void TabDragController::Detach(ReleaseCapture release_capture) {
   1378   TRACE_EVENT1("views", "TabDragController::Detach",
   1379                "release_capture", release_capture);
   1380 
   1381   attach_index_ = -1;
   1382 
   1383   // When the user detaches we assume they want to reorder.
   1384   move_behavior_ = REORDER;
   1385 
   1386   // Release ownership of the drag controller and mouse capture. When we
   1387   // reattach ownership is transfered.
   1388   if (detach_into_browser_) {
   1389     attached_tabstrip_->ReleaseDragController();
   1390     if (release_capture == RELEASE_CAPTURE)
   1391       attached_tabstrip_->GetWidget()->ReleaseCapture();
   1392   }
   1393 
   1394   mouse_move_direction_ = kMovedMouseLeft | kMovedMouseRight;
   1395 
   1396   // Prevent the WebContents HWND from being hidden by any of the model
   1397   // operations performed during the drag.
   1398   if (!detach_into_browser_)
   1399     source_dragged_contents()->IncrementCapturerCount();
   1400 
   1401   std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
   1402   TabStripModel* attached_model = GetModel(attached_tabstrip_);
   1403   std::vector<TabRendererData> tab_data;
   1404   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1405     tab_data.push_back(drag_data_[i].attached_tab->data());
   1406     int index = attached_model->GetIndexOfWebContents(drag_data_[i].contents);
   1407     DCHECK_NE(-1, index);
   1408 
   1409     // Hide the tab so that the user doesn't see it animate closed.
   1410     drag_data_[i].attached_tab->SetVisible(false);
   1411 
   1412     attached_model->DetachWebContentsAt(index);
   1413 
   1414     // Detaching resets the delegate, but we still want to be the delegate.
   1415     if (!detach_into_browser_)
   1416       drag_data_[i].contents->SetDelegate(this);
   1417 
   1418     // Detaching may end up deleting the tab, drop references to it.
   1419     drag_data_[i].attached_tab = NULL;
   1420   }
   1421 
   1422   // If we've removed the last Tab from the TabStrip, hide the frame now.
   1423   if (!attached_model->empty()) {
   1424     if (!selection_model_before_attach_.empty() &&
   1425         selection_model_before_attach_.active() >= 0 &&
   1426         selection_model_before_attach_.active() < attached_model->count()) {
   1427       // Restore the selection.
   1428       attached_model->SetSelectionFromModel(selection_model_before_attach_);
   1429     } else if (attached_tabstrip_ == source_tabstrip_ &&
   1430                !initial_selection_model_.empty()) {
   1431       // First time detaching from the source tabstrip. Reset selection model to
   1432       // initial_selection_model_. Before resetting though we have to remove all
   1433       // the tabs from initial_selection_model_ as it was created with the tabs
   1434       // still there.
   1435       ui::ListSelectionModel selection_model;
   1436       selection_model.Copy(initial_selection_model_);
   1437       for (DragData::const_reverse_iterator i(drag_data_.rbegin());
   1438            i != drag_data_.rend(); ++i) {
   1439         selection_model.DecrementFrom(i->source_model_index);
   1440       }
   1441       // We may have cleared out the selection model. Only reset it if it
   1442       // contains something.
   1443       if (!selection_model.empty())
   1444         attached_model->SetSelectionFromModel(selection_model);
   1445     }
   1446   } else if (!detach_into_browser_) {
   1447     HideFrame();
   1448   }
   1449 
   1450   // Create the dragged view.
   1451   if (!detach_into_browser_)
   1452     CreateDraggedView(tab_data, drag_bounds);
   1453 
   1454   attached_tabstrip_->DraggedTabsDetached();
   1455   attached_tabstrip_ = NULL;
   1456 }
   1457 
   1458 void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
   1459     const gfx::Point& point_in_screen) {
   1460   if (GetModel(attached_tabstrip_)->count() ==
   1461       static_cast<int>(drag_data_.size())) {
   1462     // All the tabs in a browser are being dragged but all the tabs weren't
   1463     // initially being dragged. For this to happen the user would have to
   1464     // start dragging a set of tabs, the other tabs close, then detach.
   1465     RunMoveLoop(GetWindowOffset(point_in_screen));
   1466     return;
   1467   }
   1468 
   1469   const int last_tabstrip_width = attached_tabstrip_->tab_area_width();
   1470   std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs();
   1471   OffsetX(GetAttachedDragPoint(point_in_screen).x(), &drag_bounds);
   1472 
   1473   gfx::Vector2d drag_offset;
   1474   Browser* browser = CreateBrowserForDrag(
   1475       attached_tabstrip_, point_in_screen, &drag_offset, &drag_bounds);
   1476 #if defined(OS_WIN) && defined(USE_AURA)
   1477   gfx::NativeView attached_native_view =
   1478     attached_tabstrip_->GetWidget()->GetNativeView();
   1479 #endif
   1480   Detach(host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH ?
   1481          DONT_RELEASE_CAPTURE : RELEASE_CAPTURE);
   1482   BrowserView* dragged_browser_view =
   1483       BrowserView::GetBrowserViewForBrowser(browser);
   1484   views::Widget* dragged_widget = dragged_browser_view->GetWidget();
   1485 #if defined(OS_WIN) && defined(USE_AURA)
   1486     // The Gesture recognizer does not work well currently when capture changes
   1487     // while a touch gesture is in progress. So we need to manually transfer
   1488     // gesture sequence and the GR's touch events queue to the new window. This
   1489     // should really be done somewhere in capture change code and or inside the
   1490     // GR. But we currently do not have a consistent way for doing it that would
   1491     // work in all cases. Hence this hack.
   1492     ui::GestureRecognizer::Get()->TransferEventsTo(
   1493         attached_native_view,
   1494         dragged_widget->GetNativeView());
   1495 #endif
   1496   dragged_widget->SetVisibilityChangedAnimationsEnabled(false);
   1497   Attach(dragged_browser_view->tabstrip(), gfx::Point());
   1498   AdjustBrowserAndTabBoundsForDrag(last_tabstrip_width,
   1499                                    point_in_screen,
   1500                                    &drag_bounds);
   1501   WindowPositionManagedUpdater updater;
   1502   dragged_widget->AddObserver(&updater);
   1503   browser->window()->Show();
   1504   dragged_widget->RemoveObserver(&updater);
   1505   dragged_widget->SetVisibilityChangedAnimationsEnabled(true);
   1506   // Activate may trigger a focus loss, destroying us.
   1507   {
   1508     base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
   1509     browser->window()->Activate();
   1510     if (!ref)
   1511       return;
   1512   }
   1513   RunMoveLoop(drag_offset);
   1514 }
   1515 
   1516 void TabDragController::RunMoveLoop(const gfx::Vector2d& drag_offset) {
   1517   // If the user drags the whole window we'll assume they are going to attach to
   1518   // another window and therefore want to reorder.
   1519   move_behavior_ = REORDER;
   1520 
   1521   move_loop_widget_ = GetAttachedBrowserWidget();
   1522   DCHECK(move_loop_widget_);
   1523   move_loop_widget_->AddObserver(this);
   1524   is_dragging_window_ = true;
   1525   base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
   1526   // Running the move loop releases mouse capture on non-ash, which triggers
   1527   // destroying the drag loop. Release mouse capture ourself before this while
   1528   // the DragController isn't owned by the TabStrip.
   1529   if (host_desktop_type_ != chrome::HOST_DESKTOP_TYPE_ASH) {
   1530     attached_tabstrip_->ReleaseDragController();
   1531     attached_tabstrip_->GetWidget()->ReleaseCapture();
   1532     attached_tabstrip_->OwnDragController(this);
   1533   }
   1534   const views::Widget::MoveLoopSource move_loop_source =
   1535       event_source_ == EVENT_SOURCE_MOUSE ?
   1536       views::Widget::MOVE_LOOP_SOURCE_MOUSE :
   1537       views::Widget::MOVE_LOOP_SOURCE_TOUCH;
   1538   const views::Widget::MoveLoopEscapeBehavior escape_behavior =
   1539       is_dragging_new_browser_ ?
   1540           views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE :
   1541           views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
   1542   views::Widget::MoveLoopResult result =
   1543       move_loop_widget_->RunMoveLoop(
   1544           drag_offset, move_loop_source, escape_behavior);
   1545   content::NotificationService::current()->Notify(
   1546       chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
   1547       content::NotificationService::AllBrowserContextsAndSources(),
   1548       content::NotificationService::NoDetails());
   1549 
   1550   if (!ref)
   1551     return;
   1552   // Under chromeos we immediately set the |move_loop_widget_| to NULL.
   1553   if (move_loop_widget_) {
   1554     move_loop_widget_->RemoveObserver(this);
   1555     move_loop_widget_ = NULL;
   1556   }
   1557   is_dragging_window_ = false;
   1558   waiting_for_run_loop_to_exit_ = false;
   1559   if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
   1560     end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
   1561     if (tab_strip_to_attach_to_after_exit_) {
   1562       gfx::Point point_in_screen(GetCursorScreenPoint());
   1563       Detach(DONT_RELEASE_CAPTURE);
   1564       Attach(tab_strip_to_attach_to_after_exit_, point_in_screen);
   1565       // Move the tabs into position.
   1566       MoveAttached(point_in_screen);
   1567       attached_tabstrip_->GetWidget()->Activate();
   1568       // Activate may trigger a focus loss, destroying us.
   1569       if (!ref)
   1570         return;
   1571       tab_strip_to_attach_to_after_exit_ = NULL;
   1572     }
   1573     DCHECK(attached_tabstrip_);
   1574     attached_tabstrip_->GetWidget()->SetCapture(attached_tabstrip_);
   1575   } else if (active_) {
   1576     EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ?
   1577             END_DRAG_CANCEL : END_DRAG_COMPLETE);
   1578   }
   1579 }
   1580 
   1581 int TabDragController::GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
   1582                                              int start,
   1583                                              int delta) const {
   1584   for (int i = start, tab_count = attached_tabstrip_->tab_count();
   1585        i >= 0 && i < tab_count; i += delta) {
   1586     const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
   1587     gfx::Rect left_half, right_half;
   1588     ideal_bounds.SplitVertically(&left_half, &right_half);
   1589     if (dragged_bounds.x() >= right_half.x() &&
   1590         dragged_bounds.x() < right_half.right()) {
   1591       return i + 1;
   1592     } else if (dragged_bounds.x() >= left_half.x() &&
   1593                dragged_bounds.x() < left_half.right()) {
   1594       return i;
   1595     }
   1596   }
   1597   return -1;
   1598 }
   1599 
   1600 int TabDragController::GetInsertionIndexForDraggedBounds(
   1601     const gfx::Rect& dragged_bounds) const {
   1602   int index = -1;
   1603   if (attached_tabstrip_->touch_layout_.get()) {
   1604     index = GetInsertionIndexForDraggedBoundsStacked(dragged_bounds);
   1605     if (index != -1) {
   1606       // Only move the tab to the left/right if the user actually moved the
   1607       // mouse that way. This is necessary as tabs with stacked tabs
   1608       // before/after them have multiple drag positions.
   1609       int active_index = attached_tabstrip_->touch_layout_->active_index();
   1610       if ((index < active_index &&
   1611            (mouse_move_direction_ & kMovedMouseLeft) == 0) ||
   1612           (index > active_index &&
   1613            (mouse_move_direction_ & kMovedMouseRight) == 0)) {
   1614         index = active_index;
   1615       }
   1616     }
   1617   } else {
   1618     index = GetInsertionIndexFrom(dragged_bounds, 0, 1);
   1619   }
   1620   if (index == -1) {
   1621     int tab_count = attached_tabstrip_->tab_count();
   1622     int right_tab_x = tab_count == 0 ? 0 :
   1623         attached_tabstrip_->ideal_bounds(tab_count - 1).right();
   1624     if (dragged_bounds.right() > right_tab_x) {
   1625       index = GetModel(attached_tabstrip_)->count();
   1626     } else {
   1627       index = 0;
   1628     }
   1629   }
   1630 
   1631   if (!drag_data_[0].attached_tab) {
   1632     // If 'attached_tab' is NULL, it means we're in the process of attaching and
   1633     // don't need to constrain the index.
   1634     return index;
   1635   }
   1636 
   1637   int max_index = GetModel(attached_tabstrip_)->count() -
   1638       static_cast<int>(drag_data_.size());
   1639   return std::max(0, std::min(max_index, index));
   1640 }
   1641 
   1642 bool TabDragController::ShouldDragToNextStackedTab(
   1643     const gfx::Rect& dragged_bounds,
   1644     int index) const {
   1645   if (index + 1 >= attached_tabstrip_->tab_count() ||
   1646       !attached_tabstrip_->touch_layout_->IsStacked(index + 1) ||
   1647       (mouse_move_direction_ & kMovedMouseRight) == 0)
   1648     return false;
   1649 
   1650   int active_x = attached_tabstrip_->ideal_bounds(index).x();
   1651   int next_x = attached_tabstrip_->ideal_bounds(index + 1).x();
   1652   int mid_x = std::min(next_x - kStackedDistance,
   1653                        active_x + (next_x - active_x) / 4);
   1654   return dragged_bounds.x() >= mid_x;
   1655 }
   1656 
   1657 bool TabDragController::ShouldDragToPreviousStackedTab(
   1658     const gfx::Rect& dragged_bounds,
   1659     int index) const {
   1660   if (index - 1 < attached_tabstrip_->GetMiniTabCount() ||
   1661       !attached_tabstrip_->touch_layout_->IsStacked(index - 1) ||
   1662       (mouse_move_direction_ & kMovedMouseLeft) == 0)
   1663     return false;
   1664 
   1665   int active_x = attached_tabstrip_->ideal_bounds(index).x();
   1666   int previous_x = attached_tabstrip_->ideal_bounds(index - 1).x();
   1667   int mid_x = std::max(previous_x + kStackedDistance,
   1668                        active_x - (active_x - previous_x) / 4);
   1669   return dragged_bounds.x() <= mid_x;
   1670 }
   1671 
   1672 int TabDragController::GetInsertionIndexForDraggedBoundsStacked(
   1673     const gfx::Rect& dragged_bounds) const {
   1674   StackedTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
   1675   int active_index = touch_layout->active_index();
   1676   // Search from the active index to the front of the tabstrip. Do this as tabs
   1677   // overlap each other from the active index.
   1678   int index = GetInsertionIndexFrom(dragged_bounds, active_index, -1);
   1679   if (index != active_index)
   1680     return index;
   1681   if (index == -1)
   1682     return GetInsertionIndexFrom(dragged_bounds, active_index + 1, 1);
   1683 
   1684   // The position to drag to corresponds to the active tab. If the next/previous
   1685   // tab is stacked, then shorten the distance used to determine insertion
   1686   // bounds. We do this as GetInsertionIndexFrom() uses the bounds of the
   1687   // tabs. When tabs are stacked the next/previous tab is on top of the tab.
   1688   if (active_index + 1 < attached_tabstrip_->tab_count() &&
   1689       touch_layout->IsStacked(active_index + 1)) {
   1690     index = GetInsertionIndexFrom(dragged_bounds, active_index + 1, 1);
   1691     if (index == -1 && ShouldDragToNextStackedTab(dragged_bounds, active_index))
   1692       index = active_index + 1;
   1693     else if (index == -1)
   1694       index = active_index;
   1695   } else if (ShouldDragToPreviousStackedTab(dragged_bounds, active_index)) {
   1696     index = active_index - 1;
   1697   }
   1698   return index;
   1699 }
   1700 
   1701 gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
   1702     const gfx::Point& tab_strip_point) {
   1703   // attached_tab is NULL when inserting into a new tabstrip.
   1704   if (source_tab_drag_data()->attached_tab) {
   1705     return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
   1706                      source_tab_drag_data()->attached_tab->width(),
   1707                      source_tab_drag_data()->attached_tab->height());
   1708   }
   1709 
   1710   double sel_width, unselected_width;
   1711   attached_tabstrip_->GetCurrentTabWidths(&sel_width, &unselected_width);
   1712   return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
   1713                    static_cast<int>(sel_width),
   1714                    Tab::GetStandardSize().height());
   1715 }
   1716 
   1717 gfx::Point TabDragController::GetAttachedDragPoint(
   1718     const gfx::Point& point_in_screen) {
   1719   DCHECK(attached_tabstrip_);  // The tab must be attached.
   1720 
   1721   gfx::Point tab_loc(point_in_screen);
   1722   views::View::ConvertPointFromScreen(attached_tabstrip_, &tab_loc);
   1723   const int x =
   1724       attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
   1725 
   1726   // TODO: consider caching this.
   1727   std::vector<Tab*> attached_tabs;
   1728   for (size_t i = 0; i < drag_data_.size(); ++i)
   1729     attached_tabs.push_back(drag_data_[i].attached_tab);
   1730   const int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
   1731   const int max_x = attached_tabstrip_->width() - size;
   1732   return gfx::Point(std::min(std::max(x, 0), max_x), 0);
   1733 }
   1734 
   1735 std::vector<Tab*> TabDragController::GetTabsMatchingDraggedContents(
   1736     TabStrip* tabstrip) {
   1737   TabStripModel* model = GetModel(attached_tabstrip_);
   1738   std::vector<Tab*> tabs;
   1739   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1740     int model_index = model->GetIndexOfWebContents(drag_data_[i].contents);
   1741     if (model_index == TabStripModel::kNoTab)
   1742       return std::vector<Tab*>();
   1743     tabs.push_back(tabstrip->tab_at(model_index));
   1744   }
   1745   return tabs;
   1746 }
   1747 
   1748 std::vector<gfx::Rect> TabDragController::CalculateBoundsForDraggedTabs() {
   1749   std::vector<gfx::Rect> drag_bounds;
   1750   std::vector<Tab*> attached_tabs;
   1751   for (size_t i = 0; i < drag_data_.size(); ++i)
   1752     attached_tabs.push_back(drag_data_[i].attached_tab);
   1753   attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
   1754                                                     &drag_bounds);
   1755   return drag_bounds;
   1756 }
   1757 
   1758 void TabDragController::EndDragImpl(EndDragType type) {
   1759   DCHECK(active_);
   1760   active_ = false;
   1761 
   1762   bring_to_front_timer_.Stop();
   1763   move_stacked_timer_.Stop();
   1764 
   1765   if (is_dragging_window_) {
   1766     waiting_for_run_loop_to_exit_ = true;
   1767 
   1768     if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1)) {
   1769       SetWindowPositionManaged(GetAttachedBrowserWidget()->GetNativeView(),
   1770                                true);
   1771     }
   1772 
   1773     // End the nested drag loop.
   1774     GetAttachedBrowserWidget()->EndMoveLoop();
   1775   }
   1776 
   1777   // Hide the current dock controllers.
   1778   for (size_t i = 0; i < dock_controllers_.size(); ++i) {
   1779     // Be sure and clear the controller first, that way if Hide ends up
   1780     // deleting the controller it won't call us back.
   1781     dock_controllers_[i]->clear_controller();
   1782     dock_controllers_[i]->Hide();
   1783   }
   1784   dock_controllers_.clear();
   1785   dock_windows_.clear();
   1786 
   1787   if (type != TAB_DESTROYED) {
   1788     // We only finish up the drag if we were actually dragging. If start_drag_
   1789     // is false, the user just clicked and released and didn't move the mouse
   1790     // enough to trigger a drag.
   1791     if (started_drag_) {
   1792       RestoreFocus();
   1793       if (type == CANCELED)
   1794         RevertDrag();
   1795       else
   1796         CompleteDrag();
   1797     }
   1798   } else if (drag_data_.size() > 1) {
   1799     initial_selection_model_.Clear();
   1800     RevertDrag();
   1801   }  // else case the only tab we were dragging was deleted. Nothing to do.
   1802 
   1803   if (!detach_into_browser_)
   1804     ResetDelegates();
   1805 
   1806   // Clear out drag data so we don't attempt to do anything with it.
   1807   drag_data_.clear();
   1808 
   1809   TabStrip* owning_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
   1810       attached_tabstrip_ : source_tabstrip_;
   1811   owning_tabstrip->DestroyDragController();
   1812 }
   1813 
   1814 void TabDragController::RevertDrag() {
   1815   std::vector<Tab*> tabs;
   1816   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1817     if (drag_data_[i].contents) {
   1818       // Contents is NULL if a tab was destroyed while the drag was under way.
   1819       tabs.push_back(drag_data_[i].attached_tab);
   1820       RevertDragAt(i);
   1821     }
   1822   }
   1823 
   1824   bool restore_frame = !detach_into_browser_ &&
   1825                        attached_tabstrip_ != source_tabstrip_;
   1826   if (attached_tabstrip_) {
   1827     if (attached_tabstrip_ == source_tabstrip_) {
   1828       source_tabstrip_->StoppedDraggingTabs(
   1829           tabs, initial_tab_positions_, move_behavior_ == MOVE_VISIBILE_TABS,
   1830           false);
   1831     } else {
   1832       attached_tabstrip_->DraggedTabsDetached();
   1833     }
   1834   }
   1835 
   1836   if (initial_selection_model_.empty())
   1837     ResetSelection(GetModel(source_tabstrip_));
   1838   else
   1839     GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
   1840 
   1841   // If we're not attached to any TabStrip, or attached to some other TabStrip,
   1842   // we need to restore the bounds of the original TabStrip's frame, in case
   1843   // it has been hidden.
   1844   if (restore_frame && !restore_bounds_.IsEmpty())
   1845     source_tabstrip_->GetWidget()->SetBounds(restore_bounds_);
   1846 
   1847   if (detach_into_browser_ && source_tabstrip_)
   1848     source_tabstrip_->GetWidget()->Activate();
   1849 
   1850   // Return the WebContents to normalcy.  If the tab was attached to a
   1851   // TabStrip before the revert, the decrement has already occurred.
   1852   // If the tab was destroyed, don't attempt to dereference the
   1853   // WebContents pointer.
   1854   if (!detach_into_browser_ && !attached_tabstrip_ && source_dragged_contents())
   1855     source_dragged_contents()->DecrementCapturerCount();
   1856 }
   1857 
   1858 void TabDragController::ResetSelection(TabStripModel* model) {
   1859   DCHECK(model);
   1860   ui::ListSelectionModel selection_model;
   1861   bool has_one_valid_tab = false;
   1862   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1863     // |contents| is NULL if a tab was deleted out from under us.
   1864     if (drag_data_[i].contents) {
   1865       int index = model->GetIndexOfWebContents(drag_data_[i].contents);
   1866       DCHECK_NE(-1, index);
   1867       selection_model.AddIndexToSelection(index);
   1868       if (!has_one_valid_tab || i == source_tab_index_) {
   1869         // Reset the active/lead to the first tab. If the source tab is still
   1870         // valid we'll reset these again later on.
   1871         selection_model.set_active(index);
   1872         selection_model.set_anchor(index);
   1873         has_one_valid_tab = true;
   1874       }
   1875     }
   1876   }
   1877   if (!has_one_valid_tab)
   1878     return;
   1879 
   1880   model->SetSelectionFromModel(selection_model);
   1881 }
   1882 
   1883 void TabDragController::RevertDragAt(size_t drag_index) {
   1884   DCHECK(started_drag_);
   1885   DCHECK(source_tabstrip_);
   1886 
   1887   base::AutoReset<bool> setter(&is_mutating_, true);
   1888   TabDragData* data = &(drag_data_[drag_index]);
   1889   if (attached_tabstrip_) {
   1890     int index =
   1891         GetModel(attached_tabstrip_)->GetIndexOfWebContents(data->contents);
   1892     if (attached_tabstrip_ != source_tabstrip_) {
   1893       // The Tab was inserted into another TabStrip. We need to put it back
   1894       // into the original one.
   1895       GetModel(attached_tabstrip_)->DetachWebContentsAt(index);
   1896       // TODO(beng): (Cleanup) seems like we should use Attach() for this
   1897       //             somehow.
   1898       GetModel(source_tabstrip_)->InsertWebContentsAt(
   1899           data->source_model_index, data->contents,
   1900           (data->pinned ? TabStripModel::ADD_PINNED : 0));
   1901     } else {
   1902       // The Tab was moved within the TabStrip where the drag was initiated.
   1903       // Move it back to the starting location.
   1904       GetModel(source_tabstrip_)->MoveWebContentsAt(
   1905           index, data->source_model_index, false);
   1906     }
   1907   } else {
   1908     // The Tab was detached from the TabStrip where the drag began, and has not
   1909     // been attached to any other TabStrip. We need to put it back into the
   1910     // source TabStrip.
   1911     GetModel(source_tabstrip_)->InsertWebContentsAt(
   1912         data->source_model_index, data->contents,
   1913         (data->pinned ? TabStripModel::ADD_PINNED : 0));
   1914   }
   1915 }
   1916 
   1917 void TabDragController::CompleteDrag() {
   1918   DCHECK(started_drag_);
   1919 
   1920   if (attached_tabstrip_) {
   1921     if (is_dragging_new_browser_) {
   1922       if (IsDockedOrSnapped(attached_tabstrip_)) {
   1923         DCHECK_EQ(host_desktop_type_, chrome::HOST_DESKTOP_TYPE_ASH);
   1924         was_source_maximized_ = false;
   1925         was_source_fullscreen_ = false;
   1926       }
   1927       // If source window was maximized - maximize the new window as well.
   1928       if (was_source_maximized_ || was_source_fullscreen_)
   1929         GetAttachedBrowserWidget()->Maximize();
   1930 #if defined(USE_ASH)
   1931       if (was_source_fullscreen_ &&
   1932           host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
   1933         // In fullscreen mode it is only possible to get here if the source
   1934         // was in "immersive fullscreen" mode, so toggle it back on.
   1935         ash::accelerators::ToggleFullscreen();
   1936       }
   1937 #endif
   1938     }
   1939     attached_tabstrip_->StoppedDraggingTabs(
   1940         GetTabsMatchingDraggedContents(attached_tabstrip_),
   1941         initial_tab_positions_,
   1942         move_behavior_ == MOVE_VISIBILE_TABS,
   1943         true);
   1944   } else {
   1945     if (dock_info_.type() != DockInfo::NONE) {
   1946       switch (dock_info_.type()) {
   1947         case DockInfo::LEFT_OF_WINDOW:
   1948           content::RecordAction(UserMetricsAction("DockingWindow_Left"));
   1949           break;
   1950 
   1951         case DockInfo::RIGHT_OF_WINDOW:
   1952           content::RecordAction(UserMetricsAction("DockingWindow_Right"));
   1953           break;
   1954 
   1955         case DockInfo::BOTTOM_OF_WINDOW:
   1956           content::RecordAction(UserMetricsAction("DockingWindow_Bottom"));
   1957           break;
   1958 
   1959         case DockInfo::TOP_OF_WINDOW:
   1960           content::RecordAction(UserMetricsAction("DockingWindow_Top"));
   1961           break;
   1962 
   1963         case DockInfo::MAXIMIZE:
   1964           content::RecordAction(
   1965               UserMetricsAction("DockingWindow_Maximize"));
   1966           break;
   1967 
   1968         case DockInfo::LEFT_HALF:
   1969           content::RecordAction(
   1970               UserMetricsAction("DockingWindow_LeftHalf"));
   1971           break;
   1972 
   1973         case DockInfo::RIGHT_HALF:
   1974           content::RecordAction(
   1975               UserMetricsAction("DockingWindow_RightHalf"));
   1976           break;
   1977 
   1978         case DockInfo::BOTTOM_HALF:
   1979           content::RecordAction(
   1980               UserMetricsAction("DockingWindow_BottomHalf"));
   1981           break;
   1982 
   1983         default:
   1984           NOTREACHED();
   1985           break;
   1986       }
   1987     }
   1988     // Compel the model to construct a new window for the detached
   1989     // WebContentses.
   1990     views::Widget* widget = source_tabstrip_->GetWidget();
   1991     gfx::Rect window_bounds(widget->GetRestoredBounds());
   1992     window_bounds.set_origin(GetWindowCreatePoint(last_point_in_screen_));
   1993 
   1994     // When modifying the following if statement, please make sure not to
   1995     // introduce issue listed in http://crbug.com/6223 comment #11.
   1996     bool rtl_ui = base::i18n::IsRTL();
   1997     bool has_dock_position = (dock_info_.type() != DockInfo::NONE);
   1998     if (rtl_ui && has_dock_position) {
   1999       // Mirror X axis so the docked tab is aligned using the mouse click as
   2000       // the top-right corner.
   2001       window_bounds.set_x(window_bounds.x() - window_bounds.width());
   2002     }
   2003     base::AutoReset<bool> setter(&is_mutating_, true);
   2004 
   2005     std::vector<TabStripModelDelegate::NewStripContents> contentses;
   2006     for (size_t i = 0; i < drag_data_.size(); ++i) {
   2007       TabStripModelDelegate::NewStripContents item;
   2008       item.web_contents = drag_data_[i].contents;
   2009       item.add_types = drag_data_[i].pinned ? TabStripModel::ADD_PINNED
   2010                                             : TabStripModel::ADD_NONE;
   2011       contentses.push_back(item);
   2012     }
   2013 
   2014     Browser* new_browser =
   2015         GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
   2016             contentses, window_bounds, dock_info_, widget->IsMaximized());
   2017     ResetSelection(new_browser->tab_strip_model());
   2018     new_browser->window()->Show();
   2019 
   2020     // Return the WebContents to normalcy.
   2021     if (!detach_into_browser_)
   2022       source_dragged_contents()->DecrementCapturerCount();
   2023   }
   2024 
   2025   CleanUpHiddenFrame();
   2026 }
   2027 
   2028 void TabDragController::ResetDelegates() {
   2029   DCHECK(!detach_into_browser_);
   2030   for (size_t i = 0; i < drag_data_.size(); ++i) {
   2031     if (drag_data_[i].contents &&
   2032         drag_data_[i].contents->GetDelegate() == this) {
   2033       drag_data_[i].contents->SetDelegate(
   2034           drag_data_[i].original_delegate);
   2035     }
   2036   }
   2037 }
   2038 
   2039 void TabDragController::CreateDraggedView(
   2040     const std::vector<TabRendererData>& data,
   2041     const std::vector<gfx::Rect>& renderer_bounds) {
   2042 #if !defined(USE_AURA)
   2043   DCHECK(!view_.get());
   2044   DCHECK_EQ(data.size(), drag_data_.size());
   2045 
   2046   // Set up the photo booth to start capturing the contents of the dragged
   2047   // WebContents.
   2048   NativeViewPhotobooth* photobooth = NativeViewPhotobooth::Create(
   2049       source_dragged_contents()->GetView()->GetNativeView());
   2050 
   2051   gfx::Rect content_bounds;
   2052   source_dragged_contents()->GetView()->GetContainerBounds(&content_bounds);
   2053 
   2054   std::vector<views::View*> renderers;
   2055   for (size_t i = 0; i < drag_data_.size(); ++i) {
   2056     Tab* renderer = source_tabstrip_->CreateTabForDragging();
   2057     renderer->SetData(data[i]);
   2058     renderers.push_back(renderer);
   2059   }
   2060   // DraggedTabView takes ownership of the renderers.
   2061   view_.reset(new DraggedTabView(renderers, renderer_bounds, mouse_offset_,
   2062                                  content_bounds.size(), photobooth));
   2063 #else
   2064   // Aura always hits the |detach_into_browser_| path.
   2065   NOTREACHED();
   2066 #endif
   2067 }
   2068 
   2069 gfx::Rect TabDragController::GetViewScreenBounds(
   2070     views::View* view) const {
   2071   gfx::Point view_topleft;
   2072   views::View::ConvertPointToScreen(view, &view_topleft);
   2073   gfx::Rect view_screen_bounds = view->GetLocalBounds();
   2074   view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
   2075   return view_screen_bounds;
   2076 }
   2077 
   2078 void TabDragController::HideFrame() {
   2079 #if defined(OS_WIN) && !defined(USE_AURA)
   2080   // We don't actually hide the window, rather we just move it way off-screen.
   2081   // If we actually hide it, we stop receiving drag events.
   2082   //
   2083   // Windows coordinates are 16 bit values. Additionally mouse events are
   2084   // relative, this means if we move this window to the max position it is easy
   2085   // to trigger overflow. To avoid this we don't move to the max position,
   2086   // rather some where reasonably large. This should avoid common overflow
   2087   // problems.
   2088   // An alternative approach is to query the mouse pointer and ignore the
   2089   // location on the mouse (early versions did this). This proves problematic as
   2090   // if we happen to get behind in event processing it is all to easy to process
   2091   // a release in the wrong location, triggering either an unexpected move or an
   2092   // unexpected detach.
   2093   HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView();
   2094   RECT wr;
   2095   GetWindowRect(frame_hwnd, &wr);
   2096   MoveWindow(frame_hwnd, 0x3FFF, 0x3FFF, wr.right - wr.left,
   2097              wr.bottom - wr.top, TRUE);
   2098 
   2099   // We also save the bounds of the window prior to it being moved, so that if
   2100   // the drag session is aborted we can restore them.
   2101   restore_bounds_ = gfx::Rect(wr);
   2102 #else
   2103   // Shouldn't hit as aura triggers the |detach_into_browser_| path.
   2104   NOTREACHED();
   2105 #endif
   2106 }
   2107 
   2108 void TabDragController::CleanUpHiddenFrame() {
   2109   // If the model we started dragging from is now empty, we must ask the
   2110   // delegate to close the frame.
   2111   if (!detach_into_browser_ && GetModel(source_tabstrip_)->empty())
   2112     GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession();
   2113 }
   2114 
   2115 void TabDragController::DockDisplayerDestroyed(
   2116     DockDisplayer* controller) {
   2117   DockWindows::iterator dock_i =
   2118       dock_windows_.find(controller->popup_view());
   2119   if (dock_i != dock_windows_.end())
   2120     dock_windows_.erase(dock_i);
   2121   else
   2122     NOTREACHED();
   2123 
   2124   std::vector<DockDisplayer*>::iterator i =
   2125       std::find(dock_controllers_.begin(), dock_controllers_.end(),
   2126                 controller);
   2127   if (i != dock_controllers_.end())
   2128     dock_controllers_.erase(i);
   2129   else
   2130     NOTREACHED();
   2131 }
   2132 
   2133 void TabDragController::BringWindowUnderPointToFront(
   2134     const gfx::Point& point_in_screen) {
   2135   // If we're going to dock to another window, bring it to the front.
   2136   gfx::NativeWindow window = dock_info_.window();
   2137   if (!window) {
   2138     views::View* dragged_view;
   2139     if (view_.get())
   2140       dragged_view = view_.get();
   2141     else
   2142       dragged_view = attached_tabstrip_;
   2143     gfx::NativeView dragged_native_view =
   2144         dragged_view->GetWidget()->GetNativeView();
   2145     dock_windows_.insert(dragged_native_view);
   2146     window = DockInfo::GetLocalProcessWindowAtPoint(
   2147         host_desktop_type_,
   2148         point_in_screen,
   2149         dock_windows_);
   2150     dock_windows_.erase(dragged_native_view);
   2151     // Only bring browser windows to front - only windows with a TabStrip can
   2152     // be tab drag targets.
   2153     if (!GetTabStripForWindow(window))
   2154       return;
   2155   }
   2156   if (window) {
   2157     views::Widget* widget_window = views::Widget::GetWidgetForNativeView(
   2158         window);
   2159     if (!widget_window)
   2160       return;
   2161 
   2162 #if defined(USE_ASH)
   2163     if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) {
   2164       // TODO(varkha): The code below ensures that the phantom drag widget
   2165       // is shown on top of browser windows. The code should be moved to ash/
   2166       // and the phantom should be able to assert its top-most state on its own.
   2167       // One strategy would be for DragWindowController to
   2168       // be able to observe stacking changes to the phantom drag widget's
   2169       // siblings in order to keep it on top. One way is to implement a
   2170       // notification that is sent to a window parent's observers when a
   2171       // stacking order is changed among the children of that same parent.
   2172       // Note that OnWindowStackingChanged is sent only to the child that is the
   2173       // argument of one of the Window::StackChildX calls and not to all its
   2174       // siblings affected by the stacking change.
   2175       aura::Window* browser_window = widget_window->GetNativeView();
   2176       // Find a topmost non-popup window and stack the recipient browser above
   2177       // it in order to avoid stacking the browser window on top of the phantom
   2178       // drag widget created by DragWindowController in a second display.
   2179       for (aura::Window::Windows::const_reverse_iterator it =
   2180            browser_window->parent()->children().rbegin();
   2181            it != browser_window->parent()->children().rend(); ++it) {
   2182         // If the iteration reached the recipient browser window then it is
   2183         // already topmost and it is safe to return with no stacking change.
   2184         if (*it == browser_window)
   2185           return;
   2186         if ((*it)->type() != aura::client::WINDOW_TYPE_POPUP) {
   2187           widget_window->StackAbove(*it);
   2188           break;
   2189         }
   2190       }
   2191     } else {
   2192       widget_window->StackAtTop();
   2193     }
   2194 #else
   2195     widget_window->StackAtTop();
   2196 #endif
   2197 
   2198     // The previous call made the window appear on top of the dragged window,
   2199     // move the dragged window to the front.
   2200     if (view_.get())
   2201       view_->GetWidget()->StackAtTop();
   2202     else if (is_dragging_window_)
   2203       attached_tabstrip_->GetWidget()->StackAtTop();
   2204   }
   2205 }
   2206 
   2207 TabStripModel* TabDragController::GetModel(
   2208     TabStrip* tabstrip) const {
   2209   return static_cast<BrowserTabStripController*>(tabstrip->controller())->
   2210       model();
   2211 }
   2212 
   2213 views::Widget* TabDragController::GetAttachedBrowserWidget() {
   2214   return attached_tabstrip_->GetWidget();
   2215 }
   2216 
   2217 bool TabDragController::AreTabsConsecutive() {
   2218   for (size_t i = 1; i < drag_data_.size(); ++i) {
   2219     if (drag_data_[i - 1].source_model_index + 1 !=
   2220         drag_data_[i].source_model_index) {
   2221       return false;
   2222     }
   2223   }
   2224   return true;
   2225 }
   2226 
   2227 gfx::Rect TabDragController::CalculateDraggedBrowserBounds(
   2228     TabStrip* source,
   2229     const gfx::Point& point_in_screen,
   2230     std::vector<gfx::Rect>* drag_bounds) {
   2231   gfx::Point center(0, source->height() / 2);
   2232   views::View::ConvertPointToWidget(source, &center);
   2233   gfx::Rect new_bounds(source->GetWidget()->GetRestoredBounds());
   2234   if (source->GetWidget()->IsMaximized()) {
   2235     // If the restore bounds is really small, we don't want to honor it
   2236     // (dragging a really small window looks wrong), instead make sure the new
   2237     // window is at least 50% the size of the old.
   2238     const gfx::Size max_size(
   2239         source->GetWidget()->GetWindowBoundsInScreen().size());
   2240     new_bounds.set_width(
   2241         std::max(max_size.width() / 2, new_bounds.width()));
   2242     new_bounds.set_height(
   2243         std::max(max_size.height() / 2, new_bounds.height()));
   2244   }
   2245   new_bounds.set_y(point_in_screen.y() - center.y());
   2246   switch (GetDetachPosition(point_in_screen)) {
   2247     case DETACH_BEFORE:
   2248       new_bounds.set_x(point_in_screen.x() - center.x());
   2249       new_bounds.Offset(-mouse_offset_.x(), 0);
   2250       break;
   2251     case DETACH_AFTER: {
   2252       gfx::Point right_edge(source->width(), 0);
   2253       views::View::ConvertPointToWidget(source, &right_edge);
   2254       new_bounds.set_x(point_in_screen.x() - right_edge.x());
   2255       new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
   2256       OffsetX(-(*drag_bounds)[0].x(), drag_bounds);
   2257       break;
   2258     }
   2259     default:
   2260       break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
   2261   }
   2262 
   2263   // To account for the extra vertical on restored windows that is absent on
   2264   // maximized windows, add an additional vertical offset extracted from the tab
   2265   // strip.
   2266   if (source->GetWidget()->IsMaximized())
   2267     new_bounds.Offset(0, -source->button_v_offset());
   2268   return new_bounds;
   2269 }
   2270 
   2271 void TabDragController::AdjustBrowserAndTabBoundsForDrag(
   2272     int last_tabstrip_width,
   2273     const gfx::Point& point_in_screen,
   2274     std::vector<gfx::Rect>* drag_bounds) {
   2275   attached_tabstrip_->InvalidateLayout();
   2276   attached_tabstrip_->DoLayout();
   2277   const int dragged_tabstrip_width = attached_tabstrip_->tab_area_width();
   2278 
   2279   // If the new tabstrip is smaller than the old resize the tabs.
   2280   if (dragged_tabstrip_width < last_tabstrip_width) {
   2281     const float leading_ratio =
   2282         drag_bounds->front().x() / static_cast<float>(last_tabstrip_width);
   2283     *drag_bounds = CalculateBoundsForDraggedTabs();
   2284 
   2285     if (drag_bounds->back().right() < dragged_tabstrip_width) {
   2286       const int delta_x =
   2287           std::min(static_cast<int>(leading_ratio * dragged_tabstrip_width),
   2288                    dragged_tabstrip_width -
   2289                        (drag_bounds->back().right() -
   2290                         drag_bounds->front().x()));
   2291       OffsetX(delta_x, drag_bounds);
   2292     }
   2293 
   2294     // Reposition the restored window such that the tab that was dragged remains
   2295     // under the mouse cursor.
   2296     gfx::Point offset(
   2297         static_cast<int>((*drag_bounds)[source_tab_index_].width() *
   2298                          offset_to_width_ratio_) +
   2299         (*drag_bounds)[source_tab_index_].x(), 0);
   2300     views::View::ConvertPointToWidget(attached_tabstrip_, &offset);
   2301     gfx::Rect bounds = GetAttachedBrowserWidget()->GetWindowBoundsInScreen();
   2302     bounds.set_x(point_in_screen.x() - offset.x());
   2303     GetAttachedBrowserWidget()->SetBounds(bounds);
   2304   }
   2305   attached_tabstrip_->SetTabBoundsForDrag(*drag_bounds);
   2306 }
   2307 
   2308 Browser* TabDragController::CreateBrowserForDrag(
   2309     TabStrip* source,
   2310     const gfx::Point& point_in_screen,
   2311     gfx::Vector2d* drag_offset,
   2312     std::vector<gfx::Rect>* drag_bounds) {
   2313   gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source,
   2314                                                      point_in_screen,
   2315                                                      drag_bounds));
   2316   *drag_offset = point_in_screen - new_bounds.origin();
   2317 
   2318   Profile* profile =
   2319       Profile::FromBrowserContext(drag_data_[0].contents->GetBrowserContext());
   2320   Browser::CreateParams create_params(Browser::TYPE_TABBED,
   2321                                       profile,
   2322                                       host_desktop_type_);
   2323   create_params.initial_bounds = new_bounds;
   2324   Browser* browser = new Browser(create_params);
   2325   is_dragging_new_browser_ = true;
   2326   SetWindowPositionManaged(browser->window()->GetNativeWindow(), false);
   2327   // If the window is created maximized then the bounds we supplied are ignored.
   2328   // We need to reset them again so they are honored.
   2329   browser->window()->SetBounds(new_bounds);
   2330 
   2331   return browser;
   2332 }
   2333 
   2334 gfx::Point TabDragController::GetCursorScreenPoint() {
   2335 #if defined(USE_ASH)
   2336   if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH &&
   2337       event_source_ == EVENT_SOURCE_TOUCH &&
   2338       aura::Env::GetInstance()->is_touch_down()) {
   2339     views::Widget* widget = GetAttachedBrowserWidget();
   2340     DCHECK(widget);
   2341     aura::Window* widget_window = widget->GetNativeWindow();
   2342     DCHECK(widget_window->GetRootWindow());
   2343     gfx::Point touch_point;
   2344     bool got_touch_point = ui::GestureRecognizer::Get()->
   2345         GetLastTouchPointForTarget(widget_window, &touch_point);
   2346     DCHECK(got_touch_point);
   2347     ash::wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point);
   2348     return touch_point;
   2349   }
   2350 #endif
   2351   return screen_->GetCursorScreenPoint();
   2352 }
   2353 
   2354 gfx::Vector2d TabDragController::GetWindowOffset(
   2355     const gfx::Point& point_in_screen) {
   2356   TabStrip* owning_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
   2357       attached_tabstrip_ : source_tabstrip_;
   2358   views::View* toplevel_view = owning_tabstrip->GetWidget()->GetContentsView();
   2359 
   2360   gfx::Point point = point_in_screen;
   2361   views::View::ConvertPointFromScreen(toplevel_view, &point);
   2362   return point.OffsetFromOrigin();
   2363 }
   2364