summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/views/tabs/default_tab_drag_controller.cc862
-rw-r--r--chrome/browser/ui/views/tabs/default_tab_drag_controller.h186
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller.h75
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller2.cc1425
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller2.h395
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc122
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h72
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc354
-rw-r--r--chrome/browser/ui/views/tabs/tab_dragging_test.cc514
-rw-r--r--chrome/browser/ui/views/tabs/tab_strip.cc9
-rw-r--r--chrome/browser/ui/views/tabs/tab_strip.h5
-rw-r--r--chrome/chrome_browser.gypi3
-rw-r--r--chrome/chrome_tests.gypi5
13 files changed, 1208 insertions, 2819 deletions
diff --git a/chrome/browser/ui/views/tabs/default_tab_drag_controller.cc b/chrome/browser/ui/views/tabs/default_tab_drag_controller.cc
index 523e5e4..266db28 100644
--- a/chrome/browser/ui/views/tabs/default_tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/default_tab_drag_controller.cc
@@ -22,8 +22,10 @@
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/touch_tab_strip_layout.h"
+#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/user_metrics.h"
@@ -41,11 +43,9 @@
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
-#if defined(USE_AURA)
+#if defined(USE_ASH)
#include "ash/shell.h"
-#include "chrome/browser/ui/views/tabs/tab_drag_controller2.h"
-#elif defined(OS_WIN)
-#include "chrome/browser/ui/views/tabs/tab_drag_controller2.h"
+#include "ash/wm/property_util.h"
#endif
using content::OpenURLParams;
@@ -59,7 +59,7 @@ static const int kHorizontalMoveThreshold = 16; // Pixels.
static const int kStackedDistance = 36;
// If non-null there is a drag underway.
-static DefaultTabDragController* instance_ = NULL;
+static TabDragController* instance_ = NULL;
namespace {
@@ -187,6 +187,21 @@ class DockView : public views::View {
DISALLOW_COPY_AND_ASSIGN(DockView);
};
+void SetTrackedByWorkspace(gfx::NativeWindow window, bool value) {
+#if defined(USE_ASH)
+ ash::SetTrackedByWorkspace(window, value);
+#endif
+}
+
+bool ShouldDetachIntoNewBrowser() {
+#if defined(USE_AURA)
+ return true;
+#else
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kTabBrowserDragging);
+#endif
+}
+
} // namespace
///////////////////////////////////////////////////////////////////////////////
@@ -196,19 +211,16 @@ class DockView : public views::View {
// 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 DefaultTabDragController::DockDisplayer : public ui::AnimationDelegate {
+class TabDragController::DockDisplayer : public ui::AnimationDelegate {
public:
- DockDisplayer(DefaultTabDragController* controller,
- const DockInfo& info)
+ DockDisplayer(TabDragController* 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)
popup_ = new views::Widget;
- // TODO(sky): This should "just work" on Gtk now.
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
params.transparent = true;
params.keep_on_top = true;
@@ -221,13 +233,10 @@ class DefaultTabDragController::DockDisplayer : public ui::AnimationDelegate {
else
animation_.Show();
popup_->Show();
-#else
- NOTIMPLEMENTED();
-#endif
popup_view_ = popup_->GetNativeView();
}
- ~DockDisplayer() {
+ virtual ~DockDisplayer() {
if (controller_)
controller_->DockDisplayerDestroyed(this);
}
@@ -240,15 +249,15 @@ class DefaultTabDragController::DockDisplayer : public ui::AnimationDelegate {
}
}
- // Resets the reference to the hosting DefaultTabDragController. This is
- // invoked when the DefaultTabDragController is destroyed.
+ // Resets the reference to the hosting TabDragController. This is
+ // invoked when the TabDragController is destroyed.
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
- // DefaultTabDragController is notified by way of the DockDisplayerDestroyed
+ // TabDragController is notified by way of the DockDisplayerDestroyed
// method
void Hide() {
if (hidden_)
@@ -262,26 +271,26 @@ class DefaultTabDragController::DockDisplayer : public ui::AnimationDelegate {
animation_.Hide();
}
- virtual void AnimationProgressed(const ui::Animation* animation) {
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
UpdateLayeredAlpha();
}
- virtual void AnimationEnded(const ui::Animation* animation) {
+ virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
if (!hidden_)
return;
popup_->Close();
delete this;
}
- virtual void UpdateLayeredAlpha() {
+ private:
+ void UpdateLayeredAlpha() {
double scale = in_enable_area_ ? 1 : .5;
popup_->SetOpacity(static_cast<unsigned char>(animation_.GetCurrentValue() *
scale * 255.0));
}
- private:
- // DefaultTabDragController that created us.
- DefaultTabDragController* controller_;
+ // TabDragController that created us.
+ TabDragController* controller_;
// Window we're showing.
views::Widget* popup_;
@@ -300,7 +309,7 @@ class DefaultTabDragController::DockDisplayer : public ui::AnimationDelegate {
bool in_enable_area_;
};
-DefaultTabDragController::TabDragData::TabDragData()
+TabDragController::TabDragData::TabDragData()
: contents(NULL),
original_delegate(NULL),
source_model_index(-1),
@@ -308,14 +317,15 @@ DefaultTabDragController::TabDragData::TabDragData()
pinned(false) {
}
-DefaultTabDragController::TabDragData::~TabDragData() {
+TabDragController::TabDragData::~TabDragData() {
}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, public:
+// TabDragController, public:
-DefaultTabDragController::DefaultTabDragController()
- : source_tabstrip_(NULL),
+TabDragController::TabDragController()
+ : detach_into_browser_(ShouldDetachIntoNewBrowser()),
+ source_tabstrip_(NULL),
attached_tabstrip_(NULL),
source_tab_offset_(0),
offset_to_width_ratio_(0),
@@ -326,14 +336,31 @@ DefaultTabDragController::DefaultTabDragController()
source_tab_index_(std::numeric_limits<size_t>::max()),
initial_move_(true),
move_only_(false),
- mouse_move_direction_(0) {
+ mouse_move_direction_(0),
+ is_dragging_window_(false),
+ end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
+ waiting_for_run_loop_to_exit_(false),
+ tab_strip_to_attach_to_after_exit_(NULL),
+ move_loop_widget_(NULL),
+ destroyed_(NULL) {
instance_ = this;
}
-DefaultTabDragController::~DefaultTabDragController() {
+TabDragController::~TabDragController() {
if (instance_ == this)
instance_ = NULL;
+ if (destroyed_)
+ *destroyed_ = true;
+
+ if (move_loop_widget_) {
+ move_loop_widget_->RemoveObserver(this);
+ SetTrackedByWorkspace(move_loop_widget_->GetNativeView(), true);
+ }
+
+ if (source_tabstrip_ && detach_into_browser_)
+ GetModel(source_tabstrip_)->RemoveObserver(this);
+
MessageLoopForUI::current()->RemoveObserver(this);
// Need to delete the view here manually _before_ we reset the dragged
@@ -344,10 +371,11 @@ DefaultTabDragController::~DefaultTabDragController() {
// Reset the delegate of the dragged WebContents. This ends up doing nothing
// if the drag was completed.
- ResetDelegates();
+ if (!detach_into_browser_)
+ ResetDelegates();
}
-void DefaultTabDragController::Init(
+void TabDragController::Init(
TabStrip* source_tabstrip,
BaseTab* source_tab,
const std::vector<BaseTab*>& tabs,
@@ -363,11 +391,12 @@ void DefaultTabDragController::Init(
mouse_offset_ = mouse_offset;
move_only_ = move_only;
last_screen_point_ = start_screen_point_;
-
if (move_only_) {
last_move_screen_loc_ = start_screen_point_.x();
initial_tab_positions_ = source_tabstrip->GetTabXCoordinates();
}
+ if (detach_into_browser_)
+ GetModel(source_tabstrip_)->AddObserver(this);
drag_data_.resize(tabs.size());
for (size_t i = 0; i < tabs.size(); ++i)
@@ -386,30 +415,24 @@ void DefaultTabDragController::Init(
initial_selection_model_.Copy(initial_selection_model);
}
-void DefaultTabDragController::InitTabDragData(BaseTab* tab,
- TabDragData* drag_data) {
- drag_data->source_model_index =
- source_tabstrip_->GetModelIndexOfBaseTab(tab);
- drag_data->contents = GetModel(source_tabstrip_)->GetTabContentsAt(
- drag_data->source_model_index);
- drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
- registrar_.Add(
- this,
- content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
- content::Source<WebContents>(drag_data->contents->web_contents()));
+// static
+bool TabDragController::IsAttachedTo(TabStrip* tab_strip) {
+ return (instance_ && instance_->active() &&
+ instance_->attached_tabstrip() == tab_strip);
+}
- // We need to be the delegate so we receive messages about stuff, otherwise
- // our dragged WebContents may be replaced and subsequently
- // collected/destroyed while the drag is in process, leading to nasty crashes.
- drag_data->original_delegate =
- drag_data->contents->web_contents()->GetDelegate();
- drag_data->contents->web_contents()->SetDelegate(this);
+// static
+bool TabDragController::IsActive() {
+ return instance_ && instance_->active();
}
-void DefaultTabDragController::Drag() {
+void TabDragController::Drag() {
bring_to_front_timer_.Stop();
move_stacked_timer_.Stop();
+ if (waiting_for_run_loop_to_exit_)
+ return;
+
if (!started_drag_) {
if (!CanStartDrag())
return; // User hasn't dragged far enough yet.
@@ -417,28 +440,43 @@ void DefaultTabDragController::Drag() {
started_drag_ = true;
SaveFocus();
Attach(source_tabstrip_, gfx::Point());
- // Redirect all mouse events to the TabStrip so that the tab that
- // originated the drag can safely be deleted.
- static_cast<views::internal::RootView*>(
- source_tabstrip_->GetWidget()->GetRootView())->SetMouseHandler(
- source_tabstrip_);
+ if (detach_into_browser_ && static_cast<int>(drag_data_.size()) ==
+ GetModel(source_tabstrip_)->count()) {
+ RunMoveLoop(); // Runs a nested loop, returning when done.
+ return;
+ }
}
ContinueDragging();
}
-void DefaultTabDragController::EndDrag(bool canceled) {
- EndDragImpl(canceled ? CANCELED : NORMAL);
+void TabDragController::EndDrag(bool canceled) {
+ EndDragImpl(canceled && source_tabstrip_ ? CANCELED : NORMAL);
}
-bool DefaultTabDragController::GetStartedDrag() const {
- return started_drag_;
+void TabDragController::InitTabDragData(BaseTab* tab,
+ TabDragData* drag_data) {
+ drag_data->source_model_index =
+ source_tabstrip_->GetModelIndexOfBaseTab(tab);
+ drag_data->contents = GetModel(source_tabstrip_)->GetTabContentsAt(
+ drag_data->source_model_index);
+ drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
+ registrar_.Add(
+ this,
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::Source<WebContents>(drag_data->contents->web_contents()));
+
+ if (!detach_into_browser_) {
+ drag_data->original_delegate =
+ drag_data->contents->web_contents()->GetDelegate();
+ drag_data->contents->web_contents()->SetDelegate(this);
+ }
}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, PageNavigator implementation:
+// TabDragController, PageNavigator implementation:
-WebContents* DefaultTabDragController::OpenURLFromTab(
+WebContents* TabDragController::OpenURLFromTab(
WebContents* source,
const OpenURLParams& params) {
if (source_tab_drag_data()->original_delegate) {
@@ -453,10 +491,10 @@ WebContents* DefaultTabDragController::OpenURLFromTab(
}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, content::WebContentsDelegate implementation:
+// TabDragController, content::WebContentsDelegate implementation:
-void DefaultTabDragController::NavigationStateChanged(const WebContents* source,
- unsigned changed_flags) {
+void TabDragController::NavigationStateChanged(const WebContents* source,
+ unsigned changed_flags) {
if (attached_tabstrip_) {
for (size_t i = 0; i < drag_data_.size(); ++i) {
if (drag_data_[i].contents->web_contents() == source) {
@@ -473,11 +511,11 @@ void DefaultTabDragController::NavigationStateChanged(const WebContents* source,
view_->Update();
}
-void DefaultTabDragController::AddNewContents(WebContents* source,
- WebContents* new_contents,
- WindowOpenDisposition disposition,
- const gfx::Rect& initial_pos,
- bool user_gesture) {
+void TabDragController::AddNewContents(WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
DCHECK_NE(CURRENT_TAB, disposition);
// Theoretically could be called while dragging if the page tries to
@@ -488,14 +526,14 @@ void DefaultTabDragController::AddNewContents(WebContents* source,
}
}
-void DefaultTabDragController::LoadingStateChanged(WebContents* source) {
+void TabDragController::LoadingStateChanged(WebContents* source) {
// It would be nice to respond to this message by changing the
// screen shot in the dragged tab.
if (view_.get())
view_->Update();
}
-bool DefaultTabDragController::ShouldSuppressDialogs() {
+bool TabDragController::ShouldSuppressDialogs() {
// When a dialog is about to be shown we revert the drag. Otherwise a modal
// dialog might appear and attempt to parent itself to a hidden tabcontents.
EndDragImpl(CANCELED);
@@ -503,14 +541,14 @@ bool DefaultTabDragController::ShouldSuppressDialogs() {
}
content::JavaScriptDialogCreator*
-DefaultTabDragController::GetJavaScriptDialogCreator() {
+TabDragController::GetJavaScriptDialogCreator() {
return GetJavaScriptDialogCreatorInstance();
}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, content::NotificationObserver implementation:
+// TabDragController, content::NotificationObserver implementation:
-void DefaultTabDragController::Observe(
+void TabDragController::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
@@ -532,15 +570,14 @@ void DefaultTabDragController::Observe(
}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, MessageLoop::Observer implementation:
+// TabDragController, MessageLoop::Observer implementation:
-#if defined(OS_WIN) || defined(USE_AURA)
-base::EventStatus DefaultTabDragController::WillProcessEvent(
+base::EventStatus TabDragController::WillProcessEvent(
const base::NativeEvent& event) {
return base::EVENT_CONTINUE;
}
-void DefaultTabDragController::DidProcessEvent(const base::NativeEvent& event) {
+void TabDragController::DidProcessEvent(const base::NativeEvent& event) {
// 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
@@ -550,12 +587,23 @@ void DefaultTabDragController::DidProcessEvent(const base::NativeEvent& event) {
EndDrag(true);
}
}
-#endif
+
+void TabDragController::OnWidgetMoved(views::Widget* widget) {
+ Drag();
+}
+
+void TabDragController::TabStripEmpty() {
+ DCHECK(detach_into_browser_);
+ GetModel(source_tabstrip_)->RemoveObserver(this);
+ // NULL out source_tabstrip_ so that we don't attempt to add back to it (in
+ // the case of a revert).
+ source_tabstrip_ = NULL;
+}
///////////////////////////////////////////////////////////////////////////////
-// DefaultTabDragController, private:
+// TabDragController, private:
-void DefaultTabDragController::InitWindowCreatePoint() {
+void TabDragController::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
@@ -569,7 +617,7 @@ void DefaultTabDragController::InitWindowCreatePoint() {
window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
}
-gfx::Point DefaultTabDragController::GetWindowCreatePoint() const {
+gfx::Point TabDragController::GetWindowCreatePoint() const {
gfx::Point cursor_point = GetCursorScreenPoint();
if (dock_info_.type() != DockInfo::NONE && dock_info_.in_enable_area()) {
// If we're going to dock, we need to return the exact coordinate,
@@ -594,7 +642,7 @@ gfx::Point DefaultTabDragController::GetWindowCreatePoint() const {
cursor_point.y() - window_create_point_.y());
}
-void DefaultTabDragController::UpdateDockInfo(const gfx::Point& screen_point) {
+void TabDragController::UpdateDockInfo(const gfx::Point& screen_point) {
// Update the DockInfo for the current mouse coordinates.
DockInfo dock_info = GetDockInfoAtPoint(screen_point);
if (!dock_info.equals(dock_info_)) {
@@ -623,19 +671,20 @@ void DefaultTabDragController::UpdateDockInfo(const gfx::Point& screen_point) {
}
}
-void DefaultTabDragController::SaveFocus() {
+void TabDragController::SaveFocus() {
DCHECK(!old_focused_view_); // This should only be invoked once.
+ DCHECK(source_tabstrip_);
old_focused_view_ = source_tabstrip_->GetFocusManager()->GetFocusedView();
source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
}
-void DefaultTabDragController::RestoreFocus() {
+void TabDragController::RestoreFocus() {
if (old_focused_view_ && attached_tabstrip_ == source_tabstrip_)
old_focused_view_->GetFocusManager()->SetFocusedView(old_focused_view_);
old_focused_view_ = NULL;
}
-bool DefaultTabDragController::CanStartDrag() const {
+bool TabDragController::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;
@@ -646,7 +695,9 @@ bool DefaultTabDragController::CanStartDrag() const {
pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance;
}
-void DefaultTabDragController::ContinueDragging() {
+void TabDragController::ContinueDragging() {
+ DCHECK(!detach_into_browser_ || attached_tabstrip_);
+
// 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
@@ -659,41 +710,104 @@ void DefaultTabDragController::ContinueDragging() {
// guaranteed to be correct regardless of monitor config.
gfx::Point screen_point = GetCursorScreenPoint();
- // TODO(sky): this file shouldn't be built on chromeos.
-#if defined(OS_WIN) && !defined(USE_AURA)
TabStrip* target_tabstrip = move_only_ ?
source_tabstrip_ : GetTabStripForPoint(screen_point);
-#else
- TabStrip* target_tabstrip = source_tabstrip_;
-#endif
+ bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
- 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 (tab_strip_changed) {
+ if (detach_into_browser_ &&
+ DragBrowserToNewTabStrip(target_tabstrip, screen_point) ==
+ DRAG_BROWSER_RESULT_STOP) {
+ return;
+ } else if (!detach_into_browser_) {
+ if (attached_tabstrip_)
+ Detach();
+ if (target_tabstrip)
+ Attach(target_tabstrip, screen_point);
+ }
}
- if (!target_tabstrip) {
+ if (view_.get() || is_dragging_window_) {
bring_to_front_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this,
- &DefaultTabDragController::BringWindowUnderMouseToFront);
+ &TabDragController::BringWindowUnderMouseToFront);
}
UpdateDockInfo(screen_point);
- if (attached_tabstrip_) {
- if (move_only_)
- DragActiveTabStacked(screen_point);
- else
- MoveAttached(screen_point);
- } else {
- MoveDetached(screen_point);
+ if (!is_dragging_window_) {
+ if (attached_tabstrip_) {
+ if (move_only_) {
+ DragActiveTabStacked(screen_point);
+ } else {
+ MoveAttached(screen_point);
+ if (tab_strip_changed) {
+ // Move the corresponding window to the front. We do this after the
+ // move as on windows activate triggers a synchronous paint.
+ attached_tabstrip_->GetWidget()->Activate();
+ }
+ }
+ } else {
+ MoveDetached(screen_point);
+ }
+ }
+}
+
+TabDragController::DragBrowserResultType
+TabDragController::DragBrowserToNewTabStrip(
+ TabStrip* target_tabstrip,
+ const gfx::Point& screen_point) {
+ if (!target_tabstrip) {
+ DetachIntoNewBrowserAndRunMoveLoop(screen_point);
+ return DRAG_BROWSER_RESULT_STOP;
+ }
+ if (is_dragging_window_) {
+#if defined(USE_ASH)
+ // ReleaseMouseCapture() is going to result in calling back to us (because
+ // it results in a move). That'll cause all sorts of problems. Reset the
+ // observer so we don't get notified and process the event.
+ move_loop_widget_->RemoveObserver(this);
+ move_loop_widget_ = NULL;
+#endif
+ views::Widget* browser_widget = GetAttachedBrowserWidget();
+ // Need to release the drag controller before starting the move loop as it's
+ // going to trigger capture lost, which cancels drag.
+ attached_tabstrip_->ReleaseDragController();
+ target_tabstrip->OwnDragController(this);
+ // Disable animations so that we don't see a close animation on aero.
+ browser_widget->SetVisibilityChangedAnimationsEnabled(false);
+ browser_widget->ReleaseMouseCapture();
+ // EndMoveLoop is going to snap the window back to its original location.
+ // Hide it so users don't see this.
+ browser_widget->Hide();
+ browser_widget->EndMoveLoop();
+
+ // Ideally we would always swap the tabs now, but on windows it seems that
+ // running the move loop implicitly activates the window when done, leading
+ // to all sorts of flicker. So, on windows, instead we process the move
+ // after the loop completes. But on chromeos, we can do tab swapping now to
+ // avoid the tab flashing issue(crbug.com/116329).
+#if defined(USE_ASH)
+ is_dragging_window_ = false;
+ Detach();
+ gfx::Point screen_point(GetCursorScreenPoint());
+ Attach(target_tabstrip, screen_point);
+ // Move the tabs into position.
+ MoveAttached(screen_point);
+ attached_tabstrip_->GetWidget()->Activate();
+#else
+ tab_strip_to_attach_to_after_exit_ = target_tabstrip;
+#endif
+
+ waiting_for_run_loop_to_exit_ = true;
+ end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
+ return DRAG_BROWSER_RESULT_STOP;
}
+ Detach();
+ Attach(target_tabstrip, screen_point);
+ return DRAG_BROWSER_RESULT_CONTINUE;
}
-void DefaultTabDragController::DragActiveTabStacked(
+void TabDragController::DragActiveTabStacked(
const gfx::Point& screen_point) {
if (attached_tabstrip_->tab_count() !=
static_cast<int>(initial_tab_positions_.size()))
@@ -703,7 +817,7 @@ void DefaultTabDragController::DragActiveTabStacked(
attached_tabstrip_->DragActiveTab(initial_tab_positions_, delta);
}
-void DefaultTabDragController::MoveAttachedToNextStackedIndex() {
+void TabDragController::MoveAttachedToNextStackedIndex() {
int index = attached_tabstrip_->touch_layout_->active_index();
if (index + 1 >= attached_tabstrip_->tab_count())
return;
@@ -712,7 +826,7 @@ void DefaultTabDragController::MoveAttachedToNextStackedIndex() {
StartMoveStackedTimerIfNecessary(kMoveAttachedSubsequentDelay);
}
-void DefaultTabDragController::MoveAttachedToPreviousStackedIndex() {
+void TabDragController::MoveAttachedToPreviousStackedIndex() {
int index = attached_tabstrip_->touch_layout_->active_index();
if (index <= attached_tabstrip_->GetMiniTabCount())
return;
@@ -721,9 +835,10 @@ void DefaultTabDragController::MoveAttachedToPreviousStackedIndex() {
StartMoveStackedTimerIfNecessary(kMoveAttachedSubsequentDelay);
}
-void DefaultTabDragController::MoveAttached(const gfx::Point& screen_point) {
+void TabDragController::MoveAttached(const gfx::Point& screen_point) {
DCHECK(attached_tabstrip_);
DCHECK(!view_.get());
+ DCHECK(!is_dragging_window_);
int move_delta = screen_point.x() - last_screen_point_.x();
if (move_delta > 0)
@@ -794,15 +909,16 @@ void DefaultTabDragController::MoveAttached(const gfx::Point& screen_point) {
initial_move_ = false;
}
-void DefaultTabDragController::MoveDetached(const gfx::Point& screen_point) {
+void TabDragController::MoveDetached(const gfx::Point& screen_point) {
DCHECK(!attached_tabstrip_);
DCHECK(view_.get());
+ DCHECK(!is_dragging_window_);
// Move the View. There are no changes to the model if we're detached.
view_->MoveTo(screen_point);
}
-void DefaultTabDragController::StartMoveStackedTimerIfNecessary(int delay_ms) {
+void TabDragController::StartMoveStackedTimerIfNecessary(int delay_ms) {
DCHECK(attached_tabstrip_);
TouchTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
@@ -817,18 +933,30 @@ void DefaultTabDragController::StartMoveStackedTimerIfNecessary(int delay_ms) {
move_stacked_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(delay_ms), this,
- &DefaultTabDragController::MoveAttachedToNextStackedIndex);
+ &TabDragController::MoveAttachedToNextStackedIndex);
} else if (ShouldDragToPreviousStackedTab(bounds, index)) {
move_stacked_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(delay_ms), this,
- &DefaultTabDragController::MoveAttachedToPreviousStackedIndex);
+ &TabDragController::MoveAttachedToPreviousStackedIndex);
}
}
-DockInfo DefaultTabDragController::GetDockInfoAtPoint(
+TabDragController::DetachPosition TabDragController::GetDetachPosition(
const gfx::Point& screen_point) {
- if (attached_tabstrip_) {
+ DCHECK(attached_tabstrip_);
+ gfx::Point attached_point(screen_point);
+ views::View::ConvertPointToView(NULL, attached_tabstrip_, &attached_point);
+ if (attached_point.x() < 0)
+ return DETACH_BEFORE;
+ if (attached_point.x() >= attached_tabstrip_->width())
+ return DETACH_AFTER;
+ return DETACH_ABOVE_OR_BELOW;
+}
+
+DockInfo TabDragController::GetDockInfoAtPoint(const gfx::Point& screen_point) {
+ // TODO: add support for dock info when |detach_into_browser_| is true.
+ if (attached_tabstrip_ || detach_into_browser_) {
// If the mouse is over a tab strip, don't offer a dock position.
return DockInfo();
}
@@ -840,45 +968,53 @@ DockInfo DefaultTabDragController::GetDockInfoAtPoint(
return dock_info_;
}
- gfx::NativeView dragged_hwnd = view_->GetWidget()->GetNativeView();
- dock_windows_.insert(dragged_hwnd);
+ gfx::NativeView dragged_view = view_->GetWidget()->GetNativeView();
+ dock_windows_.insert(dragged_view);
DockInfo info = DockInfo::GetDockInfoAtPoint(screen_point, dock_windows_);
- dock_windows_.erase(dragged_hwnd);
+ dock_windows_.erase(dragged_view);
return info;
}
-#if defined(OS_WIN) && !defined(USE_AURA)
-TabStrip* DefaultTabDragController::GetTabStripForPoint(
+TabStrip* TabDragController::GetTabStripForPoint(
const gfx::Point& screen_point) {
gfx::NativeView dragged_view = NULL;
- if (view_.get()) {
+ if (view_.get())
dragged_view = view_->GetWidget()->GetNativeView();
+ else if (is_dragging_window_)
+ dragged_view = attached_tabstrip_->GetWidget()->GetNativeView();
+ if (dragged_view)
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)
+ TabStrip* tab_strip = GetTabStripForWindow(local_window);
+ if (tab_strip && DoesTabStripContain(tab_strip, screen_point))
+ return tab_strip;
+ return is_dragging_window_ ? attached_tabstrip_ : NULL;
+}
+
+TabStrip* TabDragController::GetTabStripForWindow(gfx::NativeWindow window) {
+ if (!window)
return NULL;
- BrowserView* browser =
- BrowserView::GetBrowserViewForNativeWindow(local_window);
+ BrowserView* browser_view =
+ BrowserView::GetBrowserViewForNativeWindow(window);
// We don't allow drops on windows that don't have tabstrips.
- if (!browser ||
- !browser->browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
+ if (!browser_view ||
+ !browser_view->browser()->SupportsWindowFeature(
+ Browser::FEATURE_TABSTRIP))
return NULL;
- // This cast seems ugly, but the controller and the view are tighly coupled at
- // creation time, so it will be okay.
- TabStrip* other_tabstrip = static_cast<TabStrip*>(browser->tabstrip());
+ TabStrip* other_tabstrip = browser_view->tabstrip();
+ TabStrip* tab_strip =
+ attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
+ DCHECK(tab_strip);
- if (!other_tabstrip->controller()->IsCompatibleWith(source_tabstrip_))
- return NULL;
- return GetTabStripIfItContains(other_tabstrip, screen_point);
+ return other_tabstrip->controller()->IsCompatibleWith(tab_strip) ?
+ other_tabstrip : NULL;
}
-#endif
-TabStrip* DefaultTabDragController::GetTabStripIfItContains(
+bool TabDragController::DoesTabStripContain(
TabStrip* tabstrip,
const gfx::Point& screen_point) const {
static const int kVerticalDetachMagnetism = 15;
@@ -891,16 +1027,14 @@ TabStrip* DefaultTabDragController::GetTabStripIfItContains(
// 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;
- }
+ return screen_point.y() >= lower_threshold &&
+ screen_point.y() <= upper_threshold;
}
- return NULL;
+ return false;
}
-void DefaultTabDragController::Attach(TabStrip* attached_tabstrip,
- const gfx::Point& screen_point) {
+void TabDragController::Attach(TabStrip* attached_tabstrip,
+ const gfx::Point& screen_point) {
DCHECK(!attached_tabstrip_); // We should already have detached by the time
// we get here.
@@ -913,20 +1047,22 @@ void DefaultTabDragController::Attach(TabStrip* attached_tabstrip,
GetTabsMatchingDraggedContents(attached_tabstrip_);
if (tabs.empty()) {
- // There is no Tab in |attached_tabstrip| that corresponds to the dragged
- // WebContents. We must now create one.
+ // Transitioning from detached to attached to a new tabstrip. Add tabs to
+ // the new model.
selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
- // Remove ourselves as the delegate now that the dragged WebContents is
- // being inserted back into a Browser.
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- drag_data_[i].contents->web_contents()->SetDelegate(NULL);
- drag_data_[i].original_delegate = NULL;
- }
+ if (!detach_into_browser_) {
+ // Remove ourselves as the delegate now that the dragged WebContents is
+ // being inserted back into a Browser.
+ for (size_t i = 0; i < drag_data_.size(); ++i) {
+ drag_data_[i].contents->web_contents()->SetDelegate(NULL);
+ drag_data_[i].original_delegate = NULL;
+ }
- // Return the WebContents to normalcy.
- source_dragged_contents()->web_contents()->SetCapturingContents(false);
+ // Return the WebContents to normalcy.
+ source_dragged_contents()->web_contents()->SetCapturingContents(false);
+ }
// Inserting counts as a move. We don't want the tabs to jitter when the
// user moves the tab immediately after attaching it.
@@ -978,25 +1114,24 @@ void DefaultTabDragController::Attach(TabStrip* attached_tabstrip,
tabs[source_tab_index_]->width());
mouse_offset_.set_x(new_x);
- // Move the corresponding window to the front.
- attached_tabstrip_->GetWidget()->Activate();
+ // Redirect all mouse events to the TabStrip so that the tab that originated
+ // the drag can safely be deleted.
+ if (detach_into_browser_ || attached_tabstrip_ == source_tabstrip_) {
+ static_cast<views::internal::RootView*>(
+ attached_tabstrip_->GetWidget()->GetRootView())->SetMouseHandler(
+ attached_tabstrip_);
+ }
}
-void DefaultTabDragController::Detach() {
+void TabDragController::Detach() {
mouse_move_direction_ = kMovedMouseLeft | kMovedMouseRight;
// Prevent the WebContents HWND from being hidden by any of the model
// operations performed during the drag.
- source_dragged_contents()->web_contents()->SetCapturingContents(true);
-
- // Calculate the drag bounds.
- std::vector<gfx::Rect> drag_bounds;
- std::vector<BaseTab*> attached_tabs;
- for (size_t i = 0; i < drag_data_.size(); ++i)
- attached_tabs.push_back(drag_data_[i].attached_tab);
- attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
- &drag_bounds);
+ if (!detach_into_browser_)
+ source_dragged_contents()->web_contents()->SetCapturingContents(true);
+ std::vector<gfx::Rect> drag_bounds = CalculateBoundsForDraggedTabs(0);
TabStripModel* attached_model = GetModel(attached_tabstrip_);
std::vector<TabRendererData> tab_data;
for (size_t i = 0; i < drag_data_.size(); ++i) {
@@ -1010,50 +1145,139 @@ void DefaultTabDragController::Detach() {
attached_model->DetachTabContentsAt(index);
// Detaching resets the delegate, but we still want to be the delegate.
- drag_data_[i].contents->web_contents()->SetDelegate(this);
+ if (!detach_into_browser_)
+ drag_data_[i].contents->web_contents()->SetDelegate(this);
// Detaching may end up deleting the tab, drop references to it.
drag_data_[i].attached_tab = NULL;
}
// If we've removed the last Tab from the TabStrip, hide the frame now.
- if (attached_model->empty()) {
- HideFrame();
- } else if (!selection_model_before_attach_.empty() &&
- selection_model_before_attach_.active() >= 0 &&
- selection_model_before_attach_.active() <
- attached_model->count()) {
- // Restore the selection.
- attached_model->SetSelectionFromModel(selection_model_before_attach_);
- } else if (attached_tabstrip_ == source_tabstrip_ &&
- !initial_selection_model_.empty()) {
- // First time detaching from the source tabstrip. Reset selection model to
- // initial_selection_model_. Before resetting though we have to remove all
- // the tabs from initial_selection_model_ as it was created with the tabs
- // still there.
- TabStripSelectionModel selection_model;
- selection_model.Copy(initial_selection_model_);
- for (DragData::const_reverse_iterator i = drag_data_.rbegin();
- i != drag_data_.rend(); ++i) {
- selection_model.DecrementFrom(i->source_model_index);
+ if (!attached_model->empty()) {
+ if (!selection_model_before_attach_.empty() &&
+ selection_model_before_attach_.active() >= 0 &&
+ selection_model_before_attach_.active() < attached_model->count()) {
+ // Restore the selection.
+ attached_model->SetSelectionFromModel(selection_model_before_attach_);
+ } else if (attached_tabstrip_ == source_tabstrip_ &&
+ !initial_selection_model_.empty()) {
+ // First time detaching from the source tabstrip. Reset selection model to
+ // initial_selection_model_. Before resetting though we have to remove all
+ // the tabs from initial_selection_model_ as it was created with the tabs
+ // still there.
+ TabStripSelectionModel selection_model;
+ selection_model.Copy(initial_selection_model_);
+ for (DragData::const_reverse_iterator i = drag_data_.rbegin();
+ i != drag_data_.rend(); ++i) {
+ selection_model.DecrementFrom(i->source_model_index);
+ }
+ // We may have cleared out the selection model. Only reset it if it
+ // contains something.
+ if (!selection_model.empty())
+ attached_model->SetSelectionFromModel(selection_model);
}
- // We may have cleared out the selection model. Only reset it if it contains
- // something.
- if (!selection_model.empty())
- attached_model->SetSelectionFromModel(selection_model);
+ } else if (!detach_into_browser_) {
+ HideFrame();
}
// Create the dragged view.
- CreateDraggedView(tab_data, drag_bounds);
+ if (!detach_into_browser_)
+ CreateDraggedView(tab_data, drag_bounds);
attached_tabstrip_->DraggedTabsDetached();
attached_tabstrip_ = NULL;
}
-int DefaultTabDragController::GetInsertionIndexFrom(
- const gfx::Rect& dragged_bounds,
- int start,
- int delta) const {
+void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
+ const gfx::Point& screen_point) {
+ if (GetModel(attached_tabstrip_)->count() ==
+ static_cast<int>(drag_data_.size())) {
+ // All the tabs in a browser are being dragged but all the tabs weren't
+ // initially being dragged. For this to happen the user would have to
+ // start dragging a set of tabs, the other tabs close, then detach.
+ RunMoveLoop();
+ return;
+ }
+
+ // Create a new browser to house the dragged tabs and have the OS run a move
+ // loop.
+
+ gfx::Point attached_point = GetAttachedDragPoint(screen_point);
+
+ // Calculate the bounds for the tabs from the attached_tab_strip. We do this
+ // so that the tabs don't change size when detached.
+ std::vector<gfx::Rect> drag_bounds =
+ CalculateBoundsForDraggedTabs(attached_point.x());
+
+ Browser* browser = CreateBrowserForDrag(
+ attached_tabstrip_, screen_point, &drag_bounds);
+ attached_tabstrip_->ReleaseDragController();
+ Detach();
+ BrowserView* dragged_browser_view =
+ BrowserView::GetBrowserViewForBrowser(browser);
+ dragged_browser_view->GetWidget()->SetVisibilityChangedAnimationsEnabled(
+ false);
+ Attach(dragged_browser_view->tabstrip(), gfx::Point());
+ attached_tabstrip_->OwnDragController(this);
+ // TODO: come up with a cleaner way to do this.
+ attached_tabstrip_->SetTabBoundsForDrag(drag_bounds);
+
+ browser->window()->Show();
+ browser->window()->Activate();
+ dragged_browser_view->GetWidget()->SetVisibilityChangedAnimationsEnabled(
+ true);
+ RunMoveLoop();
+}
+
+void TabDragController::RunMoveLoop() {
+ move_loop_widget_ = GetAttachedBrowserWidget();
+ move_loop_widget_->AddObserver(this);
+ is_dragging_window_ = true;
+ bool destroyed = false;
+ destroyed_ = &destroyed;
+ // Running the move loop releases mouse capture on windows, which triggers
+ // destroying the drag loop. Release mouse capture ourself before this while
+ // the DragController isn't owned by the TabStrip.
+ attached_tabstrip_->ReleaseDragController();
+ attached_tabstrip_->GetWidget()->ReleaseMouseCapture();
+ attached_tabstrip_->OwnDragController(this);
+ views::Widget::MoveLoopResult result = move_loop_widget_->RunMoveLoop();
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
+ content::NotificationService::AllBrowserContextsAndSources(),
+ content::NotificationService::NoDetails());
+
+ if (destroyed)
+ return;
+ destroyed_ = NULL;
+ // Under chromeos we immediately set the |move_loop_widget_| to NULL.
+ if (move_loop_widget_) {
+ move_loop_widget_->RemoveObserver(this);
+ move_loop_widget_ = NULL;
+ }
+ is_dragging_window_ = false;
+ waiting_for_run_loop_to_exit_ = false;
+ if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
+ end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
+ if (tab_strip_to_attach_to_after_exit_) {
+ Detach();
+ gfx::Point screen_point(GetCursorScreenPoint());
+ Attach(tab_strip_to_attach_to_after_exit_, screen_point);
+ // Move the tabs into position.
+ MoveAttached(screen_point);
+ attached_tabstrip_->GetWidget()->Activate();
+ tab_strip_to_attach_to_after_exit_ = NULL;
+ }
+ DCHECK(attached_tabstrip_);
+ attached_tabstrip_->GetWidget()->SetMouseCapture(attached_tabstrip_);
+ } else if (active_) {
+ EndDrag(result == views::Widget::MOVE_LOOP_CANCELED);
+ }
+}
+
+int TabDragController::GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
+ int start,
+ int delta) const {
for (int i = start, tab_count = attached_tabstrip_->tab_count();
i >= 0 && i < tab_count; i += delta) {
const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
@@ -1070,7 +1294,7 @@ int DefaultTabDragController::GetInsertionIndexFrom(
return -1;
}
-int DefaultTabDragController::GetInsertionIndexForDraggedBounds(
+int TabDragController::GetInsertionIndexForDraggedBounds(
const gfx::Rect& dragged_bounds) const {
int index = -1;
if (attached_tabstrip_->touch_layout_.get()) {
@@ -1112,7 +1336,7 @@ int DefaultTabDragController::GetInsertionIndexForDraggedBounds(
return std::max(0, std::min(max_index, index));
}
-bool DefaultTabDragController::ShouldDragToNextStackedTab(
+bool TabDragController::ShouldDragToNextStackedTab(
const gfx::Rect& dragged_bounds,
int index) const {
if (index + 1 >= attached_tabstrip_->tab_count() ||
@@ -1127,7 +1351,7 @@ bool DefaultTabDragController::ShouldDragToNextStackedTab(
return dragged_bounds.x() >= mid_x;
}
-bool DefaultTabDragController::ShouldDragToPreviousStackedTab(
+bool TabDragController::ShouldDragToPreviousStackedTab(
const gfx::Rect& dragged_bounds,
int index) const {
if (index - 1 < attached_tabstrip_->GetMiniTabCount() ||
@@ -1142,7 +1366,7 @@ bool DefaultTabDragController::ShouldDragToPreviousStackedTab(
return dragged_bounds.x() <= mid_x;
}
-int DefaultTabDragController::GetInsertionIndexForDraggedBoundsStacked(
+int TabDragController::GetInsertionIndexForDraggedBoundsStacked(
const gfx::Rect& dragged_bounds) const {
TouchTabStripLayout* touch_layout = attached_tabstrip_->touch_layout_.get();
int active_index = touch_layout->active_index();
@@ -1171,7 +1395,7 @@ int DefaultTabDragController::GetInsertionIndexForDraggedBoundsStacked(
return index;
}
-gfx::Rect DefaultTabDragController::GetDraggedViewTabStripBounds(
+gfx::Rect TabDragController::GetDraggedViewTabStripBounds(
const gfx::Point& tab_strip_point) {
// attached_tab is NULL when inserting into a new tabstrip.
if (source_tab_drag_data()->attached_tab) {
@@ -1181,14 +1405,13 @@ gfx::Rect DefaultTabDragController::GetDraggedViewTabStripBounds(
}
double sel_width, unselected_width;
- static_cast<TabStrip*>(attached_tabstrip_)->GetCurrentTabWidths(
- &sel_width, &unselected_width);
+ attached_tabstrip_->GetCurrentTabWidths(&sel_width, &unselected_width);
return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
static_cast<int>(sel_width),
Tab::GetStandardSize().height());
}
-gfx::Point DefaultTabDragController::GetAttachedDragPoint(
+gfx::Point TabDragController::GetAttachedDragPoint(
const gfx::Point& screen_point) {
DCHECK(attached_tabstrip_); // The tab must be attached.
@@ -1206,7 +1429,7 @@ gfx::Point DefaultTabDragController::GetAttachedDragPoint(
return gfx::Point(std::min(std::max(x, 0), max_x), 0);
}
-std::vector<BaseTab*> DefaultTabDragController::GetTabsMatchingDraggedContents(
+std::vector<BaseTab*> TabDragController::GetTabsMatchingDraggedContents(
TabStrip* tabstrip) {
TabStripModel* model = GetModel(attached_tabstrip_);
std::vector<BaseTab*> tabs;
@@ -1219,12 +1442,37 @@ std::vector<BaseTab*> DefaultTabDragController::GetTabsMatchingDraggedContents(
return tabs;
}
-void DefaultTabDragController::EndDragImpl(EndDragType type) {
+std::vector<gfx::Rect> TabDragController::CalculateBoundsForDraggedTabs(
+ int x_offset) {
+ std::vector<gfx::Rect> drag_bounds;
+ std::vector<BaseTab*> attached_tabs;
+ for (size_t i = 0; i < drag_data_.size(); ++i)
+ attached_tabs.push_back(drag_data_[i].attached_tab);
+ attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
+ &drag_bounds);
+ if (x_offset != 0) {
+ for (size_t i = 0; i < drag_bounds.size(); ++i)
+ drag_bounds[i].set_x(drag_bounds[i].x() + x_offset);
+ }
+ return drag_bounds;
+}
+
+void TabDragController::EndDragImpl(EndDragType type) {
+ DCHECK(active_);
active_ = false;
bring_to_front_timer_.Stop();
move_stacked_timer_.Stop();
+ if (is_dragging_window_) {
+ if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1))
+ SetTrackedByWorkspace(GetAttachedBrowserWidget()->GetNativeView(), true);
+
+ // End the nested drag loop.
+ GetAttachedBrowserWidget()->EndMoveLoop();
+ waiting_for_run_loop_to_exit_ = true;
+ }
+
// 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
@@ -1250,15 +1498,18 @@ void DefaultTabDragController::EndDragImpl(EndDragType type) {
RevertDrag();
} // else case the only tab we were dragging was deleted. Nothing to do.
- ResetDelegates();
+ if (!detach_into_browser_)
+ ResetDelegates();
// Clear out drag data so we don't attempt to do anything with it.
drag_data_.clear();
- source_tabstrip_->DestroyDragController();
+ TabStrip* owning_tabstrip = (attached_tabstrip_ && detach_into_browser_) ?
+ attached_tabstrip_ : source_tabstrip_;
+ owning_tabstrip->DestroyDragController();
}
-void DefaultTabDragController::RevertDrag() {
+void TabDragController::RevertDrag() {
std::vector<BaseTab*> tabs;
for (size_t i = 0; i < drag_data_.size(); ++i) {
if (drag_data_[i].contents) {
@@ -1268,36 +1519,31 @@ void DefaultTabDragController::RevertDrag() {
}
}
- bool restore_frame = attached_tabstrip_ != source_tabstrip_;
- if (attached_tabstrip_ && attached_tabstrip_ == source_tabstrip_)
- source_tabstrip_->StoppedDraggingTabs(tabs);
-
- attached_tabstrip_ = source_tabstrip_;
-
- if (initial_selection_model_.empty()) {
- ResetSelection(GetModel(attached_tabstrip_));
- } else {
- GetModel(attached_tabstrip_)->SetSelectionFromModel(
- initial_selection_model_);
+ bool restore_frame = !detach_into_browser_ &&
+ attached_tabstrip_ != source_tabstrip_;
+ if (attached_tabstrip_) {
+ if (attached_tabstrip_ == source_tabstrip_)
+ source_tabstrip_->StoppedDraggingTabs(tabs);
+ else
+ attached_tabstrip_->DraggedTabsDetached();
}
+ if (initial_selection_model_.empty())
+ ResetSelection(GetModel(source_tabstrip_));
+ else
+ GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
+
// 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) && !defined(USE_AURA)
- 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
- }
- }
+ if (restore_frame && !restore_bounds_.IsEmpty())
+ source_tabstrip_->GetWidget()->SetBounds(restore_bounds_);
+
+ if (detach_into_browser_ && source_tabstrip_)
+ source_tabstrip_->GetWidget()->Activate();
}
-void DefaultTabDragController::ResetSelection(TabStripModel* model) {
+void TabDragController::ResetSelection(TabStripModel* model) {
DCHECK(model);
TabStripSelectionModel selection_model;
bool has_one_valid_tab = false;
@@ -1322,7 +1568,7 @@ void DefaultTabDragController::ResetSelection(TabStripModel* model) {
model->SetSelectionFromModel(selection_model);
}
-void DefaultTabDragController::RevertDragAt(size_t drag_index) {
+void TabDragController::RevertDragAt(size_t drag_index) {
DCHECK(started_drag_);
TabDragData* data = &(drag_data_[drag_index]);
@@ -1354,7 +1600,7 @@ void DefaultTabDragController::RevertDragAt(size_t drag_index) {
}
}
-void DefaultTabDragController::CompleteDrag() {
+void TabDragController::CompleteDrag() {
DCHECK(started_drag_);
if (attached_tabstrip_) {
@@ -1439,7 +1685,8 @@ void DefaultTabDragController::CompleteDrag() {
CleanUpHiddenFrame();
}
-void DefaultTabDragController::ResetDelegates() {
+void TabDragController::ResetDelegates() {
+ DCHECK(!detach_into_browser_);
for (size_t i = 0; i < drag_data_.size(); ++i) {
if (drag_data_[i].contents &&
drag_data_[i].contents->web_contents()->GetDelegate() == this) {
@@ -1449,7 +1696,7 @@ void DefaultTabDragController::ResetDelegates() {
}
}
-void DefaultTabDragController::CreateDraggedView(
+void TabDragController::CreateDraggedView(
const std::vector<TabRendererData>& data,
const std::vector<gfx::Rect>& renderer_bounds) {
#if !defined(USE_AURA)
@@ -1476,12 +1723,12 @@ void DefaultTabDragController::CreateDraggedView(
view_.reset(new DraggedTabView(renderers, renderer_bounds, mouse_offset_,
content_bounds.size(), photobooth));
#else
- // TODO(beng):
- NOTIMPLEMENTED();
+ // Aura always hits the |detach_into_browser_| path.
+ NOTREACHED();
#endif
}
-gfx::Point DefaultTabDragController::GetCursorScreenPoint() const {
+gfx::Point TabDragController::GetCursorScreenPoint() const {
// TODO(sky): see if we can convert to using Screen every where.
#if defined(OS_WIN) && !defined(USE_AURA)
DWORD pos = GetMessagePos();
@@ -1491,7 +1738,7 @@ gfx::Point DefaultTabDragController::GetCursorScreenPoint() const {
#endif
}
-gfx::Rect DefaultTabDragController::GetViewScreenBounds(
+gfx::Rect TabDragController::GetViewScreenBounds(
views::View* view) const {
gfx::Point view_topleft;
views::View::ConvertPointToScreen(view, &view_topleft);
@@ -1500,7 +1747,7 @@ gfx::Rect DefaultTabDragController::GetViewScreenBounds(
return view_screen_bounds;
}
-void DefaultTabDragController::HideFrame() {
+void TabDragController::HideFrame() {
#if defined(OS_WIN) && !defined(USE_AURA)
// 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.
@@ -1514,18 +1761,19 @@ void DefaultTabDragController::HideFrame() {
// the drag session is aborted we can restore them.
restore_bounds_ = gfx::Rect(wr);
#else
- NOTIMPLEMENTED();
+ // Shouldn't hit as aura triggers the |detach_into_browser_| path.
+ NOTREACHED();
#endif
}
-void DefaultTabDragController::CleanUpHiddenFrame() {
+void TabDragController::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())
+ if (!detach_into_browser_ && GetModel(source_tabstrip_)->empty())
GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession();
}
-void DefaultTabDragController::DockDisplayerDestroyed(
+void TabDragController::DockDisplayerDestroyed(
DockDisplayer* controller) {
DockWindows::iterator dock_i =
dock_windows_.find(controller->popup_view());
@@ -1543,39 +1791,50 @@ void DefaultTabDragController::DockDisplayerDestroyed(
NOTREACHED();
}
-void DefaultTabDragController::BringWindowUnderMouseToFront() {
+void TabDragController::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);
+ views::View* dragged_view;
+ if (view_.get())
+ dragged_view = view_.get();
+ else
+ dragged_view = attached_tabstrip_;
+ gfx::NativeView dragged_native_view =
+ dragged_view->GetWidget()->GetNativeView();
+ dock_windows_.insert(dragged_native_view);
window = DockInfo::GetLocalProcessWindowAtPoint(GetCursorScreenPoint(),
dock_windows_);
- dock_windows_.erase(dragged_view);
+ dock_windows_.erase(dragged_native_view);
}
if (window) {
-#if defined(OS_WIN) && !defined(USE_AURA)
- // Move the window to the front.
- SetWindowPos(window, HWND_TOP, 0, 0, 0, 0,
- SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ views::Widget* widget_window = views::Widget::GetWidgetForNativeView(
+ window);
+ if (widget_window)
+ widget_window->StackAtTop();
+ else
+ return;
// 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
+ if (view_.get())
+ view_->GetWidget()->StackAtTop();
+ else if (is_dragging_window_)
+ attached_tabstrip_->GetWidget()->StackAtTop();
}
}
-TabStripModel* DefaultTabDragController::GetModel(
+TabStripModel* TabDragController::GetModel(
TabStrip* tabstrip) const {
return static_cast<BrowserTabStripController*>(tabstrip->controller())->
model();
}
-bool DefaultTabDragController::AreTabsConsecutive() {
+views::Widget* TabDragController::GetAttachedBrowserWidget() {
+ return attached_tabstrip_->GetWidget();
+}
+
+bool TabDragController::AreTabsConsecutive() {
for (size_t i = 1; i < drag_data_.size(); ++i) {
if (drag_data_[i - 1].source_model_index + 1 !=
drag_data_[i].source_model_index) {
@@ -1585,58 +1844,37 @@ bool DefaultTabDragController::AreTabsConsecutive() {
return true;
}
-#if defined(USE_AURA) || defined(OS_WIN)
-static bool ShouldCreateTabDragController2() {
-#if defined(USE_AURA)
- return true;
-#else
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kTabBrowserDragging);
-#endif
-}
-#endif
+Browser* TabDragController::CreateBrowserForDrag(
+ TabStrip* source,
+ const gfx::Point& screen_point,
+ std::vector<gfx::Rect>* drag_bounds) {
+ Browser* browser = Browser::Create(drag_data_[0].contents->profile());
+ gfx::Point center(0, source->height() / 2);
+ views::View::ConvertPointToWidget(source, &center);
+ gfx::Rect new_bounds(source->GetWidget()->GetWindowScreenBounds());
+ new_bounds.set_y(screen_point.y() - center.y());
+ switch (GetDetachPosition(screen_point)) {
+ case DETACH_BEFORE:
+ new_bounds.set_x(screen_point.x() - center.x());
+ new_bounds.Offset(-mouse_offset_.x(), 0);
+ break;
+
+ case DETACH_AFTER: {
+ gfx::Point right_edge(source->width(), 0);
+ views::View::ConvertPointToWidget(source, &right_edge);
+ new_bounds.set_x(screen_point.x() - right_edge.x());
+ new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
+ int delta = (*drag_bounds)[0].x();
+ for (size_t i = 0; i < drag_bounds->size(); ++i)
+ (*drag_bounds)[i].Offset(-delta, 0);
+ break;
+ }
-// static
-TabDragController* TabDragController::Create(
- TabStrip* source_tabstrip,
- BaseTab* source_tab,
- const std::vector<BaseTab*>& tabs,
- const gfx::Point& mouse_offset,
- int source_tab_offset,
- const TabStripSelectionModel& initial_selection_model,
- bool move_only) {
-#if defined(USE_AURA) || defined(OS_WIN)
- if (ShouldCreateTabDragController2()) {
- TabDragController2* controller = new TabDragController2;
- // TODO: get TabDragController2 working with move_only.
- controller->Init(source_tabstrip, source_tab, tabs, mouse_offset,
- source_tab_offset, initial_selection_model);
- return controller;
+ default:
+ break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
}
-#endif
- DefaultTabDragController* controller = new DefaultTabDragController;
- controller->Init(source_tabstrip, source_tab, tabs, mouse_offset,
- source_tab_offset, initial_selection_model, move_only);
- return controller;
-}
-
-// static
-bool TabDragController::IsAttachedTo(TabStrip* tab_strip) {
-#if defined(USE_AURA) || defined(OS_WIN)
- return TabDragController2::IsActiveAndAttachedTo(tab_strip) ||
- (instance_ && instance_->active() &&
- instance_->attached_tabstrip() == tab_strip);
-#else
- return (instance_ && instance_->active() &&
- instance_->attached_tabstrip() == tab_strip);
-#endif
-}
-// static
-bool TabDragController::IsActive() {
-#if defined(USE_AURA) || defined(OS_WIN)
- return TabDragController2::IsActive() || (instance_ && instance_->active());
-#else
- return instance_ && instance_->active();
-#endif
+ SetTrackedByWorkspace(browser->window()->GetNativeHandle(), false);
+ browser->window()->SetBounds(new_bounds);
+ return browser;
}
diff --git a/chrome/browser/ui/views/tabs/default_tab_drag_controller.h b/chrome/browser/ui/views/tabs/default_tab_drag_controller.h
index f296fed..81bc8f4 100644
--- a/chrome/browser/ui/views/tabs/default_tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/default_tab_drag_controller.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_UI_VIEWS_TABS_DEFAULT_TAB_DRAG_CONTROLLER_H_
-#define CHROME_BROWSER_UI_VIEWS_TABS_DEFAULT_TAB_DRAG_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
#pragma once
#include <vector>
@@ -11,36 +11,53 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/timer.h"
+#include "chrome/browser/tabs/tab_strip_model_observer.h"
#include "chrome/browser/tabs/tab_strip_selection_model.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/tabs/dock_info.h"
-#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents_delegate.h"
#include "ui/gfx/rect.h"
+#include "ui/views/widget/widget.h"
namespace views {
class View;
}
class BaseTab;
+class Browser;
class DraggedTabView;
+struct TabRendererData;
class TabStrip;
class TabStripModel;
-
-struct TabRendererData;
-
-// TabDragController implementation that creates a widget representing the
-// dragged tabs when detached (dragged out of the source window).
-class DefaultTabDragController : public TabDragController,
- public content::WebContentsDelegate,
- public content::NotificationObserver,
- public MessageLoopForUI::Observer {
+class TabStripSelectionModel;
+
+// TabDragController is responsible for managing the tab dragging session. When
+// the user presses the mouse on a tab a new TabDragController is created and
+// Drag() is invoked as the mouse is dragged. If the mouse is dragged far enough
+// TabDragController starts a drag session. The drag session is completed when
+// EndDrag() is invoked (or the TabDragController is destroyed).
+//
+// While dragging within a tab strip TabDragController sets the bounds of the
+// tabs (this is referred to as attached). When the user drags far enough such
+// that the tabs should be moved out of the tab strip two possible things
+// can happen (this state is referred to as detached):
+// . If |detach_into_browser_| is true then a new Browser is created and
+// RunMoveLoop() is invoked on the Widget to drag the browser around. This is
+// the default on chromeos and can be enabled on windows with a flag.
+// . If |detach_into_browser_| is false a small representation of the active tab
+// is created and that is dragged around. This mode does not run a nested
+// message loop.
+class TabDragController : public content::WebContentsDelegate,
+ public content::NotificationObserver,
+ public MessageLoopForUI::Observer,
+ public views::Widget::Observer,
+ public TabStripModelObserver {
public:
- DefaultTabDragController();
- virtual ~DefaultTabDragController();
+ TabDragController();
+ virtual ~TabDragController();
- // Initializes DefaultTabDragController to drag the tabs in |tabs| originating
+ // Initializes TabDragController to drag the tabs in |tabs| originating
// from |source_tabstrip|. |source_tab| is the tab that initiated the drag and
// is contained in |tabs|. |mouse_offset| is the distance of the mouse
// pointer from the origin of the first tab in |tabs| and |source_tab_offset|
@@ -57,10 +74,31 @@ class DefaultTabDragController : public TabDragController,
const TabStripSelectionModel& initial_selection_model,
bool move_only);
+ // Returns true if there is a drag underway and the drag is attached to
+ // |tab_strip|.
+ // NOTE: this returns false if the TabDragController is in the process of
+ // finishing the drag.
+ static bool IsAttachedTo(TabStrip* tab_strip);
+
+ // Returns true if there is a drag underway.
+ static bool IsActive();
+
// See description above fields for details on these.
bool active() const { return active_; }
const TabStrip* attached_tabstrip() const { return attached_tabstrip_; }
+ // Returns true if a drag started.
+ bool started_drag() const { return started_drag_; }
+
+ // Invoked as the mouse is dragged. If the mouse moves a sufficient distance
+ // before the mouse is released, a drag session is initiated.
+ void Drag();
+
+ // Complete the current drag session. If the drag session was canceled
+ // because the user pressed escape or something interrupted it, |canceled|
+ // is true and the drag is reverted.
+ void EndDrag(bool canceled);
+
private:
class DockDisplayer;
friend class DockDisplayer;
@@ -83,6 +121,33 @@ class DefaultTabDragController : public TabDragController,
TAB_DESTROYED
};
+ // Specifies what should happen when RunMoveLoop completes.
+ enum EndRunLoopBehavior {
+ // Indicates the drag should end.
+ END_RUN_LOOP_STOP_DRAGGING,
+
+ // Indicates the drag should continue.
+ END_RUN_LOOP_CONTINUE_DRAGGING
+ };
+
+ // Enumeration of the possible positions the detached tab may detach from.
+ enum DetachPosition {
+ DETACH_BEFORE,
+ DETACH_AFTER,
+ DETACH_ABOVE_OR_BELOW
+ };
+
+ // Indicates what should happen after invoking DragBrowserToNewTabStrip().
+ enum DragBrowserResultType {
+ // The caller should return immediately. This return value is used if a
+ // nested message loop was created or we're in a nested message loop and
+ // need to exit it.
+ DRAG_BROWSER_RESULT_STOP,
+
+ // The caller should continue.
+ DRAG_BROWSER_RESULT_CONTINUE,
+ };
+
// Stores the date associated with a single tab that is being dragged.
struct TabDragData {
TabDragData();
@@ -114,11 +179,6 @@ class DefaultTabDragController : public TabDragController,
// notifications and resets the delegate of the TabContentsWrapper.
void InitTabDragData(BaseTab* tab, TabDragData* drag_data);
- // TabDragController overrides:
- virtual void Drag() OVERRIDE;
- virtual void EndDrag(bool canceled) OVERRIDE;
- virtual bool GetStartedDrag() const OVERRIDE;
-
// Overridden from content::WebContentsDelegate:
virtual content::WebContents* OpenURLFromTab(
content::WebContents* source,
@@ -141,11 +201,15 @@ class DefaultTabDragController : public TabDragController,
const content::NotificationDetails& details) OVERRIDE;
// Overridden from MessageLoop::Observer:
-#if defined(OS_WIN) || defined(USE_AURA)
virtual base::EventStatus WillProcessEvent(
const base::NativeEvent& event) OVERRIDE;
virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
-#endif
+
+ // Overriden from views::Widget::Observer:
+ virtual void OnWidgetMoved(views::Widget* widget) OVERRIDE;
+
+ // Overriden from TabStripModelObserver:
+ virtual void TabStripEmpty() OVERRIDE;
// Initialize the offset used to calculate the position to create windows
// in |GetWindowCreatePoint|. This should only be invoked from |Init|.
@@ -173,6 +237,13 @@ class DefaultTabDragController : public TabDragController,
// potentially updating the source and other TabStrips.
void ContinueDragging();
+ // Transitions dragging from |attached_tabstrip_| to |target_tabstrip|.
+ // |target_tabstrip| is NULL if the mouse is not over a valid tab strip. See
+ // DragBrowserResultType for details of the return type.
+ DragBrowserResultType DragBrowserToNewTabStrip(
+ TabStrip* target_tabstrip,
+ const gfx::Point& screen_point);
+
// Handles dragging for a touch tabstrip when the tabs are stacked. Doesn't
// actually reorder the tabs in anyway, just changes what's visible.
void DragActiveTabStacked(const gfx::Point& screen_point);
@@ -192,18 +263,24 @@ class DefaultTabDragController : public TabDragController,
// close enough to an edge with stacked tabs.
void StartMoveStackedTimerIfNecessary(int delay_ms);
-#if defined(OS_WIN) && !defined(USE_AURA)
+ // Returns the TabStrip for the specified window, or NULL if one doesn't exist
+ // or isn't compatible.
+ TabStrip* GetTabStripForWindow(gfx::NativeWindow window);
+
// Returns the compatible TabStrip that is under the specified point (screen
// coordinates), or NULL if there is none.
TabStrip* GetTabStripForPoint(const gfx::Point& screen_point);
-#endif
- DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
+ // Returns true if |tabstrip| contains the specified point in screen
+ // coordinates.
+ bool DoesTabStripContain(TabStrip* tabstrip,
+ const gfx::Point& screen_point) const;
+
+ // Returns the DetachPosition given the specified location in screen
+ // coordinates.
+ DetachPosition GetDetachPosition(const gfx::Point& screen_point);
- // Returns the specified |tabstrip| if it contains the specified point
- // (screen coordinates), NULL if it does not.
- TabStrip* GetTabStripIfItContains(TabStrip* tabstrip,
- const gfx::Point& screen_point) const;
+ DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
// Attach the dragged Tab to the specified TabStrip.
void Attach(TabStrip* attached_tabstrip, const gfx::Point& screen_point);
@@ -211,6 +288,13 @@ class DefaultTabDragController : public TabDragController,
// Detach the dragged Tab from the current TabStrip.
void Detach();
+ // Detaches the tabs being dragged, creates a new Browser to contain them and
+ // runs a nested move loop.
+ void DetachIntoNewBrowserAndRunMoveLoop(const gfx::Point& screen_point);
+
+ // Runs a nested message loop that handles moving the current Browser.
+ void RunMoveLoop();
+
// Determines the index to insert tabs at. |dragged_bounds| is the bounds of
// the tabs being dragged, |start| the index of the tab to start looking from
// and |delta| the amount to increment (1 or -1).
@@ -252,6 +336,10 @@ class DefaultTabDragController : public TabDragController,
// WebContents of the dragged tabs. Returns an empty vector if not attached.
std::vector<BaseTab*> GetTabsMatchingDraggedContents(TabStrip* tabstrip);
+ // Returns the bounds for the tabs based on the attached tab strip. The
+ // x-coordinate of each tab is offset by |x_offset|.
+ std::vector<gfx::Rect> CalculateBoundsForDraggedTabs(int x_offset);
+
// Does the work for EndDrag. If we actually started a drag and |how_end| is
// not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked.
void EndDragImpl(EndDragType how_end);
@@ -305,13 +393,24 @@ class DefaultTabDragController : public TabDragController,
return source_tab_drag_data()->contents;
}
+ // Returns the Widget of the currently attached TabStrip's BrowserView.
+ views::Widget* GetAttachedBrowserWidget();
+
// Returns true if the tabs were originality one after the other in
// |source_tabstrip_|.
bool AreTabsConsecutive();
+ // Creates and returns a new Browser to handle the drag.
+ Browser* CreateBrowserForDrag(TabStrip* source,
+ const gfx::Point& screen_point,
+ std::vector<gfx::Rect>* drag_bounds);
+
// Returns the TabStripModel for the specified tabstrip.
TabStripModel* GetModel(TabStrip* tabstrip) const;
+ // If true Detaching creates a new browser and enters a nested message loop.
+ const bool detach_into_browser_;
+
// Handles registering for notifications.
content::NotificationRegistrar registrar_;
@@ -378,11 +477,11 @@ class DefaultTabDragController : public TabDragController,
// Timer used to bring the window under the cursor to front. If the user
// stops moving the mouse for a brief time over a browser window, it is
// brought to front.
- base::OneShotTimer<DefaultTabDragController> bring_to_front_timer_;
+ base::OneShotTimer<TabDragController> bring_to_front_timer_;
// Timer used to move the stacked tabs. See comment aboue
// StartMoveStackedTimerIfNecessary().
- base::OneShotTimer<DefaultTabDragController> move_stacked_timer_;
+ base::OneShotTimer<TabDragController> move_stacked_timer_;
// Did the mouse move enough that we started a drag?
bool started_drag_;
@@ -420,7 +519,28 @@ class DefaultTabDragController : public TabDragController,
// Coordinate last used in MoveAttached().
gfx::Point last_screen_point_;
- DISALLOW_COPY_AND_ASSIGN(DefaultTabDragController);
+ // The following are needed when detaching into a browser
+ // (|detach_into_browser_| is true).
+
+ // Set to true if we've detached from a tabstrip and are running a nested
+ // move message loop.
+ bool is_dragging_window_;
+
+ EndRunLoopBehavior end_run_loop_behavior_;
+
+ // If true, we're waiting for a move loop to complete.
+ bool waiting_for_run_loop_to_exit_;
+
+ // The TabStrip to attach to after the move loop completes.
+ TabStrip* tab_strip_to_attach_to_after_exit_;
+
+ // Non-null for the duration of RunMoveLoop.
+ views::Widget* move_loop_widget_;
+
+ // If non-null set to true from destructor.
+ bool* destroyed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabDragController);
};
-#endif // CHROME_BROWSER_UI_VIEWS_TABS_DEFAULT_TAB_DRAG_CONTROLLER_H_
+#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
deleted file mode 100644
index 7d0df87..0000000
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
-#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
-#pragma once
-
-#include <vector>
-
-class BaseTab;
-class TabStrip;
-class TabStripSelectionModel;
-
-namespace gfx {
-class Point;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// TabDragController
-//
-// An object that handles a drag session for a set of Tabs within a
-// TabStrip. This object is created whenever the mouse is pressed down on a
-// Tab and destroyed when the mouse is released or the drag operation is
-// aborted. The TabStrip that the drag originated from owns this object.
-//
-///////////////////////////////////////////////////////////////////////////////
-class TabDragController {
- public:
- virtual ~TabDragController() {}
-
- // Creates and returns a new TabDragController to drag the tabs in |tabs|
- // originating from |source_tabstrip|. |source_tab| is the tab that initiated
- // the drag (the one the user pressed the moused down on) and is contained in
- // |tabs|. |mouse_offset| is the distance of the mouse pointer from the
- // origin of the first tab in |tabs| and |source_tab_offset| the offset from
- // |source_tab| (along the horizontal axis). |initial_selection_model| is the
- // selection model before the drag started and is only non-empty if
- // |source_tab| was not initially selected. |move_only| is true if the drag
- // is the result of a touch event.
- static TabDragController* Create(
- TabStrip* source_tabstrip,
- BaseTab* source_tab,
- const std::vector<BaseTab*>& tabs,
- const gfx::Point& mouse_offset,
- int source_tab_offset,
- const TabStripSelectionModel& initial_selection_model,
- bool move_only);
-
- // Returns true if there is a drag underway and the drag is attached to
- // |tab_strip|.
- // NOTE: this returns false if the TabDragController is in the process of
- // finishing the drag.
- static bool IsAttachedTo(TabStrip* tab_strip);
-
- // Returns true if there is a drag underway.
- static bool IsActive();
-
- // Responds to drag events subsequent to StartDrag. If the mouse moves a
- // sufficient distance before the mouse is released, a drag session is
- // initiated.
- virtual void Drag() = 0;
-
- // Complete the current drag session. If the drag session was canceled
- // because the user pressed escape or something interrupted it, |canceled|
- // is true so the helper can revert the state to the world before the drag
- // begun.
- virtual void EndDrag(bool canceled) = 0;
-
- // Returns true if a drag started.
- virtual bool GetStartedDrag() const = 0;
-};
-
-#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller2.cc b/chrome/browser/ui/views/tabs/tab_drag_controller2.cc
deleted file mode 100644
index 5ace1e8..0000000
--- a/chrome/browser/ui/views/tabs/tab_drag_controller2.cc
+++ /dev/null
@@ -1,1425 +0,0 @@
-// Copyright (c) 2012 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/ui/views/tabs/tab_drag_controller2.h"
-
-#include <math.h>
-#include <set>
-
-#include "base/callback.h"
-#include "base/i18n/rtl.h"
-#include "chrome/browser/extensions/extension_function_dispatcher.h"
-#include "chrome/browser/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/app_modal_dialogs/message_box_handler.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/tabs/base_tab.h"
-#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
-#include "chrome/browser/ui/views/tabs/dragged_tab_view.h"
-#include "chrome/browser/ui/views/tabs/tab.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/common/chrome_notification_types.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/user_metrics.h"
-#include "content/public/browser/web_contents.h"
-#include "grit/theme_resources.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/animation/animation.h"
-#include "ui/base/animation/animation_delegate.h"
-#include "ui/base/animation/slide_animation.h"
-#include "ui/base/events.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/screen.h"
-#include "ui/views/events/event.h"
-#include "ui/views/widget/root_view.h"
-
-#if defined(USE_ASH)
-#include "ash/wm/property_util.h"
-#endif
-
-using content::UserMetricsAction;
-using content::WebContents;
-
-static const int kHorizontalMoveThreshold = 16; // Pixels.
-
-// If non-null there is a drag underway.
-static TabDragController2* 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 OnPaintBackground(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->sk_canvas()->drawRoundRect(
- outer_rect, SkIntToScalar(kRoundedRectRadius),
- SkIntToScalar(kRoundedRectRadius), paint);
-
- ui::ResourceBundle& rb = ui::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->Translate(gfx::Point(width(), 0));
- canvas->Scale(-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);
-};
-
-} // 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 TabDragController2::DockDisplayer : public ui::AnimationDelegate {
- public:
- DockDisplayer(TabDragController2* 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()) {
- popup_ = new views::Widget;
- views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
- params.transparent = true;
- params.keep_on_top = true;
- params.bounds = info.GetPopupRect();
- popup_->Init(params);
- popup_->SetContentsView(new DockView(info.type()));
- popup_->SetOpacity(0x00);
- if (info.in_enable_area())
- animation_.Reset(1);
- else
- animation_.Show();
- popup_->Show();
- 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 TabDragController2. This is invoked
- // when the TabDragController2 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
- // TabDragController2 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 ui::Animation* animation) {
- UpdateLayeredAlpha();
- }
-
- virtual void AnimationEnded(const ui::Animation* animation) {
- if (!hidden_)
- return;
- popup_->Close();
- delete this;
- }
-
- virtual void UpdateLayeredAlpha() {
- double scale = in_enable_area_ ? 1 : .5;
- popup_->SetOpacity(static_cast<unsigned char>(animation_.GetCurrentValue() *
- scale * 255.0));
- }
-
- private:
- // TabDragController2 that created us.
- TabDragController2* 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.
- ui::SlideAnimation animation_;
-
- // Have we been hidden?
- bool hidden_;
-
- // Value of DockInfo::in_enable_area.
- bool in_enable_area_;
-};
-
-TabDragController2::TabDragData::TabDragData()
- : contents(NULL),
- source_model_index(-1),
- attached_tab(NULL),
- pinned(false) {
-}
-
-TabDragController2::TabDragData::~TabDragData() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabDragController2, public:
-
-TabDragController2::TabDragController2()
- : source_tabstrip_(NULL),
- attached_tabstrip_(NULL),
- is_dragging_window_(false),
- source_tab_offset_(0),
- offset_to_width_ratio_(0),
- old_focused_view_(NULL),
- last_move_screen_loc_(0),
- started_drag_(false),
- active_(true),
- source_tab_index_(std::numeric_limits<size_t>::max()),
- initial_move_(true),
- end_run_loop_behavior_(END_RUN_LOOP_STOP_DRAGGING),
- waiting_for_run_loop_to_exit_(false),
- tab_strip_to_attach_to_after_exit_(NULL),
- move_loop_widget_(NULL),
- destroyed_(NULL) {
- instance_ = this;
-}
-
-TabDragController2::~TabDragController2() {
- if (instance_ == this)
- instance_ = NULL;
-
- if (destroyed_)
- *destroyed_ = true;
-
- if (move_loop_widget_) {
- move_loop_widget_->RemoveObserver(this);
- SetTrackedByWorkspace(move_loop_widget_->GetNativeView(), true);
- }
-
- if (source_tabstrip_)
- GetModel(source_tabstrip_)->RemoveObserver(this);
-
- MessageLoopForUI::current()->RemoveObserver(this);
-}
-
-void TabDragController2::Init(
- TabStrip* source_tabstrip,
- BaseTab* source_tab,
- const std::vector<BaseTab*>& tabs,
- const gfx::Point& mouse_offset,
- int source_tab_offset,
- const TabStripSelectionModel& initial_selection_model) {
- DCHECK(!tabs.empty());
- DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end());
- source_tabstrip_ = source_tabstrip;
- GetModel(source_tabstrip_)->AddObserver(this);
- source_tab_offset_ = source_tab_offset;
- start_screen_point_ = GetCursorScreenPoint();
- mouse_offset_ = mouse_offset;
-
- drag_data_.resize(tabs.size());
- for (size_t i = 0; i < tabs.size(); ++i)
- InitTabDragData(tabs[i], &(drag_data_[i]));
- source_tab_index_ =
- std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
-
- // Listen for Esc key presses.
- MessageLoopForUI::current()->AddObserver(this);
-
- if (source_tab->width() > 0) {
- offset_to_width_ratio_ = static_cast<float>(source_tab_offset_) /
- static_cast<float>(source_tab->width());
- }
- InitWindowCreatePoint(source_tabstrip);
- initial_selection_model_.Copy(initial_selection_model);
-}
-
-// static
-bool TabDragController2::IsActiveAndAttachedTo(TabStrip* tab_strip) {
- return instance_ && instance_->active_ &&
- instance_->attached_tabstrip_ == tab_strip;
-}
-
-// static
-bool TabDragController2::IsActive() {
- return instance_ && instance_->active_;
-}
-
-void TabDragController2::InitTabDragData(BaseTab* tab,
- TabDragData* drag_data) {
- drag_data->source_model_index =
- source_tabstrip_->GetModelIndexOfBaseTab(tab);
- drag_data->contents = GetModel(source_tabstrip_)->GetTabContentsAt(
- drag_data->source_model_index);
- drag_data->pinned = source_tabstrip_->IsTabPinned(tab);
- registrar_.Add(
- this,
- content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
- content::Source<WebContents>(drag_data->contents->web_contents()));
-}
-
-void TabDragController2::Drag() {
- bring_to_front_timer_.Stop();
-
- if (waiting_for_run_loop_to_exit_)
- return;
-
- if (!started_drag_) {
- if (!CanStartDrag())
- return; // User hasn't dragged far enough yet.
-
- started_drag_ = true;
- SaveFocus();
- Attach(source_tabstrip_, gfx::Point());
- if (static_cast<int>(drag_data_.size()) ==
- GetModel(source_tabstrip_)->count()) {
- RunMoveLoop(); // Runs a nested loop, returning when done.
- return;
- }
- }
-
- ContinueDragging();
-}
-
-void TabDragController2::EndDrag(bool canceled) {
- // We can't revert if the source tabstrip was destroyed during the drag.
- EndDragImpl(canceled && source_tabstrip_ ? CANCELED : NORMAL);
-}
-
-bool TabDragController2::GetStartedDrag() const {
- return started_drag_;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabDragController2, content::NotificationObserver implementation:
-
-void TabDragController2::Observe(
- int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- DCHECK_EQ(type, content::NOTIFICATION_WEB_CONTENTS_DESTROYED);
- WebContents* destroyed_contents = content::Source<WebContents>(source).ptr();
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- if (drag_data_[i].contents->web_contents() == destroyed_contents) {
- drag_data_[i].contents = NULL;
- EndDragImpl(TAB_DESTROYED);
- return;
- }
- }
- // If we get here it means we got notification for a tab we don't know about.
- NOTREACHED();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabDragController2, MessageLoop::Observer implementation:
-
-#if defined(OS_WIN) || defined(USE_AURA)
-base::EventStatus TabDragController2::WillProcessEvent(
- const base::NativeEvent& event) {
- return base::EVENT_CONTINUE;
-}
-
-void TabDragController2::DidProcessEvent(const base::NativeEvent& event) {
- // 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 (ui::EventTypeFromNative(event) == ui::ET_KEY_PRESSED &&
- ui::KeyboardCodeFromNative(event) == ui::VKEY_ESCAPE) {
- EndDrag(true);
- }
-}
-#endif
-
-void TabDragController2::OnWidgetMoved(views::Widget* widget) {
- Drag();
-}
-
-void TabDragController2::TabStripEmpty() {
- GetModel(source_tabstrip_)->RemoveObserver(this);
- // NULL out source_tabstrip_ so that we don't attempt to add back to in (in
- // the case of a revert).
- source_tabstrip_ = NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// TabDragController2, private:
-
-void TabDragController2::InitWindowCreatePoint(TabStrip* tab_strip) {
- // 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 tab_strip. So, we need to get the
- // first_tab based on tab_strip, 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 = tab_strip->tab_at(0);
- views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_);
- window_create_point_ = first_source_tab_point_;
- window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y());
-}
-
-gfx::Point TabDragController2::GetWindowCreatePoint() const {
- gfx::Point cursor_point = GetCursorScreenPoint();
- if (dock_info_.type() != DockInfo::NONE && dock_info_.in_enable_area()) {
- // 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;
- }
- // If the cursor is outside the monitor area, move it inside. For example,
- // dropping a tab onto the task bar on Windows produces this situation.
- gfx::Rect work_area = gfx::Screen::GetMonitorNearestPoint(
- cursor_point).work_area();
- if (!work_area.IsEmpty()) {
- if (cursor_point.x() < work_area.x())
- cursor_point.set_x(work_area.x());
- else if (cursor_point.x() > work_area.right())
- cursor_point.set_x(work_area.right());
- if (cursor_point.y() < work_area.y())
- cursor_point.set_y(work_area.y());
- else if (cursor_point.y() > work_area.bottom())
- cursor_point.set_y(work_area.bottom());
- }
- return gfx::Point(cursor_point.x() - window_create_point_.x(),
- cursor_point.y() - window_create_point_.y());
-}
-
-void TabDragController2::UpdateDockInfo(const gfx::Point& screen_point) {
- // Update the DockInfo for the current mouse coordinates.
- DockInfo dock_info = GetDockInfoAtPoint(screen_point);
- 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 TabDragController2::SaveFocus() {
- DCHECK(!old_focused_view_); // This should only be invoked once.
- DCHECK(source_tabstrip_);
- old_focused_view_ = source_tabstrip_->GetFocusManager()->GetFocusedView();
- source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
-}
-
-void TabDragController2::RestoreFocus() {
- if (old_focused_view_ && attached_tabstrip_ == source_tabstrip_)
- old_focused_view_->GetFocusManager()->SetFocusedView(old_focused_view_);
- old_focused_view_ = NULL;
-}
-
-bool TabDragController2::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 TabDragController2::ContinueDragging() {
- DCHECK(attached_tabstrip_);
-
- // 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();
-
- // 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.
- TabStrip* target_tabstrip = GetTabStripForPoint(screen_point);
- bool tab_strip_changed = (target_tabstrip != attached_tabstrip_);
- if (tab_strip_changed) {
- if (!target_tabstrip) {
- DetachIntoNewBrowserAndRunMoveLoop(screen_point);
- return;
- }
- if (is_dragging_window_) {
-#if defined(USE_ASH)
- // ReleaseMouseCapture() is going to result in calling back to us (because
- // it results in a move). That'll cause all sorts of problems. Reset the
- // observer so we don't get notified and process the event.
- move_loop_widget_->RemoveObserver(this);
- move_loop_widget_ = NULL;
-#endif
- views::Widget* browser_widget = GetAttachedBrowserWidget();
- // Need to release the drag controller before starting the move loop as
- // it's going to trigger capture lost, which cancels drag.
- attached_tabstrip_->ReleaseDragController();
- target_tabstrip->OwnDragController(this);
- // Disable animations so that we don't see a close animation on aero.
- browser_widget->SetVisibilityChangedAnimationsEnabled(false);
- browser_widget->ReleaseMouseCapture();
- // EndMoveLoop is going to snap the window back to its original location.
- // Hide it so users don't see this.
- browser_widget->Hide();
- browser_widget->EndMoveLoop();
-
- // Ideally we would always swap the tabs now, but on windows it seems that
- // running the move loop implicitly activates the window when done,
- // leading to all sorts of flicker. So, on windows, instead we process
- // the move after the loop completes. But on chromeos, we can do tab
- // swapping now to avoid the tab flashing issue(crbug.com/116329).
-#if defined(USE_ASH)
- is_dragging_window_ = false;
- Detach();
- gfx::Point screen_point(GetCursorScreenPoint());
- Attach(target_tabstrip, screen_point);
- // Move the tabs into position.
- MoveAttached(screen_point);
- attached_tabstrip_->GetWidget()->Activate();
-#else
- tab_strip_to_attach_to_after_exit_ = target_tabstrip;
-#endif
-
- waiting_for_run_loop_to_exit_ = true;
- end_run_loop_behavior_ = END_RUN_LOOP_CONTINUE_DRAGGING;
- return;
- }
- Detach();
- Attach(target_tabstrip, screen_point);
- }
- if (is_dragging_window_) {
- bring_to_front_timer_.Start(FROM_HERE,
- base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this,
- &TabDragController2::BringWindowUnderMouseToFront);
- }
-
- UpdateDockInfo(screen_point);
-
- if (!is_dragging_window_) {
- MoveAttached(screen_point);
- if (tab_strip_changed) {
- // Move the corresponding window to the front. We do this after the move
- // as on windows activate triggers a synchronous paint.
- attached_tabstrip_->GetWidget()->Activate();
- }
- }
-}
-
-void TabDragController2::MoveAttached(const gfx::Point& screen_point) {
- DCHECK(attached_tabstrip_);
- DCHECK(!is_dragging_window_);
-
- gfx::Point dragged_view_point = GetAttachedDragPoint(screen_point);
-
- 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();
- int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
-
- std::vector<BaseTab*> tabs(drag_data_.size());
- for (size_t i = 0; i < drag_data_.size(); ++i)
- tabs[i] = drag_data_[i].attached_tab;
-
- bool did_layout = false;
- // Update the model, moving the WebContents from one index to another. Do this
- // only if we have moved a minimum distance since the last reorder (to prevent
- // jitter) or if this the first move and the tabs are not consecutive.
- if (abs(screen_point.x() - last_move_screen_loc_) > threshold ||
- (initial_move_ && !AreTabsConsecutive())) {
- TabStripModel* attached_model = GetModel(attached_tabstrip_);
- gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point);
- int to_index = GetInsertionIndexForDraggedBounds(bounds);
- TabContentsWrapper* last_contents =
- drag_data_[drag_data_.size() - 1].contents;
- int index_of_last_item =
- attached_model->GetIndexOfTabContents(last_contents);
- if (initial_move_) {
- // TabStrip determines if the tabs needs to be animated based on model
- // position. This means we need to invoke LayoutDraggedTabsAt before
- // changing the model.
- attached_tabstrip_->LayoutDraggedTabsAt(
- tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
- initial_move_);
- did_layout = true;
- }
- attached_model->MoveSelectedTabsTo(to_index);
- // Move may do nothing in certain situations (such as when dragging pinned
- // tabs). Make sure the tabstrip actually changed before updating
- // last_move_screen_loc_.
- if (index_of_last_item !=
- attached_model->GetIndexOfTabContents(last_contents)) {
- last_move_screen_loc_ = screen_point.x();
- }
- }
-
- if (!did_layout) {
- attached_tabstrip_->LayoutDraggedTabsAt(
- tabs, source_tab_drag_data()->attached_tab, dragged_view_point,
- initial_move_);
- }
-
- initial_move_ = false;
-}
-
-TabDragController2::DetachPosition TabDragController2::GetDetachPosition(
- const gfx::Point& screen_point) {
- DCHECK(attached_tabstrip_);
- gfx::Point attached_point(screen_point);
- views::View::ConvertPointToView(NULL, attached_tabstrip_, &attached_point);
- if (attached_point.x() < 0)
- return DETACH_BEFORE;
- if (attached_point.x() >= attached_tabstrip_->width())
- return DETACH_AFTER;
- return DETACH_ABOVE_OR_BELOW;
-}
-
-DockInfo TabDragController2::GetDockInfoAtPoint(
- const gfx::Point& screen_point) {
- // TODO: add support for dock info.
- if (true) {
- return DockInfo();
- }
-
- 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;
-}
-
-TabStrip* TabDragController2::GetTabStripForPoint(
- const gfx::Point& screen_point) {
- gfx::NativeView dragged_view = NULL;
- if (is_dragging_window_) {
- dragged_view = attached_tabstrip_->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);
- TabStrip* tab_strip = GetTabStripForWindow(local_window);
- if (tab_strip && DoesTabStripContain(tab_strip, screen_point))
- return tab_strip;
- return is_dragging_window_ ? attached_tabstrip_ : NULL;
-}
-
-TabStrip* TabDragController2::GetTabStripForWindow(
- gfx::NativeWindow window) {
- if (!window)
- return NULL;
- BrowserView* browser = BrowserView::GetBrowserViewForNativeWindow(window);
- // We only allow drops on windows that have tabstrips.
- if (!browser ||
- !browser->browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP))
- return NULL;
-
- TabStrip* other_tabstrip = static_cast<TabStrip*>(browser->tabstrip());
- TabStrip* tab_strip =
- attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
- DCHECK(tab_strip);
- if (!other_tabstrip->controller()->IsCompatibleWith(tab_strip))
- return NULL;
- return other_tabstrip;
-}
-
-bool TabDragController2::DoesTabStripContain(
- TabStrip* tabstrip,
- const gfx::Point& screen_point) const {
- static const int kVerticalDetachMagnetism = 15;
- // Make sure the specified screen point is actually within the bounds of the
- // specified tabstrip...
- gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip);
- 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;
- return screen_point.y() >= lower_threshold &&
- screen_point.y() <= upper_threshold;
- }
- return false;
-}
-
-void TabDragController2::Attach(TabStrip* attached_tabstrip,
- const gfx::Point& screen_point) {
- DCHECK(!attached_tabstrip_); // We should already have detached by the time
- // we get here.
-
- attached_tabstrip_ = attached_tabstrip;
-
- std::vector<BaseTab*> tabs =
- GetTabsMatchingDraggedContents(attached_tabstrip_);
-
- if (tabs.empty()) {
- // Transitioning from detached to attached to a new tabstrip. Add tabs to
- // the new model.
-
- selection_model_before_attach_.Copy(attached_tabstrip->GetSelectionModel());
-
- // 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_ = screen_point.x();
-
- // 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::Point tab_strip_point(screen_point);
- views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_strip_point);
- tab_strip_point.set_x(
- attached_tabstrip_->GetMirroredXInView(tab_strip_point.x()));
- tab_strip_point.Offset(-mouse_offset_.x(), -mouse_offset_.y());
- gfx::Rect bounds = GetDraggedViewTabStripBounds(tab_strip_point);
- int index = GetInsertionIndexForDraggedBounds(bounds);
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- int add_types = TabStripModel::ADD_NONE;
- if (drag_data_[i].pinned)
- add_types |= TabStripModel::ADD_PINNED;
- GetModel(attached_tabstrip_)->InsertTabContentsAt(
- index + i, drag_data_[i].contents, add_types);
- }
-
- tabs = GetTabsMatchingDraggedContents(attached_tabstrip_);
- }
- DCHECK_EQ(tabs.size(), drag_data_.size());
- for (size_t i = 0; i < drag_data_.size(); ++i)
- drag_data_[i].attached_tab = tabs[i];
-
- attached_tabstrip_->StartedDraggingTabs(tabs);
-
- ResetSelection(GetModel(attached_tabstrip_));
-
- // The size of the dragged tab may have changed. Adjust the x offset so that
- // ratio of mouse_offset_ to original width is maintained.
- std::vector<BaseTab*> tabs_to_source(tabs);
- tabs_to_source.erase(tabs_to_source.begin() + source_tab_index_ + 1,
- tabs_to_source.end());
- int new_x = attached_tabstrip_->GetSizeNeededForTabs(tabs_to_source) -
- tabs[source_tab_index_]->width() +
- static_cast<int>(offset_to_width_ratio_ *
- tabs[source_tab_index_]->width());
- mouse_offset_.set_x(new_x);
-
- // Redirect all mouse events to the TabStrip so that the tab that originated
- // the drag can safely be deleted.
- static_cast<views::internal::RootView*>(
- attached_tabstrip_->GetWidget()->GetRootView())->SetMouseHandler(
- attached_tabstrip_);
-}
-
-void TabDragController2::Detach() {
- TabStripModel* attached_model = GetModel(attached_tabstrip_);
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- int index = attached_model->GetIndexOfTabContents(drag_data_[i].contents);
- DCHECK_NE(-1, index);
-
- // Hide the tab so that the user doesn't see it animate closed.
- drag_data_[i].attached_tab->SetVisible(false);
-
- attached_model->DetachTabContentsAt(index);
-
- // Detaching may end up deleting the tab, drop references to it.
- drag_data_[i].attached_tab = NULL;
- }
-
- if (!attached_model->empty()) {
- if (!selection_model_before_attach_.empty() &&
- selection_model_before_attach_.active() >= 0 &&
- selection_model_before_attach_.active() <
- attached_model->count()) {
- // Restore the selection.
- attached_model->SetSelectionFromModel(selection_model_before_attach_);
- } else if (attached_tabstrip_ == source_tabstrip_ &&
- !initial_selection_model_.empty()) {
- // First time detaching from the source tabstrip. Reset selection model to
- // initial_selection_model_. Before resetting though we have to remove all
- // the tabs from initial_selection_model_ as it was created with the tabs
- // still there.
- TabStripSelectionModel selection_model;
- selection_model.Copy(initial_selection_model_);
- for (DragData::const_reverse_iterator i = drag_data_.rbegin();
- i != drag_data_.rend(); ++i) {
- selection_model.DecrementFrom(i->source_model_index);
- }
- // We may have cleared out the selection model. Only reset it if it
- // contains something.
- if (!selection_model.empty())
- attached_model->SetSelectionFromModel(selection_model);
- }
- }
-
- attached_tabstrip_->DraggedTabsDetached();
-
- attached_tabstrip_ = NULL;
-}
-
-void TabDragController2::DetachIntoNewBrowserAndRunMoveLoop(
- const gfx::Point& screen_point) {
- if (GetModel(attached_tabstrip_)->count() ==
- static_cast<int>(drag_data_.size())) {
- // All the tabs in a browser are being dragged but all the tabs weren't
- // initially being dragged. For this to happen the user would have to
- // start dragging a set of tabs, the other tabs close, then detach.
- RunMoveLoop();
- return;
- }
-
- // Create a new browser to house the dragged tabs and have the OS run a move
- // loop.
-
- gfx::Point attached_point = GetAttachedDragPoint(screen_point);
-
- // Calculate the bounds for the tabs from the attached_tab_strip. We do this
- // so that the tabs don't change size when detached.
- std::vector<gfx::Rect> drag_bounds;
- std::vector<BaseTab*> attached_tabs;
- for (size_t i = 0; i < drag_data_.size(); ++i)
- attached_tabs.push_back(drag_data_[i].attached_tab);
- attached_tabstrip_->CalculateBoundsForDraggedTabs(attached_tabs,
- &drag_bounds);
- for (size_t i = 0; i < drag_bounds.size(); ++i)
- drag_bounds[i].set_x(attached_point.x() + drag_bounds[i].x());
-
- Browser* browser = CreateBrowserForDrag(
- attached_tabstrip_, screen_point, &drag_bounds);
- attached_tabstrip_->ReleaseDragController();
- Detach();
- BrowserView* dragged_browser_view =
- BrowserView::GetBrowserViewForBrowser(browser);
- dragged_browser_view->GetWidget()->SetVisibilityChangedAnimationsEnabled(
- false);
- Attach(static_cast<TabStrip*>(dragged_browser_view->tabstrip()),
- gfx::Point());
- attached_tabstrip_->OwnDragController(this);
- // TODO: come up with a cleaner way to do this.
- static_cast<TabStrip*>(attached_tabstrip_)->SetTabBoundsForDrag(
- drag_bounds);
-
- browser->window()->Show();
- browser->window()->Activate();
- dragged_browser_view->GetWidget()->SetVisibilityChangedAnimationsEnabled(
- true);
- RunMoveLoop();
-}
-
-void TabDragController2::RunMoveLoop() {
- move_loop_widget_ = GetAttachedBrowserWidget();
- move_loop_widget_->AddObserver(this);
- is_dragging_window_ = true;
- bool destroyed = false;
- destroyed_ = &destroyed;
- // Running the move loop release mouse capture on windows, which triggers
- // destroying the drag loop. Release mouse capture ourself before this while
- // the Dragcontroller isn't owned by the TabStrip.
- attached_tabstrip_->ReleaseDragController();
- attached_tabstrip_->GetWidget()->ReleaseMouseCapture();
- attached_tabstrip_->OwnDragController(this);
- views::Widget::MoveLoopResult result = move_loop_widget_->RunMoveLoop();
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
- content::NotificationService::AllBrowserContextsAndSources(),
- content::NotificationService::NoDetails());
-
- if (destroyed)
- return;
- destroyed_ = NULL;
- // Under chromeos we immediately set the |move_loop_widget_| to NULL.
- if (move_loop_widget_) {
- move_loop_widget_->RemoveObserver(this);
- move_loop_widget_ = NULL;
- }
- is_dragging_window_ = false;
- waiting_for_run_loop_to_exit_ = false;
- if (end_run_loop_behavior_ == END_RUN_LOOP_CONTINUE_DRAGGING) {
- end_run_loop_behavior_ = END_RUN_LOOP_STOP_DRAGGING;
- if (tab_strip_to_attach_to_after_exit_) {
- Detach();
- gfx::Point screen_point(GetCursorScreenPoint());
- Attach(tab_strip_to_attach_to_after_exit_, screen_point);
- // Move the tabs into position.
- MoveAttached(screen_point);
- attached_tabstrip_->GetWidget()->Activate();
- tab_strip_to_attach_to_after_exit_ = NULL;
- }
- DCHECK(attached_tabstrip_);
- attached_tabstrip_->GetWidget()->SetMouseCapture(attached_tabstrip_);
- } else if (active_) {
- EndDrag(result == views::Widget::MOVE_LOOP_CANCELED);
- }
-}
-
-int TabDragController2::GetInsertionIndexForDraggedBounds(
- const gfx::Rect& dragged_bounds) const {
- // TODO: this is wrong. It needs to calculate the bounds after adjusting for
- // the tabs that will be inserted.
- int right_tab_x = 0;
- int index = -1;
- for (int i = 0; i < attached_tabstrip_->tab_count(); ++i) {
- const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i);
- gfx::Rect left_half, right_half;
- ideal_bounds.SplitVertically(&left_half, &right_half);
- right_tab_x = right_half.right();
- if (dragged_bounds.x() >= right_half.x() &&
- dragged_bounds.x() < right_half.right()) {
- index = i + 1;
- break;
- } else if (dragged_bounds.x() >= left_half.x() &&
- dragged_bounds.x() < left_half.right()) {
- index = i;
- break;
- }
- }
- if (index == -1) {
- if (dragged_bounds.right() > right_tab_x) {
- index = GetModel(attached_tabstrip_)->count();
- } else {
- index = 0;
- }
- }
-
- if (!drag_data_[0].attached_tab) {
- // If 'attached_tab' is NULL, it means we're in the process of attaching and
- // don't need to constrain the index.
- return index;
- }
-
- int max_index = GetModel(attached_tabstrip_)->count() -
- static_cast<int>(drag_data_.size());
- return std::max(0, std::min(max_index, index));
-}
-
-gfx::Rect TabDragController2::GetDraggedViewTabStripBounds(
- const gfx::Point& tab_strip_point) {
- // attached_tab is NULL when inserting into a new tabstrip.
- if (source_tab_drag_data()->attached_tab) {
- return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
- source_tab_drag_data()->attached_tab->width(),
- source_tab_drag_data()->attached_tab->height());
- }
-
- double sel_width, unselected_width;
- static_cast<TabStrip*>(attached_tabstrip_)->GetCurrentTabWidths(
- &sel_width, &unselected_width);
- return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(),
- static_cast<int>(sel_width),
- Tab::GetStandardSize().height());
-}
-
-gfx::Point TabDragController2::GetAttachedDragPoint(
- const gfx::Point& screen_point) {
- DCHECK(attached_tabstrip_); // The tab must be attached.
-
- gfx::Point tab_loc(screen_point);
- views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_loc);
- int x =
- attached_tabstrip_->GetMirroredXInView(tab_loc.x()) - mouse_offset_.x();
- int y = tab_loc.y() - mouse_offset_.y();
-
- // TODO: consider caching this.
- std::vector<BaseTab*> attached_tabs;
- for (size_t i = 0; i < drag_data_.size(); ++i)
- attached_tabs.push_back(drag_data_[i].attached_tab);
-
- int size = attached_tabstrip_->GetSizeNeededForTabs(attached_tabs);
-
- int max_x = attached_tabstrip_->width() - size;
- x = std::min(std::max(x, 0), max_x);
- y = 0;
- return gfx::Point(x, y);
-}
-
-std::vector<BaseTab*> TabDragController2::GetTabsMatchingDraggedContents(
- TabStrip* tabstrip) {
- TabStripModel* model = GetModel(attached_tabstrip_);
- std::vector<BaseTab*> tabs;
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- int model_index = model->GetIndexOfTabContents(drag_data_[i].contents);
- if (model_index == TabStripModel::kNoTab)
- return std::vector<BaseTab*>();
- tabs.push_back(tabstrip->tab_at(model_index));
- }
- return tabs;
-}
-
-void TabDragController2::EndDragImpl(EndDragType type) {
- DCHECK(active_);
- active_ = false;
-
- bring_to_front_timer_.Stop();
-
- if (is_dragging_window_) {
- if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1))
- SetTrackedByWorkspace(GetAttachedBrowserWidget()->GetNativeView(), true);
-
- // End the nested drag loop.
- GetAttachedBrowserWidget()->EndMoveLoop();
- waiting_for_run_loop_to_exit_ = true;
- }
-
- // 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();
- }
- } else if (drag_data_.size() > 1) {
- RevertDrag();
- } // else case the only tab we were dragging was deleted. Nothing to do.
-
- // Clear out drag data so we don't attempt to do anything with it.
- drag_data_.clear();
-
- TabStrip* owning_tabstrip =
- attached_tabstrip_ ? attached_tabstrip_ : source_tabstrip_;
- owning_tabstrip->DestroyDragController();
-}
-
-void TabDragController2::RevertDrag() {
- std::vector<BaseTab*> tabs;
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- if (drag_data_[i].contents) {
- // Contents is NULL if a tab was destroyed while the drag was under way.
- tabs.push_back(drag_data_[i].attached_tab);
- RevertDragAt(i);
- }
- }
-
- if (attached_tabstrip_ && attached_tabstrip_ == source_tabstrip_)
- attached_tabstrip_->StoppedDraggingTabs(tabs);
-
- if (initial_selection_model_.empty())
- ResetSelection(GetModel(source_tabstrip_));
- else
- GetModel(source_tabstrip_)->SetSelectionFromModel(initial_selection_model_);
-
- if (source_tabstrip_)
- source_tabstrip_->GetWidget()->Activate();
-}
-
-void TabDragController2::ResetSelection(TabStripModel* model) {
- DCHECK(model);
- TabStripSelectionModel selection_model;
- bool has_one_valid_tab = false;
- for (size_t i = 0; i < drag_data_.size(); ++i) {
- // |contents| is NULL if a tab was deleted out from under us.
- if (drag_data_[i].contents) {
- int index = model->GetIndexOfTabContents(drag_data_[i].contents);
- DCHECK_NE(-1, index);
- selection_model.AddIndexToSelection(index);
- if (!has_one_valid_tab || i == source_tab_index_) {
- // Reset the active/lead to the first tab. If the source tab is still
- // valid we'll reset these again later on.
- selection_model.set_active(index);
- selection_model.set_anchor(index);
- has_one_valid_tab = true;
- }
- }
- }
- if (!has_one_valid_tab)
- return;
-
- model->SetSelectionFromModel(selection_model);
-}
-
-void TabDragController2::RevertDragAt(size_t drag_index) {
- DCHECK(started_drag_);
-
- TabDragData* data = &(drag_data_[drag_index]);
- int index =
- GetModel(attached_tabstrip_)->GetIndexOfTabContents(data->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.
- GetModel(source_tabstrip_)->InsertTabContentsAt(
- data->source_model_index, data->contents,
- (data->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, data->source_model_index, false);
- }
-}
-
-void TabDragController2::CompleteDrag() {
- DCHECK(started_drag_);
-
- if (attached_tabstrip_) {
- attached_tabstrip_->StoppedDraggingTabs(
- GetTabsMatchingDraggedContents(attached_tabstrip_));
- } else {
- NOTIMPLEMENTED();
-
- if (dock_info_.type() != DockInfo::NONE) {
- switch (dock_info_.type()) {
- case DockInfo::LEFT_OF_WINDOW:
- content::RecordAction(UserMetricsAction("DockingWindow_Left"));
- break;
-
- case DockInfo::RIGHT_OF_WINDOW:
- content::RecordAction(UserMetricsAction("DockingWindow_Right"));
- break;
-
- case DockInfo::BOTTOM_OF_WINDOW:
- content::RecordAction(UserMetricsAction("DockingWindow_Bottom"));
- break;
-
- case DockInfo::TOP_OF_WINDOW:
- content::RecordAction(UserMetricsAction("DockingWindow_Top"));
- break;
-
- case DockInfo::MAXIMIZE:
- content::RecordAction(UserMetricsAction("DockingWindow_Maximize"));
- break;
-
- case DockInfo::LEFT_HALF:
- content::RecordAction(UserMetricsAction("DockingWindow_LeftHalf"));
- break;
-
- case DockInfo::RIGHT_HALF:
- content::RecordAction(UserMetricsAction("DockingWindow_RightHalf"));
- break;
-
- case DockInfo::BOTTOM_HALF:
- content::RecordAction(UserMetricsAction("DockingWindow_BottomHalf"));
- break;
-
- default:
- NOTREACHED();
- break;
- }
- }
- // Compel the model to construct a new window for the detached WebContents.
- views::Widget* widget = source_tabstrip_->GetWidget();
- gfx::Rect window_bounds(widget->GetRestoredBounds());
- 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(
- drag_data_[0].contents, window_bounds, dock_info_,
- widget->IsMaximized());
- TabStripModel* new_model = new_browser->tabstrip_model();
- new_model->SetTabPinned(
- new_model->GetIndexOfTabContents(drag_data_[0].contents),
- drag_data_[0].pinned);
- for (size_t i = 1; i < drag_data_.size(); ++i) {
- new_model->InsertTabContentsAt(
- static_cast<int>(i),
- drag_data_[i].contents,
- drag_data_[i].pinned ? TabStripModel::ADD_PINNED :
- TabStripModel::ADD_NONE);
- }
- ResetSelection(new_browser->tabstrip_model());
- new_browser->window()->Show();
- }
-}
-
-gfx::Point TabDragController2::GetCursorScreenPoint() const {
- // TODO(sky): see if we can convert to using Screen every where.
-#if defined(OS_WIN) && !defined(USE_AURA)
- DWORD pos = GetMessagePos();
- return gfx::Point(pos);
-#else
- return gfx::Screen::GetCursorScreenPoint();
-#endif
-}
-
-gfx::Rect TabDragController2::GetViewScreenBounds(views::View* view) const {
- gfx::Point view_topleft;
- views::View::ConvertPointToScreen(view, &view_topleft);
- gfx::Rect view_screen_bounds = view->GetLocalBounds();
- view_screen_bounds.Offset(view_topleft.x(), view_topleft.y());
- return view_screen_bounds;
-}
-
-void TabDragController2::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 TabDragController2::BringWindowUnderMouseToFront() {
- gfx::NativeWindow window = dock_info_.window();
- if (!window) {
- gfx::NativeView dragged_view =
- attached_tabstrip_->GetWidget()->GetNativeView();
- dock_windows_.insert(dragged_view);
- window = DockInfo::GetLocalProcessWindowAtPoint(GetCursorScreenPoint(),
- dock_windows_);
- dock_windows_.erase(dragged_view);
- }
- if (window) {
- views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
- if (widget) {
- widget->StackAtTop();
- attached_tabstrip_->GetWidget()->StackAtTop();
- }
- }
-}
-
-// static
-TabStripModel* TabDragController2::GetModel(TabStrip* tabstrip) {
- return static_cast<BrowserTabStripController*>(tabstrip->controller())->
- model();
-}
-
-views::Widget* TabDragController2::GetAttachedBrowserWidget() {
- return BrowserView::GetBrowserViewForNativeWindow(
- attached_tabstrip_->GetWidget()->GetNativeView())->GetWidget();
-}
-
-bool TabDragController2::AreTabsConsecutive() {
- for (size_t i = 1; i < drag_data_.size(); ++i) {
- if (drag_data_[i - 1].source_model_index + 1 !=
- drag_data_[i].source_model_index) {
- return false;
- }
- }
- return true;
-}
-
-Browser* TabDragController2::CreateBrowserForDrag(
- TabStrip* source,
- const gfx::Point& screen_point,
- std::vector<gfx::Rect>* drag_bounds) {
- Browser* browser = Browser::Create(drag_data_[0].contents->profile());
- gfx::Point center(0, source->height() / 2);
- views::View::ConvertPointToWidget(source, &center);
- gfx::Rect new_bounds(source->GetWidget()->GetWindowScreenBounds());
- new_bounds.set_y(screen_point.y() - center.y());
- switch (GetDetachPosition(screen_point)) {
- case DETACH_BEFORE:
- new_bounds.set_x(screen_point.x() - center.x());
- new_bounds.Offset(-mouse_offset_.x(), 0);
- break;
-
- case DETACH_AFTER: {
- gfx::Point right_edge(source->width(), 0);
- views::View::ConvertPointToWidget(source, &right_edge);
- new_bounds.set_x(screen_point.x() - right_edge.x());
- new_bounds.Offset(drag_bounds->back().right() - mouse_offset_.x(), 0);
- int delta = (*drag_bounds)[0].x();
- for (size_t i = 0; i < drag_bounds->size(); ++i)
- (*drag_bounds)[i].Offset(-delta, 0);
- break;
- }
-
- default:
- break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
- }
-
- SetTrackedByWorkspace(browser->window()->GetNativeHandle(), false);
- browser->window()->SetBounds(new_bounds);
- return browser;
-}
-
-void TabDragController2::SetTrackedByWorkspace(gfx::NativeWindow window,
- bool value) {
-#if defined(USE_ASH)
- ash::SetTrackedByWorkspace(window, value);
-#endif
-}
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller2.h b/chrome/browser/ui/views/tabs/tab_drag_controller2.h
deleted file mode 100644
index 8af2a28..0000000
--- a/chrome/browser/ui/views/tabs/tab_drag_controller2.h
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER2_H_
-#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER2_H_
-#pragma once
-
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "base/timer.h"
-#include "chrome/browser/tabs/tab_strip_model_observer.h"
-#include "chrome/browser/tabs/tab_strip_selection_model.h"
-#include "chrome/browser/ui/tabs/dock_info.h"
-#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "ui/gfx/rect.h"
-#include "ui/views/widget/widget.h"
-
-namespace views {
-class View;
-} // namespace views
-
-class BaseTab;
-class Browser;
-class TabContentsWrapper;
-struct TabRendererData;
-class TabStripModel;
-
-// Implementation of TabDragController that creates a real Browser.
-class TabDragController2 : public TabDragController,
- public content::NotificationObserver,
- public MessageLoopForUI::Observer,
- public views::Widget::Observer,
- public TabStripModelObserver {
- public:
- TabDragController2();
- virtual ~TabDragController2();
-
- // Initializes TabDragController2 to drag the tabs in |tabs| originating
- // from |source_tabstrip|. |source_tab| is the tab that initiated the drag and
- // is contained in |tabs|. |mouse_offset| is the distance of the mouse
- // pointer from the origin of the first tab in |tabs| and |source_tab_offset|
- // the offset from |source_tab|. |source_tab_offset| is the horizontal distant
- // for a horizontal tab strip, and the vertical distance for a vertical tab
- // strip. |initial_selection_model| is the selection model before the drag
- // started and is only non-empty if |source_tab| was not initially selected.
- void Init(TabStrip* source_tabstrip,
- BaseTab* source_tab,
- const std::vector<BaseTab*>& tabs,
- const gfx::Point& mouse_offset,
- int source_tab_offset,
- const TabStripSelectionModel& initial_selection_model);
-
- // Returns true if there is an active TabDragController2 that is attached to
- // |tab_strip|.
- static bool IsActiveAndAttachedTo(TabStrip* tab_strip);
-
- // Returns true if there is an active TabDragController2.
- static bool IsActive();
-
- private:
- class DockDisplayer;
- friend class DockDisplayer;
-
- typedef std::set<gfx::NativeView> DockWindows;
-
- // Specifies what should happen when RunMoveLoop complets.
- enum EndRunLoopBehavior {
- // Indicates the drag should end.
- END_RUN_LOOP_STOP_DRAGGING,
-
- // Indicates the drag should continue.
- END_RUN_LOOP_CONTINUE_DRAGGING
- };
-
- // Enumeration of the ways a drag session can end.
- enum EndDragType {
- // Drag session exited normally: the user released the mouse.
- NORMAL,
-
- // The drag session was canceled (alt-tab during drag, escape ...)
- CANCELED,
-
- // The tab (NavigationController) was destroyed during the drag.
- TAB_DESTROYED
- };
-
- // Enumeration of the possible positions the detached tab may detach from.
- enum DetachPosition {
- DETACH_BEFORE,
- DETACH_AFTER,
- DETACH_ABOVE_OR_BELOW
- };
-
- // Stores the date associated with a single tab that is being dragged.
- struct TabDragData {
- TabDragData();
- ~TabDragData();
-
- // The TabContentsWrapper being dragged.
- TabContentsWrapper* contents;
-
- // This is the index of the tab in |source_tabstrip_| when the drag
- // began. This is used to restore the previous state if the drag is aborted.
- int source_model_index;
-
- // If attached this is the tab in |attached_tabstrip_|.
- BaseTab* attached_tab;
-
- // Is the tab pinned?
- bool pinned;
- };
-
- typedef std::vector<TabDragData> DragData;
-
- // Sets |drag_data| from |tab|. This also registers for necessary
- // notifications and resets the delegate of the TabContentsWrapper.
- void InitTabDragData(BaseTab* tab, TabDragData* drag_data);
-
- // TabDragController overrides;
- virtual void Drag() OVERRIDE;
- virtual void EndDrag(bool canceled) OVERRIDE;
- virtual bool GetStartedDrag() const OVERRIDE;
-
- // Overridden from content::NotificationObserver:
- virtual void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) OVERRIDE;
-
- // Overridden from MessageLoop::Observer:
-#if defined(OS_WIN) || defined(USE_AURA)
- virtual base::EventStatus WillProcessEvent(
- const base::NativeEvent& event) OVERRIDE;
- virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
-#endif
-
- // Overriden from views::Widget::Observer:
- virtual void OnWidgetMoved(views::Widget* widget) OVERRIDE;
-
- // Overriden from TabStripModelObserver:
- virtual void TabStripEmpty() OVERRIDE;
-
- // Initialize the offset used to calculate the position to create windows
- // in |GetWindowCreatePoint|. This should only be invoked from |Init|.
- void InitWindowCreatePoint(TabStrip* tab_strip);
-
- // Returns the point where a detached window should be created given the
- // current mouse position.
- gfx::Point GetWindowCreatePoint() const;
-
- void UpdateDockInfo(const gfx::Point& screen_point);
-
- // Saves focus in the window that the drag initiated from. Focus will be
- // restored appropriately if the drag ends within this same window.
- void SaveFocus();
-
- // Restore focus to the View that had focus before the drag was started, if
- // the drag ends within the same Window as it began.
- void RestoreFocus();
-
- // Tests whether the position of the mouse is past a minimum elasticity
- // threshold required to start a drag.
- bool CanStartDrag() const;
-
- // Move the DraggedTabView according to the current mouse screen position,
- // potentially updating the source and other TabStrips.
- void ContinueDragging();
-
- // Handles dragging tabs while the tabs are attached.
- void MoveAttached(const gfx::Point& screen_point);
-
- // Returns the DetachPosition given the specified location in screen
- // coordinates.
- DetachPosition GetDetachPosition(const gfx::Point& screen_point);
-
- // Returns the TabStrip for the specified window, or NULL if one doesn't exist
- // or isn't appropriate.
- TabStrip* GetTabStripForWindow(gfx::NativeWindow window);
-
- // Returns the compatible TabStrip that is under the specified point (screen
- // coordinates), or NULL if there is none.
- TabStrip* GetTabStripForPoint(const gfx::Point& screen_point);
-
- DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
-
- // Returns true if |tabstrip| contains the specified point in screen
- // coordinates.
- bool DoesTabStripContain(TabStrip* tabstrip,
- const gfx::Point& screen_point) const;
-
- // Attach the dragged Tab to the specified TabStrip.
- void Attach(TabStrip* attached_tabstrip, const gfx::Point& screen_point);
-
- // Detach the dragged Tab from the current TabStrip.
- void Detach();
-
- // Detaches the tabs being dragged, creates a new Browser to contain them and
- // runs a nested move loop.
- void DetachIntoNewBrowserAndRunMoveLoop(const gfx::Point& screen_point);
-
- // Runs a nested message loop that handles moving the current Browser.
- void RunMoveLoop();
-
- // Returns the index where the dragged WebContents should be inserted into
- // |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
- // coordinates relative to |attached_tabstrip_| and has had the mirroring
- // transformation applied.
- // NOTE: this is invoked from |Attach| before the tabs have been inserted.
- int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
-
- // Retrieve the bounds of the DraggedTabView relative to the attached
- // TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
- // system.
- gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
-
- // Get the position of the dragged tab view relative to the attached tab
- // strip with the mirroring transform applied.
- gfx::Point GetAttachedDragPoint(const gfx::Point& screen_point);
-
- // Finds the Tabs within the specified TabStrip that corresponds to the
- // WebContents of the dragged tabs. Returns an empty vector if not attached.
- std::vector<BaseTab*> GetTabsMatchingDraggedContents(TabStrip* tabstrip);
-
- // Does the work for EndDrag. If we actually started a drag and |how_end| is
- // not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked.
- void EndDragImpl(EndDragType how_end);
-
- // Reverts a cancelled drag operation.
- void RevertDrag();
-
- // Reverts the tab at |drag_index| in |drag_data_|.
- void RevertDragAt(size_t drag_index);
-
- // Selects the dragged tabs in |model|. Does nothing if there are no longer
- // any dragged contents (as happens when a WebContents is deleted out from
- // under us).
- void ResetSelection(TabStripModel* model);
-
- // Finishes a succesful drag operation.
- void CompleteDrag();
-
- // Utility for getting the mouse position in screen coordinates.
- gfx::Point GetCursorScreenPoint() const;
-
- // Returns the bounds (in screen coordinates) of the specified View.
- gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
-
- void DockDisplayerDestroyed(DockDisplayer* controller);
-
- void BringWindowUnderMouseToFront();
-
- // Convenience for getting the TabDragData corresponding to the tab the user
- // started dragging.
- TabDragData* source_tab_drag_data() {
- return &(drag_data_[source_tab_index_]);
- }
-
- // Convenience for |source_tab_drag_data()->contents|.
- TabContentsWrapper* source_dragged_contents() {
- return source_tab_drag_data()->contents;
- }
-
- // Returns true if the tabs were originality one after the other in
- // |source_tabstrip_|.
- bool AreTabsConsecutive();
-
- // Returns the TabStripModel for the specified tabstrip.
- static TabStripModel* GetModel(TabStrip* tabstrip);
-
- // Returns the Widget of the currently attached TabStrip's BrowserView.
- views::Widget* GetAttachedBrowserWidget();
-
- // Creates and returns a new Browser to handle the drag.
- Browser* CreateBrowserForDrag(TabStrip* source,
- const gfx::Point& screen_point,
- std::vector<gfx::Rect>* drag_bounds);
-
- // Marks whether |window| is constrained. While dragging we set this to false
- // to allow maximized windows to move any where.
- void SetTrackedByWorkspace(gfx::NativeWindow window, bool value);
-
- // Handles registering for notifications.
- content::NotificationRegistrar registrar_;
-
- // The TabStrip the drag originated from. This is set to NULL if the source
- // tabstrip is deleted while we're detached.
- TabStrip* source_tabstrip_;
-
- // The TabStrip the dragged Tab is currently attached to, or NULL if the
- // dragged Tab is detached.
- TabStrip* attached_tabstrip_;
-
- // Set to true if we've detached from a tabstrip and are running a nested
- // move message loop.
- bool is_dragging_window_;
-
- // The position of the mouse (in screen coordinates) at the start of the drag
- // operation. This is used to calculate minimum elasticity before a
- // DraggedTabView is constructed.
- gfx::Point start_screen_point_;
-
- // This is the offset of the mouse from the top left of the Tab where
- // dragging begun. This is used to ensure that the dragged view is always
- // positioned at the correct location during the drag, and to ensure that the
- // detached window is created at the right location.
- gfx::Point mouse_offset_;
-
- // Offset of the mouse relative to the source tab.
- int source_tab_offset_;
-
- // Ratio of the x-coordinate of the |source_tab_offset_| to the width of the
- // tab. Not used for vertical tabs.
- float offset_to_width_ratio_;
-
- // A hint to use when positioning new windows created by detaching Tabs. This
- // is the distance of the mouse from the top left of the dragged tab as if it
- // were the distance of the mouse from the top left of the first tab in the
- // attached TabStrip from the top left of the window.
- // TODO(sky): clean this up, can likely be removed.
- gfx::Point window_create_point_;
-
- // Location of the first tab in the source tabstrip in screen coordinates.
- // This is used to calculate window_create_point_.
- gfx::Point first_source_tab_point_;
-
- // The bounds of the browser window before the last Tab was detached. When
- // the last Tab is detached, rather than destroying the frame (which would
- // abort the drag session), the frame is moved off-screen. If the drag is
- // aborted (e.g. by the user pressing Esc, or capture being lost), the Tab is
- // attached to the hidden frame and the frame moved back to these bounds.
- gfx::Rect restore_bounds_;
-
- // The last view that had focus in the window containing |source_tab_|. This
- // is saved so that focus can be restored properly when a drag begins and
- // ends within this same window.
- views::View* old_focused_view_;
-
- // The position along the major axis of the mouse cursor in screen coordinates
- // at the time of the last re-order event.
- int last_move_screen_loc_;
-
- DockInfo dock_info_;
-
- DockWindows dock_windows_;
-
- std::vector<DockDisplayer*> dock_controllers_;
-
- // Timer used to bring the window under the cursor to front. If the user
- // stops moving the mouse for a brief time over a browser window, it is
- // brought to front.
- base::OneShotTimer<TabDragController2> bring_to_front_timer_;
-
- // Did the mouse move enough that we started a drag?
- bool started_drag_;
-
- // Is the drag active?
- bool active_;
-
- DragData drag_data_;
-
- // Index of the source tab in drag_data_.
- size_t source_tab_index_;
-
- // True until |MoveAttached| is invoked once.
- bool initial_move_;
-
- // The selection model before the drag started. See comment above Init() for
- // details.
- TabStripSelectionModel initial_selection_model_;
-
- // The selection model of |attached_tabstrip_| before the tabs were attached.
- TabStripSelectionModel selection_model_before_attach_;
-
- EndRunLoopBehavior end_run_loop_behavior_;
-
- // If true, we're waiting for a move loop to complete.
- bool waiting_for_run_loop_to_exit_;
-
- // The TabStrip to attach to after the move loop completes.
- TabStrip* tab_strip_to_attach_to_after_exit_;
-
- // Non-null for the duration of RunMoveLoop.
- views::Widget* move_loop_widget_;
-
- // If non-null set to true from destructor.
- bool* destroyed_;
-
- DISALLOW_COPY_AND_ASSIGN(TabDragController2);
-};
-
-#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER2_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc
index 3e0390a..7984afc 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/ui/views/tabs/tab_drag_controller2.h"
+#include "chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h"
#include "base/bind.h"
#include "base/callback.h"
@@ -14,6 +14,7 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/default_tab_drag_controller.h"
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/common/chrome_switches.h"
@@ -30,9 +31,8 @@
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
-namespace {
+namespace test {
-// See comments above QuitWhenNotDragging.
class QuitDraggingObserver : public content::NotificationObserver {
public:
QuitDraggingObserver() {
@@ -98,59 +98,70 @@ void QuitWhenNotDragging() {
new QuitDraggingObserver(); // QuitDraggingObserver deletes itself.
}
+TabStrip* GetTabStripForBrowser(Browser* browser) {
+ BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
+ return static_cast<TabStrip*>(browser_view->tabstrip());
}
-class TabDragController2Test : public InProcessBrowserTest {
- public:
- TabDragController2Test() {}
+} // namespace test
- static TabStrip* GetTabStripForBrowser(Browser* browser) {
- BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
- return static_cast<TabStrip*>(browser_view->tabstrip());
- }
+using test::GetCenterInScreenCoordinates;
+using test::SetID;
+using test::ResetIDs;
+using test::IDString;
+using test::QuitWhenNotDragging;
+using test::GetTabStripForBrowser;
- void StopAnimating(TabStrip* tab_strip) {
- tab_strip->StopAnimating(true);
- }
+TabDragControllerTest::TabDragControllerTest() {
+}
- void AddTabAndResetBrowser(Browser* browser) {
- AddBlankTabAndShow(browser);
- StopAnimating(GetTabStripForBrowser(browser));
- ResetIDs(browser->tabstrip_model(), 0);
- }
+TabDragControllerTest::~TabDragControllerTest() {
+}
- // Creates another window and resizes |browser()| and the new browser to
- // be side by side.
- Browser* CreateAnotherWindowBrowserAndRelayout() {
- // Add another tab.
- AddTabAndResetBrowser(browser());
-
- // Create another browser.
- Browser* browser2 = CreateBrowser(browser()->profile());
- ResetIDs(browser2->tabstrip_model(), 100);
-
- // Resize the two windows so they're right next to each other.
- gfx::Rect work_area = gfx::Screen::GetMonitorNearestWindow(
- browser()->window()->GetNativeHandle()).work_area();
- gfx::Size half_size =
- gfx::Size(work_area.width() / 3 - 10, work_area.height() / 2 - 10);
- browser()->window()->SetBounds(gfx::Rect(work_area.origin(), half_size));
- browser2->window()->SetBounds(gfx::Rect(
- work_area.x() + half_size.width(), work_area.y(),
- half_size.width(), half_size.height()));
- return browser2;
- }
+void TabDragControllerTest::StopAnimating(TabStrip* tab_strip) {
+ tab_strip->StopAnimating(true);
+}
+
+void TabDragControllerTest::AddTabAndResetBrowser(Browser* browser) {
+ AddBlankTabAndShow(browser);
+ StopAnimating(GetTabStripForBrowser(browser));
+ ResetIDs(browser->tabstrip_model(), 0);
+}
+
+Browser* TabDragControllerTest::CreateAnotherWindowBrowserAndRelayout() {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+
+ // Create another browser.
+ Browser* browser2 = CreateBrowser(browser()->profile());
+ ResetIDs(browser2->tabstrip_model(), 100);
+
+ // Resize the two windows so they're right next to each other.
+ gfx::Rect work_area = gfx::Screen::GetMonitorNearestWindow(
+ browser()->window()->GetNativeHandle()).work_area();
+ gfx::Size half_size =
+ gfx::Size(work_area.width() / 3 - 10, work_area.height() / 2 - 10);
+ browser()->window()->SetBounds(gfx::Rect(work_area.origin(), half_size));
+ browser2->window()->SetBounds(gfx::Rect(
+ work_area.x() + half_size.width(), work_area.y(),
+ half_size.width(), half_size.height()));
+ return browser2;
+}
+
+class DetachToBrowserTabDragControllerTest : public TabDragControllerTest {
+ public:
+ DetachToBrowserTabDragControllerTest() {}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kTabBrowserDragging);
}
private:
- DISALLOW_COPY_AND_ASSIGN(TabDragController2Test);
+ DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
};
// Creates a browser with two tabs, drags the second to the first.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragInSameWindow) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest, DragInSameWindow) {
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -165,7 +176,7 @@ IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragInSameWindow) {
ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
ui_controls::LEFT, ui_controls::UP));
EXPECT_EQ("1 0", IDString(model));
- EXPECT_FALSE(TabDragController2::IsActive());
+ EXPECT_FALSE(TabDragController::IsActive());
EXPECT_FALSE(tab_strip->IsDragSessionActive());
}
@@ -189,7 +200,8 @@ void DragToSeparateWindowStep2(TabStrip* not_attached_tab_strip,
} // namespace
// Creates two browsers, drags from first into second.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragToSeparateWindow) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DragToSeparateWindow) {
TabStrip* tab_strip = GetTabStripForBrowser(browser());
// Create another browser.
@@ -227,7 +239,8 @@ IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragToSeparateWindow) {
}
// Drags from browser to separate window and releases mouse.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DetachToOwnWindow) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DetachToOwnWindow) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -263,7 +276,8 @@ IN_PROC_BROWSER_TEST_F(TabDragController2Test, DetachToOwnWindow) {
}
// Deletes a tab being dragged before the user moved enough to start a drag.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DeleteBeforeStartedDragging) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DeleteBeforeStartedDragging) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -289,7 +303,8 @@ IN_PROC_BROWSER_TEST_F(TabDragController2Test, DeleteBeforeStartedDragging) {
}
// Deletes a tab being dragged while still attached.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DeleteTabWhileAttached) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DeleteTabWhileAttached) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -327,7 +342,8 @@ void DeleteWhileDetachedStep2(TabContentsWrapper* tab) {
// Deletes a tab being dragged after dragging a tab so that a new window is
// created.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DeleteTabWhileDetached) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DeleteTabWhileDetached) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -367,7 +383,8 @@ void DeleteSourceDetachedStep2(TabContentsWrapper* tab) {
// Detaches a tab and while detached deletes a tab from the source so that the
// source window closes then presses escape to cancel the drag.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DeleteSourceDetached) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DeleteSourceDetached) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -406,7 +423,7 @@ void PressEscapeWhileDetachedStep2() {
// This is disabled until NativeViewHost::Detach really detaches.
// Detaches a tab and while detached presses escape to revert the drag.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test,
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
DISABLED_PressEscapeWhileDetached) {
// Add another tab.
AddTabAndResetBrowser(browser());
@@ -448,7 +465,7 @@ void DragAllStep2() {
} // namespace
// Selects multiple tabs and starts dragging the window.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragAll) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest, DragAll) {
// Add another tab.
AddTabAndResetBrowser(browser());
TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -500,7 +517,8 @@ void DragAllToSeparateWindowStep2(TabStrip* attached_tab_strip,
} // namespace
// Creates two browsers, selects all tabs in first and drags into second.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test, DragAllToSeparateWindow) {
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
+ DragAllToSeparateWindow) {
TabStrip* tab_strip = GetTabStripForBrowser(browser());
// Create another browser.
@@ -560,7 +578,7 @@ void DragAllToSeparateWindowAndCancelStep2(TabStrip* attached_tab_strip,
// Creates two browsers, selects all tabs in first, drags into second, then hits
// escape.
-IN_PROC_BROWSER_TEST_F(TabDragController2Test,
+IN_PROC_BROWSER_TEST_F(DetachToBrowserTabDragControllerTest,
DragAllToSeparateWindowAndCancel) {
TabStrip* tab_strip = GetTabStripForBrowser(browser());
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
new file mode 100644
index 0000000..401342e
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_INTERACTIVE_UITEST_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_INTERACTIVE_UITEST_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/test/base/in_process_browser_test.h"
+
+class Browser;
+class TabStrip;
+class TabStripModel;
+
+namespace content {
+class WebContents;
+}
+
+namespace gfx {
+class Point;
+}
+
+namespace views {
+class View;
+}
+
+// TabDragControllerTest is the basis for the two tests that exercise
+// TabDragController.
+class TabDragControllerTest : public InProcessBrowserTest {
+ public:
+ TabDragControllerTest();
+ virtual ~TabDragControllerTest();
+
+ // Cover for TabStrip::StopAnimating(true).
+ void StopAnimating(TabStrip* tab_strip);
+
+ // Adds a new blank tab to |browser|, stops animations and resets the ids of
+ // the tabs in |browser|.
+ void AddTabAndResetBrowser(Browser* browser);
+
+ // Creates a new Browser and resizes |browser()| and the new browser to be
+ // side by side.
+ Browser* CreateAnotherWindowBrowserAndRelayout();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TabDragControllerTest);
+};
+
+namespace test {
+
+// Returns the TabStrip for |browser|.
+TabStrip* GetTabStripForBrowser(Browser* browser);
+
+// Returns the center of |view| in screen coordinates.
+gfx::Point GetCenterInScreenCoordinates(const views::View* view);
+
+// Sets the id of |tab_contents| to |id|.
+void SetID(content::WebContents* tab_contents, int id);
+
+// Resets the ids of all the tabs in |model| starting at |start|. That is, the
+// id of the first tab is set to |start|, the second tab |start + 1| ...
+void ResetIDs(TabStripModel* model, int start);
+
+// Returns a string representation of the ids of the tabs in |model|. Each id
+// is separated by a space.
+std::string IDString(TabStripModel* model);
+
+}
+
+#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_INTERACTIVE_UITEST_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc
new file mode 100644
index 0000000..b5eded7
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc
@@ -0,0 +1,354 @@
+// Copyright (c) 2012 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/ui/views/tabs/tab_drag_controller_interactive_uitest.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/property_bag.h"
+#include "base/string_number_conversions.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/default_tab_drag_controller.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/ui_controls/ui_controls.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+using test::GetCenterInScreenCoordinates;
+using test::SetID;
+using test::ResetIDs;
+using test::IDString;
+using test::GetTabStripForBrowser;
+
+// The tests in this file exercise detaching the dragged tab into a standalone
+// window (not a Browser).
+
+// Creates a browser with two tabs, drags the second to the first.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DragInSameWindow) {
+ AddTabAndResetBrowser(browser());
+
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+ TabStripModel* model = browser()->tabstrip_model();
+
+ gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_1_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::UP));
+ EXPECT_EQ("1 0", IDString(model));
+ EXPECT_FALSE(TabDragController::IsActive());
+ EXPECT_FALSE(tab_strip->IsDragSessionActive());
+}
+
+// Creates two browsers, drags from first into second.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DragToSeparateWindow) {
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Create another browser.
+ Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
+ TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+
+ // Move to the first tab and drag it enough so that it detaches, but not
+ // enough that it attaches to browser2.
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x(),
+ tab_0_center.y() + tab_strip->height() + 20)));
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Drag into the second browser.
+ gfx::Point target_point(tab_strip2->width() -1, tab_strip2->height() / 2);
+ views::View::ConvertPointToScreen(tab_strip2, &target_point);
+ ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
+
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Release the mouse, ending the drag session.
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::UP));
+ ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+ EXPECT_EQ("100 0", IDString(browser2->tabstrip_model()));
+ EXPECT_EQ("1", IDString(browser()->tabstrip_model()));
+}
+
+// Drags from browser to separate window and releases mouse.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DetachToOwnWindow) {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Move to the first tab and drag it enough so that it detaches.
+ gfx::Point tab_0_center(
+ GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_controls::SendMouseMove(
+ tab_0_center.x(), tab_0_center.y() + tab_strip->height() + 20));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::UP));
+
+ // Should no longer be dragging.
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+
+ // There should now be another browser.
+ ASSERT_EQ(2u, BrowserList::size());
+ Browser* new_browser = BrowserList::GetLastActive();
+ ASSERT_NE(browser(), new_browser);
+ TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
+ ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+
+ EXPECT_EQ("0", IDString(new_browser->tabstrip_model()));
+ EXPECT_EQ("1", IDString(browser()->tabstrip_model()));
+}
+
+// Deletes a tab being dragged before the user moved enough to start a drag.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteBeforeStartedDragging) {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Click on the first tab, but don't move it.
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+
+ // Should be dragging.
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Delete the tab being dragged.
+ delete browser()->tabstrip_model()->GetTabContentsAt(0);
+
+ // Should have canceled dragging.
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+
+ EXPECT_EQ("1", IDString(browser()->tabstrip_model()));
+}
+
+// Deletes a tab being dragged while still attached.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteTabWhileAttached) {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Click on the first tab and move it enough so that it starts dragging but is
+ // still attached.
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x() + 20, tab_0_center.y())));
+
+ // Should be dragging.
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Delete the tab being dragged.
+ delete browser()->tabstrip_model()->GetTabContentsAt(0);
+
+ // Should have canceled dragging.
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+
+ EXPECT_EQ("1", IDString(browser()->tabstrip_model()));
+}
+
+// Deletes a tab being dragged after dragging a tab so that a new window is
+// created.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteTabWhileDetached) {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Move to the first tab and drag it enough so that it detaches.
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ TabContentsWrapper* to_delete =
+ browser()->tabstrip_model()->GetTabContentsAt(0);
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x(),
+ tab_0_center.y() + tab_strip->height() + 20)));
+ delete to_delete;
+
+ // Should not be dragging.
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+
+ EXPECT_EQ("1", IDString(browser()->tabstrip_model()));
+}
+
+namespace {
+
+void DeleteSourceDetachedStep2(TabContentsWrapper* tab) {
+ // This ends up closing the source window.
+ delete tab;
+ // Cancel the drag.
+ ui_controls::SendKeyPress(NULL, ui::VKEY_ESCAPE, false, false, false, false);
+}
+
+} // namespace
+
+// Detaches a tab and while detached deletes a tab from the source and releases
+// the mouse.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteSourceDetached) {
+ // Add another tab.
+ AddTabAndResetBrowser(browser());
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Move to the first tab and drag it enough so that it detaches.
+ gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ TabContentsWrapper* to_delete =
+ browser()->tabstrip_model()->GetTabContentsAt(1);
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x(),
+ tab_0_center.y() + tab_strip->height() + 20)));
+ delete to_delete;
+
+ // Should still be dragging.
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Release the mouse.
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::UP));
+
+ // Releasing the mouse should destroy the existing browser and create a new
+ // one.
+ ASSERT_EQ(1u, BrowserList::size());
+ Browser* new_browser = *BrowserList::begin();
+ EXPECT_NE(new_browser, browser());
+
+ ASSERT_FALSE(GetTabStripForBrowser(new_browser)->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+
+ EXPECT_EQ("0", IDString(new_browser->tabstrip_model()));
+}
+
+// Creates two browsers, selects all tabs in first and drags into second.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DragAllToSeparateWindow) {
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Create another browser.
+ Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
+ TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+
+ browser()->tabstrip_model()->AddTabAtToSelection(0);
+ browser()->tabstrip_model()->AddTabAtToSelection(1);
+
+ // Move to the first tab and drag it enough so that it detaches, but not
+ // enough that it attaches to browser2.
+ gfx::Point tab_0_center(
+ GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x(),
+ tab_0_center.y() + tab_strip->height() + 20)));
+
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+ ASSERT_EQ(2u, BrowserList::size());
+
+ // Drag to tab_strip2.
+ gfx::Point target_point(tab_strip2->width() - 1,
+ tab_strip2->height() / 2);
+ views::View::ConvertPointToScreen(tab_strip2, &target_point);
+ ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
+
+ // Should now be attached to tab_strip2.
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+
+ // Release the mouse, stopping the drag session.
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::UP));
+ ASSERT_FALSE(TabDragController::IsActive());
+ EXPECT_EQ("100 0 1", IDString(browser2->tabstrip_model()));
+}
+
+// Creates two browsers, selects all tabs in first, drags into second, then hits
+// escape.
+IN_PROC_BROWSER_TEST_F(TabDragControllerTest,
+ DragAllToSeparateWindowAndCancel) {
+ TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+ // Create another browser.
+ Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
+ TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+
+ browser()->tabstrip_model()->AddTabAtToSelection(0);
+ browser()->tabstrip_model()->AddTabAtToSelection(1);
+
+ // Move to the first tab and drag it enough so that it detaches, but not
+ // enough that it attaches to browser2.
+ gfx::Point tab_0_center(
+ GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
+ ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+ ui_controls::LEFT, ui_controls::DOWN));
+ ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
+ gfx::Point(tab_0_center.x(),
+ tab_0_center.y() + tab_strip->height() + 20)));
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+ ASSERT_EQ(2u, BrowserList::size());
+
+ // Drag to tab_strip2.
+ gfx::Point target_point(tab_strip2->width() - 1,
+ tab_strip2->height() / 2);
+ views::View::ConvertPointToScreen(tab_strip2, &target_point);
+ ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
+
+ ASSERT_TRUE(tab_strip->IsDragSessionActive());
+ ASSERT_TRUE(TabDragController::IsActive());
+ ASSERT_EQ(2u, BrowserList::size());
+
+ // Cancel the drag.
+ ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+ browser2, ui::VKEY_ESCAPE, false, false, false, false));
+
+ ASSERT_FALSE(tab_strip->IsDragSessionActive());
+ ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+ ASSERT_FALSE(TabDragController::IsActive());
+ ASSERT_EQ(2u, BrowserList::size());
+ EXPECT_EQ("0 1", IDString(browser()->tabstrip_model()));
+ EXPECT_EQ("100", IDString(browser2->tabstrip_model()));
+}
diff --git a/chrome/browser/ui/views/tabs/tab_dragging_test.cc b/chrome/browser/ui/views/tabs/tab_dragging_test.cc
deleted file mode 100644
index 900d2a2..0000000
--- a/chrome/browser/ui/views/tabs/tab_dragging_test.cc
+++ /dev/null
@@ -1,514 +0,0 @@
-// Copyright (c) 2011 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 "base/command_line.h"
-#include "base/file_util.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/ui/view_ids.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/automation/automation_proxy.h"
-#include "chrome/test/automation/browser_proxy.h"
-#include "chrome/test/automation/tab_proxy.h"
-#include "chrome/test/automation/window_proxy.h"
-#include "chrome/test/ui/ui_test.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/net_util.h"
-#include "ui/gfx/rect.h"
-#include "ui/views/events/event.h"
-
-#if defined(OS_LINUX)
-// This test doesn't make sense on chromeos as chromeos doesn't allow dragging
-// tabs out.
-#define MAYBE_Tab2OutOfTabStrip DISABLED_Tab2OutOfTabStrip
-#else
-// Flaky, http://crbug.com/62311.
-#define MAYBE_Tab2OutOfTabStrip DISABLED_Tab2OutOfTabStrip
-#endif
-
-#if defined(OS_LINUX)
-// Disabled on Toolkit views bot. See http://crbug.com/42614
-#define MAYBE_Tab1Tab3Escape DISABLED_Tab1Tab3Escape
-#elif defined(OS_WIN)
-// Disabled on Windows. See http://crbug.com/57687
-#define MAYBE_Tab1Tab3Escape DISABLED_Tab1Tab3Escape
-#else
-#define MAYBE_Tab1Tab3Escape Tab1Tab3Escape
-#endif
-
-// These tests fail on Linux because we haven't implemented all of tab dragging
-// (it's not needed on chromeos). See http://crbug.com/10941
-#if defined(OS_LINUX)
-#define MAYBE_Tab1Tab2 DISABLED_Tab1Tab2
-#define MAYBE_Tab1Tab3 DISABLED_Tab1Tab3
-#else
-// Flaky, http://crbug.com/62311.
-#define MAYBE_Tab1Tab2 DISABLED_Tab1Tab2
-#define MAYBE_Tab1Tab3 DISABLED_Tab1Tab3
-#endif
-
-class TabDraggingTest : public UITest {
- protected:
- TabDraggingTest() {
- show_window_ = true;
- }
-};
-
-// Automated UI test to open three tabs in a new window, and drag Tab_1 into
-// the position of Tab_2.
-TEST_F(TabDraggingTest, MAYBE_Tab1Tab2) {
- scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
- ASSERT_TRUE(browser.get());
- scoped_refptr<WindowProxy> window(browser->GetWindow());
- ASSERT_TRUE(window.get());
-
- // Get initial tab count.
- int initial_tab_count = 0;
- ASSERT_TRUE(browser->GetTabCount(&initial_tab_count));
- ASSERT_TRUE(1 == initial_tab_count);
-
- // Get Tab_1 which comes with the browser window.
- scoped_refptr<TabProxy> tab1(browser->GetTab(0));
- ASSERT_TRUE(tab1.get());
- GURL tab1_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url));
-
- // Add Tab_2.
- GURL tab2_url("about:");
- ASSERT_TRUE(browser->AppendTab(tab2_url));
- scoped_refptr<TabProxy> tab2(browser->GetTab(1));
- ASSERT_TRUE(tab2.get());
-
- // Add Tab_3.
- GURL tab3_url("about:plugins");
- ASSERT_TRUE(browser->AppendTab(tab3_url));
- scoped_refptr<TabProxy> tab3(browser->GetTab(2));
- ASSERT_TRUE(tab3.get());
-
- // Make sure 3 tabs are open.
- ASSERT_TRUE(browser->WaitForTabCountToBecome(initial_tab_count + 2));
-
- // Get bounds for the tabs.
- gfx::Rect bounds1;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds1, false));
- EXPECT_LT(0, bounds1.x());
- EXPECT_LT(0, bounds1.width());
- EXPECT_LT(0, bounds1.height());
-
- gfx::Rect bounds2;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_1, &bounds2, false));
- EXPECT_LT(0, bounds2.width());
- EXPECT_LT(0, bounds2.height());
- EXPECT_LT(bounds1.x(), bounds2.x());
- EXPECT_EQ(bounds2.y(), bounds1.y());
-
- gfx::Rect bounds3;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_2, &bounds3, false));
- EXPECT_LT(0, bounds3.width());
- EXPECT_LT(0, bounds3.height());
- EXPECT_LT(bounds2.x(), bounds3.x());
- EXPECT_EQ(bounds3.y(), bounds2.y());
-
- // Get url Bar bounds.
- gfx::Rect urlbar_bounds;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds,
- false));
- EXPECT_LT(0, urlbar_bounds.x());
- EXPECT_LT(0, urlbar_bounds.y());
- EXPECT_LT(0, urlbar_bounds.width());
- EXPECT_LT(0, urlbar_bounds.height());
-
- /*
- TEST: Move Tab_1 to the position of Tab_2
- ____________ ____________ ____________
- / \ / \ / \
- | Tab_1 | Tab_2 | Tab_3 |
- ---- ---- ---- ---- ---- ---- ---- ---- ----
- x---- ---->
- ____________
- / X \
- | Tab_1 |
- ---- ---- ----
- */
-
- gfx::Point start(bounds1.x() + bounds1.width() / 2,
- bounds1.y() + bounds1.height() / 2);
- gfx::Point end(start.x() + 2 * bounds1.width() / 3, start.y());
- ASSERT_TRUE(browser->SimulateDrag(start, end,
- ui::EF_LEFT_MOUSE_BUTTON,
- false));
-
- // Now check for expected results.
- tab1 = browser->GetTab(0);
- ASSERT_TRUE(tab1.get());
- GURL tab1_new_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url));
-
- tab2 = browser->GetTab(1);
- ASSERT_TRUE(tab2.get());
- GURL tab2_new_url;
- ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url));
-
- EXPECT_EQ(tab1_url.spec(), tab2_new_url.spec());
- EXPECT_EQ(tab2_url.spec(), tab1_new_url.spec());
-}
-
-// Drag Tab_1 into the position of Tab_3.
-TEST_F(TabDraggingTest, MAYBE_Tab1Tab3) {
- scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
- ASSERT_TRUE(browser.get());
- scoped_refptr<WindowProxy> window(browser->GetWindow());
- ASSERT_TRUE(window.get());
-
- // Get initial tab count.
- int initial_tab_count = 0;
- ASSERT_TRUE(browser->GetTabCount(&initial_tab_count));
- ASSERT_TRUE(1 == initial_tab_count);
-
- // Get Tab_1 which comes with the browser window.
- scoped_refptr<TabProxy> tab1(browser->GetTab(0));
- ASSERT_TRUE(tab1.get());
- GURL tab1_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url));
-
- // Add Tab_2.
- GURL tab2_url("about:");
- ASSERT_TRUE(browser->AppendTab(tab2_url));
- scoped_refptr<TabProxy> tab2(browser->GetTab(1));
- ASSERT_TRUE(tab2.get());
-
- // Add Tab_3.
- GURL tab3_url("about:plugins");
- ASSERT_TRUE(browser->AppendTab(tab3_url));
- scoped_refptr<TabProxy> tab3(browser->GetTab(2));
- ASSERT_TRUE(tab3.get());
-
- // Make sure 3 tabs are open.
- ASSERT_TRUE(browser->WaitForTabCountToBecome(initial_tab_count + 2));
-
- // Get bounds for the tabs.
- gfx::Rect bounds1;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds1, false));
- EXPECT_LT(0, bounds1.x());
- EXPECT_LT(0, bounds1.width());
- EXPECT_LT(0, bounds1.height());
-
- gfx::Rect bounds2;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_1, &bounds2, false));
- EXPECT_LT(0, bounds2.width());
- EXPECT_LT(0, bounds2.height());
- EXPECT_LT(bounds1.x(), bounds2.x());
- EXPECT_EQ(bounds2.y(), bounds1.y());
-
- gfx::Rect bounds3;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_2, &bounds3, false));
- EXPECT_LT(0, bounds3.width());
- EXPECT_LT(0, bounds3.height());
- EXPECT_LT(bounds2.x(), bounds3.x());
- EXPECT_EQ(bounds3.y(), bounds2.y());
-
- // Get url Bar bounds.
- gfx::Rect urlbar_bounds;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds,
- false));
- EXPECT_LT(0, urlbar_bounds.x());
- EXPECT_LT(0, urlbar_bounds.y());
- EXPECT_LT(0, urlbar_bounds.width());
- EXPECT_LT(0, urlbar_bounds.height());
-
- /*
- TEST: Move Tab_1 to the middle position of Tab_3
- ____________ ____________ ____________
- / \ / \ / \
- | Tab_1 | Tab_2 | Tab_3 |
- ---- ---- ---- ---- ---- ---- ---- ---- ----
- x---- ---- ---- ---- ---- ---->
- ____________
- / X \
- | Tab_1 |
- ---- ---- ----
- */
-
- gfx::Point start(bounds1.x() + bounds1.width() / 2,
- bounds1.y() + bounds1.height() / 2);
- gfx::Point end(start.x() + bounds1.width() / 2 + bounds2.width() +
- bounds3.width() / 2,
- start.y());
- ASSERT_TRUE(browser->SimulateDrag(start, end,
- ui::EF_LEFT_MOUSE_BUTTON,
- false));
-
- // Now check for expected results.
- tab1 = browser->GetTab(0);
- ASSERT_TRUE(tab1.get());
- GURL tab1_new_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url));
-
- tab2 = browser->GetTab(1);
- ASSERT_TRUE(tab2.get());
- GURL tab2_new_url;
- ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url));
-
- tab3 = browser->GetTab(2);
- ASSERT_TRUE(tab3.get());
- GURL tab3_new_url;
- ASSERT_TRUE(tab3->GetCurrentURL(&tab3_new_url));
-
- EXPECT_EQ(tab1_new_url.spec(), tab2_url.spec());
- EXPECT_EQ(tab2_new_url.spec(), tab3_url.spec());
- EXPECT_EQ(tab3_new_url.spec(), tab1_url.spec());
-}
-
-// Drag Tab_1 into the position of Tab_3, and press ESCAPE before releasing the
-// left mouse button.
-TEST_F(TabDraggingTest, MAYBE_Tab1Tab3Escape) {
- scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
- ASSERT_TRUE(browser.get());
- scoped_refptr<WindowProxy> window(browser->GetWindow());
- ASSERT_TRUE(window.get());
-
- // Get initial tab count.
- int initial_tab_count = 0;
- ASSERT_TRUE(browser->GetTabCount(&initial_tab_count));
- ASSERT_TRUE(1 == initial_tab_count);
-
- // Get Tab_1 which comes with the browser window.
- scoped_refptr<TabProxy> tab1(browser->GetTab(0));
- ASSERT_TRUE(tab1.get());
- GURL tab1_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url));
-
- // Add Tab_2.
- GURL tab2_url("about:blank");
- ASSERT_TRUE(browser->AppendTab(tab2_url));
- scoped_refptr<TabProxy> tab2(browser->GetTab(1));
- ASSERT_TRUE(tab2.get());
-
- // Add Tab_3.
- GURL tab3_url("about:plugins");
- ASSERT_TRUE(browser->AppendTab(tab3_url));
- scoped_refptr<TabProxy> tab3(browser->GetTab(2));
- ASSERT_TRUE(tab3.get());
-
- // Make sure 3 tabs are open.
- ASSERT_TRUE(browser->WaitForTabCountToBecome(initial_tab_count + 2));
-
- // Get bounds for the tabs.
- gfx::Rect bounds1;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds1, false));
- EXPECT_LT(0, bounds1.x());
- EXPECT_LT(0, bounds1.width());
- EXPECT_LT(0, bounds1.height());
-
- gfx::Rect bounds2;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_1, &bounds2, false));
- EXPECT_LT(0, bounds2.width());
- EXPECT_LT(0, bounds2.height());
- EXPECT_LT(bounds1.x(), bounds2.x());
- EXPECT_EQ(bounds2.y(), bounds1.y());
-
- gfx::Rect bounds3;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_2, &bounds3, false));
- EXPECT_LT(0, bounds3.width());
- EXPECT_LT(0, bounds3.height());
- EXPECT_LT(bounds2.x(), bounds3.x());
- EXPECT_EQ(bounds3.y(), bounds2.y());
-
- // Get url Bar bounds.
- gfx::Rect urlbar_bounds;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds,
- false));
- EXPECT_LT(0, urlbar_bounds.x());
- EXPECT_LT(0, urlbar_bounds.y());
- EXPECT_LT(0, urlbar_bounds.width());
- EXPECT_LT(0, urlbar_bounds.height());
-
- /*
- TEST: Move Tab_1 to the middle position of Tab_3
- ____________ ____________ ____________
- / \ / \ / \
- | Tab_1 | Tab_2 | Tab_3 |
- ---- ---- ---- ---- ---- ---- ---- ---- ----
- x---- ---- ---- ---- ---- ----> + ESCAPE
- ____________
- / X \
- | Tab_1 |
- ---- ---- ----
- */
-
- gfx::Point start(bounds1.x() + bounds1.width() / 2,
- bounds1.y() + bounds1.height() / 2);
- gfx::Point end(start.x() + bounds1.width() / 2 + bounds2.width() +
- bounds3.width() / 2,
- start.y());
-
- // Simulate drag with 'true' as the last parameter. This will interrupt
- // in-flight with Escape.
- ASSERT_TRUE(browser->SimulateDrag(start, end,
- ui::EF_LEFT_MOUSE_BUTTON,
- true));
-
- // Now check for expected results.
- tab1 = browser->GetTab(0);
- ASSERT_TRUE(tab1.get());
- GURL tab1_new_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url));
-
- tab2 = browser->GetTab(1);
- ASSERT_TRUE(tab2.get());
- GURL tab2_new_url;
- ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url));
-
- tab3 = browser->GetTab(2);
- ASSERT_TRUE(tab3.get());
- GURL tab3_new_url;
- ASSERT_TRUE(tab3->GetCurrentURL(&tab3_new_url));
-
- // The tabs should be in their original positions.
- EXPECT_EQ(tab1_new_url.spec(), tab1_url.spec());
- EXPECT_EQ(tab2_new_url.spec(), tab2_url.spec());
- EXPECT_EQ(tab3_new_url.spec(), tab3_url.spec());
-}
-
-// Drag Tab_2 out of the Tab strip. A new window should open with this tab.
-TEST_F(TabDraggingTest, MAYBE_Tab2OutOfTabStrip) {
- scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
- ASSERT_TRUE(browser.get());
- scoped_refptr<WindowProxy> window(browser->GetWindow());
- ASSERT_TRUE(window.get());
-
- // Get initial tab count.
- int initial_tab_count = 0;
- ASSERT_TRUE(browser->GetTabCount(&initial_tab_count));
- ASSERT_TRUE(1 == initial_tab_count);
-
- // Get Tab_1 which comes with the browser window.
- scoped_refptr<TabProxy> tab1(browser->GetTab(0));
- ASSERT_TRUE(tab1.get());
- GURL tab1_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url));
-
- // Add Tab_2.
- GURL tab2_url("about:version");
- ASSERT_TRUE(browser->AppendTab(tab2_url));
- scoped_refptr<TabProxy> tab2(browser->GetTab(1));
- ASSERT_TRUE(tab2.get());
-
- // Add Tab_3.
- GURL tab3_url("about:plugins");
- ASSERT_TRUE(browser->AppendTab(tab3_url));
- scoped_refptr<TabProxy> tab3(browser->GetTab(2));
- ASSERT_TRUE(tab3.get());
-
- // Make sure 3 tabs are opened.
- ASSERT_TRUE(browser->WaitForTabCountToBecome(initial_tab_count + 2));
-
- // Make sure all the tab URL specs are different.
- ASSERT_TRUE(tab1_url != tab2_url);
- ASSERT_TRUE(tab1_url != tab3_url);
- ASSERT_TRUE(tab2_url != tab3_url);
-
- // Get bounds for the tabs.
- gfx::Rect bounds1;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds1, false));
- EXPECT_LT(0, bounds1.x());
- EXPECT_LT(0, bounds1.width());
- EXPECT_LT(0, bounds1.height());
-
- gfx::Rect bounds2;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_1, &bounds2, false));
- EXPECT_LT(0, bounds2.width());
- EXPECT_LT(0, bounds2.height());
- EXPECT_LT(bounds1.x(), bounds2.x());
- EXPECT_EQ(bounds2.y(), bounds1.y());
-
- gfx::Rect bounds3;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_2, &bounds3, false));
- EXPECT_LT(0, bounds3.width());
- EXPECT_LT(0, bounds3.height());
- EXPECT_LT(bounds2.x(), bounds3.x());
- EXPECT_EQ(bounds3.y(), bounds2.y());
-
- // Get url Bar bounds.
- gfx::Rect urlbar_bounds;
- ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds,
- false));
- EXPECT_LT(0, urlbar_bounds.x());
- EXPECT_LT(0, urlbar_bounds.y());
- EXPECT_LT(0, urlbar_bounds.width());
- EXPECT_LT(0, urlbar_bounds.height());
-
- /*
- TEST: Move Tab_2 down, out of the tab strip.
- This should result in the following:
- 1- Tab_3 shift left in place of Tab_2 in Window 1
- 2- Tab_1 to remain in its place
- 3- Tab_2 openes in a new window
-
- ____________ ____________ ____________
- / \ / \ / \
- | Tab_1 | Tab_2 | Tab_3 |
- ---- ---- ---- ---- ---- ---- ---- ---- ----
- x
- |
- | (Drag this below, out of tab strip)
- V
- ____________
- / X \
- | Tab_2 | (New Window)
- ---- ---- ---- ---- ---- ---- ----
- */
-
- gfx::Point start(bounds2.x() + bounds2.width() / 2,
- bounds2.y() + bounds2.height() / 2);
- gfx::Point end(start.x(),
- start.y() + 3 * urlbar_bounds.height());
-
- // Simulate tab drag.
- ASSERT_TRUE(browser->SimulateDrag(start, end,
- ui::EF_LEFT_MOUSE_BUTTON,
- false));
-
- // Now, first make sure that the old window has only two tabs remaining.
- int new_tab_count = 0;
- ASSERT_TRUE(browser->GetTabCount(&new_tab_count));
- ASSERT_EQ(2, new_tab_count);
-
- // Get the two tabs - they are called Tab_1 and Tab_2 in the old window.
- tab1 = browser->GetTab(0);
- ASSERT_TRUE(tab1.get());
- GURL tab1_new_url;
- ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url));
-
- tab2 = browser->GetTab(1);
- ASSERT_TRUE(tab2.get());
- GURL tab2_new_url;
- ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url));
-
- // Now check for proper shifting of tabs; i.e., Tab_3 in window 1 should
- // shift left to the position of Tab_2; Tab_1 should stay where it was.
- EXPECT_EQ(tab1_new_url.spec(), tab1_url.spec());
- EXPECT_EQ(tab2_new_url.spec(), tab3_url.spec());
-
- // Now check to make sure a new window has opened.
- scoped_refptr<BrowserProxy> browser2(automation()->GetBrowserWindow(1));
- ASSERT_TRUE(browser2.get());
- scoped_refptr<WindowProxy> window2(browser2->GetWindow());
- ASSERT_TRUE(window2.get());
-
- // Make sure that the new window has only one tab.
- int tab_count_window_2 = 0;
- ASSERT_TRUE(browser2->GetTabCount(&tab_count_window_2));
- ASSERT_EQ(1, tab_count_window_2);
-
- // Get Tab_1_2 which should be Tab_1 in Window 2.
- scoped_refptr<TabProxy> tab1_2(browser2->GetTab(0));
- ASSERT_TRUE(tab1_2.get());
- GURL tab1_2_url;
- ASSERT_TRUE(tab1_2->GetCurrentURL(&tab1_2_url));
-
- // Tab_1_2 of Window 2 should essentially be Tab_2 of Window 1.
- EXPECT_EQ(tab1_2_url.spec(), tab2_url.spec());
- EXPECT_NE(tab1_2_url.spec(), tab1_url.spec());
- EXPECT_NE(tab1_2_url.spec(), tab3_url.spec());
-}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index b790eab..ebad0be 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -20,8 +20,8 @@
#include "chrome/browser/tabs/tab_strip_selection_model.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/tabs/default_tab_drag_controller.h"
#include "chrome/browser/ui/views/tabs/tab.h"
-#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
#include "chrome/browser/ui/views/tabs/touch_tab_strip_layout.h"
#include "chrome/common/chrome_switches.h"
@@ -888,9 +888,10 @@ void TabStrip::MaybeStartDrag(
(event.type() == ui::ET_TOUCH_PRESSED ||
(event.type() == ui::ET_MOUSE_PRESSED &&
event.flags() & ui::EF_CONTROL_DOWN));
- drag_controller_.reset(TabDragController::Create(
+ drag_controller_.reset(new TabDragController);
+ drag_controller_->Init(
this, tab, tabs, gfx::Point(x, y), tab->GetMirroredXInView(event.x()),
- selection_model, move_only));
+ selection_model, move_only);
}
void TabStrip::ContinueDrag(const views::MouseEvent& event) {
@@ -901,7 +902,7 @@ void TabStrip::ContinueDrag(const views::MouseEvent& event) {
bool TabStrip::EndDrag(bool canceled) {
if (!drag_controller_.get())
return false;
- bool started_drag = drag_controller_->GetStartedDrag();
+ bool started_drag = drag_controller_->started_drag();
drag_controller_->EndDrag(canceled);
return started_drag;
}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 76e9a38..6ebe33f 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -222,9 +222,8 @@ class TabStrip : public views::View,
class RemoveTabDelegate;
- friend class DefaultTabDragController;
- friend class TabDragController2;
- friend class TabDragController2Test;
+ friend class TabDragController;
+ friend class TabDragControllerTest;
// Used during a drop session of a url. Tracks the position of the drop as
// well as a window used to highlight where the drop occurs.
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 6ac2f68..ec9731e 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3535,9 +3535,6 @@
'browser/ui/views/tabs/tab.cc',
'browser/ui/views/tabs/tab.h',
'browser/ui/views/tabs/tab_controller.h',
- 'browser/ui/views/tabs/tab_drag_controller.h',
- 'browser/ui/views/tabs/tab_drag_controller2.h',
- 'browser/ui/views/tabs/tab_drag_controller2.cc',
'browser/ui/views/tabs/tab_renderer_data.cc',
'browser/ui/views/tabs/tab_renderer_data.h',
'browser/ui/views/tabs/tab_strip.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 0be1ab5..e58af95 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -517,8 +517,9 @@
'browser/ui/views/menu_item_view_test.cc',
'browser/ui/views/menu_model_adapter_test.cc',
'browser/ui/views/ssl_client_certificate_selector_browsertest.cc',
- 'browser/ui/views/tabs/tab_dragging_test.cc',
+ 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h',
'browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc',
+ 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc',
'browser/ui/webui/inspect_ui_browsertest.cc',
'test/base/chrome_test_launcher.cc',
'test/base/view_event_test_base.cc',
@@ -551,7 +552,6 @@
'browser/ui/views/find_bar_host_interactive_uitest.cc',
'browser/ui/views/menu_item_view_test.cc',
'browser/ui/views/menu_model_adapter_test.cc',
- 'browser/ui/views/tabs/tab_dragging_test.cc',
'test/base/view_event_test_base.cc',
'test/base/view_event_test_base.h',
],
@@ -570,7 +570,6 @@
'browser/ui/views/find_bar_host_interactive_uitest.cc',
'browser/ui/views/menu_item_view_test.cc',
'browser/ui/views/menu_model_adapter_test.cc',
- 'browser/ui/views/tabs/tab_dragging_test.cc',
'browser/ui/views/tabs/tab_drag_controller2_interactive_uitest.cc',
'test/base/view_event_test_base.cc',
'test/base/view_event_test_base.h',