Home | History | Annotate | Download | only in tabs
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
      6 #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
      7 
      8 #include <vector>
      9 
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/timer/timer.h"
     13 #include "chrome/browser/ui/tabs/dock_info.h"
     14 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
     15 #include "chrome/browser/ui/views/tabs/tab_strip_types.h"
     16 #include "content/public/browser/notification_observer.h"
     17 #include "content/public/browser/notification_registrar.h"
     18 #include "content/public/browser/web_contents_delegate.h"
     19 #include "ui/base/models/list_selection_model.h"
     20 #include "ui/gfx/rect.h"
     21 #include "ui/views/widget/widget_observer.h"
     22 
     23 namespace gfx {
     24 class Screen;
     25 }
     26 namespace ui {
     27 class ListSelectionModel;
     28 }
     29 namespace views {
     30 class View;
     31 }
     32 class Browser;
     33 class DraggedTabView;
     34 class Tab;
     35 struct TabRendererData;
     36 class TabStrip;
     37 class TabStripModel;
     38 
     39 // TabDragController is responsible for managing the tab dragging session. When
     40 // the user presses the mouse on a tab a new TabDragController is created and
     41 // Drag() is invoked as the mouse is dragged. If the mouse is dragged far enough
     42 // TabDragController starts a drag session. The drag session is completed when
     43 // EndDrag() is invoked (or the TabDragController is destroyed).
     44 //
     45 // While dragging within a tab strip TabDragController sets the bounds of the
     46 // tabs (this is referred to as attached). When the user drags far enough such
     47 // that the tabs should be moved out of the tab strip two possible things
     48 // can happen (this state is referred to as detached):
     49 // . If |detach_into_browser_| is true then a new Browser is created and
     50 //   RunMoveLoop() is invoked on the Widget to drag the browser around. This is
     51 //   the default on chromeos and can be enabled on windows with a flag.
     52 // . If |detach_into_browser_| is false a small representation of the active tab
     53 //   is created and that is dragged around. This mode does not run a nested
     54 //   message loop.
     55 class TabDragController : public content::WebContentsDelegate,
     56                           public content::NotificationObserver,
     57                           public base::MessageLoopForUI::Observer,
     58                           public views::WidgetObserver,
     59                           public TabStripModelObserver {
     60  public:
     61   enum DetachBehavior {
     62     DETACHABLE,
     63     NOT_DETACHABLE
     64   };
     65 
     66   // What should happen as the mouse is dragged within the tabstrip.
     67   enum MoveBehavior {
     68     // Only the set of visible tabs should change. This is only applicable when
     69     // using touch layout.
     70     MOVE_VISIBILE_TABS,
     71 
     72     // Typical behavior where tabs are dragged around.
     73     REORDER
     74   };
     75 
     76   // Indicates the event source that initiated the drag.
     77   enum EventSource {
     78     EVENT_SOURCE_MOUSE,
     79     EVENT_SOURCE_TOUCH,
     80   };
     81 
     82   // Amount above or below the tabstrip the user has to drag before detaching.
     83   static const int kTouchVerticalDetachMagnetism;
     84   static const int kVerticalDetachMagnetism;
     85 
     86   TabDragController();
     87   virtual ~TabDragController();
     88 
     89   // Initializes TabDragController to drag the tabs in |tabs| originating
     90   // from |source_tabstrip|. |source_tab| is the tab that initiated the drag and
     91   // is contained in |tabs|.  |mouse_offset| is the distance of the mouse
     92   // pointer from the origin of the first tab in |tabs| and |source_tab_offset|
     93   // the offset from |source_tab|. |source_tab_offset| is the horizontal distant
     94   // for a horizontal tab strip, and the vertical distance for a vertical tab
     95   // strip. |initial_selection_model| is the selection model before the drag
     96   // started and is only non-empty if |source_tab| was not initially selected.
     97   void Init(TabStrip* source_tabstrip,
     98             Tab* source_tab,
     99             const std::vector<Tab*>& tabs,
    100             const gfx::Point& mouse_offset,
    101             int source_tab_offset,
    102             const ui::ListSelectionModel& initial_selection_model,
    103             DetachBehavior detach_behavior,
    104             MoveBehavior move_behavior,
    105             EventSource event_source);
    106 
    107   // Returns true if there is a drag underway and the drag is attached to
    108   // |tab_strip|.
    109   // NOTE: this returns false if the TabDragController is in the process of
    110   // finishing the drag.
    111   static bool IsAttachedTo(TabStrip* tab_strip);
    112 
    113   // Returns true if there is a drag underway.
    114   static bool IsActive();
    115 
    116   // Used to determine whether the tab drag controller detaches dragged tabs
    117   // into new browser windows while the drag is in process.
    118   static bool ShouldDetachIntoNewBrowser();
    119 
    120   // Sets the move behavior. Has no effect if started_drag() is true.
    121   void SetMoveBehavior(MoveBehavior behavior);
    122   MoveBehavior move_behavior() const { return move_behavior_; }
    123 
    124   EventSource event_source() const { return event_source_; }
    125 
    126   // See description above fields for details on these.
    127   bool active() const { return active_; }
    128   const TabStrip* attached_tabstrip() const { return attached_tabstrip_; }
    129 
    130   // Returns true if a drag started.
    131   bool started_drag() const { return started_drag_; }
    132 
    133   // Returns true if mutating the TabStripModel.
    134   bool is_mutating() const { return is_mutating_; }
    135 
    136   // Returns true if we've detached from a tabstrip and are running a nested
    137   // move message loop.
    138   bool is_dragging_window() const { return is_dragging_window_; }
    139 
    140   // Invoked to drag to the new location, in screen coordinates.
    141   void Drag(const gfx::Point& point_in_screen);
    142 
    143   // Complete the current drag session.
    144   void EndDrag(EndDragReason reason);
    145 
    146  private:
    147   class DockDisplayer;
    148   friend class DockDisplayer;
    149 
    150   typedef std::set<gfx::NativeView> DockWindows;
    151 
    152   // Used to indicate the direction the mouse has moved when attached.
    153   static const int kMovedMouseLeft  = 1 << 0;
    154   static const int kMovedMouseRight = 1 << 1;
    155 
    156   // Enumeration of the ways a drag session can end.
    157   enum EndDragType {
    158     // Drag session exited normally: the user released the mouse.
    159     NORMAL,
    160 
    161     // The drag session was canceled (alt-tab during drag, escape ...)
    162     CANCELED,
    163 
    164     // The tab (NavigationController) was destroyed during the drag.
    165     TAB_DESTROYED
    166   };
    167 
    168   // Whether Detach() should release capture or not.
    169   enum ReleaseCapture {
    170     RELEASE_CAPTURE,
    171     DONT_RELEASE_CAPTURE,
    172   };
    173 
    174   // Specifies what should happen when RunMoveLoop completes.
    175   enum EndRunLoopBehavior {
    176     // Indicates the drag should end.
    177     END_RUN_LOOP_STOP_DRAGGING,
    178 
    179     // Indicates the drag should continue.
    180     END_RUN_LOOP_CONTINUE_DRAGGING
    181   };
    182 
    183   // Enumeration of the possible positions the detached tab may detach from.
    184   enum DetachPosition {
    185     DETACH_BEFORE,
    186     DETACH_AFTER,
    187     DETACH_ABOVE_OR_BELOW
    188   };
    189 
    190   // Indicates what should happen after invoking DragBrowserToNewTabStrip().
    191   enum DragBrowserResultType {
    192     // The caller should return immediately. This return value is used if a
    193     // nested message loop was created or we're in a nested message loop and
    194     // need to exit it.
    195     DRAG_BROWSER_RESULT_STOP,
    196 
    197     // The caller should continue.
    198     DRAG_BROWSER_RESULT_CONTINUE,
    199   };
    200 
    201   // Stores the date associated with a single tab that is being dragged.
    202   struct TabDragData {
    203     TabDragData();
    204     ~TabDragData();
    205 
    206     // The WebContents being dragged.
    207     content::WebContents* contents;
    208 
    209     // The original content::WebContentsDelegate of |contents|, before it was
    210     // detached from the browser window. We store this so that we can forward
    211     // certain delegate notifications back to it if we can't handle them
    212     // locally.
    213     content::WebContentsDelegate* original_delegate;
    214 
    215     // This is the index of the tab in |source_tabstrip_| when the drag
    216     // began. This is used to restore the previous state if the drag is aborted.
    217     int source_model_index;
    218 
    219     // If attached this is the tab in |attached_tabstrip_|.
    220     Tab* attached_tab;
    221 
    222     // Is the tab pinned?
    223     bool pinned;
    224   };
    225 
    226   typedef std::vector<TabDragData> DragData;
    227 
    228   // Sets |drag_data| from |tab|. This also registers for necessary
    229   // notifications and resets the delegate of the WebContents.
    230   void InitTabDragData(Tab* tab, TabDragData* drag_data);
    231 
    232   // Overridden from content::WebContentsDelegate:
    233   virtual content::WebContents* OpenURLFromTab(
    234       content::WebContents* source,
    235       const content::OpenURLParams& params) OVERRIDE;
    236   virtual void NavigationStateChanged(const content::WebContents* source,
    237                                       unsigned changed_flags) OVERRIDE;
    238   virtual void AddNewContents(content::WebContents* source,
    239                               content::WebContents* new_contents,
    240                               WindowOpenDisposition disposition,
    241                               const gfx::Rect& initial_pos,
    242                               bool user_gesture,
    243                               bool* was_blocked) OVERRIDE;
    244   virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
    245   virtual bool ShouldSuppressDialogs() OVERRIDE;
    246   virtual content::JavaScriptDialogManager*
    247       GetJavaScriptDialogManager() OVERRIDE;
    248 
    249   // Overridden from content::NotificationObserver:
    250   virtual void Observe(int type,
    251                        const content::NotificationSource& source,
    252                        const content::NotificationDetails& details) OVERRIDE;
    253 
    254   // Overridden from MessageLoop::Observer:
    255   virtual base::EventStatus WillProcessEvent(
    256       const base::NativeEvent& event) OVERRIDE;
    257   virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
    258 
    259   // Overriden from views::WidgetObserver:
    260   virtual void OnWidgetBoundsChanged(views::Widget* widget,
    261                                      const gfx::Rect& new_bounds) OVERRIDE;
    262 
    263   // Overriden from TabStripModelObserver:
    264   virtual void TabStripEmpty() OVERRIDE;
    265 
    266   // Initialize the offset used to calculate the position to create windows
    267   // in |GetWindowCreatePoint|. This should only be invoked from |Init|.
    268   void InitWindowCreatePoint();
    269 
    270   // Returns the point where a detached window should be created given the
    271   // current mouse position |origin|.
    272   gfx::Point GetWindowCreatePoint(const gfx::Point& origin) const;
    273 
    274   void UpdateDockInfo(const gfx::Point& point_in_screen);
    275 
    276   // Saves focus in the window that the drag initiated from. Focus will be
    277   // restored appropriately if the drag ends within this same window.
    278   void SaveFocus();
    279 
    280   // Restore focus to the View that had focus before the drag was started, if
    281   // the drag ends within the same Window as it began.
    282   void RestoreFocus();
    283 
    284   // Tests whether |point_in_screen| is past a minimum elasticity threshold
    285   // required to start a drag.
    286   bool CanStartDrag(const gfx::Point& point_in_screen) const;
    287 
    288   // Move the DraggedTabView according to the current mouse screen position,
    289   // potentially updating the source and other TabStrips.
    290   void ContinueDragging(const gfx::Point& point_in_screen);
    291 
    292   // Transitions dragging from |attached_tabstrip_| to |target_tabstrip|.
    293   // |target_tabstrip| is NULL if the mouse is not over a valid tab strip.  See
    294   // DragBrowserResultType for details of the return type.
    295   DragBrowserResultType DragBrowserToNewTabStrip(
    296       TabStrip* target_tabstrip,
    297       const gfx::Point& point_in_screen);
    298 
    299   // Handles dragging for a touch tabstrip when the tabs are stacked. Doesn't
    300   // actually reorder the tabs in anyway, just changes what's visible.
    301   void DragActiveTabStacked(const gfx::Point& point_in_screen);
    302 
    303   // Moves the active tab to the next/previous tab. Used when the next/previous
    304   // tab is stacked.
    305   void MoveAttachedToNextStackedIndex(const gfx::Point& point_in_screen);
    306   void MoveAttachedToPreviousStackedIndex(const gfx::Point& point_in_screen);
    307 
    308   // Handles dragging tabs while the tabs are attached.
    309   void MoveAttached(const gfx::Point& point_in_screen);
    310 
    311   // Handles dragging while the tabs are detached.
    312   void MoveDetached(const gfx::Point& point_in_screen);
    313 
    314   // If necessary starts the |move_stacked_timer_|. The timer is started if
    315   // close enough to an edge with stacked tabs.
    316   void StartMoveStackedTimerIfNecessary(
    317       const gfx::Point& point_in_screen,
    318       int delay_ms);
    319 
    320   // Returns the TabStrip for the specified window, or NULL if one doesn't exist
    321   // or isn't compatible.
    322   TabStrip* GetTabStripForWindow(gfx::NativeWindow window);
    323 
    324   // Returns the compatible TabStrip to drag to at the specified point (screen
    325   // coordinates), or NULL if there is none.
    326   TabStrip* GetTargetTabStripForPoint(const gfx::Point& point_in_screen);
    327 
    328   // Returns true if |tabstrip| contains the specified point in screen
    329   // coordinates.
    330   bool DoesTabStripContain(TabStrip* tabstrip,
    331                            const gfx::Point& point_in_screen) const;
    332 
    333   // Returns the DetachPosition given the specified location in screen
    334   // coordinates.
    335   DetachPosition GetDetachPosition(const gfx::Point& point_in_screen);
    336 
    337   DockInfo GetDockInfoAtPoint(const gfx::Point& point_in_screen);
    338 
    339   // Attach the dragged Tab to the specified TabStrip.
    340   void Attach(TabStrip* attached_tabstrip, const gfx::Point& point_in_screen);
    341 
    342   // Detach the dragged Tab from the current TabStrip.
    343   void Detach(ReleaseCapture release_capture);
    344 
    345   // Detaches the tabs being dragged, creates a new Browser to contain them and
    346   // runs a nested move loop.
    347   void DetachIntoNewBrowserAndRunMoveLoop(const gfx::Point& point_in_screen);
    348 
    349   // Runs a nested message loop that handles moving the current
    350   // Browser. |drag_offset| is the offset from the window origin and is used in
    351   // calculating the location of the window offset from the cursor while
    352   // dragging.
    353   void RunMoveLoop(const gfx::Vector2d& drag_offset);
    354 
    355   // Determines the index to insert tabs at. |dragged_bounds| is the bounds of
    356   // the tabs being dragged, |start| the index of the tab to start looking from
    357   // and |delta| the amount to increment (1 or -1).
    358   int GetInsertionIndexFrom(const gfx::Rect& dragged_bounds,
    359                             int start,
    360                             int delta) const;
    361 
    362   // Returns the index where the dragged WebContents should be inserted into
    363   // |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
    364   // coordinates relative to |attached_tabstrip_| and has had the mirroring
    365   // transformation applied.
    366   // NOTE: this is invoked from |Attach| before the tabs have been inserted.
    367   int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
    368 
    369   // Returns true if |dragged_bounds| is close enough to the next stacked tab
    370   // so that the active tab should be dragged there.
    371   bool ShouldDragToNextStackedTab(const gfx::Rect& dragged_bounds,
    372                                   int index) const;
    373 
    374   // Returns true if |dragged_bounds| is close enough to the previous stacked
    375   // tab so that the active tab should be dragged there.
    376   bool ShouldDragToPreviousStackedTab(const gfx::Rect& dragged_bounds,
    377                                       int index) const;
    378 
    379   // Used by GetInsertionIndexForDraggedBounds() when the tabstrip is stacked.
    380   int GetInsertionIndexForDraggedBoundsStacked(
    381       const gfx::Rect& dragged_bounds) const;
    382 
    383   // Retrieve the bounds of the DraggedTabView relative to the attached
    384   // TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
    385   // system.
    386   gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
    387 
    388   // Get the position of the dragged tab view relative to the attached tab
    389   // strip with the mirroring transform applied.
    390   gfx::Point GetAttachedDragPoint(const gfx::Point& point_in_screen);
    391 
    392   // Finds the Tabs within the specified TabStrip that corresponds to the
    393   // WebContents of the dragged tabs. Returns an empty vector if not attached.
    394   std::vector<Tab*> GetTabsMatchingDraggedContents(TabStrip* tabstrip);
    395 
    396   // Returns the bounds for the tabs based on the attached tab strip. The
    397   // x-coordinate of each tab is offset by |x_offset|.
    398   std::vector<gfx::Rect> CalculateBoundsForDraggedTabs(int x_offset);
    399 
    400   // Does the work for EndDrag. If we actually started a drag and |how_end| is
    401   // not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked.
    402   void EndDragImpl(EndDragType how_end);
    403 
    404   // Reverts a cancelled drag operation.
    405   void RevertDrag();
    406 
    407   // Reverts the tab at |drag_index| in |drag_data_|.
    408   void RevertDragAt(size_t drag_index);
    409 
    410   // Selects the dragged tabs in |model|. Does nothing if there are no longer
    411   // any dragged contents (as happens when a WebContents is deleted out from
    412   // under us).
    413   void ResetSelection(TabStripModel* model);
    414 
    415   // Finishes a succesful drag operation.
    416   void CompleteDrag();
    417 
    418   // Resets the delegates of the WebContents.
    419   void ResetDelegates();
    420 
    421   // Create the DraggedTabView.
    422   void CreateDraggedView(const std::vector<TabRendererData>& data,
    423                          const std::vector<gfx::Rect>& renderer_bounds);
    424 
    425   // Returns the bounds (in screen coordinates) of the specified View.
    426   gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
    427 
    428   // Hides the frame for the window that contains the TabStrip the current
    429   // drag session was initiated from.
    430   void HideFrame();
    431 
    432   // Closes a hidden frame at the end of a drag session.
    433   void CleanUpHiddenFrame();
    434 
    435   void DockDisplayerDestroyed(DockDisplayer* controller);
    436 
    437   void BringWindowUnderPointToFront(const gfx::Point& point_in_screen);
    438 
    439   // Convenience for getting the TabDragData corresponding to the tab the user
    440   // started dragging.
    441   TabDragData* source_tab_drag_data() {
    442     return &(drag_data_[source_tab_index_]);
    443   }
    444 
    445   // Convenience for |source_tab_drag_data()->contents|.
    446   content::WebContents* source_dragged_contents() {
    447     return source_tab_drag_data()->contents;
    448   }
    449 
    450   // Returns the Widget of the currently attached TabStrip's BrowserView.
    451   views::Widget* GetAttachedBrowserWidget();
    452 
    453   // Returns true if the tabs were originality one after the other in
    454   // |source_tabstrip_|.
    455   bool AreTabsConsecutive();
    456 
    457   // Creates and returns a new Browser to handle the drag.
    458   Browser* CreateBrowserForDrag(TabStrip* source,
    459                                 const gfx::Point& point_in_screen,
    460                                 gfx::Vector2d* drag_offset,
    461                                 std::vector<gfx::Rect>* drag_bounds);
    462 
    463   // Returns the TabStripModel for the specified tabstrip.
    464   TabStripModel* GetModel(TabStrip* tabstrip) const;
    465 
    466   // Returns the location of the cursor. This is either the location of the
    467   // mouse or the location of the current touch point.
    468   gfx::Point GetCursorScreenPoint();
    469 
    470   // Returns the offset from the top left corner of the window to
    471   // |point_in_screen|.
    472   gfx::Vector2d GetWindowOffset(const gfx::Point& point_in_screen);
    473 
    474   // Returns true if moving the mouse only changes the visible tabs.
    475   bool move_only() const {
    476     return (move_behavior_ == MOVE_VISIBILE_TABS) != 0;
    477   }
    478 
    479   // If true Detaching creates a new browser and enters a nested message loop.
    480   bool detach_into_browser_;
    481 
    482   // Handles registering for notifications.
    483   content::NotificationRegistrar registrar_;
    484 
    485   EventSource event_source_;
    486 
    487   // The TabStrip the drag originated from.
    488   TabStrip* source_tabstrip_;
    489 
    490   // The TabStrip the dragged Tab is currently attached to, or NULL if the
    491   // dragged Tab is detached.
    492   TabStrip* attached_tabstrip_;
    493 
    494   // The screen that this drag is associated with. Cached, because other UI
    495   // elements are NULLd at various points during the lifetime of this object.
    496   gfx::Screen* screen_;
    497 
    498   // The desktop type that this drag is associated with. Cached, because other
    499   // UI elements are NULLd at various points during the lifetime of this
    500   // object.
    501   chrome::HostDesktopType host_desktop_type_;
    502 
    503   // The visual representation of the dragged Tab.
    504   scoped_ptr<DraggedTabView> view_;
    505 
    506   // The position of the mouse (in screen coordinates) at the start of the drag
    507   // operation. This is used to calculate minimum elasticity before a
    508   // DraggedTabView is constructed.
    509   gfx::Point start_point_in_screen_;
    510 
    511   // This is the offset of the mouse from the top left of the Tab where
    512   // dragging begun. This is used to ensure that the dragged view is always
    513   // positioned at the correct location during the drag, and to ensure that the
    514   // detached window is created at the right location.
    515   gfx::Point mouse_offset_;
    516 
    517   // Ratio of the x-coordinate of the |source_tab_offset| to the width of the
    518   // tab. Not used for vertical tabs.
    519   float offset_to_width_ratio_;
    520 
    521   // A hint to use when positioning new windows created by detaching Tabs. This
    522   // is the distance of the mouse from the top left of the dragged tab as if it
    523   // were the distance of the mouse from the top left of the first tab in the
    524   // attached TabStrip from the top left of the window.
    525   gfx::Point window_create_point_;
    526 
    527   // Location of the first tab in the source tabstrip in screen coordinates.
    528   // This is used to calculate window_create_point_.
    529   gfx::Point first_source_tab_point_;
    530 
    531   // The bounds of the browser window before the last Tab was detached. When
    532   // the last Tab is detached, rather than destroying the frame (which would
    533   // abort the drag session), the frame is moved off-screen. If the drag is
    534   // aborted (e.g. by the user pressing Esc, or capture being lost), the Tab is
    535   // attached to the hidden frame and the frame moved back to these bounds.
    536   gfx::Rect restore_bounds_;
    537 
    538   // The last view that had focus in the window containing |source_tab_|. This
    539   // is saved so that focus can be restored properly when a drag begins and
    540   // ends within this same window.
    541   views::View* old_focused_view_;
    542 
    543   // The position along the major axis of the mouse cursor in screen coordinates
    544   // at the time of the last re-order event.
    545   int last_move_screen_loc_;
    546 
    547   DockInfo dock_info_;
    548 
    549   DockWindows dock_windows_;
    550 
    551   std::vector<DockDisplayer*> dock_controllers_;
    552 
    553   // Timer used to bring the window under the cursor to front. If the user
    554   // stops moving the mouse for a brief time over a browser window, it is
    555   // brought to front.
    556   base::OneShotTimer<TabDragController> bring_to_front_timer_;
    557 
    558   // Timer used to move the stacked tabs. See comment aboue
    559   // StartMoveStackedTimerIfNecessary().
    560   base::OneShotTimer<TabDragController> move_stacked_timer_;
    561 
    562   // Did the mouse move enough that we started a drag?
    563   bool started_drag_;
    564 
    565   // Is the drag active?
    566   bool active_;
    567 
    568   DragData drag_data_;
    569 
    570   // Index of the source tab in drag_data_.
    571   size_t source_tab_index_;
    572 
    573   // True until |MoveAttached| is invoked once.
    574   bool initial_move_;
    575 
    576   // The selection model before the drag started. See comment above Init() for
    577   // details.
    578   ui::ListSelectionModel initial_selection_model_;
    579 
    580   // The selection model of |attached_tabstrip_| before the tabs were attached.
    581   ui::ListSelectionModel selection_model_before_attach_;
    582 
    583   // Initial x-coordinates of the tabs when the drag started. Only used for
    584   // touch mode.
    585   std::vector<int> initial_tab_positions_;
    586 
    587   DetachBehavior detach_behavior_;
    588   MoveBehavior move_behavior_;
    589 
    590   // Updated as the mouse is moved when attached. Indicates whether the mouse
    591   // has ever moved to the left or right. If the tabs are ever detached this
    592   // is set to kMovedMouseRight | kMovedMouseLeft.
    593   int mouse_move_direction_;
    594 
    595   // Last location used in screen coordinates.
    596   gfx::Point last_point_in_screen_;
    597 
    598   // The following are needed when detaching into a browser
    599   // (|detach_into_browser_| is true).
    600 
    601   // See description above getter.
    602   bool is_dragging_window_;
    603 
    604   EndRunLoopBehavior end_run_loop_behavior_;
    605 
    606   // If true, we're waiting for a move loop to complete.
    607   bool waiting_for_run_loop_to_exit_;
    608 
    609   // The TabStrip to attach to after the move loop completes.
    610   TabStrip* tab_strip_to_attach_to_after_exit_;
    611 
    612   // Non-null for the duration of RunMoveLoop.
    613   views::Widget* move_loop_widget_;
    614 
    615   // If non-null set to true from destructor.
    616   bool* destroyed_;
    617 
    618   // See description above getter.
    619   bool is_mutating_;
    620 
    621   DISALLOW_COPY_AND_ASSIGN(TabDragController);
    622 };
    623 
    624 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
    625