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