Home | History | Annotate | Download | only in tabs
      1 // Copyright (c) 2011 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/dragged_tab_controller.h"
      6 
      7 #include <math.h>
      8 #include <set>
      9 
     10 #include "base/callback.h"
     11 #include "base/i18n/rtl.h"
     12 #include "chrome/browser/extensions/extension_function_dispatcher.h"
     13 #include "chrome/browser/metrics/user_metrics.h"
     14 #include "chrome/browser/tabs/tab_strip_model.h"
     15 #include "chrome/browser/ui/browser_window.h"
     16 #include "chrome/browser/ui/views/frame/browser_view.h"
     17 #include "chrome/browser/ui/views/tabs/base_tab.h"
     18 #include "chrome/browser/ui/views/tabs/base_tab_strip.h"
     19 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
     20 #include "chrome/browser/ui/views/tabs/dragged_tab_view.h"
     21 #include "chrome/browser/ui/views/tabs/native_view_photobooth.h"
     22 #include "chrome/browser/ui/views/tabs/side_tab.h"
     23 #include "chrome/browser/ui/views/tabs/side_tab_strip.h"
     24 #include "chrome/browser/ui/views/tabs/tab.h"
     25 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     26 #include "content/browser/tab_contents/tab_contents.h"
     27 #include "content/common/notification_details.h"
     28 #include "content/common/notification_source.h"
     29 #include "grit/theme_resources.h"
     30 #include "third_party/skia/include/core/SkBitmap.h"
     31 #include "ui/base/animation/animation.h"
     32 #include "ui/base/animation/animation_delegate.h"
     33 #include "ui/base/animation/slide_animation.h"
     34 #include "ui/base/resource/resource_bundle.h"
     35 #include "ui/gfx/canvas_skia.h"
     36 #include "views/events/event.h"
     37 #include "views/screen.h"
     38 #include "views/widget/root_view.h"
     39 #include "views/widget/widget.h"
     40 #include "views/window/window.h"
     41 
     42 #if defined(OS_WIN)
     43 #include "views/widget/widget_win.h"
     44 #endif
     45 
     46 #if defined(OS_LINUX)
     47 #include <gdk/gdk.h>  // NOLINT
     48 #include <gdk/gdkkeysyms.h>  // NOLINT
     49 #endif
     50 
     51 static const int kHorizontalMoveThreshold = 16;  // Pixels.
     52 
     53 // Distance in pixels the user must move the mouse before we consider moving
     54 // an attached vertical tab.
     55 static const int kVerticalMoveThreshold = 8;
     56 
     57 // If non-null there is a drag underway.
     58 static DraggedTabController* instance_;
     59 
     60 namespace {
     61 
     62 // Delay, in ms, during dragging before we bring a window to front.
     63 const int kBringToFrontDelay = 750;
     64 
     65 // Radius of the rect drawn by DockView.
     66 const int kRoundedRectRadius = 4;
     67 
     68 // Spacing between tab icons when DockView is showing a docking location that
     69 // contains more than one tab.
     70 const int kTabSpacing = 4;
     71 
     72 // DockView is the view responsible for giving a visual indicator of where a
     73 // dock is going to occur.
     74 
     75 class DockView : public views::View {
     76  public:
     77   explicit DockView(DockInfo::Type type) : type_(type) {}
     78 
     79   virtual gfx::Size GetPreferredSize() {
     80     return gfx::Size(DockInfo::popup_width(), DockInfo::popup_height());
     81   }
     82 
     83   virtual void OnPaintBackground(gfx::Canvas* canvas) {
     84     SkRect outer_rect = { SkIntToScalar(0), SkIntToScalar(0),
     85                           SkIntToScalar(width()),
     86                           SkIntToScalar(height()) };
     87 
     88     // Fill the background rect.
     89     SkPaint paint;
     90     paint.setColor(SkColorSetRGB(108, 108, 108));
     91     paint.setStyle(SkPaint::kFill_Style);
     92     canvas->AsCanvasSkia()->drawRoundRect(
     93         outer_rect, SkIntToScalar(kRoundedRectRadius),
     94         SkIntToScalar(kRoundedRectRadius), paint);
     95 
     96     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     97 
     98     SkBitmap* high_icon = rb.GetBitmapNamed(IDR_DOCK_HIGH);
     99     SkBitmap* wide_icon = rb.GetBitmapNamed(IDR_DOCK_WIDE);
    100 
    101     canvas->Save();
    102     bool rtl_ui = base::i18n::IsRTL();
    103     if (rtl_ui) {
    104       // Flip canvas to draw the mirrored tab images for RTL UI.
    105       canvas->TranslateInt(width(), 0);
    106       canvas->ScaleInt(-1, 1);
    107     }
    108     int x_of_active_tab = width() / 2 + kTabSpacing / 2;
    109     int x_of_inactive_tab = width() / 2 - high_icon->width() - kTabSpacing / 2;
    110     switch (type_) {
    111       case DockInfo::LEFT_OF_WINDOW:
    112       case DockInfo::LEFT_HALF:
    113         if (!rtl_ui)
    114           std::swap(x_of_active_tab, x_of_inactive_tab);
    115         canvas->DrawBitmapInt(*high_icon, x_of_active_tab,
    116                               (height() - high_icon->height()) / 2);
    117         if (type_ == DockInfo::LEFT_OF_WINDOW) {
    118           DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab,
    119                               (height() - high_icon->height()) / 2);
    120         }
    121         break;
    122 
    123 
    124       case DockInfo::RIGHT_OF_WINDOW:
    125       case DockInfo::RIGHT_HALF:
    126         if (rtl_ui)
    127           std::swap(x_of_active_tab, x_of_inactive_tab);
    128         canvas->DrawBitmapInt(*high_icon, x_of_active_tab,
    129                               (height() - high_icon->height()) / 2);
    130         if (type_ == DockInfo::RIGHT_OF_WINDOW) {
    131          DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab,
    132                              (height() - high_icon->height()) / 2);
    133         }
    134         break;
    135 
    136       case DockInfo::TOP_OF_WINDOW:
    137         canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2,
    138                               height() / 2 - high_icon->height());
    139         break;
    140 
    141       case DockInfo::MAXIMIZE: {
    142         SkBitmap* max_icon = rb.GetBitmapNamed(IDR_DOCK_MAX);
    143         canvas->DrawBitmapInt(*max_icon, (width() - max_icon->width()) / 2,
    144                               (height() - max_icon->height()) / 2);
    145         break;
    146       }
    147 
    148       case DockInfo::BOTTOM_HALF:
    149       case DockInfo::BOTTOM_OF_WINDOW:
    150         canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2,
    151                               height() / 2 + kTabSpacing / 2);
    152         if (type_ == DockInfo::BOTTOM_OF_WINDOW) {
    153           DrawBitmapWithAlpha(canvas, *wide_icon,
    154               (width() - wide_icon->width()) / 2,
    155               height() / 2 - kTabSpacing / 2 - wide_icon->height());
    156         }
    157         break;
    158 
    159       default:
    160         NOTREACHED();
    161         break;
    162     }
    163     canvas->Restore();
    164   }
    165 
    166  private:
    167   void DrawBitmapWithAlpha(gfx::Canvas* canvas, const SkBitmap& image,
    168                            int x, int y) {
    169     SkPaint paint;
    170     paint.setAlpha(128);
    171     canvas->DrawBitmapInt(image, x, y, paint);
    172   }
    173 
    174   DockInfo::Type type_;
    175 
    176   DISALLOW_COPY_AND_ASSIGN(DockView);
    177 };
    178 
    179 // Returns the the x-coordinate of |point| if the type of tabstrip is horizontal
    180 // otherwise returns the y-coordinate.
    181 int MajorAxisValue(const gfx::Point& point, BaseTabStrip* tabstrip) {
    182   return (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) ?
    183       point.x() : point.y();
    184 }
    185 
    186 }  // namespace
    187 
    188 ///////////////////////////////////////////////////////////////////////////////
    189 // DockDisplayer
    190 
    191 // DockDisplayer is responsible for giving the user a visual indication of a
    192 // possible dock position (as represented by DockInfo). DockDisplayer shows
    193 // a window with a DockView in it. Two animations are used that correspond to
    194 // the state of DockInfo::in_enable_area.
    195 class DraggedTabController::DockDisplayer : public ui::AnimationDelegate {
    196  public:
    197   DockDisplayer(DraggedTabController* controller,
    198                 const DockInfo& info)
    199       : controller_(controller),
    200         popup_(NULL),
    201         popup_view_(NULL),
    202         ALLOW_THIS_IN_INITIALIZER_LIST(animation_(this)),
    203         hidden_(false),
    204         in_enable_area_(info.in_enable_area()) {
    205 #if defined(OS_WIN)
    206     // TODO(sky): This should "just work" on Gtk now.
    207     views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
    208     params.transparent = true;
    209     params.keep_on_top = true;
    210     popup_ = views::Widget::CreateWidget(params);
    211     popup_->SetOpacity(0x00);
    212     popup_->Init(NULL, info.GetPopupRect());
    213     popup_->SetContentsView(new DockView(info.type()));
    214     if (info.in_enable_area())
    215       animation_.Reset(1);
    216     else
    217       animation_.Show();
    218     popup_->Show();
    219 #else
    220     NOTIMPLEMENTED();
    221 #endif
    222     popup_view_ = popup_->GetNativeView();
    223   }
    224 
    225   ~DockDisplayer() {
    226     if (controller_)
    227       controller_->DockDisplayerDestroyed(this);
    228   }
    229 
    230   // Updates the state based on |in_enable_area|.
    231   void UpdateInEnabledArea(bool in_enable_area) {
    232     if (in_enable_area != in_enable_area_) {
    233       in_enable_area_ = in_enable_area;
    234       UpdateLayeredAlpha();
    235     }
    236   }
    237 
    238   // Resets the reference to the hosting DraggedTabController. This is invoked
    239   // when the DraggedTabController is destoryed.
    240   void clear_controller() { controller_ = NULL; }
    241 
    242   // NativeView of the window we create.
    243   gfx::NativeView popup_view() { return popup_view_; }
    244 
    245   // Starts the hide animation. When the window is closed the
    246   // DraggedTabController is notified by way of the DockDisplayerDestroyed
    247   // method
    248   void Hide() {
    249     if (hidden_)
    250       return;
    251 
    252     if (!popup_) {
    253       delete this;
    254       return;
    255     }
    256     hidden_ = true;
    257     animation_.Hide();
    258   }
    259 
    260   virtual void AnimationProgressed(const ui::Animation* animation) {
    261     UpdateLayeredAlpha();
    262   }
    263 
    264   virtual void AnimationEnded(const ui::Animation* animation) {
    265     if (!hidden_)
    266       return;
    267 #if defined(OS_WIN)
    268     static_cast<views::WidgetWin*>(popup_)->Close();
    269 #else
    270     NOTIMPLEMENTED();
    271 #endif
    272     delete this;
    273   }
    274 
    275   virtual void UpdateLayeredAlpha() {
    276 #if defined(OS_WIN)
    277     double scale = in_enable_area_ ? 1 : .5;
    278     static_cast<views::WidgetWin*>(popup_)->SetOpacity(
    279         static_cast<BYTE>(animation_.GetCurrentValue() * scale * 255.0));
    280     popup_->GetRootView()->SchedulePaint();
    281 #else
    282     NOTIMPLEMENTED();
    283 #endif
    284   }
    285 
    286  private:
    287   // DraggedTabController that created us.
    288   DraggedTabController* controller_;
    289 
    290   // Window we're showing.
    291   views::Widget* popup_;
    292 
    293   // NativeView of |popup_|. We cache this to avoid the possibility of
    294   // invoking a method on popup_ after we close it.
    295   gfx::NativeView popup_view_;
    296 
    297   // Animation for when first made visible.
    298   ui::SlideAnimation animation_;
    299 
    300   // Have we been hidden?
    301   bool hidden_;
    302 
    303   // Value of DockInfo::in_enable_area.
    304   bool in_enable_area_;
    305 };
    306 
    307 DraggedTabController::TabDragData::TabDragData()
    308     : contents(NULL),
    309       original_delegate(NULL),
    310       source_model_index(-1),
    311       attached_tab(NULL),
    312       pinned(false) {
    313 }
    314 
    315 DraggedTabController::TabDragData::~TabDragData() {
    316 }
    317 
    318 ///////////////////////////////////////////////////////////////////////////////
    319 // DraggedTabController, public:
    320 
    321 DraggedTabController::DraggedTabController()
    322     : source_tabstrip_(NULL),
    323       attached_tabstrip_(NULL),
    324       source_tab_offset_(0),
    325       offset_to_width_ratio_(0),
    326       old_focused_view_(NULL),
    327       last_move_screen_loc_(0),
    328       started_drag_(false),
    329       active_(true),
    330       source_tab_index_(std::numeric_limits<size_t>::max()),
    331       initial_move_(true) {
    332   instance_ = this;
    333 }
    334 
    335 DraggedTabController::~DraggedTabController() {
    336   if (instance_ == this)
    337     instance_ = NULL;
    338 
    339   MessageLoopForUI::current()->RemoveObserver(this);
    340   // Need to delete the view here manually _before_ we reset the dragged
    341   // contents to NULL, otherwise if the view is animating to its destination
    342   // bounds, it won't be able to clean up properly since its cleanup routine
    343   // uses GetIndexForDraggedContents, which will be invalid.
    344   view_.reset(NULL);
    345 
    346   // Reset the delegate of the dragged TabContents. This ends up doing nothing
    347   // if the drag was completed.
    348   ResetDelegates();
    349 }
    350 
    351 void DraggedTabController::Init(BaseTabStrip* source_tabstrip,
    352                                 BaseTab* source_tab,
    353                                 const std::vector<BaseTab*>& tabs,
    354                                 const gfx::Point& mouse_offset,
    355                                 int source_tab_offset) {
    356   DCHECK(!tabs.empty());
    357   DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
    358   source_tabstrip_ = source_tabstrip;
    359   source_tab_offset_ = source_tab_offset;
    360   start_screen_point_ = GetCursorScreenPoint();
    361   mouse_offset_ = mouse_offset;
    362 
    363   drag_data_.resize(tabs.size());
    364   for (size_t i = 0; i < tabs.size(); ++i)
    365     InitTabDragData(tabs[i], &(drag_data_[i]));
    366   source_tab_index_ =
    367       std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
    368 
    369   // Listen for Esc key presses.
    370   MessageLoopForUI::current()->AddObserver(this);
    371 
    372   if (source_tab->width() > 0) {
    373     offset_to_width_ratio_ = static_cast<float>(source_tab_offset_) /
    374         static_cast<float>(source_tab->width());
    375   }
    376   InitWindowCreatePoint();
    377 }
    378 
    379 // static
    380 bool DraggedTabController::IsAttachedTo(BaseTabStrip* tab_strip) {
    381   return instance_ && instance_->active_ &&
    382       instance_->attached_tabstrip_ == tab_strip;
    383 }
    384 
    385 void DraggedTabController::Drag() {
    386   bring_to_front_timer_.Stop();
    387 
    388   if (!started_drag_) {
    389     if (!CanStartDrag())
    390       return;  // User hasn't dragged far enough yet.
    391 
    392     started_drag_ = true;
    393     SaveFocus();
    394     Attach(source_tabstrip_, gfx::Point());
    395   }
    396 
    397   ContinueDragging();
    398 }
    399 
    400 void DraggedTabController::EndDrag(bool canceled) {
    401   EndDragImpl(canceled ? CANCELED : NORMAL);
    402 }
    403 
    404 void DraggedTabController::InitTabDragData(BaseTab* tab,
    405                                            TabDragData* drag_data) {
    406   drag_data->source_model_index =
    407       source_tabstrip_->GetModelIndexOfBaseTab(tab);
    408   drag_data->contents = GetModel(source_tabstrip_)->GetTabContentsAt(
    409       drag_data->source_model_index);
    410   drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
    411   registrar_.Add(this,
    412                  NotificationType::TAB_CONTENTS_DESTROYED,
    413                  Source<TabContents>(drag_data->contents->tab_contents()));
    414 
    415   // We need to be the delegate so we receive messages about stuff, otherwise
    416   // our dragged TabContents may be replaced and subsequently
    417   // collected/destroyed while the drag is in process, leading to nasty crashes.
    418   drag_data->original_delegate =
    419       drag_data->contents->tab_contents()->delegate();
    420   drag_data->contents->tab_contents()->set_delegate(this);
    421 }
    422 
    423 ///////////////////////////////////////////////////////////////////////////////
    424 // DraggedTabController, PageNavigator implementation:
    425 
    426 void DraggedTabController::OpenURLFromTab(TabContents* source,
    427                                           const GURL& url,
    428                                           const GURL& referrer,
    429                                           WindowOpenDisposition disposition,
    430                                           PageTransition::Type transition) {
    431   if (source_tab_drag_data()->original_delegate) {
    432     if (disposition == CURRENT_TAB)
    433       disposition = NEW_WINDOW;
    434 
    435     source_tab_drag_data()->original_delegate->OpenURLFromTab(
    436         source, url, referrer, disposition, transition);
    437   }
    438 }
    439 
    440 ///////////////////////////////////////////////////////////////////////////////
    441 // DraggedTabController, TabContentsDelegate implementation:
    442 
    443 void DraggedTabController::NavigationStateChanged(const TabContents* source,
    444                                                   unsigned changed_flags) {
    445   if (view_.get())
    446     view_->Update();
    447 }
    448 
    449 void DraggedTabController::AddNewContents(TabContents* source,
    450                                           TabContents* new_contents,
    451                                           WindowOpenDisposition disposition,
    452                                           const gfx::Rect& initial_pos,
    453                                           bool user_gesture) {
    454   DCHECK_NE(CURRENT_TAB, disposition);
    455 
    456   // Theoretically could be called while dragging if the page tries to
    457   // spawn a window. Route this message back to the browser in most cases.
    458   if (source_tab_drag_data()->original_delegate) {
    459     source_tab_drag_data()->original_delegate->AddNewContents(
    460         source, new_contents, disposition, initial_pos, user_gesture);
    461   }
    462 }
    463 
    464 void DraggedTabController::ActivateContents(TabContents* contents) {
    465   // Ignored.
    466 }
    467 
    468 void DraggedTabController::DeactivateContents(TabContents* contents) {
    469   // Ignored.
    470 }
    471 
    472 void DraggedTabController::LoadingStateChanged(TabContents* source) {
    473   // It would be nice to respond to this message by changing the
    474   // screen shot in the dragged tab.
    475   if (view_.get())
    476     view_->Update();
    477 }
    478 
    479 void DraggedTabController::CloseContents(TabContents* source) {
    480   // Theoretically could be called by a window. Should be ignored
    481   // because window.close() is ignored (usually, even though this
    482   // method gets called.)
    483 }
    484 
    485 void DraggedTabController::MoveContents(TabContents* source,
    486                                         const gfx::Rect& pos) {
    487   // Theoretically could be called by a web page trying to move its
    488   // own window. Should be ignored since we're moving the window...
    489 }
    490 
    491 void DraggedTabController::UpdateTargetURL(TabContents* source,
    492                                            const GURL& url) {
    493   // Ignored.
    494 }
    495 
    496 bool DraggedTabController::ShouldSuppressDialogs() {
    497   // When a dialog is about to be shown we revert the drag. Otherwise a modal
    498   // dialog might appear and attempt to parent itself to a hidden tabcontents.
    499   EndDragImpl(CANCELED);
    500   return false;
    501 }
    502 
    503 ///////////////////////////////////////////////////////////////////////////////
    504 // DraggedTabController, NotificationObserver implementation:
    505 
    506 void DraggedTabController::Observe(NotificationType type,
    507                                    const NotificationSource& source,
    508                                    const NotificationDetails& details) {
    509   DCHECK_EQ(type.value, NotificationType::TAB_CONTENTS_DESTROYED);
    510   TabContents* destroyed_contents = Source<TabContents>(source).ptr();
    511   for (size_t i = 0; i < drag_data_.size(); ++i) {
    512     if (drag_data_[i].contents->tab_contents() == destroyed_contents) {
    513       // One of the tabs we're dragging has been destroyed. Cancel the drag.
    514       if (destroyed_contents->delegate() == this)
    515         destroyed_contents->set_delegate(NULL);
    516       drag_data_[i].contents = NULL;
    517       drag_data_[i].original_delegate = NULL;
    518       EndDragImpl(TAB_DESTROYED);
    519       return;
    520     }
    521   }
    522   // If we get here it means we got notification for a tab we don't know about.
    523   NOTREACHED();
    524 }
    525 
    526 ///////////////////////////////////////////////////////////////////////////////
    527 // DraggedTabController, MessageLoop::Observer implementation:
    528 
    529 #if defined(OS_WIN)
    530 void DraggedTabController::WillProcessMessage(const MSG& msg) {
    531 }
    532 
    533 void DraggedTabController::DidProcessMessage(const MSG& msg) {
    534   // If the user presses ESC during a drag, we need to abort and revert things
    535   // to the way they were. This is the most reliable way to do this since no
    536   // single view or window reliably receives events throughout all the various
    537   // kinds of tab dragging.
    538   if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
    539     EndDrag(true);
    540 }
    541 #else
    542 void DraggedTabController::WillProcessEvent(GdkEvent* event) {
    543 }
    544 
    545 void DraggedTabController::DidProcessEvent(GdkEvent* event) {
    546   if (event->type == GDK_KEY_PRESS &&
    547       reinterpret_cast<GdkEventKey*>(event)->keyval == GDK_Escape) {
    548     EndDrag(true);
    549   }
    550 }
    551 #endif
    552 
    553 ///////////////////////////////////////////////////////////////////////////////
    554 // DraggedTabController, private:
    555 
    556 void DraggedTabController::InitWindowCreatePoint() {
    557   // window_create_point_ is only used in CompleteDrag() (through
    558   // GetWindowCreatePoint() to get the start point of the docked window) when
    559   // the attached_tabstrip_ is NULL and all the window's related bound
    560   // information are obtained from source_tabstrip_. So, we need to get the
    561   // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
    562   // the window_create_point_ is not in the correct coordinate system. Please
    563   // refer to http://crbug.com/6223 comment #15 for detailed information.
    564   views::View* first_tab = source_tabstrip_->base_tab_at_tab_index(0);
    565   views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
    566   window_create_point_ = first_source_tab_point_;
    567   window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
    568 }
    569 
    570 gfx::Point DraggedTabController::GetWindowCreatePoint() const {
    571   gfx::Point cursor_point = GetCursorScreenPoint();
    572   if (dock_info_.type() != DockInfo::NONE && dock_info_.in_enable_area()) {
    573     // If we're going to dock, we need to return the exact coordinate,
    574     // otherwise we may attempt to maximize on the wrong monitor.
    575     return cursor_point;
    576   }
    577   // If the cursor is outside the monitor area, move it inside. For example,
    578   // dropping a tab onto the task bar on Windows produces this situation.
    579   gfx::Rect work_area = views::Screen::GetMonitorWorkAreaNearestPoint(
    580       cursor_point);
    581   if (!work_area.IsEmpty()) {
    582     if (cursor_point.x() < work_area.x())
    583       cursor_point.set_x(work_area.x());
    584     else if (cursor_point.x() > work_area.right())
    585       cursor_point.set_x(work_area.right());
    586     if (cursor_point.y() < work_area.y())
    587       cursor_point.set_y(work_area.y());
    588     else if (cursor_point.y() > work_area.bottom())
    589       cursor_point.set_y(work_area.bottom());
    590   }
    591   return gfx::Point(cursor_point.x() - window_create_point_.x(),
    592                     cursor_point.y() - window_create_point_.y());
    593 }
    594 
    595 void DraggedTabController::UpdateDockInfo(const gfx::Point& screen_point) {
    596   // Update the DockInfo for the current mouse coordinates.
    597   DockInfo dock_info = GetDockInfoAtPoint(screen_point);
    598   if (source_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP &&
    599       ((dock_info.type() == DockInfo::LEFT_OF_WINDOW &&
    600         !base::i18n::IsRTL()) ||
    601        (dock_info.type() == DockInfo::RIGHT_OF_WINDOW &&
    602         base::i18n::IsRTL()))) {
    603     // For side tabs it's way to easy to trigger to docking along the left/right
    604     // edge, so we disable it.
    605     dock_info = DockInfo();
    606   }
    607   if (!dock_info.equals(dock_info_)) {
    608     // DockInfo for current position differs.
    609     if (dock_info_.type() != DockInfo::NONE &&
    610         !dock_controllers_.empty()) {
    611       // Hide old visual indicator.
    612       dock_controllers_.back()->Hide();
    613     }
    614     dock_info_ = dock_info;
    615     if (dock_info_.type() != DockInfo::NONE) {
    616       // Show new docking position.
    617       DockDisplayer* controller = new DockDisplayer(this, dock_info_);
    618       if (controller->popup_view()) {
    619         dock_controllers_.push_back(controller);
    620         dock_windows_.insert(controller->popup_view());
    621       } else {
    622         delete controller;
    623       }
    624     }
    625   } else if (dock_info_.type() != DockInfo::NONE &&
    626              !dock_controllers_.empty()) {
    627     // Current dock position is the same as last, update the controller's
    628     // in_enable_area state as it may have changed.
    629     dock_controllers_.back()->UpdateInEnabledArea(dock_info_.in_enable_area());
    630   }
    631 }
    632 
    633 void DraggedTabController::SaveFocus() {
    634   DCHECK(!old_focused_view_);  // This should only be invoked once.
    635   old_focused_view_ = source_tabstrip_->GetFocusManager()->GetFocusedView();
    636   source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
    637 }
    638 
    639 void DraggedTabController::RestoreFocus() {
    640   if (old_focused_view_ && attached_tabstrip_ == source_tabstrip_)
    641     old_focused_view_->GetFocusManager()->SetFocusedView(old_focused_view_);
    642   old_focused_view_ = NULL;
    643 }
    644 
    645 bool DraggedTabController::CanStartDrag() const {
    646   // Determine if the mouse has moved beyond a minimum elasticity distance in
    647   // any direction from the starting point.
    648   static const int kMinimumDragDistance = 10;
    649   gfx::Point screen_point = GetCursorScreenPoint();
    650   int x_offset = abs(screen_point.x() - start_screen_point_.x());
    651   int y_offset = abs(screen_point.y() - start_screen_point_.y());
    652   return sqrt(pow(static_cast<float>(x_offset), 2) +
    653               pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
    654 }
    655 
    656 void DraggedTabController::ContinueDragging() {
    657   // Note that the coordinates given to us by |drag_event| are basically
    658   // useless, since they're in source_tab_ coordinates. On the surface, you'd
    659   // think we could just convert them to screen coordinates, however in the
    660   // situation where we're dragging the last tab in a window when multiple
    661   // windows are open, the coordinates of |source_tab_| are way off in
    662   // hyperspace since the window was moved there instead of being closed so
    663   // that we'd keep receiving events. And our ConvertPointToScreen methods
    664   // aren't really multi-screen aware. So really it's just safer to get the
    665   // actual position of the mouse cursor directly from Windows here, which is
    666   // guaranteed to be correct regardless of monitor config.
    667   gfx::Point screen_point = GetCursorScreenPoint();
    668 
    669 #if defined(OS_LINUX)
    670   // We don't allow detaching in chrome os.
    671   BaseTabStrip* target_tabstrip = source_tabstrip_;
    672 #else
    673   // Determine whether or not we have dragged over a compatible TabStrip in
    674   // another browser window. If we have, we should attach to it and start
    675   // dragging within it.
    676   BaseTabStrip* target_tabstrip = GetTabStripForPoint(screen_point);
    677 #endif
    678   if (target_tabstrip != attached_tabstrip_) {
    679     // Make sure we're fully detached from whatever TabStrip we're attached to
    680     // (if any).
    681     if (attached_tabstrip_)
    682       Detach();
    683     if (target_tabstrip)
    684       Attach(target_tabstrip, screen_point);
    685   }
    686   if (!target_tabstrip) {
    687     bring_to_front_timer_.Start(
    688         base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this,
    689         &DraggedTabController::BringWindowUnderMouseToFront);
    690   }
    691 
    692   UpdateDockInfo(screen_point);
    693 
    694   if (attached_tabstrip_)
    695     MoveAttached(screen_point);
    696   else
    697     MoveDetached(screen_point);
    698 }
    699 
    700 void DraggedTabController::MoveAttached(const gfx::Point& screen_point) {
    701   DCHECK(attached_tabstrip_);
    702   DCHECK(!view_.get());
    703 
    704   gfx::Point dragged_view_point = GetAttachedDragPoint(screen_point);
    705 
    706   int threshold = kVerticalMoveThreshold;
    707   if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    708     TabStrip* tab_strip = static_cast<TabStrip*>(attached_tabstrip_);
    709 
    710     // Determine the horizontal move threshold. This is dependent on the width
    711     // of tabs. The smaller the tabs compared to the standard size, the smaller
    712     // the threshold.
    713     double unselected, selected;
    714     tab_strip->GetCurrentTabWidths(&unselected, &selected);
    715     double ratio = unselected / Tab::GetStandardSize().width();
    716     threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
    717   }
    718 
    719   std::vector<BaseTab*> tabs(drag_data_.size());
    720   for (size_t i = 0; i < drag_data_.size(); ++i)
    721     tabs[i] = drag_data_[i].attached_tab;
    722 
    723   bool did_layout = false;
    724   // Update the model, moving the TabContents from one index to another. Do this
    725   // only if we have moved a minimum distance since the last reorder (to prevent
    726   // jitter) or if this the first move and the tabs are not consecutive.
    727   if (abs(MajorAxisValue(screen_point, attached_tabstrip_) -
    728           last_move_screen_loc_) > threshold ||
    729       (initial_move_ && !AreTabsConsecutive())) {
    730     TabStripModel* attached_model = GetModel(attached_tabstrip_);
    731     gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
    732     int to_index = GetInsertionIndexForDraggedBounds(bounds);
    733     TabContentsWrapper* last_contents =
    734         drag_data_[drag_data_.size() - 1].contents;
    735     int index_of_last_item =
    736           attached_model->GetIndexOfTabContents(last_contents);
    737     if (initial_move_) {
    738       // TabStrip determines if the tabs needs to be animated based on model
    739       // position. This means we need to invoke LayoutDraggedTabsAt before
    740       // changing the model.
    741       attached_tabstrip_->LayoutDraggedTabsAt(
    742           tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
    743           initial_move_);
    744       did_layout = true;
    745     }
    746     attached_model->MoveSelectedTabsTo(to_index);
    747     // Move may do nothing in certain situations (such as when dragging pinned
    748     // tabs). Make sure the tabstrip actually changed before updating
    749     // last_move_screen_loc_.
    750     if (index_of_last_item !=
    751         attached_model->GetIndexOfTabContents(last_contents)) {
    752       last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip_);
    753     }
    754   }
    755 
    756   if (!did_layout) {
    757     attached_tabstrip_->LayoutDraggedTabsAt(
    758         tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
    759         initial_move_);
    760   }
    761 
    762   initial_move_ = false;
    763 }
    764 
    765 void DraggedTabController::MoveDetached(const gfx::Point& screen_point) {
    766   DCHECK(!attached_tabstrip_);
    767   DCHECK(view_.get());
    768 
    769   // Move the View. There are no changes to the model if we're detached.
    770   view_->MoveTo(screen_point);
    771 }
    772 
    773 DockInfo DraggedTabController::GetDockInfoAtPoint(
    774     const gfx::Point& screen_point) {
    775   if (attached_tabstrip_) {
    776     // If the mouse is over a tab strip, don't offer a dock position.
    777     return DockInfo();
    778   }
    779 
    780   if (dock_info_.IsValidForPoint(screen_point)) {
    781     // It's possible any given screen coordinate has multiple docking
    782     // positions. Check the current info first to avoid having the docking
    783     // position bounce around.
    784     return dock_info_;
    785   }
    786 
    787   gfx::NativeView dragged_hwnd = view_->GetWidget()->GetNativeView();
    788   dock_windows_.insert(dragged_hwnd);
    789   DockInfo info = DockInfo::GetDockInfoAtPoint(screen_point, dock_windows_);
    790   dock_windows_.erase(dragged_hwnd);
    791   return info;
    792 }
    793 
    794 BaseTabStrip* DraggedTabController::GetTabStripForPoint(
    795     const gfx::Point& screen_point) {
    796   gfx::NativeView dragged_view = NULL;
    797   if (view_.get()) {
    798     dragged_view = view_->GetWidget()->GetNativeView();
    799     dock_windows_.insert(dragged_view);
    800   }
    801   gfx::NativeWindow local_window =
    802       DockInfo::GetLocalProcessWindowAtPoint(screen_point, dock_windows_);
    803   if (dragged_view)
    804     dock_windows_.erase(dragged_view);
    805   if (!local_window)
    806     return NULL;
    807   BrowserView* browser =
    808       BrowserView::GetBrowserViewForNativeWindow(local_window);
    809   // We don't allow drops on windows that don't have tabstrips.
    810   if (!browser ||
    811       !browser->browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
    812     return NULL;
    813 
    814   // This cast seems ugly, but the controller and the view are tighly coupled at
    815   // creation time, so it will be okay.
    816   BaseTabStrip* other_tabstrip =
    817       static_cast<BaseTabStrip*>(browser->tabstrip());
    818 
    819   if (!other_tabstrip->controller()->IsCompatibleWith(source_tabstrip_))
    820     return NULL;
    821   return GetTabStripIfItContains(other_tabstrip, screen_point);
    822 }
    823 
    824 BaseTabStrip* DraggedTabController::GetTabStripIfItContains(
    825     BaseTabStrip* tabstrip,
    826     const gfx::Point& screen_point) const {
    827   static const int kVerticalDetachMagnetism = 15;
    828   static const int kHorizontalDetachMagnetism = 15;
    829   // Make sure the specified screen point is actually within the bounds of the
    830   // specified tabstrip...
    831   gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
    832   if (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    833     if (screen_point.x() < tabstrip_bounds.right() &&
    834         screen_point.x() >= tabstrip_bounds.x()) {
    835       // TODO(beng): make this be relative to the start position of the mouse
    836       // for the source TabStrip.
    837       int upper_threshold = tabstrip_bounds.bottom() + kVerticalDetachMagnetism;
    838       int lower_threshold = tabstrip_bounds.y() - kVerticalDetachMagnetism;
    839       if (screen_point.y() >= lower_threshold &&
    840           screen_point.y() <= upper_threshold) {
    841         return tabstrip;
    842       }
    843     }
    844   } else {
    845     if (screen_point.y() < tabstrip_bounds.bottom() &&
    846         screen_point.y() >= tabstrip_bounds.y()) {
    847       int upper_threshold = tabstrip_bounds.right() +
    848           kHorizontalDetachMagnetism;
    849       int lower_threshold = tabstrip_bounds.x() - kHorizontalDetachMagnetism;
    850       if (screen_point.x() >= lower_threshold &&
    851           screen_point.x() <= upper_threshold) {
    852         return tabstrip;
    853       }
    854     }
    855   }
    856   return NULL;
    857 }
    858 
    859 void DraggedTabController::Attach(BaseTabStrip* attached_tabstrip,
    860                                   const gfx::Point& screen_point) {
    861   DCHECK(!attached_tabstrip_);  // We should already have detached by the time
    862                                 // we get here.
    863 
    864   attached_tabstrip_ = attached_tabstrip;
    865 
    866   // And we don't need the dragged view.
    867   view_.reset();
    868 
    869   std::vector<BaseTab*> tabs =
    870       GetTabsMatchingDraggedContents(attached_tabstrip_);
    871 
    872   if (tabs.empty()) {
    873     // There is no Tab in |attached_tabstrip| that corresponds to the dragged
    874     // TabContents. We must now create one.
    875 
    876     // Remove ourselves as the delegate now that the dragged TabContents is
    877     // being inserted back into a Browser.
    878     for (size_t i = 0; i < drag_data_.size(); ++i) {
    879       drag_data_[i].contents->tab_contents()->set_delegate(NULL);
    880       drag_data_[i].original_delegate = NULL;
    881     }
    882 
    883     // Return the TabContents' to normalcy.
    884     source_dragged_contents()->tab_contents()->set_capturing_contents(false);
    885 
    886     // Inserting counts as a move. We don't want the tabs to jitter when the
    887     // user moves the tab immediately after attaching it.
    888     last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip);
    889 
    890     // Figure out where to insert the tab based on the bounds of the dragged
    891     // representation and the ideal bounds of the other Tabs already in the
    892     // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
    893     // changing due to animation).
    894     gfx::Point tab_strip_point(screen_point);
    895     views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_strip_point);
    896     tab_strip_point.set_x(
    897         attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
    898     tab_strip_point.Offset(-mouse_offset_.x(), -mouse_offset_.y());
    899     gfx::Rect bounds = GetDraggedViewTabStripBounds(tab_strip_point);
    900     int index = GetInsertionIndexForDraggedBounds(bounds);
    901     attached_tabstrip_->set_attaching_dragged_tab(true);
    902     for (size_t i = 0; i < drag_data_.size(); ++i) {
    903       int add_types = TabStripModel::ADD_NONE;
    904       if (drag_data_[i].pinned)
    905         add_types |= TabStripModel::ADD_PINNED;
    906       GetModel(attached_tabstrip_)->InsertTabContentsAt(
    907           index + i, drag_data_[i].contents, add_types);
    908     }
    909     attached_tabstrip_->set_attaching_dragged_tab(false);
    910 
    911     tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
    912   }
    913   DCHECK_EQ(tabs.size(), drag_data_.size());
    914   for (size_t i = 0; i < drag_data_.size(); ++i)
    915     drag_data_[i].attached_tab = tabs[i];
    916 
    917   attached_tabstrip_->StartedDraggingTabs(tabs);
    918 
    919   ResetSelection(GetModel(attached_tabstrip_));
    920 
    921   if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    922     // The size of the dragged tab may have changed. Adjust the x offset so that
    923     // ratio of mouse_offset_ to original width is maintained.
    924     std::vector<BaseTab*> tabs_to_source(tabs);
    925     tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
    926                          tabs_to_source.end());
    927     int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
    928         tabs[source_tab_index_]->width() +
    929         static_cast<int>(offset_to_width_ratio_ *
    930                          tabs[source_tab_index_]->width());
    931     mouse_offset_.set_x(new_x);
    932   }
    933 
    934   // Move the corresponding window to the front.
    935   attached_tabstrip_->GetWindow()->Activate();
    936 }
    937 
    938 void DraggedTabController::Detach() {
    939   // Prevent the TabContents' HWND from being hidden by any of the model
    940   // operations performed during the drag.
    941   source_dragged_contents()->tab_contents()->set_capturing_contents(true);
    942 
    943   // Calculate the drag bounds.
    944   std::vector<gfx::Rect> drag_bounds;
    945   std::vector<BaseTab*> attached_tabs;
    946   for (size_t i = 0; i < drag_data_.size(); ++i)
    947     attached_tabs.push_back(drag_data_[i].attached_tab);
    948   attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
    949                                                     &drag_bounds);
    950 
    951   TabStripModel* attached_model = GetModel(attached_tabstrip_);
    952   std::vector<TabRendererData> tab_data;
    953   for (size_t i = 0; i < drag_data_.size(); ++i) {
    954     tab_data.push_back(drag_data_[i].attached_tab->data());
    955     int index = attached_model->GetIndexOfTabContents(drag_data_[i].contents);
    956     DCHECK_NE(-1, index);
    957 
    958     // Hide the tab so that the user doesn't see it animate closed.
    959     drag_data_[i].attached_tab->SetVisible(false);
    960 
    961     attached_model->DetachTabContentsAt(index);
    962 
    963     // Detaching resets the delegate, but we still want to be the delegate.
    964     drag_data_[i].contents->tab_contents()->set_delegate(this);
    965 
    966     // Detaching may end up deleting the tab, drop references to it.
    967     drag_data_[i].attached_tab = NULL;
    968   }
    969 
    970   // If we've removed the last Tab from the TabStrip, hide the frame now.
    971   if (attached_model->empty())
    972     HideFrame();
    973 
    974   // Create the dragged view.
    975   CreateDraggedView(tab_data, drag_bounds);
    976 
    977   attached_tabstrip_ = NULL;
    978 }
    979 
    980 int DraggedTabController::GetInsertionIndexForDraggedBounds(
    981     const gfx::Rect& dragged_bounds) const {
    982   int right_tab_x = 0;
    983   int bottom_tab_y = 0;
    984   int index = -1;
    985   for (int i = 0; i < attached_tabstrip_->tab_count(); ++i) {
    986     const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
    987     if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    988       gfx::Rect left_half = ideal_bounds;
    989       left_half.set_width(left_half.width() / 2);
    990       gfx::Rect right_half = ideal_bounds;
    991       right_half.set_width(ideal_bounds.width() - left_half.width());
    992       right_half.set_x(left_half.right());
    993       right_tab_x = right_half.right();
    994       if (dragged_bounds.x() >= right_half.x() &&
    995           dragged_bounds.x() < right_half.right()) {
    996         index = i + 1;
    997         break;
    998       } else if (dragged_bounds.x() >= left_half.x() &&
    999                  dragged_bounds.x() < left_half.right()) {
   1000         index = i;
   1001         break;
   1002       }
   1003     } else {
   1004       // Vertical tab strip.
   1005       int max_y = ideal_bounds.bottom();
   1006       int mid_y = ideal_bounds.y() + ideal_bounds.height() / 2;
   1007       bottom_tab_y = max_y;
   1008       if (dragged_bounds.y() < mid_y) {
   1009         index = i;
   1010         break;
   1011       } else if (dragged_bounds.y() >= mid_y && dragged_bounds.y() < max_y) {
   1012         index = i + 1;
   1013         break;
   1014       }
   1015     }
   1016   }
   1017   if (index == -1) {
   1018     if ((attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP &&
   1019          dragged_bounds.right() > right_tab_x) ||
   1020         (attached_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP &&
   1021          dragged_bounds.y() >= bottom_tab_y)) {
   1022       index = GetModel(attached_tabstrip_)->count();
   1023     } else {
   1024       index = 0;
   1025     }
   1026   }
   1027 
   1028   if (!drag_data_[0].attached_tab) {
   1029     // If 'attached_tab' is NULL, it means we're in the process of attaching and
   1030     // don't need to constrain the index.
   1031     return index;
   1032   }
   1033 
   1034   int max_index = GetModel(attached_tabstrip_)->count() -
   1035       static_cast<int>(drag_data_.size());
   1036   return std::max(0, std::min(max_index, index));
   1037 }
   1038 
   1039 gfx::Rect DraggedTabController::GetDraggedViewTabStripBounds(
   1040     const gfx::Point& tab_strip_point) {
   1041   // attached_tab is NULL when inserting into a new tabstrip.
   1042   if (source_tab_drag_data()->attached_tab) {
   1043     return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
   1044                      source_tab_drag_data()->attached_tab->width(),
   1045                      source_tab_drag_data()->attached_tab->height());
   1046   }
   1047 
   1048   if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
   1049     double sel_width, unselected_width;
   1050     static_cast<TabStrip*>(attached_tabstrip_)->GetCurrentTabWidths(
   1051         &sel_width, &unselected_width);
   1052     return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
   1053                      static_cast<int>(sel_width),
   1054                      Tab::GetStandardSize().height());
   1055   }
   1056 
   1057   return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
   1058                    attached_tabstrip_->width(),
   1059                    SideTab::GetPreferredHeight());
   1060 }
   1061 
   1062 gfx::Point DraggedTabController::GetAttachedDragPoint(
   1063     const gfx::Point& screen_point) {
   1064   DCHECK(attached_tabstrip_);  // The tab must be attached.
   1065 
   1066   gfx::Point tab_loc(screen_point);
   1067   views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_loc);
   1068   int x =
   1069       attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
   1070   int y = tab_loc.y() - mouse_offset_.y();
   1071 
   1072   // TODO: consider caching this.
   1073   std::vector<BaseTab*> attached_tabs;
   1074   for (size_t i = 0; i < drag_data_.size(); ++i)
   1075     attached_tabs.push_back(drag_data_[i].attached_tab);
   1076 
   1077   int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
   1078 
   1079   if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
   1080     int max_x = attached_tabstrip_->width() - size;
   1081     x = std::min(std::max(x, 0), max_x);
   1082     y = 0;
   1083   } else {
   1084     x = SideTabStrip::kTabStripInset;
   1085     int max_y = attached_tabstrip_->height() - size;
   1086     y = std::min(std::max(y, SideTabStrip::kTabStripInset), max_y);
   1087   }
   1088   return gfx::Point(x, y);
   1089 }
   1090 
   1091 std::vector<BaseTab*> DraggedTabController::GetTabsMatchingDraggedContents(
   1092     BaseTabStrip* tabstrip) {
   1093   TabStripModel* model = GetModel(attached_tabstrip_);
   1094   std::vector<BaseTab*> tabs;
   1095   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1096     int model_index = model->GetIndexOfTabContents(drag_data_[i].contents);
   1097     if (model_index == TabStripModel::kNoTab)
   1098       return std::vector<BaseTab*>();
   1099     tabs.push_back(tabstrip->GetBaseTabAtModelIndex(model_index));
   1100   }
   1101   return tabs;
   1102 }
   1103 
   1104 void DraggedTabController::EndDragImpl(EndDragType type) {
   1105   active_ = false;
   1106 
   1107   bring_to_front_timer_.Stop();
   1108 
   1109   // Hide the current dock controllers.
   1110   for (size_t i = 0; i < dock_controllers_.size(); ++i) {
   1111     // Be sure and clear the controller first, that way if Hide ends up
   1112     // deleting the controller it won't call us back.
   1113     dock_controllers_[i]->clear_controller();
   1114     dock_controllers_[i]->Hide();
   1115   }
   1116   dock_controllers_.clear();
   1117   dock_windows_.clear();
   1118 
   1119   if (type != TAB_DESTROYED) {
   1120     // We only finish up the drag if we were actually dragging. If start_drag_
   1121     // is false, the user just clicked and released and didn't move the mouse
   1122     // enough to trigger a drag.
   1123     if (started_drag_) {
   1124       RestoreFocus();
   1125       if (type == CANCELED)
   1126         RevertDrag();
   1127       else
   1128         CompleteDrag();
   1129     }
   1130   } else if (drag_data_.size() > 1) {
   1131     RevertDrag();
   1132   }  // else case the only tab we were dragging was deleted. Nothing to do.
   1133 
   1134   ResetDelegates();
   1135 
   1136   // Clear out drag data so we don't attempt to do anything with it.
   1137   drag_data_.clear();
   1138 
   1139   source_tabstrip_->DestroyDragController();
   1140 }
   1141 
   1142 void DraggedTabController::RevertDrag() {
   1143   std::vector<BaseTab*> tabs;
   1144   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1145     if (drag_data_[i].contents) {
   1146       // Contents is NULL if a tab was destroyed while the drag was under way.
   1147       tabs.push_back(drag_data_[i].attached_tab);
   1148       RevertDragAt(i);
   1149     }
   1150   }
   1151 
   1152   bool restore_frame = attached_tabstrip_ != source_tabstrip_;
   1153   if (attached_tabstrip_ && attached_tabstrip_ == source_tabstrip_)
   1154     source_tabstrip_->StoppedDraggingTabs(tabs);
   1155 
   1156   attached_tabstrip_ = source_tabstrip_;
   1157 
   1158   ResetSelection(GetModel(attached_tabstrip_));
   1159 
   1160   // If we're not attached to any TabStrip, or attached to some other TabStrip,
   1161   // we need to restore the bounds of the original TabStrip's frame, in case
   1162   // it has been hidden.
   1163   if (restore_frame) {
   1164     if (!restore_bounds_.IsEmpty()) {
   1165 #if defined(OS_WIN)
   1166       HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView();
   1167       MoveWindow(frame_hwnd, restore_bounds_.x(), restore_bounds_.y(),
   1168                  restore_bounds_.width(), restore_bounds_.height(), TRUE);
   1169 #else
   1170       NOTIMPLEMENTED();
   1171 #endif
   1172     }
   1173   }
   1174 }
   1175 
   1176 void DraggedTabController::ResetSelection(TabStripModel* model) {
   1177   DCHECK(model);
   1178   TabStripSelectionModel selection_model;
   1179   bool has_one_valid_tab = false;
   1180   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1181     // |contents| is NULL if a tab was deleted out from under us.
   1182     if (drag_data_[i].contents) {
   1183       int index = model->GetIndexOfTabContents(drag_data_[i].contents);
   1184       DCHECK_NE(-1, index);
   1185       selection_model.AddIndexToSelection(index);
   1186       if (!has_one_valid_tab || i == source_tab_index_) {
   1187         // Reset the active/lead to the first tab. If the source tab is still
   1188         // valid we'll reset these again later on.
   1189         selection_model.set_active(index);
   1190         selection_model.set_anchor(index);
   1191         has_one_valid_tab = true;
   1192       }
   1193     }
   1194   }
   1195   if (!has_one_valid_tab)
   1196     return;
   1197 
   1198   model->SetSelectionFromModel(selection_model);
   1199 }
   1200 
   1201 void DraggedTabController::RevertDragAt(size_t drag_index) {
   1202   DCHECK(started_drag_);
   1203 
   1204   TabDragData* data = &(drag_data_[drag_index]);
   1205   if (attached_tabstrip_) {
   1206     int index =
   1207         GetModel(attached_tabstrip_)->GetIndexOfTabContents(data->contents);
   1208     if (attached_tabstrip_ != source_tabstrip_) {
   1209       // The Tab was inserted into another TabStrip. We need to put it back
   1210       // into the original one.
   1211       GetModel(attached_tabstrip_)->DetachTabContentsAt(index);
   1212       // TODO(beng): (Cleanup) seems like we should use Attach() for this
   1213       //             somehow.
   1214       GetModel(source_tabstrip_)->InsertTabContentsAt(
   1215           data->source_model_index, data->contents,
   1216           (data->pinned ? TabStripModel::ADD_PINNED : 0));
   1217     } else {
   1218       // The Tab was moved within the TabStrip where the drag was initiated.
   1219       // Move it back to the starting location.
   1220       GetModel(source_tabstrip_)->MoveTabContentsAt(
   1221           index, data->source_model_index, false);
   1222     }
   1223   } else {
   1224     // The Tab was detached from the TabStrip where the drag began, and has not
   1225     // been attached to any other TabStrip. We need to put it back into the
   1226     // source TabStrip.
   1227     GetModel(source_tabstrip_)->InsertTabContentsAt(
   1228         data->source_model_index, data->contents,
   1229         (data->pinned ? TabStripModel::ADD_PINNED : 0));
   1230   }
   1231 }
   1232 
   1233 void DraggedTabController::CompleteDrag() {
   1234   DCHECK(started_drag_);
   1235 
   1236   if (attached_tabstrip_) {
   1237     attached_tabstrip_->StoppedDraggingTabs(
   1238         GetTabsMatchingDraggedContents(attached_tabstrip_));
   1239   } else {
   1240     if (dock_info_.type() != DockInfo::NONE) {
   1241       Profile* profile = GetModel(source_tabstrip_)->profile();
   1242       switch (dock_info_.type()) {
   1243         case DockInfo::LEFT_OF_WINDOW:
   1244           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Left"),
   1245                                     profile);
   1246           break;
   1247 
   1248         case DockInfo::RIGHT_OF_WINDOW:
   1249           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Right"),
   1250                                     profile);
   1251           break;
   1252 
   1253         case DockInfo::BOTTOM_OF_WINDOW:
   1254           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Bottom"),
   1255                                     profile);
   1256           break;
   1257 
   1258         case DockInfo::TOP_OF_WINDOW:
   1259           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Top"),
   1260                                     profile);
   1261           break;
   1262 
   1263         case DockInfo::MAXIMIZE:
   1264           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Maximize"),
   1265                                     profile);
   1266           break;
   1267 
   1268         case DockInfo::LEFT_HALF:
   1269           UserMetrics::RecordAction(UserMetricsAction("DockingWindow_LeftHalf"),
   1270                                     profile);
   1271           break;
   1272 
   1273         case DockInfo::RIGHT_HALF:
   1274           UserMetrics::RecordAction(
   1275               UserMetricsAction("DockingWindow_RightHalf"),
   1276               profile);
   1277           break;
   1278 
   1279         case DockInfo::BOTTOM_HALF:
   1280           UserMetrics::RecordAction(
   1281               UserMetricsAction("DockingWindow_BottomHalf"),
   1282               profile);
   1283           break;
   1284 
   1285         default:
   1286           NOTREACHED();
   1287           break;
   1288       }
   1289     }
   1290     // Compel the model to construct a new window for the detached TabContents.
   1291     views::Window* window = source_tabstrip_->GetWindow();
   1292     gfx::Rect window_bounds(window->GetNormalBounds());
   1293     window_bounds.set_origin(GetWindowCreatePoint());
   1294     // When modifying the following if statement, please make sure not to
   1295     // introduce issue listed in http://crbug.com/6223 comment #11.
   1296     bool rtl_ui = base::i18n::IsRTL();
   1297     bool has_dock_position = (dock_info_.type() != DockInfo::NONE);
   1298     if (rtl_ui && has_dock_position) {
   1299       // Mirror X axis so the docked tab is aligned using the mouse click as
   1300       // the top-right corner.
   1301       window_bounds.set_x(window_bounds.x() - window_bounds.width());
   1302     }
   1303     Browser* new_browser =
   1304         GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
   1305             drag_data_[0].contents, window_bounds, dock_info_,
   1306             window->IsMaximized());
   1307     TabStripModel* new_model = new_browser->tabstrip_model();
   1308     new_model->SetTabPinned(
   1309         new_model->GetIndexOfTabContents(drag_data_[0].contents),
   1310         drag_data_[0].pinned);
   1311     for (size_t i = 1; i < drag_data_.size(); ++i) {
   1312       new_model->InsertTabContentsAt(
   1313           static_cast<int>(i),
   1314           drag_data_[i].contents,
   1315           drag_data_[i].pinned ? TabStripModel::ADD_PINNED :
   1316                                  TabStripModel::ADD_NONE);
   1317     }
   1318     ResetSelection(new_browser->tabstrip_model());
   1319     new_browser->window()->Show();
   1320   }
   1321 
   1322   CleanUpHiddenFrame();
   1323 }
   1324 
   1325 void DraggedTabController::ResetDelegates() {
   1326   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1327     if (drag_data_[i].contents &&
   1328         drag_data_[i].contents->tab_contents()->delegate() == this) {
   1329       drag_data_[i].contents->tab_contents()->set_delegate(
   1330           drag_data_[i].original_delegate);
   1331     }
   1332   }
   1333 }
   1334 
   1335 void DraggedTabController::CreateDraggedView(
   1336     const std::vector<TabRendererData>& data,
   1337     const std::vector<gfx::Rect>& renderer_bounds) {
   1338   DCHECK(!view_.get());
   1339   DCHECK_EQ(data.size(), drag_data_.size());
   1340 
   1341   // Set up the photo booth to start capturing the contents of the dragged
   1342   // TabContents.
   1343   NativeViewPhotobooth* photobooth =
   1344       NativeViewPhotobooth::Create(
   1345           source_dragged_contents()->tab_contents()->GetNativeView());
   1346 
   1347   gfx::Rect content_bounds;
   1348   source_dragged_contents()->tab_contents()->GetContainerBounds(
   1349       &content_bounds);
   1350 
   1351   std::vector<views::View*> renderers;
   1352   for (size_t i = 0; i < drag_data_.size(); ++i) {
   1353     BaseTab* renderer = source_tabstrip_->CreateTabForDragging();
   1354     renderer->SetData(data[i]);
   1355     renderers.push_back(renderer);
   1356   }
   1357   // DraggedTabView takes ownership of the renderers.
   1358   view_.reset(new DraggedTabView(renderers, renderer_bounds, mouse_offset_,
   1359                                  content_bounds.size(), photobooth));
   1360 }
   1361 
   1362 gfx::Point DraggedTabController::GetCursorScreenPoint() const {
   1363 #if defined(OS_WIN)
   1364   DWORD pos = GetMessagePos();
   1365   return gfx::Point(pos);
   1366 #else
   1367   gint x, y;
   1368   gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
   1369   return gfx::Point(x, y);
   1370 #endif
   1371 }
   1372 
   1373 gfx::Rect DraggedTabController::GetViewScreenBounds(views::View* view) const {
   1374   gfx::Point view_topleft;
   1375   views::View::ConvertPointToScreen(view, &view_topleft);
   1376   gfx::Rect view_screen_bounds = view->GetLocalBounds();
   1377   view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
   1378   return view_screen_bounds;
   1379 }
   1380 
   1381 void DraggedTabController::HideFrame() {
   1382 #if defined(OS_WIN)
   1383   // We don't actually hide the window, rather we just move it way off-screen.
   1384   // If we actually hide it, we stop receiving drag events.
   1385   HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView();
   1386   RECT wr;
   1387   GetWindowRect(frame_hwnd, &wr);
   1388   MoveWindow(frame_hwnd, 0xFFFF, 0xFFFF, wr.right - wr.left,
   1389              wr.bottom - wr.top, TRUE);
   1390 
   1391   // We also save the bounds of the window prior to it being moved, so that if
   1392   // the drag session is aborted we can restore them.
   1393   restore_bounds_ = gfx::Rect(wr);
   1394 #else
   1395   NOTIMPLEMENTED();
   1396 #endif
   1397 }
   1398 
   1399 void DraggedTabController::CleanUpHiddenFrame() {
   1400   // If the model we started dragging from is now empty, we must ask the
   1401   // delegate to close the frame.
   1402   if (GetModel(source_tabstrip_)->empty())
   1403     GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession();
   1404 }
   1405 
   1406 void DraggedTabController::DockDisplayerDestroyed(
   1407     DockDisplayer* controller) {
   1408   DockWindows::iterator dock_i =
   1409       dock_windows_.find(controller->popup_view());
   1410   if (dock_i != dock_windows_.end())
   1411     dock_windows_.erase(dock_i);
   1412   else
   1413     NOTREACHED();
   1414 
   1415   std::vector<DockDisplayer*>::iterator i =
   1416       std::find(dock_controllers_.begin(), dock_controllers_.end(),
   1417                 controller);
   1418   if (i != dock_controllers_.end())
   1419     dock_controllers_.erase(i);
   1420   else
   1421     NOTREACHED();
   1422 }
   1423 
   1424 void DraggedTabController::BringWindowUnderMouseToFront() {
   1425   // If we're going to dock to another window, bring it to the front.
   1426   gfx::NativeWindow window = dock_info_.window();
   1427   if (!window) {
   1428     gfx::NativeView dragged_view = view_->GetWidget()->GetNativeView();
   1429     dock_windows_.insert(dragged_view);
   1430     window = DockInfo::GetLocalProcessWindowAtPoint(GetCursorScreenPoint(),
   1431                                                     dock_windows_);
   1432     dock_windows_.erase(dragged_view);
   1433   }
   1434   if (window) {
   1435 #if defined(OS_WIN)
   1436     // Move the window to the front.
   1437     SetWindowPos(window, HWND_TOP, 0, 0, 0, 0,
   1438                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
   1439 
   1440     // The previous call made the window appear on top of the dragged window,
   1441     // move the dragged window to the front.
   1442     SetWindowPos(view_->GetWidget()->GetNativeView(), HWND_TOP, 0, 0, 0, 0,
   1443                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
   1444 #else
   1445     NOTIMPLEMENTED();
   1446 #endif
   1447   }
   1448 }
   1449 
   1450 TabStripModel* DraggedTabController::GetModel(BaseTabStrip* tabstrip) const {
   1451   return static_cast<BrowserTabStripController*>(tabstrip->controller())->
   1452       model();
   1453 }
   1454 
   1455 bool DraggedTabController::AreTabsConsecutive() {
   1456   for (size_t i = 1; i < drag_data_.size(); ++i) {
   1457     if (drag_data_[i - 1].source_model_index + 1 !=
   1458         drag_data_[i].source_model_index) {
   1459       return false;
   1460     }
   1461   }
   1462   return true;
   1463 }
   1464