| OLD | NEW | 
 | (Empty) | 
|    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 #include "chrome/browser/ui/gtk/tabs/dragged_tab_controller_gtk.h" |  | 
|    6  |  | 
|    7 #include <algorithm> |  | 
|    8  |  | 
|    9 #include "base/bind.h" |  | 
|   10 #include "base/bind_helpers.h" |  | 
|   11 #include "base/i18n/rtl.h" |  | 
|   12 #include "chrome/browser/chrome_notification_types.h" |  | 
|   13 #include "chrome/browser/platform_util.h" |  | 
|   14 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h" |  | 
|   15 #include "chrome/browser/ui/browser.h" |  | 
|   16 #include "chrome/browser/ui/gtk/browser_window_gtk.h" |  | 
|   17 #include "chrome/browser/ui/gtk/gtk_util.h" |  | 
|   18 #include "chrome/browser/ui/gtk/tabs/dragged_view_gtk.h" |  | 
|   19 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h" |  | 
|   20 #include "chrome/browser/ui/gtk/tabs/window_finder.h" |  | 
|   21 #include "chrome/browser/ui/media_utils.h" |  | 
|   22 #include "chrome/browser/ui/tabs/tab_strip_model.h" |  | 
|   23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" |  | 
|   24 #include "content/public/browser/notification_source.h" |  | 
|   25 #include "content/public/browser/web_contents.h" |  | 
|   26 #include "content/public/browser/web_contents_view.h" |  | 
|   27 #include "ui/base/gtk/gtk_screen_util.h" |  | 
|   28 #include "ui/gfx/screen.h" |  | 
|   29  |  | 
|   30 using content::OpenURLParams; |  | 
|   31 using content::WebContents; |  | 
|   32  |  | 
|   33 namespace { |  | 
|   34  |  | 
|   35 // Delay, in ms, during dragging before we bring a window to front. |  | 
|   36 const int kBringToFrontDelay = 750; |  | 
|   37  |  | 
|   38 // Used to determine how far a tab must obscure another tab in order to swap |  | 
|   39 // their indexes. |  | 
|   40 const int kHorizontalMoveThreshold = 16;  // pixels |  | 
|   41  |  | 
|   42 // How far a drag must pull a tab out of the tabstrip in order to detach it. |  | 
|   43 const int kVerticalDetachMagnetism = 15;  // pixels |  | 
|   44  |  | 
|   45 // Returns the bounds that will be used for insertion index calculation. |  | 
|   46 // |bounds| is the actual tab bounds, but subtracting the overlapping areas from |  | 
|   47 // both sides makes the calculations much simpler. |  | 
|   48 gfx::Rect GetEffectiveBounds(const gfx::Rect& bounds) { |  | 
|   49   gfx::Rect effective_bounds(bounds); |  | 
|   50   effective_bounds.set_width(effective_bounds.width() - 2 * 16); |  | 
|   51   effective_bounds.set_x(effective_bounds.x() + 16); |  | 
|   52   return effective_bounds; |  | 
|   53 } |  | 
|   54  |  | 
|   55 }  // namespace |  | 
|   56  |  | 
|   57 DraggedTabControllerGtk::DraggedTabControllerGtk( |  | 
|   58     TabStripGtk* source_tabstrip, |  | 
|   59     TabGtk* source_tab, |  | 
|   60     const std::vector<TabGtk*>& tabs) |  | 
|   61     : source_tabstrip_(source_tabstrip), |  | 
|   62       attached_tabstrip_(source_tabstrip), |  | 
|   63       in_destructor_(false), |  | 
|   64       last_move_screen_x_(0), |  | 
|   65       initial_move_(true) { |  | 
|   66   DCHECK(!tabs.empty()); |  | 
|   67   DCHECK(std::find(tabs.begin(), tabs.end(), source_tab) != tabs.end()); |  | 
|   68  |  | 
|   69   std::vector<DraggedTabData> drag_data; |  | 
|   70   for (size_t i = 0; i < tabs.size(); i++) |  | 
|   71     drag_data.push_back(InitDraggedTabData(tabs[i])); |  | 
|   72  |  | 
|   73   int source_tab_index = |  | 
|   74       std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin(); |  | 
|   75   drag_data_.reset(new DragData(drag_data, source_tab_index)); |  | 
|   76 } |  | 
|   77  |  | 
|   78 DraggedTabControllerGtk::~DraggedTabControllerGtk() { |  | 
|   79   in_destructor_ = true; |  | 
|   80   // Need to delete the dragged tab here manually _before_ we reset the dragged |  | 
|   81   // contents to NULL, otherwise if the view is animating to its destination |  | 
|   82   // bounds, it won't be able to clean up properly since its cleanup routine |  | 
|   83   // uses GetIndexForDraggedContents, which will be invalid. |  | 
|   84   CleanUpDraggedTabs(); |  | 
|   85   dragged_view_.reset(); |  | 
|   86   drag_data_.reset(); |  | 
|   87 } |  | 
|   88  |  | 
|   89 void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) { |  | 
|   90   start_screen_point_ = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); |  | 
|   91   mouse_offset_ = mouse_offset; |  | 
|   92 } |  | 
|   93  |  | 
|   94 void DraggedTabControllerGtk::Drag() { |  | 
|   95   if (!drag_data_->GetSourceTabData()->tab_ || |  | 
|   96       !drag_data_->GetSourceWebContents()) { |  | 
|   97     return; |  | 
|   98   } |  | 
|   99  |  | 
|  100   bring_to_front_timer_.Stop(); |  | 
|  101  |  | 
|  102   EnsureDraggedView(); |  | 
|  103  |  | 
|  104   // Before we get to dragging anywhere, ensure that we consider ourselves |  | 
|  105   // attached to the source tabstrip. |  | 
|  106   if (drag_data_->GetSourceTabData()->tab_->IsVisible()) { |  | 
|  107     Attach(source_tabstrip_, gfx::Point()); |  | 
|  108   } |  | 
|  109  |  | 
|  110   if (!drag_data_->GetSourceTabData()->tab_->IsVisible()) { |  | 
|  111     // TODO(jhawkins): Save focus. |  | 
|  112     ContinueDragging(); |  | 
|  113   } |  | 
|  114 } |  | 
|  115  |  | 
|  116 bool DraggedTabControllerGtk::EndDrag(bool canceled) { |  | 
|  117   return EndDragImpl(canceled ? CANCELED : NORMAL); |  | 
|  118 } |  | 
|  119  |  | 
|  120 TabGtk* DraggedTabControllerGtk::GetDraggedTabForContents( |  | 
|  121     WebContents* contents) { |  | 
|  122   if (attached_tabstrip_ == source_tabstrip_) { |  | 
|  123     for (size_t i = 0; i < drag_data_->size(); i++) { |  | 
|  124       if (contents == drag_data_->get(i)->contents_) |  | 
|  125         return drag_data_->get(i)->tab_; |  | 
|  126     } |  | 
|  127   } |  | 
|  128   return NULL; |  | 
|  129 } |  | 
|  130  |  | 
|  131 bool DraggedTabControllerGtk::IsDraggingTab(const TabGtk* tab) { |  | 
|  132   for (size_t i = 0; i < drag_data_->size(); i++) { |  | 
|  133     if (tab == drag_data_->get(i)->tab_) |  | 
|  134       return true; |  | 
|  135   } |  | 
|  136   return false; |  | 
|  137 } |  | 
|  138  |  | 
|  139 bool DraggedTabControllerGtk::IsDraggingWebContents( |  | 
|  140     const WebContents* web_contents) { |  | 
|  141   for (size_t i = 0; i < drag_data_->size(); i++) { |  | 
|  142     if (web_contents == drag_data_->get(i)->contents_) |  | 
|  143       return true; |  | 
|  144   } |  | 
|  145   return false; |  | 
|  146 } |  | 
|  147  |  | 
|  148 bool DraggedTabControllerGtk::IsTabDetached(const TabGtk* tab) { |  | 
|  149   return IsDraggingTab(tab) && attached_tabstrip_ == NULL; |  | 
|  150 } |  | 
|  151  |  | 
|  152 DraggedTabData DraggedTabControllerGtk::InitDraggedTabData(TabGtk* tab) { |  | 
|  153   int source_model_index = source_tabstrip_->GetIndexOfTab(tab); |  | 
|  154   WebContents* contents = |  | 
|  155       source_tabstrip_->model()->GetWebContentsAt(source_model_index); |  | 
|  156   bool pinned = source_tabstrip_->IsTabPinned(tab); |  | 
|  157   bool mini = source_tabstrip_->model()->IsMiniTab(source_model_index); |  | 
|  158   // We need to be the delegate so we receive messages about stuff, |  | 
|  159   // otherwise our dragged_contents() may be replaced and subsequently |  | 
|  160   // collected/destroyed while the drag is in process, leading to |  | 
|  161   // nasty crashes. |  | 
|  162   content::WebContentsDelegate* original_delegate = contents->GetDelegate(); |  | 
|  163   contents->SetDelegate(this); |  | 
|  164  |  | 
|  165   DraggedTabData dragged_tab_data(tab, contents, original_delegate, |  | 
|  166                                   source_model_index, pinned, mini); |  | 
|  167   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |  | 
|  168                  content::Source<WebContents>(contents)); |  | 
|  169   return dragged_tab_data; |  | 
|  170 } |  | 
|  171  |  | 
|  172 //////////////////////////////////////////////////////////////////////////////// |  | 
|  173 // DraggedTabControllerGtk, content::WebContentsDelegate implementation: |  | 
|  174  |  | 
|  175 WebContents* DraggedTabControllerGtk::OpenURLFromTab( |  | 
|  176     WebContents* source, |  | 
|  177     const OpenURLParams& params) { |  | 
|  178   if (drag_data_->GetSourceTabData()->original_delegate_) { |  | 
|  179     OpenURLParams forward_params = params; |  | 
|  180     if (params.disposition == CURRENT_TAB) |  | 
|  181       forward_params.disposition = NEW_WINDOW; |  | 
|  182  |  | 
|  183     return drag_data_->GetSourceTabData()->original_delegate_-> |  | 
|  184         OpenURLFromTab(source, forward_params); |  | 
|  185   } |  | 
|  186   return NULL; |  | 
|  187 } |  | 
|  188  |  | 
|  189 void DraggedTabControllerGtk::NavigationStateChanged(const WebContents* source, |  | 
|  190                                                      unsigned changed_flags) { |  | 
|  191   if (dragged_view_.get()) |  | 
|  192     dragged_view_->Update(); |  | 
|  193 } |  | 
|  194  |  | 
|  195 void DraggedTabControllerGtk::AddNewContents(WebContents* source, |  | 
|  196                                              WebContents* new_contents, |  | 
|  197                                              WindowOpenDisposition disposition, |  | 
|  198                                              const gfx::Rect& initial_pos, |  | 
|  199                                              bool user_gesture, |  | 
|  200                                              bool* was_blocked) { |  | 
|  201   DCHECK(disposition != CURRENT_TAB); |  | 
|  202  |  | 
|  203   // Theoretically could be called while dragging if the page tries to |  | 
|  204   // spawn a window. Route this message back to the browser in most cases. |  | 
|  205   if (drag_data_->GetSourceTabData()->original_delegate_) { |  | 
|  206     drag_data_->GetSourceTabData()->original_delegate_->AddNewContents( |  | 
|  207         source, new_contents, disposition, initial_pos, user_gesture, |  | 
|  208         was_blocked); |  | 
|  209   } |  | 
|  210 } |  | 
|  211  |  | 
|  212 void DraggedTabControllerGtk::LoadingStateChanged(WebContents* source, |  | 
|  213                                                   bool to_different_document) { |  | 
|  214   // TODO(jhawkins): It would be nice to respond to this message by changing the |  | 
|  215   // screen shot in the dragged tab. |  | 
|  216   if (dragged_view_.get()) |  | 
|  217     dragged_view_->Update(); |  | 
|  218 } |  | 
|  219  |  | 
|  220 content::JavaScriptDialogManager* |  | 
|  221 DraggedTabControllerGtk::GetJavaScriptDialogManager() { |  | 
|  222   return GetJavaScriptDialogManagerInstance(); |  | 
|  223 } |  | 
|  224  |  | 
|  225 //////////////////////////////////////////////////////////////////////////////// |  | 
|  226 // DraggedTabControllerGtk, content::NotificationObserver implementation: |  | 
|  227  |  | 
|  228 void DraggedTabControllerGtk::Observe( |  | 
|  229     int type, |  | 
|  230     const content::NotificationSource& source, |  | 
|  231     const content::NotificationDetails& details) { |  | 
|  232   DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type); |  | 
|  233   WebContents* destroyed_web_contents = |  | 
|  234       content::Source<WebContents>(source).ptr(); |  | 
|  235   for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  236     if (drag_data_->get(i)->contents_ == destroyed_web_contents) { |  | 
|  237       // One of the tabs we're dragging has been destroyed. Cancel the drag. |  | 
|  238       if (destroyed_web_contents->GetDelegate() == this) |  | 
|  239         destroyed_web_contents->SetDelegate(NULL); |  | 
|  240       drag_data_->get(i)->contents_ = NULL; |  | 
|  241       drag_data_->get(i)->original_delegate_ = NULL; |  | 
|  242       EndDragImpl(TAB_DESTROYED); |  | 
|  243       return; |  | 
|  244     } |  | 
|  245   } |  | 
|  246   // If we get here it means we got notification for a tab we don't know about. |  | 
|  247   NOTREACHED(); |  | 
|  248 } |  | 
|  249  |  | 
|  250 void DraggedTabControllerGtk::RequestMediaAccessPermission( |  | 
|  251     content::WebContents* web_contents, |  | 
|  252     const content::MediaStreamRequest& request, |  | 
|  253     const content::MediaResponseCallback& callback) { |  | 
|  254   ::RequestMediaAccessPermission( |  | 
|  255       web_contents, |  | 
|  256       Profile::FromBrowserContext(web_contents->GetBrowserContext()), |  | 
|  257       request, |  | 
|  258       callback); |  | 
|  259 } |  | 
|  260  |  | 
|  261 gfx::Point DraggedTabControllerGtk::GetWindowCreatePoint() const { |  | 
|  262   gfx::Point creation_point = |  | 
|  263       gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); |  | 
|  264   gfx::Point distance_from_origin = |  | 
|  265       dragged_view_->GetDistanceFromTabStripOriginToMousePointer(); |  | 
|  266   // TODO(dpapad): offset also because of tabstrip origin being different than |  | 
|  267   // window origin. |  | 
|  268   creation_point.Offset(-distance_from_origin.x(), -distance_from_origin.y()); |  | 
|  269   return creation_point; |  | 
|  270 } |  | 
|  271  |  | 
|  272 void DraggedTabControllerGtk::ContinueDragging() { |  | 
|  273   // TODO(jhawkins): We don't handle the situation where the last tab is dragged |  | 
|  274   // out of a window, so we'll just go with the way Windows handles dragging for |  | 
|  275   // now. |  | 
|  276   gfx::Point screen_point = |  | 
|  277       gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); |  | 
|  278  |  | 
|  279   // Determine whether or not we have dragged over a compatible TabStrip in |  | 
|  280   // another browser window. If we have, we should attach to it and start |  | 
|  281   // dragging within it. |  | 
|  282   TabStripGtk* target_tabstrip = GetTabStripForPoint(screen_point); |  | 
|  283   if (target_tabstrip != attached_tabstrip_) { |  | 
|  284     // Make sure we're fully detached from whatever TabStrip we're attached to |  | 
|  285     // (if any). |  | 
|  286     if (attached_tabstrip_) |  | 
|  287       Detach(); |  | 
|  288  |  | 
|  289     if (target_tabstrip) |  | 
|  290       Attach(target_tabstrip, screen_point); |  | 
|  291   } |  | 
|  292  |  | 
|  293   if (!target_tabstrip) { |  | 
|  294     bring_to_front_timer_.Start(FROM_HERE, |  | 
|  295         base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this, |  | 
|  296         &DraggedTabControllerGtk::BringWindowUnderMouseToFront); |  | 
|  297   } |  | 
|  298  |  | 
|  299   if (attached_tabstrip_) |  | 
|  300     MoveAttached(screen_point); |  | 
|  301   else |  | 
|  302     MoveDetached(screen_point); |  | 
|  303 } |  | 
|  304  |  | 
|  305 void DraggedTabControllerGtk::RestoreSelection(TabStripModel* model) { |  | 
|  306   for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  307     WebContents* contents = drag_data_->get(i)->contents_; |  | 
|  308     // If a tab is destroyed while dragging contents might be null. See |  | 
|  309     // http://crbug.com/115409. |  | 
|  310     if (contents) { |  | 
|  311       int index = model->GetIndexOfWebContents(contents); |  | 
|  312       CHECK(index != TabStripModel::kNoTab); |  | 
|  313       model->AddTabAtToSelection(index); |  | 
|  314     } |  | 
|  315   } |  | 
|  316 } |  | 
|  317  |  | 
|  318 void DraggedTabControllerGtk::MoveAttached(const gfx::Point& screen_point) { |  | 
|  319   DCHECK(attached_tabstrip_); |  | 
|  320  |  | 
|  321   gfx::Point dragged_view_point = GetDraggedViewPoint(screen_point); |  | 
|  322   TabStripModel* attached_model = attached_tabstrip_->model(); |  | 
|  323  |  | 
|  324   std::vector<TabGtk*> tabs(drag_data_->GetDraggedTabs()); |  | 
|  325  |  | 
|  326   // Determine the horizontal move threshold. This is dependent on the width |  | 
|  327   // of tabs. The smaller the tabs compared to the standard size, the smaller |  | 
|  328   // the threshold. |  | 
|  329   double unselected, selected; |  | 
|  330   attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected); |  | 
|  331   double ratio = unselected / TabGtk::GetStandardSize().width(); |  | 
|  332   int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold); |  | 
|  333  |  | 
|  334   // Update the model, moving the WebContents from one index to another. |  | 
|  335   // Do this only if we have moved a minimum distance since the last reorder (to |  | 
|  336   // prevent jitter) or if this is the first move and the tabs are not |  | 
|  337   // consecutive. |  | 
|  338   if (abs(screen_point.x() - last_move_screen_x_) > threshold || |  | 
|  339       (initial_move_ && !AreTabsConsecutive())) { |  | 
|  340     if (initial_move_ && !AreTabsConsecutive()) { |  | 
|  341       // Making dragged tabs adjacent, this is done only once, if necessary. |  | 
|  342       attached_tabstrip_->model()->MoveSelectedTabsTo( |  | 
|  343           drag_data_->GetSourceTabData()->source_model_index_ - |  | 
|  344               drag_data_->source_tab_index()); |  | 
|  345     } |  | 
|  346     gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_point); |  | 
|  347     int to_index = GetInsertionIndexForDraggedBounds( |  | 
|  348         GetEffectiveBounds(bounds)); |  | 
|  349     to_index = NormalizeIndexToAttachedTabStrip(to_index); |  | 
|  350     last_move_screen_x_ = screen_point.x(); |  | 
|  351     attached_model->MoveSelectedTabsTo(to_index); |  | 
|  352   } |  | 
|  353  |  | 
|  354   dragged_view_->MoveAttachedTo(dragged_view_point); |  | 
|  355   initial_move_ = false; |  | 
|  356 } |  | 
|  357  |  | 
|  358 void DraggedTabControllerGtk::MoveDetached(const gfx::Point& screen_point) { |  | 
|  359   DCHECK(!attached_tabstrip_); |  | 
|  360   // Just moving the dragged view. There are no changes to the model if we're |  | 
|  361   // detached. |  | 
|  362   dragged_view_->MoveDetachedTo(screen_point); |  | 
|  363 } |  | 
|  364  |  | 
|  365 TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint( |  | 
|  366     const gfx::Point& screen_point) { |  | 
|  367   gfx::NativeWindow local_window = GetLocalProcessWindow(screen_point); |  | 
|  368   if (!local_window) |  | 
|  369     return NULL; |  | 
|  370  |  | 
|  371   BrowserWindowGtk* browser = |  | 
|  372       BrowserWindowGtk::GetBrowserWindowForNativeWindow(local_window); |  | 
|  373   if (!browser) |  | 
|  374     return NULL; |  | 
|  375  |  | 
|  376   TabStripGtk* other_tabstrip = browser->tabstrip(); |  | 
|  377   if (!other_tabstrip->IsCompatibleWith(source_tabstrip_)) |  | 
|  378     return NULL; |  | 
|  379  |  | 
|  380   return GetTabStripIfItContains(other_tabstrip, screen_point); |  | 
|  381 } |  | 
|  382  |  | 
|  383 TabStripGtk* DraggedTabControllerGtk::GetTabStripIfItContains( |  | 
|  384     TabStripGtk* tabstrip, const gfx::Point& screen_point) const { |  | 
|  385   // Make sure the specified screen point is actually within the bounds of the |  | 
|  386   // specified tabstrip... |  | 
|  387   gfx::Rect tabstrip_bounds = |  | 
|  388       ui::GetWidgetScreenBounds(tabstrip->tabstrip_.get()); |  | 
|  389   if (screen_point.x() < tabstrip_bounds.right() && |  | 
|  390       screen_point.x() >= tabstrip_bounds.x()) { |  | 
|  391     // TODO(beng): make this be relative to the start position of the mouse for |  | 
|  392     // the source TabStrip. |  | 
|  393     int upper_threshold = tabstrip_bounds.bottom() + kVerticalDetachMagnetism; |  | 
|  394     int lower_threshold = tabstrip_bounds.y() - kVerticalDetachMagnetism; |  | 
|  395     if (screen_point.y() >= lower_threshold && |  | 
|  396         screen_point.y() <= upper_threshold) { |  | 
|  397       return tabstrip; |  | 
|  398     } |  | 
|  399   } |  | 
|  400  |  | 
|  401   return NULL; |  | 
|  402 } |  | 
|  403  |  | 
|  404 void DraggedTabControllerGtk::Attach(TabStripGtk* attached_tabstrip, |  | 
|  405                                      const gfx::Point& screen_point) { |  | 
|  406   attached_tabstrip_ = attached_tabstrip; |  | 
|  407   attached_tabstrip_->GenerateIdealBounds(); |  | 
|  408  |  | 
|  409   std::vector<TabGtk*> attached_dragged_tabs = |  | 
|  410       GetTabsMatchingDraggedContents(attached_tabstrip_); |  | 
|  411  |  | 
|  412   // Update the tab first, so we can ask it for its bounds and determine |  | 
|  413   // where to insert the hidden tab. |  | 
|  414  |  | 
|  415   // If this is the first time Attach is called for this drag, we're attaching |  | 
|  416   // to the source tabstrip, and we should assume the tab count already |  | 
|  417   // includes this tab since we haven't been detached yet. If we don't do this, |  | 
|  418   // the dragged representation will be a different size to others in the |  | 
|  419   // tabstrip. |  | 
|  420   int tab_count = attached_tabstrip_->GetTabCount(); |  | 
|  421   int mini_tab_count = attached_tabstrip_->GetMiniTabCount(); |  | 
|  422   if (attached_dragged_tabs.size() == 0) { |  | 
|  423     tab_count += drag_data_->size(); |  | 
|  424     mini_tab_count += drag_data_->mini_tab_count(); |  | 
|  425   } |  | 
|  426  |  | 
|  427   double unselected_width = 0, selected_width = 0; |  | 
|  428   attached_tabstrip_->GetDesiredTabWidths(tab_count, mini_tab_count, |  | 
|  429                                           &unselected_width, &selected_width); |  | 
|  430  |  | 
|  431   GtkWidget* parent_window = gtk_widget_get_parent( |  | 
|  432       gtk_widget_get_parent(attached_tabstrip_->tabstrip_.get())); |  | 
|  433   gfx::Rect window_bounds = ui::GetWidgetScreenBounds(parent_window); |  | 
|  434   dragged_view_->Attach(static_cast<int>(floor(selected_width + 0.5)), |  | 
|  435                         TabGtk::GetMiniWidth(), window_bounds.width()); |  | 
|  436  |  | 
|  437   if (attached_dragged_tabs.size() == 0) { |  | 
|  438     // There is no tab in |attached_tabstrip| that corresponds to the dragged |  | 
|  439     // WebContents. We must now create one. |  | 
|  440  |  | 
|  441     // Remove ourselves as the delegate now that the dragged WebContents |  | 
|  442     // is being inserted back into a Browser. |  | 
|  443     for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  444       drag_data_->get(i)->contents_->SetDelegate(NULL); |  | 
|  445       drag_data_->get(i)->original_delegate_ = NULL; |  | 
|  446     } |  | 
|  447  |  | 
|  448     // We need to ask the tabstrip we're attached to ensure that the ideal |  | 
|  449     // bounds for all its tabs are correctly generated, because the calculation |  | 
|  450     // in GetInsertionIndexForDraggedBounds needs them to be to figure out the |  | 
|  451     // appropriate insertion index. |  | 
|  452     attached_tabstrip_->GenerateIdealBounds(); |  | 
|  453  |  | 
|  454     // Inserting counts as a move. We don't want the tabs to jitter when the |  | 
|  455     // user moves the tab immediately after attaching it. |  | 
|  456     last_move_screen_x_ = screen_point.x(); |  | 
|  457  |  | 
|  458     // Figure out where to insert the tab based on the bounds of the dragged |  | 
|  459     // representation and the ideal bounds of the other tabs already in the |  | 
|  460     // strip. ("ideal bounds" are stable even if the tabs' actual bounds are |  | 
|  461     // changing due to animation). |  | 
|  462     gfx::Rect bounds = |  | 
|  463         GetDraggedViewTabStripBounds(GetDraggedViewPoint(screen_point)); |  | 
|  464     int index = GetInsertionIndexForDraggedBounds(GetEffectiveBounds(bounds)); |  | 
|  465     for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  466       attached_tabstrip_->model()->InsertWebContentsAt( |  | 
|  467           index + i, drag_data_->get(i)->contents_, |  | 
|  468           drag_data_->GetAddTypesForDraggedTabAt(i)); |  | 
|  469     } |  | 
|  470     RestoreSelection(attached_tabstrip_->model()); |  | 
|  471     attached_dragged_tabs = GetTabsMatchingDraggedContents(attached_tabstrip_); |  | 
|  472   } |  | 
|  473   // We should now have a tab. |  | 
|  474   DCHECK(attached_dragged_tabs.size() == drag_data_->size()); |  | 
|  475   SetDraggedTabsVisible(false, false); |  | 
|  476   // TODO(jhawkins): Move the corresponding window to the front. |  | 
|  477 } |  | 
|  478  |  | 
|  479 void DraggedTabControllerGtk::Detach() { |  | 
|  480   // Update the Model. |  | 
|  481   TabStripModel* attached_model = attached_tabstrip_->model(); |  | 
|  482   for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  483     int index = |  | 
|  484         attached_model->GetIndexOfWebContents(drag_data_->get(i)->contents_); |  | 
|  485     if (index >= 0 && index < attached_model->count()) { |  | 
|  486       // Sometimes, DetachWebContentsAt has consequences that result in |  | 
|  487       // attached_tabstrip_ being set to NULL, so we need to save it first. |  | 
|  488       attached_model->DetachWebContentsAt(index); |  | 
|  489     } |  | 
|  490   } |  | 
|  491  |  | 
|  492   // If we've removed the last tab from the tabstrip, hide the frame now. |  | 
|  493   if (attached_model->empty()) |  | 
|  494     HideWindow(); |  | 
|  495  |  | 
|  496   // Update the dragged tab. This NULL check is necessary apparently in some |  | 
|  497   // conditions during automation where the view_ is destroyed inside a |  | 
|  498   // function call preceding this point but after it is created. |  | 
|  499   if (dragged_view_.get()) { |  | 
|  500     dragged_view_->Detach(); |  | 
|  501   } |  | 
|  502  |  | 
|  503   // Detaching resets the delegate, but we still want to be the delegate. |  | 
|  504   for (size_t i = 0; i < drag_data_->size(); ++i) |  | 
|  505     drag_data_->get(i)->contents_->SetDelegate(this); |  | 
|  506  |  | 
|  507   attached_tabstrip_ = NULL; |  | 
|  508 } |  | 
|  509  |  | 
|  510 gfx::Point DraggedTabControllerGtk::ConvertScreenPointToTabStripPoint( |  | 
|  511     TabStripGtk* tabstrip, const gfx::Point& screen_point) { |  | 
|  512   return screen_point - ui::GetWidgetScreenOffset(tabstrip->tabstrip_.get()); |  | 
|  513 } |  | 
|  514  |  | 
|  515 gfx::Rect DraggedTabControllerGtk::GetDraggedViewTabStripBounds( |  | 
|  516     const gfx::Point& screen_point) { |  | 
|  517   gfx::Point client_point = |  | 
|  518       ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point); |  | 
|  519   gfx::Size tab_size = dragged_view_->attached_tab_size(); |  | 
|  520   return gfx::Rect(client_point.x(), client_point.y(), |  | 
|  521                    dragged_view_->GetTotalWidthInTabStrip(), tab_size.height()); |  | 
|  522 } |  | 
|  523  |  | 
|  524 int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds( |  | 
|  525     const gfx::Rect& dragged_bounds) { |  | 
|  526   int dragged_bounds_start = gtk_util::MirroredLeftPointForRect( |  | 
|  527       attached_tabstrip_->widget(), |  | 
|  528       dragged_bounds); |  | 
|  529   int dragged_bounds_end = gtk_util::MirroredRightPointForRect( |  | 
|  530       attached_tabstrip_->widget(), |  | 
|  531       dragged_bounds); |  | 
|  532   int right_tab_x = 0; |  | 
|  533   int index = -1; |  | 
|  534  |  | 
|  535   for (int i = 0; i < attached_tabstrip_->GetTabCount(); i++) { |  | 
|  536     // Divides each tab into two halves to see if the dragged tab has crossed |  | 
|  537     // the halfway boundary necessary to move past the next tab. |  | 
|  538     gfx::Rect ideal_bounds = attached_tabstrip_->GetIdealBounds(i); |  | 
|  539     gfx::Rect left_half, right_half; |  | 
|  540     ideal_bounds.SplitVertically(&left_half, &right_half); |  | 
|  541     right_tab_x = right_half.x(); |  | 
|  542  |  | 
|  543     if (dragged_bounds_start >= right_half.x() && |  | 
|  544         dragged_bounds_start < right_half.right()) { |  | 
|  545       index = i + 1; |  | 
|  546       break; |  | 
|  547     } else if (dragged_bounds_start >= left_half.x() && |  | 
|  548                dragged_bounds_start < left_half.right()) { |  | 
|  549       index = i; |  | 
|  550       break; |  | 
|  551     } |  | 
|  552   } |  | 
|  553  |  | 
|  554   if (index == -1) { |  | 
|  555     if (dragged_bounds_end > right_tab_x) |  | 
|  556       index = attached_tabstrip_->GetTabCount(); |  | 
|  557     else |  | 
|  558       index = 0; |  | 
|  559   } |  | 
|  560  |  | 
|  561   TabGtk* tab = GetTabMatchingDraggedContents( |  | 
|  562       attached_tabstrip_, drag_data_->GetSourceWebContents()); |  | 
|  563   if (tab == NULL) { |  | 
|  564     // If dragged tabs are not attached yet, we don't need to constrain the |  | 
|  565     // index. |  | 
|  566     return index; |  | 
|  567   } |  | 
|  568  |  | 
|  569   int max_index = |  | 
|  570       attached_tabstrip_-> GetTabCount() - static_cast<int>(drag_data_->size()); |  | 
|  571   return std::max(0, std::min(max_index, index)); |  | 
|  572 } |  | 
|  573  |  | 
|  574 gfx::Point DraggedTabControllerGtk::GetDraggedViewPoint( |  | 
|  575     const gfx::Point& screen_point) { |  | 
|  576   int x = screen_point.x() - |  | 
|  577       dragged_view_->GetWidthInTabStripUpToMousePointer(); |  | 
|  578   int y = screen_point.y() - mouse_offset_.y(); |  | 
|  579  |  | 
|  580   // If we're not attached, we just use x and y from above. |  | 
|  581   if (attached_tabstrip_) { |  | 
|  582     gfx::Rect tabstrip_bounds = |  | 
|  583         ui::GetWidgetScreenBounds(attached_tabstrip_->tabstrip_.get()); |  | 
|  584     // Snap the dragged tab to the tabstrip if we are attached, detaching |  | 
|  585     // only when the mouse position (screen_point) exceeds the screen bounds |  | 
|  586     // of the tabstrip. |  | 
|  587     if (x < tabstrip_bounds.x() && screen_point.x() >= tabstrip_bounds.x()) |  | 
|  588       x = tabstrip_bounds.x(); |  | 
|  589  |  | 
|  590     gfx::Size tab_size = dragged_view_->attached_tab_size(); |  | 
|  591     int vertical_drag_magnetism = tab_size.height() * 2; |  | 
|  592     int vertical_detach_point = tabstrip_bounds.y() - vertical_drag_magnetism; |  | 
|  593     if (y < tabstrip_bounds.y() && screen_point.y() >= vertical_detach_point) |  | 
|  594       y = tabstrip_bounds.y(); |  | 
|  595  |  | 
|  596     // Make sure the tab can't be dragged off the right side of the tabstrip |  | 
|  597     // unless the mouse pointer passes outside the bounds of the strip by |  | 
|  598     // clamping the position of the dragged window to the tabstrip width less |  | 
|  599     // the width of one tab until the mouse pointer (screen_point) exceeds the |  | 
|  600     // screen bounds of the tabstrip. |  | 
|  601     int max_x = |  | 
|  602         tabstrip_bounds.right() - dragged_view_->GetTotalWidthInTabStrip(); |  | 
|  603     int max_y = tabstrip_bounds.bottom() - tab_size.height(); |  | 
|  604     if (x > max_x && screen_point.x() <= tabstrip_bounds.right()) |  | 
|  605       x = max_x; |  | 
|  606     if (y > max_y && screen_point.y() <= |  | 
|  607         (tabstrip_bounds.bottom() + vertical_drag_magnetism)) { |  | 
|  608       y = max_y; |  | 
|  609     } |  | 
|  610   } |  | 
|  611   return gfx::Point(x, y); |  | 
|  612 } |  | 
|  613  |  | 
|  614 int DraggedTabControllerGtk::NormalizeIndexToAttachedTabStrip(int index) const { |  | 
|  615   if (index >= attached_tabstrip_->model_->count()) |  | 
|  616     return attached_tabstrip_->model_->count() - 1; |  | 
|  617   if (index == TabStripModel::kNoTab) |  | 
|  618     return 0; |  | 
|  619   return index; |  | 
|  620 } |  | 
|  621  |  | 
|  622 TabGtk* DraggedTabControllerGtk::GetTabMatchingDraggedContents( |  | 
|  623     TabStripGtk* tabstrip, WebContents* web_contents) { |  | 
|  624   int index = tabstrip->model()->GetIndexOfWebContents(web_contents); |  | 
|  625   return index == TabStripModel::kNoTab ? NULL : tabstrip->GetTabAt(index); |  | 
|  626 } |  | 
|  627  |  | 
|  628 std::vector<TabGtk*> DraggedTabControllerGtk::GetTabsMatchingDraggedContents( |  | 
|  629     TabStripGtk* tabstrip) { |  | 
|  630   std::vector<TabGtk*> dragged_tabs; |  | 
|  631   for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  632     if (!drag_data_->get(i)->contents_) |  | 
|  633       continue; |  | 
|  634     TabGtk* tab = GetTabMatchingDraggedContents(tabstrip, |  | 
|  635                                                 drag_data_->get(i)->contents_); |  | 
|  636     if (tab) |  | 
|  637       dragged_tabs.push_back(tab); |  | 
|  638   } |  | 
|  639   return dragged_tabs; |  | 
|  640 } |  | 
|  641  |  | 
|  642 void DraggedTabControllerGtk::SetDraggedTabsVisible(bool visible, |  | 
|  643                                                     bool repaint) { |  | 
|  644   std::vector<TabGtk*> dragged_tabs(GetTabsMatchingDraggedContents( |  | 
|  645       attached_tabstrip_)); |  | 
|  646   for (size_t i = 0; i < dragged_tabs.size(); ++i) { |  | 
|  647     dragged_tabs[i]->SetVisible(visible); |  | 
|  648     dragged_tabs[i]->set_dragging(!visible); |  | 
|  649     if (repaint) |  | 
|  650       dragged_tabs[i]->SchedulePaint(); |  | 
|  651   } |  | 
|  652 } |  | 
|  653  |  | 
|  654 bool DraggedTabControllerGtk::EndDragImpl(EndDragType type) { |  | 
|  655   bring_to_front_timer_.Stop(); |  | 
|  656  |  | 
|  657   // WARNING: this may be invoked multiple times. In particular, if deletion |  | 
|  658   // occurs after a delay (as it does when the tab is released in the original |  | 
|  659   // tab strip) and the navigation controller/tab contents is deleted before |  | 
|  660   // the animation finishes, this is invoked twice. The second time through |  | 
|  661   // type == TAB_DESTROYED. |  | 
|  662  |  | 
|  663   bool destroy_now = true; |  | 
|  664   if (type != TAB_DESTROYED) { |  | 
|  665     // If we never received a drag-motion event, the drag will never have |  | 
|  666     // started in the sense that |dragged_view_| will be NULL. We don't need to |  | 
|  667     // revert or complete the drag in that case. |  | 
|  668     if (dragged_view_.get()) { |  | 
|  669       if (type == CANCELED) { |  | 
|  670         RevertDrag(); |  | 
|  671       } else { |  | 
|  672         destroy_now = CompleteDrag(); |  | 
|  673       } |  | 
|  674     } |  | 
|  675   } else if (drag_data_->size() > 0) { |  | 
|  676     RevertDrag(); |  | 
|  677   } |  | 
|  678  |  | 
|  679   ResetDelegates(); |  | 
|  680  |  | 
|  681   // If we're not destroyed now, we'll be destroyed asynchronously later. |  | 
|  682   if (destroy_now) |  | 
|  683     source_tabstrip_->DestroyDragController(); |  | 
|  684  |  | 
|  685   return destroy_now; |  | 
|  686 } |  | 
|  687  |  | 
|  688 void DraggedTabControllerGtk::RevertDrag() { |  | 
|  689   // We save this here because code below will modify |attached_tabstrip_|. |  | 
|  690   bool restore_window = attached_tabstrip_ != source_tabstrip_; |  | 
|  691   if (attached_tabstrip_) { |  | 
|  692     if (attached_tabstrip_ != source_tabstrip_) { |  | 
|  693       // The tabs were inserted into another tabstrip. We need to put them back |  | 
|  694       // into the original one. |  | 
|  695       for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  696         if (!drag_data_->get(i)->contents_) |  | 
|  697           continue; |  | 
|  698         int index = attached_tabstrip_->model()->GetIndexOfWebContents( |  | 
|  699             drag_data_->get(i)->contents_); |  | 
|  700         attached_tabstrip_->model()->DetachWebContentsAt(index); |  | 
|  701       } |  | 
|  702       // TODO(beng): (Cleanup) seems like we should use Attach() for this |  | 
|  703       //             somehow. |  | 
|  704       attached_tabstrip_ = source_tabstrip_; |  | 
|  705       for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  706         if (!drag_data_->get(i)->contents_) |  | 
|  707           continue; |  | 
|  708         attached_tabstrip_->model()->InsertWebContentsAt( |  | 
|  709             drag_data_->get(i)->source_model_index_, |  | 
|  710             drag_data_->get(i)->contents_, |  | 
|  711             drag_data_->GetAddTypesForDraggedTabAt(i)); |  | 
|  712       } |  | 
|  713     } else { |  | 
|  714       // The tabs were moved within the tabstrip where the drag was initiated. |  | 
|  715       // Move them back to their starting locations. |  | 
|  716       for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  717         if (!drag_data_->get(i)->contents_) |  | 
|  718           continue; |  | 
|  719         int index = attached_tabstrip_->model()->GetIndexOfWebContents( |  | 
|  720             drag_data_->get(i)->contents_); |  | 
|  721         source_tabstrip_->model()->MoveWebContentsAt( |  | 
|  722             index, drag_data_->get(i)->source_model_index_, true); |  | 
|  723       } |  | 
|  724     } |  | 
|  725   } else { |  | 
|  726     // TODO(beng): (Cleanup) seems like we should use Attach() for this |  | 
|  727     //             somehow. |  | 
|  728     attached_tabstrip_ = source_tabstrip_; |  | 
|  729     // The tab was detached from the tabstrip where the drag began, and has not |  | 
|  730     // been attached to any other tabstrip. We need to put it back into the |  | 
|  731     // source tabstrip. |  | 
|  732     for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  733       if (!drag_data_->get(i)->contents_) |  | 
|  734         continue; |  | 
|  735       source_tabstrip_->model()->InsertWebContentsAt( |  | 
|  736           drag_data_->get(i)->source_model_index_, |  | 
|  737           drag_data_->get(i)->contents_, |  | 
|  738           drag_data_->GetAddTypesForDraggedTabAt(i)); |  | 
|  739     } |  | 
|  740   } |  | 
|  741   RestoreSelection(source_tabstrip_->model()); |  | 
|  742  |  | 
|  743   // If we're not attached to any tab strip, or attached to some other tab |  | 
|  744   // strip, we need to restore the bounds of the original tab strip's frame, in |  | 
|  745   // case it has been hidden. |  | 
|  746   if (restore_window) |  | 
|  747     ShowWindow(); |  | 
|  748  |  | 
|  749   SetDraggedTabsVisible(true, false); |  | 
|  750 } |  | 
|  751  |  | 
|  752 bool DraggedTabControllerGtk::CompleteDrag() { |  | 
|  753   bool destroy_immediately = true; |  | 
|  754   if (attached_tabstrip_) { |  | 
|  755     // We don't need to do anything other than make the tabs visible again, |  | 
|  756     // since the dragged view is going away. |  | 
|  757     dragged_view_->AnimateToBounds(GetAnimateBounds(), |  | 
|  758         base::Bind(&DraggedTabControllerGtk::OnAnimateToBoundsComplete, |  | 
|  759                    base::Unretained(this))); |  | 
|  760     destroy_immediately = false; |  | 
|  761   } else { |  | 
|  762     // Compel the model to construct a new window for the detached |  | 
|  763     // WebContentses. |  | 
|  764     BrowserWindowGtk* window = source_tabstrip_->window(); |  | 
|  765     gfx::Rect window_bounds = window->GetRestoredBounds(); |  | 
|  766     window_bounds.set_origin(GetWindowCreatePoint()); |  | 
|  767  |  | 
|  768     std::vector<TabStripModelDelegate::NewStripContents> contentses; |  | 
|  769     for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  770       TabStripModelDelegate::NewStripContents item; |  | 
|  771       item.web_contents = drag_data_->get(i)->contents_; |  | 
|  772       item.add_types = drag_data_->GetAddTypesForDraggedTabAt(i); |  | 
|  773       contentses.push_back(item); |  | 
|  774     }; |  | 
|  775  |  | 
|  776     Browser* new_browser = |  | 
|  777         source_tabstrip_->model()->delegate()->CreateNewStripWithContents( |  | 
|  778             contentses, window_bounds, window->IsMaximized()); |  | 
|  779     RestoreSelection(new_browser->tab_strip_model()); |  | 
|  780     new_browser->window()->Show(); |  | 
|  781     CleanUpHiddenFrame(); |  | 
|  782   } |  | 
|  783  |  | 
|  784   return destroy_immediately; |  | 
|  785 } |  | 
|  786  |  | 
|  787 void DraggedTabControllerGtk::ResetDelegates() { |  | 
|  788   for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  789     if (drag_data_->get(i)->contents_ && |  | 
|  790         drag_data_->get(i)->contents_->GetDelegate() == this) { |  | 
|  791       drag_data_->get(i)->ResetDelegate(); |  | 
|  792     } |  | 
|  793   } |  | 
|  794 } |  | 
|  795  |  | 
|  796 void DraggedTabControllerGtk::EnsureDraggedView() { |  | 
|  797   if (!dragged_view_.get()) { |  | 
|  798     gfx::Rect rect; |  | 
|  799     drag_data_->GetSourceWebContents()->GetView()->GetContainerBounds(&rect); |  | 
|  800     dragged_view_.reset(new DraggedViewGtk(drag_data_.get(), mouse_offset_, |  | 
|  801                                            rect.size())); |  | 
|  802   } |  | 
|  803 } |  | 
|  804  |  | 
|  805 gfx::Rect DraggedTabControllerGtk::GetAnimateBounds() { |  | 
|  806   // A hidden widget moved with gtk_fixed_move in a GtkFixed container doesn't |  | 
|  807   // update its allocation until after the widget is shown, so we have to use |  | 
|  808   // the tab bounds we keep track of. |  | 
|  809   // |  | 
|  810   // We use the requested bounds instead of the allocation because the |  | 
|  811   // allocation is relative to the first windowed widget ancestor of the tab. |  | 
|  812   // Because of this, we can't use the tabs allocation to get the screen bounds. |  | 
|  813   std::vector<TabGtk*> tabs = GetTabsMatchingDraggedContents( |  | 
|  814       attached_tabstrip_); |  | 
|  815   TabGtk* tab = !base::i18n::IsRTL() ? tabs.front() : tabs.back(); |  | 
|  816   gfx::Rect bounds = tab->GetRequisition(); |  | 
|  817   GtkWidget* widget = tab->widget(); |  | 
|  818   GtkWidget* parent = gtk_widget_get_parent(widget); |  | 
|  819   bounds.Offset(ui::GetWidgetScreenOffset(parent)); |  | 
|  820  |  | 
|  821   return gfx::Rect(bounds.x(), bounds.y(), |  | 
|  822                    dragged_view_->GetTotalWidthInTabStrip(), bounds.height()); |  | 
|  823 } |  | 
|  824  |  | 
|  825 void DraggedTabControllerGtk::HideWindow() { |  | 
|  826   GtkWidget* tabstrip = source_tabstrip_->widget(); |  | 
|  827   GtkWindow* window = platform_util::GetTopLevel(tabstrip); |  | 
|  828   gtk_widget_hide(GTK_WIDGET(window)); |  | 
|  829 } |  | 
|  830  |  | 
|  831 void DraggedTabControllerGtk::ShowWindow() { |  | 
|  832   GtkWidget* tabstrip = source_tabstrip_->widget(); |  | 
|  833   GtkWindow* window = platform_util::GetTopLevel(tabstrip); |  | 
|  834   gtk_window_present(window); |  | 
|  835 } |  | 
|  836  |  | 
|  837 void DraggedTabControllerGtk::CleanUpHiddenFrame() { |  | 
|  838   // If the model we started dragging from is now empty, we must ask the |  | 
|  839   // delegate to close the frame. |  | 
|  840   if (source_tabstrip_->model()->empty()) |  | 
|  841     source_tabstrip_->model()->delegate()->CloseFrameAfterDragSession(); |  | 
|  842 } |  | 
|  843  |  | 
|  844 void DraggedTabControllerGtk::CleanUpDraggedTabs() { |  | 
|  845   // If we were attached to the source tabstrip, dragged tabs will be in use. If |  | 
|  846   // we were detached or attached to another tabstrip, we can safely remove |  | 
|  847   // them and delete them now. |  | 
|  848   if (attached_tabstrip_ != source_tabstrip_) { |  | 
|  849     for (size_t i = 0; i < drag_data_->size(); ++i) { |  | 
|  850       if (drag_data_->get(i)->contents_) { |  | 
|  851         registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |  | 
|  852             content::Source<WebContents>(drag_data_->get(i)->contents_)); |  | 
|  853       } |  | 
|  854       source_tabstrip_->DestroyDraggedTab(drag_data_->get(i)->tab_); |  | 
|  855       drag_data_->get(i)->tab_ = NULL; |  | 
|  856     } |  | 
|  857   } |  | 
|  858 } |  | 
|  859  |  | 
|  860 void DraggedTabControllerGtk::OnAnimateToBoundsComplete() { |  | 
|  861   // Sometimes, for some reason, in automation we can be called back on a |  | 
|  862   // detach even though we aren't attached to a tabstrip. Guard against that. |  | 
|  863   if (attached_tabstrip_) |  | 
|  864     SetDraggedTabsVisible(true, true); |  | 
|  865  |  | 
|  866   CleanUpHiddenFrame(); |  | 
|  867  |  | 
|  868   if (!in_destructor_) |  | 
|  869     source_tabstrip_->DestroyDragController(); |  | 
|  870 } |  | 
|  871  |  | 
|  872 void DraggedTabControllerGtk::BringWindowUnderMouseToFront() { |  | 
|  873   // If we're going to dock to another window, bring it to the front. |  | 
|  874   gfx::NativeWindow window = GetLocalProcessWindow( |  | 
|  875       gfx::Screen::GetNativeScreen()->GetCursorScreenPoint()); |  | 
|  876   if (window) |  | 
|  877     gtk_window_present(GTK_WINDOW(window)); |  | 
|  878 } |  | 
|  879  |  | 
|  880 bool DraggedTabControllerGtk::AreTabsConsecutive() { |  | 
|  881   for (size_t i = 1; i < drag_data_->size(); ++i) { |  | 
|  882     if (drag_data_->get(i - 1)->source_model_index_ + 1 != |  | 
|  883         drag_data_->get(i)->source_model_index_) { |  | 
|  884       return false; |  | 
|  885     } |  | 
|  886   } |  | 
|  887   return true; |  | 
|  888 } |  | 
|  889  |  | 
|  890 gfx::NativeWindow DraggedTabControllerGtk::GetLocalProcessWindow( |  | 
|  891     const gfx::Point& screen_point) { |  | 
|  892   std::set<GtkWidget*> dragged_window; |  | 
|  893   dragged_window.insert(dragged_view_->widget()); |  | 
|  894   return GetLocalProcessWindowAtPoint( |  | 
|  895       gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(), |  | 
|  896       dragged_window); |  | 
|  897  |  | 
|  898 } |  | 
| OLD | NEW |