// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/views/tabs/dragged_tab_controller.h"

#include <math.h>
#include <set>

#include "app/animation.h"
#include "app/slide_animation.h"
#include "app/resource_bundle.h"
#include "base/callback.h"
#include "base/i18n/rtl.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/tabs/base_tab.h"
#include "chrome/browser/views/tabs/base_tab_strip.h"
#include "chrome/browser/views/tabs/browser_tab_strip_controller.h"
#include "chrome/browser/views/tabs/dragged_tab_view.h"
#include "chrome/browser/views/tabs/native_view_photobooth.h"
#include "chrome/browser/views/tabs/side_tab.h"
#include "chrome/browser/views/tabs/side_tab_strip.h"
#include "chrome/browser/views/tabs/tab.h"
#include "chrome/browser/views/tabs/tab_strip.h"
#include "chrome/common/notification_service.h"
#include "gfx/canvas_skia.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/event.h"
#include "views/widget/root_view.h"
#include "views/widget/widget.h"
#include "views/window/window.h"

#if defined(OS_WIN)
#include "views/widget/widget_win.h"
#endif

#if defined(OS_LINUX)
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#endif

static const int kHorizontalMoveThreshold = 16;  // Pixels.

// Distance in pixels the user must move the mouse before we consider moving
// an attached vertical tab.
static const int kVerticalMoveThreshold = 8;

// If non-null there is a drag underway.
static DraggedTabController* instance_;

namespace {

// Delay, in ms, during dragging before we bring a window to front.
const int kBringToFrontDelay = 750;

// Radius of the rect drawn by DockView.
const int kRoundedRectRadius = 4;

// Spacing between tab icons when DockView is showing a docking location that
// contains more than one tab.
const int kTabSpacing = 4;

// DockView is the view responsible for giving a visual indicator of where a
// dock is going to occur.

class DockView : public views::View {
 public:
  explicit DockView(DockInfo::Type type) : type_(type) {}

  virtual gfx::Size GetPreferredSize() {
    return gfx::Size(DockInfo::popup_width(), DockInfo::popup_height());
  }

  virtual void PaintBackground(gfx::Canvas* canvas) {
    SkRect outer_rect = { SkIntToScalar(0), SkIntToScalar(0),
                          SkIntToScalar(width()),
                          SkIntToScalar(height()) };

    // Fill the background rect.
    SkPaint paint;
    paint.setColor(SkColorSetRGB(108, 108, 108));
    paint.setStyle(SkPaint::kFill_Style);
    canvas->AsCanvasSkia()->drawRoundRect(
        outer_rect, SkIntToScalar(kRoundedRectRadius),
        SkIntToScalar(kRoundedRectRadius), paint);

    ResourceBundle& rb = ResourceBundle::GetSharedInstance();

    SkBitmap* high_icon = rb.GetBitmapNamed(IDR_DOCK_HIGH);
    SkBitmap* wide_icon = rb.GetBitmapNamed(IDR_DOCK_WIDE);

    canvas->Save();
    bool rtl_ui = base::i18n::IsRTL();
    if (rtl_ui) {
      // Flip canvas to draw the mirrored tab images for RTL UI.
      canvas->TranslateInt(width(), 0);
      canvas->ScaleInt(-1, 1);
    }
    int x_of_active_tab = width() / 2 + kTabSpacing / 2;
    int x_of_inactive_tab = width() / 2 - high_icon->width() - kTabSpacing / 2;
    switch (type_) {
      case DockInfo::LEFT_OF_WINDOW:
      case DockInfo::LEFT_HALF:
        if (!rtl_ui)
          std::swap(x_of_active_tab, x_of_inactive_tab);
        canvas->DrawBitmapInt(*high_icon, x_of_active_tab,
                              (height() - high_icon->height()) / 2);
        if (type_ == DockInfo::LEFT_OF_WINDOW) {
          DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab,
                              (height() - high_icon->height()) / 2);
        }
        break;


      case DockInfo::RIGHT_OF_WINDOW:
      case DockInfo::RIGHT_HALF:
        if (rtl_ui)
          std::swap(x_of_active_tab, x_of_inactive_tab);
        canvas->DrawBitmapInt(*high_icon, x_of_active_tab,
                              (height() - high_icon->height()) / 2);
        if (type_ == DockInfo::RIGHT_OF_WINDOW) {
         DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab,
                             (height() - high_icon->height()) / 2);
        }
        break;

      case DockInfo::TOP_OF_WINDOW:
        canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2,
                              height() / 2 - high_icon->height());
        break;

      case DockInfo::MAXIMIZE: {
        SkBitmap* max_icon = rb.GetBitmapNamed(IDR_DOCK_MAX);
        canvas->DrawBitmapInt(*max_icon, (width() - max_icon->width()) / 2,
                              (height() - max_icon->height()) / 2);
        break;
      }

      case DockInfo::BOTTOM_HALF:
      case DockInfo::BOTTOM_OF_WINDOW:
        canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2,
                              height() / 2 + kTabSpacing / 2);
        if (type_ == DockInfo::BOTTOM_OF_WINDOW) {
          DrawBitmapWithAlpha(canvas, *wide_icon,
              (width() - wide_icon->width()) / 2,
              height() / 2 - kTabSpacing / 2 - wide_icon->height());
        }
        break;

      default:
        NOTREACHED();
        break;
    }
    canvas->Restore();
  }

 private:
  void DrawBitmapWithAlpha(gfx::Canvas* canvas, const SkBitmap& image,
                           int x, int y) {
    SkPaint paint;
    paint.setAlpha(128);
    canvas->DrawBitmapInt(image, x, y, paint);
  }

  DockInfo::Type type_;

  DISALLOW_COPY_AND_ASSIGN(DockView);
};

gfx::Point ConvertScreenPointToTabStripPoint(BaseTabStrip* tabstrip,
                                             const gfx::Point& screen_point) {
  gfx::Point tabstrip_topleft;
  views::View::ConvertPointToScreen(tabstrip, &tabstrip_topleft);
  return gfx::Point(screen_point.x() - tabstrip_topleft.x(),
                    screen_point.y() - tabstrip_topleft.y());
}

// Returns the the x-coordinate of |point| if the type of tabstrip is horizontal
// otherwise returns the y-coordinate.
int MajorAxisValue(const gfx::Point& point, BaseTabStrip* tabstrip) {
  return (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) ?
      point.x() : point.y();
}

}  // namespace

///////////////////////////////////////////////////////////////////////////////
// DockDisplayer

// DockDisplayer is responsible for giving the user a visual indication of a
// possible dock position (as represented by DockInfo). DockDisplayer shows
// a window with a DockView in it. Two animations are used that correspond to
// the state of DockInfo::in_enable_area.
class DraggedTabController::DockDisplayer : public AnimationDelegate {
 public:
  DockDisplayer(DraggedTabController* controller,
                const DockInfo& info)
      : controller_(controller),
        popup_(NULL),
        popup_view_(NULL),
        ALLOW_THIS_IN_INITIALIZER_LIST(animation_(this)),
        hidden_(false),
        in_enable_area_(info.in_enable_area()) {
#if defined(OS_WIN)
    views::WidgetWin* popup = new views::WidgetWin;
    popup_ = popup;
    popup->set_window_style(WS_POPUP);
    popup->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW |
                               WS_EX_TOPMOST);
    popup->SetOpacity(0x00);
    popup->Init(NULL, info.GetPopupRect());
    popup->SetContentsView(new DockView(info.type()));
    if (info.in_enable_area())
      animation_.Reset(1);
    else
      animation_.Show();
    popup->SetWindowPos(HWND_TOP, 0, 0, 0, 0,
        SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_SHOWWINDOW);
#else
    NOTIMPLEMENTED();
#endif
    popup_view_ = popup_->GetNativeView();
  }

  ~DockDisplayer() {
    if (controller_)
      controller_->DockDisplayerDestroyed(this);
  }

  // Updates the state based on |in_enable_area|.
  void UpdateInEnabledArea(bool in_enable_area) {
    if (in_enable_area != in_enable_area_) {
      in_enable_area_ = in_enable_area;
      UpdateLayeredAlpha();
    }
  }

  // Resets the reference to the hosting DraggedTabController. This is invoked
  // when the DraggedTabController is destoryed.
  void clear_controller() { controller_ = NULL; }

  // NativeView of the window we create.
  gfx::NativeView popup_view() { return popup_view_; }

  // Starts the hide animation. When the window is closed the
  // DraggedTabController is notified by way of the DockDisplayerDestroyed
  // method
  void Hide() {
    if (hidden_)
      return;

    if (!popup_) {
      delete this;
      return;
    }
    hidden_ = true;
    animation_.Hide();
  }

  virtual void AnimationProgressed(const Animation* animation) {
    UpdateLayeredAlpha();
  }

  virtual void AnimationEnded(const Animation* animation) {
    if (!hidden_)
      return;
#if defined(OS_WIN)
    static_cast<views::WidgetWin*>(popup_)->Close();
#else
    NOTIMPLEMENTED();
#endif
    delete this;
  }

  virtual void UpdateLayeredAlpha() {
#if defined(OS_WIN)
    double scale = in_enable_area_ ? 1 : .5;
    static_cast<views::WidgetWin*>(popup_)->SetOpacity(
        static_cast<BYTE>(animation_.GetCurrentValue() * scale * 255.0));
    popup_->GetRootView()->SchedulePaint();
#else
    NOTIMPLEMENTED();
#endif
  }

 private:
  // DraggedTabController that created us.
  DraggedTabController* controller_;

  // Window we're showing.
  views::Widget* popup_;

  // NativeView of |popup_|. We cache this to avoid the possibility of
  // invoking a method on popup_ after we close it.
  gfx::NativeView popup_view_;

  // Animation for when first made visible.
  SlideAnimation animation_;

  // Have we been hidden?
  bool hidden_;

  // Value of DockInfo::in_enable_area.
  bool in_enable_area_;
};

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, public:

DraggedTabController::DraggedTabController(BaseTab* source_tab,
                                           BaseTabStrip* source_tabstrip)
    : dragged_contents_(NULL),
      original_delegate_(NULL),
      source_tabstrip_(source_tabstrip),
      source_model_index_(source_tabstrip->GetModelIndexOfBaseTab(source_tab)),
      attached_tabstrip_(NULL),
      attached_tab_(NULL),
      offset_to_width_ratio_(0),
      old_focused_view_(NULL),
      last_move_screen_loc_(0),
      mini_(source_tab->data().mini),
      pinned_(source_tabstrip->IsTabPinned(source_tab)),
      started_drag_(false),
      active_(true) {
  instance_ = this;
  SetDraggedContents(
      GetModel(source_tabstrip_)->GetTabContentsAt(source_model_index_));
  // Listen for Esc key presses.
  MessageLoopForUI::current()->AddObserver(this);
}

DraggedTabController::~DraggedTabController() {
  if (instance_ == this)
    instance_ = NULL;

  MessageLoopForUI::current()->RemoveObserver(this);
  // Need to delete the view here manually _before_ we reset the dragged
  // contents to NULL, otherwise if the view is animating to its destination
  // bounds, it won't be able to clean up properly since its cleanup routine
  // uses GetIndexForDraggedContents, which will be invalid.
  view_.reset(NULL);
  SetDraggedContents(NULL);  // This removes our observer.
}

// static
bool DraggedTabController::IsAttachedTo(BaseTabStrip* tab_strip) {
  return instance_ && instance_->active_ &&
      instance_->attached_tabstrip_ == tab_strip;
}

void DraggedTabController::CaptureDragInfo(views::View* tab,
                                           const gfx::Point& mouse_offset) {
  if (tab->width() > 0) {
    offset_to_width_ratio_ = static_cast<float>(mouse_offset.x()) /
        static_cast<float>(tab->width());
  }
  start_screen_point_ = GetCursorScreenPoint();
  mouse_offset_ = mouse_offset;
  InitWindowCreatePoint();
}

void DraggedTabController::Drag() {
  bring_to_front_timer_.Stop();

  // Before we get to dragging anywhere, ensure that we consider ourselves
  // attached to the source tabstrip.
  if (!started_drag_ && CanStartDrag()) {
    started_drag_ = true;
    SaveFocus();
    Attach(source_tabstrip_, gfx::Point());
  }

  if (started_drag_)
    ContinueDragging();
}

void DraggedTabController::EndDrag(bool canceled) {
  EndDragImpl(canceled ? CANCELED : NORMAL);
}

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, PageNavigator implementation:

void DraggedTabController::OpenURLFromTab(TabContents* source,
                                          const GURL& url,
                                          const GURL& referrer,
                                          WindowOpenDisposition disposition,
                                          PageTransition::Type transition) {
  if (original_delegate_) {
    if (disposition == CURRENT_TAB)
      disposition = NEW_WINDOW;

    original_delegate_->OpenURLFromTab(source, url, referrer,
                                       disposition, transition);
  }
}

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, TabContentsDelegate implementation:

void DraggedTabController::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (view_.get())
    view_->Update();
}

void DraggedTabController::AddNewContents(TabContents* source,
                                          TabContents* new_contents,
                                          WindowOpenDisposition disposition,
                                          const gfx::Rect& initial_pos,
                                          bool user_gesture) {
  DCHECK(disposition != CURRENT_TAB);

  // Theoretically could be called while dragging if the page tries to
  // spawn a window. Route this message back to the browser in most cases.
  if (original_delegate_) {
    original_delegate_->AddNewContents(source, new_contents, disposition,
                                       initial_pos, user_gesture);
  }
}

void DraggedTabController::ActivateContents(TabContents* contents) {
  // Ignored.
}

void DraggedTabController::DeactivateContents(TabContents* contents) {
  // Ignored.
}

void DraggedTabController::LoadingStateChanged(TabContents* source) {
  // It would be nice to respond to this message by changing the
  // screen shot in the dragged tab.
  if (view_.get())
    view_->Update();
}

void DraggedTabController::CloseContents(TabContents* source) {
  // Theoretically could be called by a window. Should be ignored
  // because window.close() is ignored (usually, even though this
  // method gets called.)
}

void DraggedTabController::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
  // Theoretically could be called by a web page trying to move its
  // own window. Should be ignored since we're moving the window...
}

void DraggedTabController::ToolbarSizeChanged(TabContents* source,
                                            bool finished) {
  // Dragged tabs don't care about this.
}

void DraggedTabController::URLStarredChanged(TabContents* source,
                                             bool starred) {
  // Ignored.
}

void DraggedTabController::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  // Ignored.
}

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, NotificationObserver implementation:

void DraggedTabController::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
  DCHECK(Source<TabContents>(source).ptr() == dragged_contents_);
  EndDragImpl(TAB_DESTROYED);
}

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, MessageLoop::Observer implementation:

#if defined(OS_WIN)
void DraggedTabController::WillProcessMessage(const MSG& msg) {
}

void DraggedTabController::DidProcessMessage(const MSG& msg) {
  // If the user presses ESC during a drag, we need to abort and revert things
  // to the way they were. This is the most reliable way to do this since no
  // single view or window reliably receives events throughout all the various
  // kinds of tab dragging.
  if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
    EndDrag(true);
}
#else
void DraggedTabController::WillProcessEvent(GdkEvent* event) {
}

void DraggedTabController::DidProcessEvent(GdkEvent* event) {
  if (event->type == GDK_KEY_PRESS &&
      reinterpret_cast<GdkEventKey*>(event)->keyval == GDK_Escape) {
    EndDrag(true);
  }
}
#endif

///////////////////////////////////////////////////////////////////////////////
// DraggedTabController, private:

void DraggedTabController::InitWindowCreatePoint() {
  // window_create_point_ is only used in CompleteDrag() (through
  // GetWindowCreatePoint() to get the start point of the docked window) when
  // the attached_tabstrip_ is NULL and all the window's related bound
  // information are obtained from source_tabstrip_. So, we need to get the
  // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise,
  // the window_create_point_ is not in the correct coordinate system. Please
  // refer to http://crbug.com/6223 comment #15 for detailed information.
  views::View* first_tab = source_tabstrip_->base_tab_at_tab_index(0);
  views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
  UpdateWindowCreatePoint();
}

void DraggedTabController::UpdateWindowCreatePoint() {
  // See comments in InitWindowCreatePoint for details on this.
  window_create_point_ = first_source_tab_point_;
  window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
}

gfx::Point DraggedTabController::GetWindowCreatePoint() const {
  gfx::Point cursor_point = GetCursorScreenPoint();
  if (dock_info_.type() != DockInfo::NONE) {
    // If we're going to dock, we need to return the exact coordinate,
    // otherwise we may attempt to maximize on the wrong monitor.
    return cursor_point;
  }
  return gfx::Point(cursor_point.x() - window_create_point_.x(),
                    cursor_point.y() - window_create_point_.y());
}

void DraggedTabController::UpdateDockInfo(const gfx::Point& screen_point) {
  // Update the DockInfo for the current mouse coordinates.
  DockInfo dock_info = GetDockInfoAtPoint(screen_point);
  if (source_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP &&
      ((dock_info.type() == DockInfo::LEFT_OF_WINDOW &&
        !base::i18n::IsRTL()) ||
       (dock_info.type() == DockInfo::RIGHT_OF_WINDOW &&
        base::i18n::IsRTL()))) {
    // For side tabs it's way to easy to trigger to docking along the left/right
    // edge, so we disable it.
    dock_info = DockInfo();
  }
  if (!dock_info.equals(dock_info_)) {
    // DockInfo for current position differs.
    if (dock_info_.type() != DockInfo::NONE &&
        !dock_controllers_.empty()) {
      // Hide old visual indicator.
      dock_controllers_.back()->Hide();
    }
    dock_info_ = dock_info;
    if (dock_info_.type() != DockInfo::NONE) {
      // Show new docking position.
      DockDisplayer* controller = new DockDisplayer(this, dock_info_);
      if (controller->popup_view()) {
        dock_controllers_.push_back(controller);
        dock_windows_.insert(controller->popup_view());
      } else {
        delete controller;
      }
    }
  } else if (dock_info_.type() != DockInfo::NONE &&
             !dock_controllers_.empty()) {
    // Current dock position is the same as last, update the controller's
    // in_enable_area state as it may have changed.
    dock_controllers_.back()->UpdateInEnabledArea(dock_info_.in_enable_area());
  }
}

void DraggedTabController::SetDraggedContents(TabContents* new_contents) {
  if (dragged_contents_) {
    registrar_.Remove(this,
                      NotificationType::TAB_CONTENTS_DESTROYED,
                      Source<TabContents>(dragged_contents_));
    if (original_delegate_)
      dragged_contents_->set_delegate(original_delegate_);
  }
  original_delegate_ = NULL;
  dragged_contents_ = new_contents;
  if (dragged_contents_) {
    registrar_.Add(this,
                   NotificationType::TAB_CONTENTS_DESTROYED,
                   Source<TabContents>(dragged_contents_));

    // We need to be the delegate so we receive messages about stuff,
    // otherwise our dragged_contents() may be replaced and subsequently
    // collected/destroyed while the drag is in process, leading to
    // nasty crashes.
    original_delegate_ = dragged_contents_->delegate();
    dragged_contents_->set_delegate(this);
  }
}

void DraggedTabController::SaveFocus() {
  if (!old_focused_view_) {
    old_focused_view_ = source_tabstrip_->GetRootView()->GetFocusedView();
    source_tabstrip_->GetRootView()->FocusView(source_tabstrip_);
  }
}

void DraggedTabController::RestoreFocus() {
  if (old_focused_view_ && attached_tabstrip_ == source_tabstrip_)
    old_focused_view_->GetRootView()->FocusView(old_focused_view_);
  old_focused_view_ = NULL;
}

bool DraggedTabController::CanStartDrag() const {
  // Determine if the mouse has moved beyond a minimum elasticity distance in
  // any direction from the starting point.
  static const int kMinimumDragDistance = 10;
  gfx::Point screen_point = GetCursorScreenPoint();
  int x_offset = abs(screen_point.x() - start_screen_point_.x());
  int y_offset = abs(screen_point.y() - start_screen_point_.y());
  return sqrt(pow(static_cast<float>(x_offset), 2) +
              pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
}

void DraggedTabController::ContinueDragging() {
  // Note that the coordinates given to us by |drag_event| are basically
  // useless, since they're in source_tab_ coordinates. On the surface, you'd
  // think we could just convert them to screen coordinates, however in the
  // situation where we're dragging the last tab in a window when multiple
  // windows are open, the coordinates of |source_tab_| are way off in
  // hyperspace since the window was moved there instead of being closed so
  // that we'd keep receiving events. And our ConvertPointToScreen methods
  // aren't really multi-screen aware. So really it's just safer to get the
  // actual position of the mouse cursor directly from Windows here, which is
  // guaranteed to be correct regardless of monitor config.
  gfx::Point screen_point = GetCursorScreenPoint();

#if defined(OS_CHROMEOS)
  // We don't allow detaching in chrome os.
  BaseTabStrip* target_tabstrip = source_tabstrip_;
#else
  // Determine whether or not we have dragged over a compatible TabStrip in
  // another browser window. If we have, we should attach to it and start
  // dragging within it.
  BaseTabStrip* target_tabstrip = GetTabStripForPoint(screen_point);
#endif
  if (target_tabstrip != attached_tabstrip_) {
    // Make sure we're fully detached from whatever TabStrip we're attached to
    // (if any).
    if (attached_tabstrip_)
      Detach();
    if (target_tabstrip)
      Attach(target_tabstrip, screen_point);
  }
  if (!target_tabstrip) {
    bring_to_front_timer_.Start(
        base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this,
        &DraggedTabController::BringWindowUnderMouseToFront);
  }

  UpdateDockInfo(screen_point);

  if (attached_tabstrip_)
    MoveAttachedTab(screen_point);
  else
    MoveDetachedTab(screen_point);
}

void DraggedTabController::MoveAttachedTab(const gfx::Point& screen_point) {
  DCHECK(attached_tabstrip_);
  DCHECK(!view_.get());

  gfx::Point dragged_view_point = GetAttachedTabDragPoint(screen_point);
  TabStripModel* attached_model = GetModel(attached_tabstrip_);
  int from_index = attached_model->GetIndexOfTabContents(dragged_contents_);

  int threshold = kVerticalMoveThreshold;
  if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    TabStrip* tab_strip = static_cast<TabStrip*>(attached_tabstrip_);

    // Determine the horizontal move threshold. This is dependent on the width
    // of tabs. The smaller the tabs compared to the standard size, the smaller
    // the threshold.
    double unselected, selected;
    tab_strip->GetCurrentTabWidths(&unselected, &selected);
    double ratio = unselected / Tab::GetStandardSize().width();
    threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
  }

  // Update the model, moving the TabContents from one index to another. Do this
  // only if we have moved a minimum distance since the last reorder (to prevent
  // jitter).
  if (abs(MajorAxisValue(screen_point, attached_tabstrip_) -
          last_move_screen_loc_) > threshold) {
    gfx::Point dragged_view_screen_point(dragged_view_point);
    views::View::ConvertPointToScreen(attached_tabstrip_,
                                      &dragged_view_screen_point);
    gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_screen_point);
    int to_index = GetInsertionIndexForDraggedBounds(bounds, true);
    to_index = NormalizeIndexToAttachedTabStrip(to_index);
    if (from_index != to_index) {
      last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip_);
      attached_model->MoveTabContentsAt(from_index, to_index, true);
    }
  }

  attached_tab_->SchedulePaint();
  attached_tab_->SetX(dragged_view_point.x());
  attached_tab_->SetX(
      attached_tabstrip_->MirroredLeftPointForRect(attached_tab_->bounds()));
  attached_tab_->SetY(dragged_view_point.y());
  attached_tab_->SchedulePaint();
}

void DraggedTabController::MoveDetachedTab(const gfx::Point& screen_point) {
  DCHECK(!attached_tabstrip_);
  DCHECK(view_.get());

  int x = screen_point.x() - mouse_offset_.x();
  int y = screen_point.y() - mouse_offset_.y();

  // Move the View. There are no changes to the model if we're detached.
  view_->MoveTo(gfx::Point(x, y));
}

DockInfo DraggedTabController::GetDockInfoAtPoint(
    const gfx::Point& screen_point) {
  if (attached_tabstrip_) {
    // If the mouse is over a tab strip, don't offer a dock position.
    return DockInfo();
  }

  if (dock_info_.IsValidForPoint(screen_point)) {
    // It's possible any given screen coordinate has multiple docking
    // positions. Check the current info first to avoid having the docking
    // position bounce around.
    return dock_info_;
  }

  gfx::NativeView dragged_hwnd = view_->GetWidget()->GetNativeView();
  dock_windows_.insert(dragged_hwnd);
  DockInfo info = DockInfo::GetDockInfoAtPoint(screen_point, dock_windows_);
  dock_windows_.erase(dragged_hwnd);
  return info;
}

BaseTabStrip* DraggedTabController::GetTabStripForPoint(
    const gfx::Point& screen_point) {
  gfx::NativeView dragged_view = NULL;
  if (view_.get()) {
    dragged_view = view_->GetWidget()->GetNativeView();
    dock_windows_.insert(dragged_view);
  }
  gfx::NativeWindow local_window =
      DockInfo::GetLocalProcessWindowAtPoint(screen_point, dock_windows_);
  if (dragged_view)
    dock_windows_.erase(dragged_view);
  if (!local_window)
    return NULL;
  BrowserView* browser =
      BrowserView::GetBrowserViewForNativeWindow(local_window);
  // We don't allow drops on windows that don't have tabstrips.
  if (!browser ||
      !browser->browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
    return NULL;

  BaseTabStrip* other_tabstrip = browser->tabstrip();
  if (!other_tabstrip->controller()->IsCompatibleWith(source_tabstrip_))
    return NULL;
  return GetTabStripIfItContains(other_tabstrip, screen_point);
}

BaseTabStrip* DraggedTabController::GetTabStripIfItContains(
    BaseTabStrip* tabstrip, const gfx::Point& screen_point) const {
  static const int kVerticalDetachMagnetism = 15;
  static const int kHorizontalDetachMagnetism = 15;
  // Make sure the specified screen point is actually within the bounds of the
  // specified tabstrip...
  gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
  if (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    if (screen_point.x() < tabstrip_bounds.right() &&
        screen_point.x() >= tabstrip_bounds.x()) {
      // TODO(beng): make this be relative to the start position of the mouse
      // for the source TabStrip.
      int upper_threshold = tabstrip_bounds.bottom() + kVerticalDetachMagnetism;
      int lower_threshold = tabstrip_bounds.y() - kVerticalDetachMagnetism;
      if (screen_point.y() >= lower_threshold &&
          screen_point.y() <= upper_threshold) {
        return tabstrip;
      }
    }
  } else {
    if (screen_point.y() < tabstrip_bounds.bottom() &&
        screen_point.y() >= tabstrip_bounds.y()) {
      int upper_threshold = tabstrip_bounds.right() +
          kHorizontalDetachMagnetism;
      int lower_threshold = tabstrip_bounds.x() - kHorizontalDetachMagnetism;
      if (screen_point.x() >= lower_threshold &&
          screen_point.x() <= upper_threshold) {
        return tabstrip;
      }
    }
  }
  return NULL;
}

void DraggedTabController::Attach(BaseTabStrip* attached_tabstrip,
                                  const gfx::Point& screen_point) {
  DCHECK(!attached_tabstrip_);  // We should already have detached by the time
                                // we get here.
  DCHECK(!attached_tab_);  // Similarly there should be no attached tab.

  attached_tabstrip_ = attached_tabstrip;

  // We don't need the photo-booth while we're attached.
  photobooth_.reset(NULL);

  // And we don't need the dragged view.
  view_.reset();

  BaseTab* tab = GetTabMatchingDraggedContents(attached_tabstrip_);

  if (!tab) {
    // There is no Tab in |attached_tabstrip| that corresponds to the dragged
    // TabContents. We must now create one.

    // Remove ourselves as the delegate now that the dragged TabContents is
    // being inserted back into a Browser.
    dragged_contents_->set_delegate(NULL);
    original_delegate_ = NULL;

    // Return the TabContents' to normalcy.
    dragged_contents_->set_capturing_contents(false);

    // Inserting counts as a move. We don't want the tabs to jitter when the
    // user moves the tab immediately after attaching it.
    last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip);

    // Figure out where to insert the tab based on the bounds of the dragged
    // representation and the ideal bounds of the other Tabs already in the
    // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are
    // changing due to animation).
    gfx::Rect bounds = GetDraggedViewTabStripBounds(screen_point);
    int index = GetInsertionIndexForDraggedBounds(bounds, false);
    attached_tabstrip_->set_attaching_dragged_tab(true);
    GetModel(attached_tabstrip_)->InsertTabContentsAt(
        index, dragged_contents_,
        TabStripModel::ADD_SELECTED |
            (pinned_ ? TabStripModel::ADD_PINNED : 0));
    attached_tabstrip_->set_attaching_dragged_tab(false);

    tab = GetTabMatchingDraggedContents(attached_tabstrip_);
  }
  DCHECK(tab);  // We should now have a tab.
  attached_tab_ = tab;
  attached_tabstrip_->StartedDraggingTab(tab);

  if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    // The size of the dragged tab may have changed. Adjust the x offset so that
    // ratio of mouse_offset_ to original width is maintained.
    mouse_offset_.set_x(static_cast<int>(offset_to_width_ratio_ *
                                         static_cast<int>(tab->width())));
  }

  // Move the corresponding window to the front.
  attached_tabstrip_->GetWindow()->Activate();
}

void DraggedTabController::Detach() {
  // Prevent the TabContents' HWND from being hidden by any of the model
  // operations performed during the drag.
  dragged_contents_->set_capturing_contents(true);

  // Update the Model.
  TabRendererData tab_data = attached_tab_->data();
  TabStripModel* attached_model = GetModel(attached_tabstrip_);
  int index = attached_model->GetIndexOfTabContents(dragged_contents_);
  DCHECK(index != -1);
  // Hide the tab so that the user doesn't see it animate closed.
  attached_tab_->SetVisible(false);
  int attached_tab_width = attached_tab_->width();
  attached_model->DetachTabContentsAt(index);
  // Detaching may end up deleting the tab, drop references to it.
  attached_tab_ = NULL;

  // If we've removed the last Tab from the TabStrip, hide the frame now.
  if (attached_model->empty())
    HideFrame();

  // Set up the photo booth to start capturing the contents of the dragged
  // TabContents.
  if (!photobooth_.get()) {
    photobooth_.reset(
        NativeViewPhotobooth::Create(dragged_contents_->GetNativeView()));
  }

  // Create the dragged view.
  EnsureDraggedView(tab_data);
  view_->SetTabWidthAndUpdate(attached_tab_width, photobooth_.get());

  // Detaching resets the delegate, but we still want to be the delegate.
  dragged_contents_->set_delegate(this);

  attached_tabstrip_ = NULL;
}

int DraggedTabController::GetInsertionIndexForDraggedBounds(
    const gfx::Rect& dragged_bounds,
    bool is_tab_attached) const {
  int right_tab_x = 0;
  int bottom_tab_y = 0;

  // If the UI layout of the tab strip is right-to-left, we need to mirror the
  // bounds of the dragged tab before performing the drag/drop related
  // calculations. We mirror the dragged bounds because we determine the
  // position of each tab on the tab strip by calling GetBounds() (without the
  // mirroring transformation flag) which effectively means that even though
  // the tabs are rendered from right to left, the code performs the
  // calculation as if the tabs are laid out from left to right. Mirroring the
  // dragged bounds adjusts the coordinates of the tab we are dragging so that
  // it uses the same orientation used by the tabs on the tab strip.
  gfx::Rect adjusted_bounds(dragged_bounds);
  adjusted_bounds.set_x(
      attached_tabstrip_->MirroredLeftPointForRect(adjusted_bounds));

  int index = -1;
  for (int i = 0; i < attached_tabstrip_->tab_count(); ++i) {
    const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
    if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
      gfx::Rect left_half = ideal_bounds;
      left_half.set_width(left_half.width() / 2);
      gfx::Rect right_half = ideal_bounds;
      right_half.set_width(ideal_bounds.width() - left_half.width());
      right_half.set_x(left_half.right());
      right_tab_x = right_half.right();
      if (adjusted_bounds.x() >= right_half.x() &&
          adjusted_bounds.x() < right_half.right()) {
        index = i + 1;
        break;
      } else if (adjusted_bounds.x() >= left_half.x() &&
                 adjusted_bounds.x() < left_half.right()) {
        index = i;
        break;
      }
    } else {
      // Vertical tab strip.
      int max_y = ideal_bounds.bottom();
      int mid_y = ideal_bounds.y() + ideal_bounds.height() / 2;
      bottom_tab_y = max_y;
      if (adjusted_bounds.y() < mid_y) {
        index = i;
        break;
      } else if (adjusted_bounds.y() >= mid_y && adjusted_bounds.y() < max_y) {
        index = i + 1;
        break;
      }
    }
  }
  if (index == -1) {
    if ((attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP &&
         adjusted_bounds.right() > right_tab_x) ||
        (attached_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP &&
         adjusted_bounds.y() >= bottom_tab_y)) {
      index = GetModel(attached_tabstrip_)->count();
    } else {
      index = 0;
    }
  }

  index = GetModel(attached_tabstrip_)->ConstrainInsertionIndex(index, mini_);
  if (is_tab_attached && mini_ &&
      index == GetModel(attached_tabstrip_)->IndexOfFirstNonMiniTab()) {
    index--;
  }
  return index;
}

gfx::Rect DraggedTabController::GetDraggedViewTabStripBounds(
    const gfx::Point& screen_point) {
  gfx::Point client_point =
      ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point);
  // attached_tab_ is NULL when inserting into a new tabstrip.
  if (attached_tab_) {
    return gfx::Rect(client_point.x(), client_point.y(),
                     attached_tab_->width(), attached_tab_->height());
  }

  if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    double sel_width, unselected_width;
    static_cast<TabStrip*>(attached_tabstrip_)->GetCurrentTabWidths(
        &sel_width, &unselected_width);
    return gfx::Rect(client_point.x(), client_point.y(),
                     static_cast<int>(sel_width),
                     Tab::GetStandardSize().height());
  }

  return gfx::Rect(client_point.x(), client_point.y(),
                   attached_tabstrip_->width(),
                   SideTab::GetPreferredHeight());
}

gfx::Point DraggedTabController::GetAttachedTabDragPoint(
    const gfx::Point& screen_point) {
  DCHECK(attached_tabstrip_);  // The tab must be attached.

  int x = screen_point.x() - mouse_offset_.x();
  int y = screen_point.y() - mouse_offset_.y();

  gfx::Point tab_loc(x, y);
  views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_loc);

  x = tab_loc.x();
  y = tab_loc.y();

  const gfx::Size& tab_size = attached_tab_->bounds().size();

  if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) {
    int max_x = attached_tabstrip_->bounds().right() - tab_size.width();
    x = std::min(std::max(x, 0), max_x);
    y = 0;
  } else {
    x = SideTabStrip::kTabStripInset;
    int max_y = attached_tabstrip_->bounds().bottom() - tab_size.height();
    y = std::min(std::max(y, SideTabStrip::kTabStripInset), max_y);
  }
  return gfx::Point(x, y);
}

BaseTab* DraggedTabController::GetTabMatchingDraggedContents(
    BaseTabStrip* tabstrip) const {
  int model_index =
      GetModel(tabstrip)->GetIndexOfTabContents(dragged_contents_);
  return model_index == TabStripModel::kNoTab ?
      NULL : tabstrip->GetBaseTabAtModelIndex(model_index);
}

void DraggedTabController::EndDragImpl(EndDragType type) {
  // WARNING: this may be invoked multiple times. In particular, if deletion
  // occurs after a delay (as it does when the tab is released in the original
  // tab strip) and the navigation controller/tab contents is deleted before
  // the animation finishes, this is invoked twice. The second time through
  // type == TAB_DESTROYED.

  active_ = false;

  bring_to_front_timer_.Stop();

  // Hide the current dock controllers.
  for (size_t i = 0; i < dock_controllers_.size(); ++i) {
    // Be sure and clear the controller first, that way if Hide ends up
    // deleting the controller it won't call us back.
    dock_controllers_[i]->clear_controller();
    dock_controllers_[i]->Hide();
  }
  dock_controllers_.clear();
  dock_windows_.clear();

  if (type != TAB_DESTROYED) {
    // We only finish up the drag if we were actually dragging. If start_drag_
    // is false, the user just clicked and released and didn't move the mouse
    // enough to trigger a drag.
    if (started_drag_) {
      RestoreFocus();
      if (type == CANCELED)
        RevertDrag();
      else
        CompleteDrag();
    }
    if (dragged_contents_ && dragged_contents_->delegate() == this)
      dragged_contents_->set_delegate(original_delegate_);
  } else {
    // If we get here it means the NavigationController is going down. Don't
    // attempt to do any cleanup other than resetting the delegate (if we're
    // still the delegate).
    if (dragged_contents_ && dragged_contents_->delegate() == this)
      dragged_contents_->set_delegate(NULL);
    dragged_contents_ = NULL;
  }

  // The delegate of the dragged contents should have been reset. Unset the
  // original delegate so that we don't attempt to reset the delegate when
  // deleted.
  DCHECK(!dragged_contents_ || dragged_contents_->delegate() != this);
  original_delegate_ = NULL;

  source_tabstrip_->DestroyDragController();
}

void DraggedTabController::RevertDrag() {
  DCHECK(started_drag_);

  // We save this here because code below will modify |attached_tabstrip_|.
  bool restore_frame = attached_tabstrip_ != source_tabstrip_;
  if (attached_tabstrip_) {
    int index = GetModel(attached_tabstrip_)->GetIndexOfTabContents(
        dragged_contents_);
    if (attached_tabstrip_ != source_tabstrip_) {
      // The Tab was inserted into another TabStrip. We need to put it back
      // into the original one.
      GetModel(attached_tabstrip_)->DetachTabContentsAt(index);
      // TODO(beng): (Cleanup) seems like we should use Attach() for this
      //             somehow.
      attached_tabstrip_ = source_tabstrip_;
      GetModel(source_tabstrip_)->InsertTabContentsAt(
          source_model_index_, dragged_contents_,
          TabStripModel::ADD_SELECTED |
              (pinned_ ? TabStripModel::ADD_PINNED : 0));
    } else {
      // The Tab was moved within the TabStrip where the drag was initiated.
      // Move it back to the starting location.
      GetModel(source_tabstrip_)->MoveTabContentsAt(index, source_model_index_,
          true);
      source_tabstrip_->StoppedDraggingTab(attached_tab_);
    }
  } else {
    // TODO(beng): (Cleanup) seems like we should use Attach() for this
    //             somehow.
    attached_tabstrip_ = source_tabstrip_;
    // The Tab was detached from the TabStrip where the drag began, and has not
    // been attached to any other TabStrip. We need to put it back into the
    // source TabStrip.
    GetModel(source_tabstrip_)->InsertTabContentsAt(
        source_model_index_, dragged_contents_,
        TabStripModel::ADD_SELECTED |
            (pinned_ ? TabStripModel::ADD_PINNED : 0));
  }

  // If we're not attached to any TabStrip, or attached to some other TabStrip,
  // we need to restore the bounds of the original TabStrip's frame, in case
  // it has been hidden.
  if (restore_frame) {
    if (!restore_bounds_.IsEmpty()) {
#if defined(OS_WIN)
      HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView();
      MoveWindow(frame_hwnd, restore_bounds_.x(), restore_bounds_.y(),
                 restore_bounds_.width(), restore_bounds_.height(), TRUE);
#else
      NOTIMPLEMENTED();
#endif
    }
  }
}

void DraggedTabController::CompleteDrag() {
  DCHECK(started_drag_);

  if (attached_tabstrip_) {
    attached_tabstrip_->StoppedDraggingTab(attached_tab_);
  } else {
    if (dock_info_.type() != DockInfo::NONE) {
      Profile* profile = GetModel(source_tabstrip_)->profile();
      switch (dock_info_.type()) {
        case DockInfo::LEFT_OF_WINDOW:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Left"),
                                    profile);
          break;

        case DockInfo::RIGHT_OF_WINDOW:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Right"),
                                    profile);
          break;

        case DockInfo::BOTTOM_OF_WINDOW:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Bottom"),
                                    profile);
          break;

        case DockInfo::TOP_OF_WINDOW:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Top"),
                                    profile);
          break;

        case DockInfo::MAXIMIZE:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Maximize"),
                                    profile);
          break;

        case DockInfo::LEFT_HALF:
          UserMetrics::RecordAction(UserMetricsAction("DockingWindow_LeftHalf"),
                                    profile);
          break;

        case DockInfo::RIGHT_HALF:
          UserMetrics::RecordAction(
              UserMetricsAction("DockingWindow_RightHalf"),
              profile);
          break;

        case DockInfo::BOTTOM_HALF:
          UserMetrics::RecordAction(
              UserMetricsAction("DockingWindow_BottomHalf"),
              profile);
          break;

        default:
          NOTREACHED();
          break;
      }
    }
    // Compel the model to construct a new window for the detached TabContents.
    views::Window* window = source_tabstrip_->GetWindow();
    gfx::Rect window_bounds(window->GetNormalBounds());
    window_bounds.set_origin(GetWindowCreatePoint());
    // When modifying the following if statement, please make sure not to
    // introduce issue listed in http://crbug.com/6223 comment #11.
    bool rtl_ui = base::i18n::IsRTL();
    bool has_dock_position = (dock_info_.type() != DockInfo::NONE);
    if (rtl_ui && has_dock_position) {
      // Mirror X axis so the docked tab is aligned using the mouse click as
      // the top-right corner.
      window_bounds.set_x(window_bounds.x() - window_bounds.width());
    }
    Browser* new_browser =
        GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents(
        dragged_contents_, window_bounds, dock_info_, window->IsMaximized());
    TabStripModel* new_model = new_browser->tabstrip_model();
    new_model->SetTabPinned(new_model->GetIndexOfTabContents(dragged_contents_),
                            pinned_);
    new_browser->window()->Show();
  }

  CleanUpHiddenFrame();
}

void DraggedTabController::EnsureDraggedView(const TabRendererData& data) {
  if (!view_.get()) {
    gfx::Rect tab_bounds;
    dragged_contents_->GetContainerBounds(&tab_bounds);
    BaseTab* renderer = source_tabstrip_->CreateTabForDragging();
    renderer->SetData(data);
    // DraggedTabView takes ownership of renderer.
    view_.reset(new DraggedTabView(renderer, mouse_offset_,
                                   tab_bounds.size(),
                                   Tab::GetMinimumSelectedSize()));
  }
}

gfx::Point DraggedTabController::GetCursorScreenPoint() const {
#if defined(OS_WIN)
  DWORD pos = GetMessagePos();
  return gfx::Point(pos);
#else
  gint x, y;
  gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
  return gfx::Point(x, y);
#endif
}

gfx::Rect DraggedTabController::GetViewScreenBounds(views::View* view) const {
  gfx::Point view_topleft;
  views::View::ConvertPointToScreen(view, &view_topleft);
  gfx::Rect view_screen_bounds = view->GetLocalBounds(true);
  view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
  return view_screen_bounds;
}

int DraggedTabController::NormalizeIndexToAttachedTabStrip(int index) const {
  DCHECK(attached_tabstrip_) << "Can only be called when attached!";
  TabStripModel* attached_model = GetModel(attached_tabstrip_);
  if (index >= attached_model->count())
    return attached_model->count() - 1;
  if (index == TabStripModel::kNoTab)
    return 0;
  return index;
}

void DraggedTabController::HideFrame() {
#if defined(OS_WIN)
  // We don't actually hide the window, rather we just move it way off-screen.
  // If we actually hide it, we stop receiving drag events.
  HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView();
  RECT wr;
  GetWindowRect(frame_hwnd, &wr);
  MoveWindow(frame_hwnd, 0xFFFF, 0xFFFF, wr.right - wr.left,
             wr.bottom - wr.top, TRUE);

  // We also save the bounds of the window prior to it being moved, so that if
  // the drag session is aborted we can restore them.
  restore_bounds_ = gfx::Rect(wr);
#else
  NOTIMPLEMENTED();
#endif
}

void DraggedTabController::CleanUpHiddenFrame() {
  // If the model we started dragging from is now empty, we must ask the
  // delegate to close the frame.
  if (GetModel(source_tabstrip_)->empty())
    GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession();
}

void DraggedTabController::DockDisplayerDestroyed(
    DockDisplayer* controller) {
  DockWindows::iterator dock_i =
      dock_windows_.find(controller->popup_view());
  if (dock_i != dock_windows_.end())
    dock_windows_.erase(dock_i);
  else
    NOTREACHED();

  std::vector<DockDisplayer*>::iterator i =
      std::find(dock_controllers_.begin(), dock_controllers_.end(),
                controller);
  if (i != dock_controllers_.end())
    dock_controllers_.erase(i);
  else
    NOTREACHED();
}

void DraggedTabController::BringWindowUnderMouseToFront() {
  // If we're going to dock to another window, bring it to the front.
  gfx::NativeWindow window = dock_info_.window();
  if (!window) {
    gfx::NativeView dragged_view = view_->GetWidget()->GetNativeView();
    dock_windows_.insert(dragged_view);
    window = DockInfo::GetLocalProcessWindowAtPoint(GetCursorScreenPoint(),
                                                    dock_windows_);
    dock_windows_.erase(dragged_view);
  }
  if (window) {
#if defined(OS_WIN)
    // Move the window to the front.
    SetWindowPos(window, HWND_TOP, 0, 0, 0, 0,
                 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

    // The previous call made the window appear on top of the dragged window,
    // move the dragged window to the front.
    SetWindowPos(view_->GetWidget()->GetNativeView(), HWND_TOP, 0, 0, 0, 0,
                 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
#else
    NOTIMPLEMENTED();
#endif
  }
}

TabStripModel* DraggedTabController::GetModel(BaseTabStrip* tabstrip) const {
  return static_cast<BrowserTabStripController*>(tabstrip->controller())->
      model();
}