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 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include <algorithm>
     10 #include <string>
     11 
     12 #include "base/bind.h"
     13 #include "base/debug/trace_event.h"
     14 #include "base/i18n/rtl.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
     19 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
     20 #include "chrome/browser/autocomplete/autocomplete_match.h"
     21 #include "chrome/browser/chrome_notification_types.h"
     22 #include "chrome/browser/profiles/profile.h"
     23 #include "chrome/browser/themes/theme_properties.h"
     24 #include "chrome/browser/themes/theme_service.h"
     25 #include "chrome/browser/ui/browser.h"
     26 #include "chrome/browser/ui/browser_navigator.h"
     27 #include "chrome/browser/ui/browser_tabstrip.h"
     28 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     29 #include "chrome/browser/ui/gtk/custom_button.h"
     30 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     31 #include "chrome/browser/ui/gtk/gtk_util.h"
     32 #include "chrome/browser/ui/gtk/tabs/dragged_tab_controller_gtk.h"
     33 #include "chrome/browser/ui/gtk/tabs/tab_strip_menu_controller.h"
     34 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     35 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
     36 #include "content/public/browser/notification_source.h"
     37 #include "content/public/browser/user_metrics.h"
     38 #include "content/public/browser/web_contents.h"
     39 #include "grit/generated_resources.h"
     40 #include "grit/theme_resources.h"
     41 #include "grit/ui_resources.h"
     42 #include "ui/base/animation/animation_delegate.h"
     43 #include "ui/base/animation/slide_animation.h"
     44 #include "ui/base/dragdrop/gtk_dnd_util.h"
     45 #include "ui/base/gtk/gtk_compat.h"
     46 #include "ui/base/gtk/gtk_screen_util.h"
     47 #include "ui/base/l10n/l10n_util.h"
     48 #include "ui/base/resource/resource_bundle.h"
     49 #include "ui/gfx/gtk_util.h"
     50 #include "ui/gfx/image/image.h"
     51 #include "ui/gfx/point.h"
     52 
     53 using content::UserMetricsAction;
     54 using content::WebContents;
     55 
     56 namespace {
     57 
     58 const int kDefaultAnimationDurationMs = 100;
     59 const int kResizeLayoutAnimationDurationMs = 166;
     60 const int kReorderAnimationDurationMs = 166;
     61 const int kAnimateToBoundsDurationMs = 150;
     62 const int kMiniTabAnimationDurationMs = 150;
     63 
     64 const int kNewTabButtonHOffset = -5;
     65 const int kNewTabButtonVOffset = 5;
     66 
     67 // The delay between when the mouse leaves the tabstrip and the resize animation
     68 // is started.
     69 const int kResizeTabsTimeMs = 300;
     70 
     71 // A very short time just to make sure we don't clump up our Layout() calls
     72 // during slow window resizes.
     73 const int kLayoutAfterSizeAllocateMs = 10;
     74 
     75 // The range outside of the tabstrip where the pointer must enter/leave to
     76 // start/stop the resize animation.
     77 const int kTabStripAnimationVSlop = 40;
     78 
     79 const int kHorizontalMoveThreshold = 16;  // pixels
     80 
     81 // The horizontal offset from one tab to the next, which results in overlapping
     82 // tabs.
     83 const int kTabHOffset = -16;
     84 
     85 // Inverse ratio of the width of a tab edge to the width of the tab. When
     86 // hovering over the left or right edge of a tab, the drop indicator will
     87 // point between tabs.
     88 const int kTabEdgeRatioInverse = 4;
     89 
     90 // Size of the drop indicator.
     91 static int drop_indicator_width;
     92 static int drop_indicator_height;
     93 
     94 inline int Round(double x) {
     95   return static_cast<int>(x + 0.5);
     96 }
     97 
     98 // widget->allocation is not guaranteed to be set.  After window creation,
     99 // we pick up the normal bounds by connecting to the configure-event signal.
    100 gfx::Rect GetInitialWidgetBounds(GtkWidget* widget) {
    101   GtkRequisition request;
    102   gtk_widget_size_request(widget, &request);
    103   return gfx::Rect(0, 0, request.width, request.height);
    104 }
    105 
    106 // Sort rectangles based on their x position.  We don't care about y position
    107 // so we don't bother breaking ties.
    108 int CompareGdkRectangles(const void* p1, const void* p2) {
    109   int p1_x = static_cast<const GdkRectangle*>(p1)->x;
    110   int p2_x = static_cast<const GdkRectangle*>(p2)->x;
    111   if (p1_x < p2_x)
    112     return -1;
    113   else if (p1_x == p2_x)
    114     return 0;
    115   return 1;
    116 }
    117 
    118 bool GdkRectMatchesTabFaviconBounds(const GdkRectangle& gdk_rect, TabGtk* tab) {
    119   gfx::Rect favicon_bounds = tab->favicon_bounds();
    120   return gdk_rect.x == favicon_bounds.x() + tab->x() &&
    121       gdk_rect.y == favicon_bounds.y() + tab->y() &&
    122       gdk_rect.width == favicon_bounds.width() &&
    123       gdk_rect.height == favicon_bounds.height();
    124 }
    125 
    126 }  // namespace
    127 
    128 ////////////////////////////////////////////////////////////////////////////////
    129 //
    130 // TabAnimation
    131 //
    132 //  A base class for all TabStrip animations.
    133 //
    134 class TabStripGtk::TabAnimation : public ui::AnimationDelegate {
    135  public:
    136   friend class TabStripGtk;
    137 
    138   // Possible types of animation.
    139   enum Type {
    140     INSERT,
    141     REMOVE,
    142     MOVE,
    143     RESIZE,
    144     MINI,
    145     MINI_MOVE
    146   };
    147 
    148   TabAnimation(TabStripGtk* tabstrip, Type type)
    149       : tabstrip_(tabstrip),
    150         animation_(this),
    151         start_selected_width_(0),
    152         start_unselected_width_(0),
    153         end_selected_width_(0),
    154         end_unselected_width_(0),
    155         layout_on_completion_(false),
    156         type_(type) {
    157   }
    158   virtual ~TabAnimation() {}
    159 
    160   Type type() const { return type_; }
    161 
    162   void Start() {
    163     animation_.SetSlideDuration(GetDuration());
    164     animation_.SetTweenType(ui::Tween::EASE_OUT);
    165     if (!animation_.IsShowing()) {
    166       animation_.Reset();
    167       animation_.Show();
    168     }
    169   }
    170 
    171   void Stop() {
    172     animation_.Stop();
    173   }
    174 
    175   void set_layout_on_completion(bool layout_on_completion) {
    176     layout_on_completion_ = layout_on_completion;
    177   }
    178 
    179   // Retrieves the width for the Tab at the specified index if an animation is
    180   // active.
    181   static double GetCurrentTabWidth(TabStripGtk* tabstrip,
    182                                    TabStripGtk::TabAnimation* animation,
    183                                    int index) {
    184     TabGtk* tab = tabstrip->GetTabAt(index);
    185     double tab_width;
    186     if (tab->mini()) {
    187       tab_width = TabGtk::GetMiniWidth();
    188     } else {
    189       double unselected, selected;
    190       tabstrip->GetCurrentTabWidths(&unselected, &selected);
    191       tab_width = tab->IsActive() ? selected : unselected;
    192     }
    193 
    194     if (animation) {
    195       double specified_tab_width = animation->GetWidthForTab(index);
    196       if (specified_tab_width != -1)
    197         tab_width = specified_tab_width;
    198     }
    199 
    200     return tab_width;
    201   }
    202 
    203   // Overridden from ui::AnimationDelegate:
    204   virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
    205     tabstrip_->AnimationLayout(end_unselected_width_);
    206   }
    207 
    208   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
    209     tabstrip_->FinishAnimation(this, layout_on_completion_);
    210     // This object is destroyed now, so we can't do anything else after this.
    211   }
    212 
    213   virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE {
    214     AnimationEnded(animation);
    215   }
    216 
    217   // Returns the gap before the tab at the specified index. Subclass if during
    218   // an animation you need to insert a gap before a tab.
    219   virtual double GetGapWidth(int index) {
    220     return 0;
    221   }
    222 
    223  protected:
    224   // Returns the duration of the animation.
    225   virtual int GetDuration() const {
    226     return kDefaultAnimationDurationMs;
    227   }
    228 
    229   // Subclasses override to return the width of the Tab at the specified index
    230   // at the current animation frame. -1 indicates the default width should be
    231   // used for the Tab.
    232   virtual double GetWidthForTab(int index) const {
    233     return -1;  // Use default.
    234   }
    235 
    236   // Figure out the desired start and end widths for the specified pre- and
    237   // post- animation tab counts.
    238   void GenerateStartAndEndWidths(int start_tab_count, int end_tab_count,
    239                                  int start_mini_count,
    240                                  int end_mini_count) {
    241     tabstrip_->GetDesiredTabWidths(start_tab_count, start_mini_count,
    242                                    &start_unselected_width_,
    243                                    &start_selected_width_);
    244     double standard_tab_width =
    245         static_cast<double>(TabRendererGtk::GetStandardSize().width());
    246 
    247     if ((end_tab_count - start_tab_count) > 0 &&
    248         start_unselected_width_ < standard_tab_width) {
    249       double minimum_tab_width = static_cast<double>(
    250           TabRendererGtk::GetMinimumUnselectedSize().width());
    251       start_unselected_width_ -= minimum_tab_width / start_tab_count;
    252     }
    253 
    254     tabstrip_->GenerateIdealBounds();
    255     tabstrip_->GetDesiredTabWidths(end_tab_count, end_mini_count,
    256                                    &end_unselected_width_,
    257                                    &end_selected_width_);
    258   }
    259 
    260   TabStripGtk* tabstrip_;
    261   ui::SlideAnimation animation_;
    262 
    263   double start_selected_width_;
    264   double start_unselected_width_;
    265   double end_selected_width_;
    266   double end_unselected_width_;
    267 
    268  private:
    269   // True if a complete re-layout is required upon completion of the animation.
    270   // Subclasses set this if they don't perform a complete layout
    271   // themselves and canceling the animation may leave the strip in an
    272   // inconsistent state.
    273   bool layout_on_completion_;
    274 
    275   const Type type_;
    276 
    277   DISALLOW_COPY_AND_ASSIGN(TabAnimation);
    278 };
    279 
    280 ////////////////////////////////////////////////////////////////////////////////
    281 
    282 // Handles insertion of a Tab at |index|.
    283 class InsertTabAnimation : public TabStripGtk::TabAnimation {
    284  public:
    285   InsertTabAnimation(TabStripGtk* tabstrip, int index)
    286       : TabAnimation(tabstrip, INSERT),
    287         index_(index) {
    288     int tab_count = tabstrip->GetTabCount();
    289     int end_mini_count = tabstrip->GetMiniTabCount();
    290     int start_mini_count = end_mini_count;
    291     if (index < end_mini_count)
    292       start_mini_count--;
    293     GenerateStartAndEndWidths(tab_count - 1, tab_count, start_mini_count,
    294                               end_mini_count);
    295   }
    296   virtual ~InsertTabAnimation() {}
    297 
    298  protected:
    299   // Overridden from TabStripGtk::TabAnimation:
    300   virtual double GetWidthForTab(int index) const OVERRIDE {
    301     if (index == index_) {
    302       bool is_selected = tabstrip_->model()->active_index() == index;
    303       double start_width, target_width;
    304       if (index < tabstrip_->GetMiniTabCount()) {
    305         start_width = TabGtk::GetMinimumSelectedSize().width();
    306         target_width = TabGtk::GetMiniWidth();
    307       } else {
    308         target_width =
    309             is_selected ? end_unselected_width_ : end_selected_width_;
    310         start_width =
    311             is_selected ? TabGtk::GetMinimumSelectedSize().width() :
    312                           TabGtk::GetMinimumUnselectedSize().width();
    313       }
    314 
    315       double delta = target_width - start_width;
    316       if (delta > 0)
    317         return start_width + (delta * animation_.GetCurrentValue());
    318 
    319       return start_width;
    320     }
    321 
    322     if (tabstrip_->GetTabAt(index)->mini())
    323       return TabGtk::GetMiniWidth();
    324 
    325     if (tabstrip_->GetTabAt(index)->IsActive()) {
    326       double delta = end_selected_width_ - start_selected_width_;
    327       return start_selected_width_ + (delta * animation_.GetCurrentValue());
    328     }
    329 
    330     double delta = end_unselected_width_ - start_unselected_width_;
    331     return start_unselected_width_ + (delta * animation_.GetCurrentValue());
    332   }
    333 
    334  private:
    335   int index_;
    336 
    337   DISALLOW_COPY_AND_ASSIGN(InsertTabAnimation);
    338 };
    339 
    340 ////////////////////////////////////////////////////////////////////////////////
    341 
    342 // Handles removal of a Tab from |index|
    343 class RemoveTabAnimation : public TabStripGtk::TabAnimation {
    344  public:
    345   RemoveTabAnimation(TabStripGtk* tabstrip, int index, WebContents* contents)
    346       : TabAnimation(tabstrip, REMOVE),
    347         index_(index) {
    348     int tab_count = tabstrip->GetTabCount();
    349     int start_mini_count = tabstrip->GetMiniTabCount();
    350     int end_mini_count = start_mini_count;
    351     if (index < start_mini_count)
    352       end_mini_count--;
    353     GenerateStartAndEndWidths(tab_count, tab_count - 1, start_mini_count,
    354                               end_mini_count);
    355     // If the last non-mini-tab is being removed we force a layout on
    356     // completion. This is necessary as the value returned by GetTabHOffset
    357     // changes once the tab is actually removed (which happens at the end of
    358     // the animation), and unless we layout GetTabHOffset won't be called after
    359     // the removal.
    360     // We do the same when the last mini-tab is being removed for the same
    361     // reason.
    362     set_layout_on_completion(start_mini_count > 0 &&
    363                              (end_mini_count == 0 ||
    364                               (start_mini_count == end_mini_count &&
    365                                tab_count == start_mini_count + 1)));
    366   }
    367 
    368   virtual ~RemoveTabAnimation() {}
    369 
    370   // Returns the index of the tab being removed.
    371   int index() const { return index_; }
    372 
    373  protected:
    374   // Overridden from TabStripGtk::TabAnimation:
    375   virtual double GetWidthForTab(int index) const OVERRIDE {
    376     TabGtk* tab = tabstrip_->GetTabAt(index);
    377 
    378     if (index == index_) {
    379       // The tab(s) being removed are gradually shrunken depending on the state
    380       // of the animation.
    381       if (tab->mini()) {
    382         return animation_.CurrentValueBetween(TabGtk::GetMiniWidth(),
    383                                               -kTabHOffset);
    384       }
    385 
    386       // Removed animated Tabs are never selected.
    387       double start_width = start_unselected_width_;
    388       // Make sure target_width is at least abs(kTabHOffset), otherwise if
    389       // less than kTabHOffset during layout tabs get negatively offset.
    390       double target_width =
    391           std::max(abs(kTabHOffset),
    392                    TabGtk::GetMinimumUnselectedSize().width() + kTabHOffset);
    393       return animation_.CurrentValueBetween(start_width, target_width);
    394     }
    395 
    396     if (tab->mini())
    397       return TabGtk::GetMiniWidth();
    398 
    399     if (tabstrip_->available_width_for_tabs_ != -1 &&
    400         index_ != tabstrip_->GetTabCount() - 1) {
    401       return TabStripGtk::TabAnimation::GetWidthForTab(index);
    402     }
    403 
    404     // All other tabs are sized according to the start/end widths specified at
    405     // the start of the animation.
    406     if (tab->IsActive()) {
    407       double delta = end_selected_width_ - start_selected_width_;
    408       return start_selected_width_ + (delta * animation_.GetCurrentValue());
    409     }
    410 
    411     double delta = end_unselected_width_ - start_unselected_width_;
    412     return start_unselected_width_ + (delta * animation_.GetCurrentValue());
    413   }
    414 
    415   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
    416     tabstrip_->RemoveTabAt(index_);
    417     TabStripGtk::TabAnimation::AnimationEnded(animation);
    418   }
    419 
    420  private:
    421   int index_;
    422 
    423   DISALLOW_COPY_AND_ASSIGN(RemoveTabAnimation);
    424 };
    425 
    426 ////////////////////////////////////////////////////////////////////////////////
    427 
    428 // Handles the movement of a Tab from one position to another.
    429 class MoveTabAnimation : public TabStripGtk::TabAnimation {
    430  public:
    431   MoveTabAnimation(TabStripGtk* tabstrip, int tab_a_index, int tab_b_index)
    432       : TabAnimation(tabstrip, MOVE),
    433         start_tab_a_bounds_(tabstrip_->GetIdealBounds(tab_b_index)),
    434         start_tab_b_bounds_(tabstrip_->GetIdealBounds(tab_a_index)) {
    435     tab_a_ = tabstrip_->GetTabAt(tab_a_index);
    436     tab_b_ = tabstrip_->GetTabAt(tab_b_index);
    437 
    438     // Since we don't do a full TabStrip re-layout, we need to force a full
    439     // layout upon completion since we're not guaranteed to be in a good state
    440     // if for example the animation is canceled.
    441     set_layout_on_completion(true);
    442   }
    443   virtual ~MoveTabAnimation() {}
    444 
    445   // Overridden from ui::AnimationDelegate:
    446   virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
    447     // Position Tab A
    448     double distance = start_tab_b_bounds_.x() - start_tab_a_bounds_.x();
    449     double delta = distance * animation_.GetCurrentValue();
    450     double new_x = start_tab_a_bounds_.x() + delta;
    451     gfx::Rect bounds(Round(new_x), start_tab_a_bounds_.y(), tab_a_->width(),
    452         tab_a_->height());
    453     tabstrip_->SetTabBounds(tab_a_, bounds);
    454 
    455     // Position Tab B
    456     distance = start_tab_a_bounds_.x() - start_tab_b_bounds_.x();
    457     delta = distance * animation_.GetCurrentValue();
    458     new_x = start_tab_b_bounds_.x() + delta;
    459     bounds = gfx::Rect(Round(new_x), start_tab_b_bounds_.y(), tab_b_->width(),
    460         tab_b_->height());
    461     tabstrip_->SetTabBounds(tab_b_, bounds);
    462   }
    463 
    464  protected:
    465   // Overridden from TabStripGtk::TabAnimation:
    466   virtual int GetDuration() const OVERRIDE {
    467     return kReorderAnimationDurationMs;
    468   }
    469 
    470  private:
    471   // The two tabs being exchanged.
    472   TabGtk* tab_a_;
    473   TabGtk* tab_b_;
    474 
    475   // ...and their bounds.
    476   gfx::Rect start_tab_a_bounds_;
    477   gfx::Rect start_tab_b_bounds_;
    478 
    479   DISALLOW_COPY_AND_ASSIGN(MoveTabAnimation);
    480 };
    481 
    482 ////////////////////////////////////////////////////////////////////////////////
    483 
    484 // Handles the animated resize layout of the entire TabStrip from one width
    485 // to another.
    486 class ResizeLayoutAnimation : public TabStripGtk::TabAnimation {
    487  public:
    488   explicit ResizeLayoutAnimation(TabStripGtk* tabstrip)
    489       : TabAnimation(tabstrip, RESIZE) {
    490     int tab_count = tabstrip->GetTabCount();
    491     int mini_tab_count = tabstrip->GetMiniTabCount();
    492     GenerateStartAndEndWidths(tab_count, tab_count, mini_tab_count,
    493                               mini_tab_count);
    494     InitStartState();
    495   }
    496   virtual ~ResizeLayoutAnimation() {}
    497 
    498   // Overridden from ui::AnimationDelegate:
    499   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
    500     tabstrip_->needs_resize_layout_ = false;
    501     TabStripGtk::TabAnimation::AnimationEnded(animation);
    502   }
    503 
    504  protected:
    505   // Overridden from TabStripGtk::TabAnimation:
    506   virtual int GetDuration() const OVERRIDE {
    507     return kResizeLayoutAnimationDurationMs;
    508   }
    509 
    510   virtual double GetWidthForTab(int index) const OVERRIDE {
    511     TabGtk* tab = tabstrip_->GetTabAt(index);
    512 
    513     if (tab->mini())
    514       return TabGtk::GetMiniWidth();
    515 
    516     if (tab->IsActive()) {
    517       return animation_.CurrentValueBetween(start_selected_width_,
    518                                             end_selected_width_);
    519     }
    520 
    521     return animation_.CurrentValueBetween(start_unselected_width_,
    522                                           end_unselected_width_);
    523   }
    524 
    525  private:
    526   // We need to start from the current widths of the Tabs as they were last
    527   // laid out, _not_ the last known good state, which is what'll be done if we
    528   // don't measure the Tab sizes here and just go with the default TabAnimation
    529   // behavior...
    530   void InitStartState() {
    531     for (int i = 0; i < tabstrip_->GetTabCount(); ++i) {
    532       TabGtk* current_tab = tabstrip_->GetTabAt(i);
    533       if (!current_tab->mini()) {
    534         if (current_tab->IsActive()) {
    535           start_selected_width_ = current_tab->width();
    536         } else {
    537           start_unselected_width_ = current_tab->width();
    538         }
    539       }
    540     }
    541   }
    542 
    543   DISALLOW_COPY_AND_ASSIGN(ResizeLayoutAnimation);
    544 };
    545 
    546 // Handles a tabs mini-state changing while the tab does not change position
    547 // in the model.
    548 class MiniTabAnimation : public TabStripGtk::TabAnimation {
    549  public:
    550   MiniTabAnimation(TabStripGtk* tabstrip, int index)
    551       : TabAnimation(tabstrip, MINI),
    552         index_(index) {
    553     int tab_count = tabstrip->GetTabCount();
    554     int start_mini_count = tabstrip->GetMiniTabCount();
    555     int end_mini_count = start_mini_count;
    556     if (tabstrip->GetTabAt(index)->mini())
    557       start_mini_count--;
    558     else
    559       start_mini_count++;
    560     tabstrip_->GetTabAt(index)->set_animating_mini_change(true);
    561     GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
    562                               end_mini_count);
    563   }
    564 
    565  protected:
    566   // Overridden from TabStripGtk::TabAnimation:
    567   virtual int GetDuration() const OVERRIDE {
    568     return kMiniTabAnimationDurationMs;
    569   }
    570 
    571   virtual double GetWidthForTab(int index) const OVERRIDE {
    572     TabGtk* tab = tabstrip_->GetTabAt(index);
    573 
    574     if (index == index_) {
    575       if (tab->mini()) {
    576         return animation_.CurrentValueBetween(
    577             start_selected_width_,
    578             static_cast<double>(TabGtk::GetMiniWidth()));
    579       } else {
    580         return animation_.CurrentValueBetween(
    581             static_cast<double>(TabGtk::GetMiniWidth()),
    582             end_selected_width_);
    583       }
    584     } else if (tab->mini()) {
    585       return TabGtk::GetMiniWidth();
    586     }
    587 
    588     if (tab->IsActive()) {
    589       return animation_.CurrentValueBetween(start_selected_width_,
    590                                             end_selected_width_);
    591     }
    592 
    593     return animation_.CurrentValueBetween(start_unselected_width_,
    594                                           end_unselected_width_);
    595   }
    596 
    597  private:
    598   // Index of the tab whose mini-state changed.
    599   int index_;
    600 
    601   DISALLOW_COPY_AND_ASSIGN(MiniTabAnimation);
    602 };
    603 
    604 ////////////////////////////////////////////////////////////////////////////////
    605 
    606 // Handles the animation when a tabs mini-state changes and the tab moves as a
    607 // result.
    608 class MiniMoveAnimation : public TabStripGtk::TabAnimation {
    609  public:
    610   MiniMoveAnimation(TabStripGtk* tabstrip,
    611                     int from_index,
    612                     int to_index,
    613                     const gfx::Rect& start_bounds)
    614       : TabAnimation(tabstrip, MINI_MOVE),
    615         tab_(tabstrip->GetTabAt(to_index)),
    616         start_bounds_(start_bounds),
    617         from_index_(from_index),
    618         to_index_(to_index) {
    619     int tab_count = tabstrip->GetTabCount();
    620     int start_mini_count = tabstrip->GetMiniTabCount();
    621     int end_mini_count = start_mini_count;
    622     if (tabstrip->GetTabAt(to_index)->mini())
    623       start_mini_count--;
    624     else
    625       start_mini_count++;
    626     GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
    627                               end_mini_count);
    628     target_bounds_ = tabstrip->GetIdealBounds(to_index);
    629     tab_->set_animating_mini_change(true);
    630   }
    631 
    632   // Overridden from ui::AnimationDelegate:
    633   virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
    634     // Do the normal layout.
    635     TabAnimation::AnimationProgressed(animation);
    636 
    637     // Then special case the position of the tab being moved.
    638     int x = animation_.CurrentValueBetween(start_bounds_.x(),
    639                                            target_bounds_.x());
    640     int width = animation_.CurrentValueBetween(start_bounds_.width(),
    641                                                target_bounds_.width());
    642     gfx::Rect tab_bounds(x, start_bounds_.y(), width,
    643                          start_bounds_.height());
    644     tabstrip_->SetTabBounds(tab_, tab_bounds);
    645   }
    646 
    647   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
    648     tabstrip_->needs_resize_layout_ = false;
    649     TabStripGtk::TabAnimation::AnimationEnded(animation);
    650   }
    651 
    652   virtual double GetGapWidth(int index) OVERRIDE {
    653     if (to_index_ < from_index_) {
    654       // The tab was made mini.
    655       if (index == to_index_) {
    656         double current_size =
    657             animation_.CurrentValueBetween(0, target_bounds_.width());
    658         if (current_size < -kTabHOffset)
    659           return -(current_size + kTabHOffset);
    660       } else if (index == from_index_ + 1) {
    661         return animation_.CurrentValueBetween(start_bounds_.width(), 0);
    662       }
    663     } else {
    664       // The tab was was made a normal tab.
    665       if (index == from_index_) {
    666         return animation_.CurrentValueBetween(
    667             TabGtk::GetMiniWidth() + kTabHOffset, 0);
    668       }
    669     }
    670     return 0;
    671   }
    672 
    673  protected:
    674   // Overridden from TabStripGtk::TabAnimation:
    675   virtual int GetDuration() const OVERRIDE {
    676     return kReorderAnimationDurationMs;
    677   }
    678 
    679   virtual double GetWidthForTab(int index) const OVERRIDE {
    680     TabGtk* tab = tabstrip_->GetTabAt(index);
    681 
    682     if (index == to_index_)
    683       return animation_.CurrentValueBetween(0, target_bounds_.width());
    684 
    685     if (tab->mini())
    686       return TabGtk::GetMiniWidth();
    687 
    688     if (tab->IsActive()) {
    689       return animation_.CurrentValueBetween(start_selected_width_,
    690                                             end_selected_width_);
    691     }
    692 
    693     return animation_.CurrentValueBetween(start_unselected_width_,
    694                                           end_unselected_width_);
    695   }
    696 
    697  private:
    698   // The tab being moved.
    699   TabGtk* tab_;
    700 
    701   // Initial bounds of tab_.
    702   gfx::Rect start_bounds_;
    703 
    704   // Target bounds.
    705   gfx::Rect target_bounds_;
    706 
    707   // Start and end indices of the tab.
    708   int from_index_;
    709   int to_index_;
    710 
    711   DISALLOW_COPY_AND_ASSIGN(MiniMoveAnimation);
    712 };
    713 
    714 ////////////////////////////////////////////////////////////////////////////////
    715 // TabStripGtk, public:
    716 
    717 // static
    718 const int TabStripGtk::mini_to_non_mini_gap_ = 3;
    719 
    720 TabStripGtk::TabStripGtk(TabStripModel* model, BrowserWindowGtk* window)
    721     : current_unselected_width_(TabGtk::GetStandardSize().width()),
    722       current_selected_width_(TabGtk::GetStandardSize().width()),
    723       available_width_for_tabs_(-1),
    724       needs_resize_layout_(false),
    725       tab_vertical_offset_(0),
    726       model_(model),
    727       window_(window),
    728       theme_service_(GtkThemeService::GetFrom(model->profile())),
    729       weak_factory_(this),
    730       layout_factory_(this),
    731       added_as_message_loop_observer_(false),
    732       hover_tab_selector_(model) {
    733 }
    734 
    735 TabStripGtk::~TabStripGtk() {
    736   model_->RemoveObserver(this);
    737   tabstrip_.Destroy();
    738 
    739   // Free any remaining tabs.  This is needed to free the very last tab,
    740   // because it is not animated on close.  This also happens when all of the
    741   // tabs are closed at once.
    742   std::vector<TabData>::iterator iterator = tab_data_.begin();
    743   for (; iterator < tab_data_.end(); iterator++) {
    744     delete iterator->tab;
    745   }
    746 
    747   tab_data_.clear();
    748 
    749   // Make sure we unhook ourselves as a message loop observer so that we don't
    750   // crash in the case where the user closes the last tab in a window.
    751   RemoveMessageLoopObserver();
    752 }
    753 
    754 void TabStripGtk::Init() {
    755   model_->AddObserver(this);
    756 
    757   tabstrip_.Own(gtk_fixed_new());
    758   ViewIDUtil::SetID(tabstrip_.get(), VIEW_ID_TAB_STRIP);
    759   // We want the tab strip to be horizontally shrinkable, so that the Chrome
    760   // window can be resized freely.
    761   gtk_widget_set_size_request(tabstrip_.get(), 0,
    762                               TabGtk::GetMinimumUnselectedSize().height());
    763   gtk_widget_set_app_paintable(tabstrip_.get(), TRUE);
    764   gtk_drag_dest_set(tabstrip_.get(), GTK_DEST_DEFAULT_ALL,
    765                     NULL, 0,
    766                     static_cast<GdkDragAction>(
    767                         GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
    768   static const int targets[] = { ui::TEXT_URI_LIST,
    769                                  ui::NETSCAPE_URL,
    770                                  ui::TEXT_PLAIN,
    771                                  -1 };
    772   ui::SetDestTargetList(tabstrip_.get(), targets);
    773 
    774   g_signal_connect(tabstrip_.get(), "map",
    775                    G_CALLBACK(OnMapThunk), this);
    776   g_signal_connect(tabstrip_.get(), "expose-event",
    777                    G_CALLBACK(OnExposeThunk), this);
    778   g_signal_connect(tabstrip_.get(), "size-allocate",
    779                    G_CALLBACK(OnSizeAllocateThunk), this);
    780   g_signal_connect(tabstrip_.get(), "drag-motion",
    781                    G_CALLBACK(OnDragMotionThunk), this);
    782   g_signal_connect(tabstrip_.get(), "drag-drop",
    783                    G_CALLBACK(OnDragDropThunk), this);
    784   g_signal_connect(tabstrip_.get(), "drag-leave",
    785                    G_CALLBACK(OnDragLeaveThunk), this);
    786   g_signal_connect(tabstrip_.get(), "drag-data-received",
    787                    G_CALLBACK(OnDragDataReceivedThunk), this);
    788 
    789   newtab_button_.reset(MakeNewTabButton());
    790   newtab_surface_bounds_.SetRect(0, 0, newtab_button_->SurfaceWidth(),
    791                                  newtab_button_->SurfaceHeight());
    792 
    793   gtk_widget_show_all(tabstrip_.get());
    794 
    795   bounds_ = GetInitialWidgetBounds(tabstrip_.get());
    796 
    797   if (drop_indicator_width == 0) {
    798     // Direction doesn't matter, both images are the same size.
    799     GdkPixbuf* drop_image = GetDropArrowImage(true)->ToGdkPixbuf();
    800     drop_indicator_width = gdk_pixbuf_get_width(drop_image);
    801     drop_indicator_height = gdk_pixbuf_get_height(drop_image);
    802   }
    803 
    804   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
    805                  content::Source<ThemeService>(theme_service_));
    806   theme_service_->InitThemesFor(this);
    807 
    808   ViewIDUtil::SetDelegateForWidget(widget(), this);
    809 }
    810 
    811 void TabStripGtk::Show() {
    812   gtk_widget_show(tabstrip_.get());
    813 }
    814 
    815 void TabStripGtk::Hide() {
    816   gtk_widget_hide(tabstrip_.get());
    817 }
    818 
    819 bool TabStripGtk::IsActiveDropTarget() const {
    820   for (int i = 0; i < GetTabCount(); ++i) {
    821     TabGtk* tab = GetTabAt(i);
    822     if (tab->dragging())
    823       return true;
    824   }
    825   return false;
    826 }
    827 
    828 void TabStripGtk::Layout() {
    829   // Called from:
    830   // - window resize
    831   // - animation completion
    832   StopAnimation();
    833 
    834   GenerateIdealBounds();
    835   int tab_count = GetTabCount();
    836   int tab_right = 0;
    837   for (int i = 0; i < tab_count; ++i) {
    838     const gfx::Rect& bounds = tab_data_.at(i).ideal_bounds;
    839     TabGtk* tab = GetTabAt(i);
    840     tab->set_animating_mini_change(false);
    841     tab->set_vertical_offset(tab_vertical_offset_);
    842     SetTabBounds(tab, bounds);
    843     tab_right = bounds.right();
    844     tab_right += GetTabHOffset(i + 1);
    845   }
    846 
    847   LayoutNewTabButton(static_cast<double>(tab_right), current_unselected_width_);
    848 }
    849 
    850 void TabStripGtk::SchedulePaint() {
    851   gtk_widget_queue_draw(tabstrip_.get());
    852 }
    853 
    854 void TabStripGtk::SetBounds(const gfx::Rect& bounds) {
    855   bounds_ = bounds;
    856 }
    857 
    858 void TabStripGtk::UpdateLoadingAnimations() {
    859   for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
    860     TabGtk* current_tab = GetTabAt(i);
    861     if (current_tab->closing()) {
    862       --index;
    863     } else {
    864       TabRendererGtk::AnimationState state;
    865       content::WebContents* web_contents = model_->GetWebContentsAt(index);
    866       if (!web_contents|| !web_contents->IsLoading()) {
    867         state = TabGtk::ANIMATION_NONE;
    868       } else if (web_contents->IsWaitingForResponse()) {
    869         state = TabGtk::ANIMATION_WAITING;
    870       } else {
    871         state = TabGtk::ANIMATION_LOADING;
    872       }
    873       if (current_tab->ValidateLoadingAnimation(state)) {
    874         // Queue the tab's icon area to be repainted.
    875         gfx::Rect favicon_bounds = current_tab->favicon_bounds();
    876         gtk_widget_queue_draw_area(tabstrip_.get(),
    877             favicon_bounds.x() + current_tab->x(),
    878             favicon_bounds.y() + current_tab->y(),
    879             favicon_bounds.width(),
    880             favicon_bounds.height());
    881       }
    882     }
    883   }
    884 }
    885 
    886 bool TabStripGtk::IsCompatibleWith(TabStripGtk* other) {
    887   return model_->profile() == other->model()->profile();
    888 }
    889 
    890 bool TabStripGtk::IsAnimating() const {
    891   return active_animation_.get() != NULL;
    892 }
    893 
    894 void TabStripGtk::DestroyDragController() {
    895   drag_controller_.reset();
    896 }
    897 
    898 void TabStripGtk::DestroyDraggedTab(TabGtk* tab) {
    899   // We could be running an animation that references this Tab.
    900   StopAnimation();
    901 
    902   // Make sure we leave the tab_data_ vector in a consistent state, otherwise
    903   // we'll be pointing to tabs that have been deleted and removed from the
    904   // child view list.
    905   std::vector<TabData>::iterator it = tab_data_.begin();
    906   for (; it != tab_data_.end(); ++it) {
    907     if (it->tab == tab) {
    908       if (!model_->closing_all())
    909         NOTREACHED() << "Leaving in an inconsistent state!";
    910       tab_data_.erase(it);
    911       break;
    912     }
    913   }
    914 
    915   gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), tab->widget());
    916   // If we delete the dragged source tab here, the DestroyDragWidget posted
    917   // task will be run after the tab is deleted, leading to a crash.
    918   base::MessageLoop::current()->DeleteSoon(FROM_HERE, tab);
    919 
    920   // Force a layout here, because if we've just quickly drag detached a Tab,
    921   // the stopping of the active animation above may have left the TabStrip in a
    922   // bad (visual) state.
    923   Layout();
    924 }
    925 
    926 gfx::Rect TabStripGtk::GetIdealBounds(int index) {
    927   DCHECK(index >= 0 && index < GetTabCount());
    928   return tab_data_.at(index).ideal_bounds;
    929 }
    930 
    931 void TabStripGtk::SetVerticalOffset(int offset) {
    932   tab_vertical_offset_ = offset;
    933   Layout();
    934 }
    935 
    936 gfx::Point TabStripGtk::GetTabStripOriginForWidget(GtkWidget* target) {
    937   int x, y;
    938   GtkAllocation widget_allocation;
    939   gtk_widget_get_allocation(widget(), &widget_allocation);
    940   if (!gtk_widget_translate_coordinates(widget(), target,
    941       -widget_allocation.x, 0, &x, &y)) {
    942     // If the tab strip isn't showing, give the coordinates relative to the
    943     // toplevel instead.
    944     if (!gtk_widget_translate_coordinates(
    945         gtk_widget_get_toplevel(widget()), target, 0, 0, &x, &y)) {
    946       NOTREACHED();
    947     }
    948   }
    949   if (!gtk_widget_get_has_window(target)) {
    950     GtkAllocation target_allocation;
    951     gtk_widget_get_allocation(target, &target_allocation);
    952     x += target_allocation.x;
    953     y += target_allocation.y;
    954   }
    955   return gfx::Point(x, y);
    956 }
    957 
    958 ////////////////////////////////////////////////////////////////////////////////
    959 // ViewIDUtil::Delegate implementation
    960 
    961 GtkWidget* TabStripGtk::GetWidgetForViewID(ViewID view_id) {
    962   if (GetTabCount() > 0) {
    963     if (view_id == VIEW_ID_TAB_LAST) {
    964       return GetTabAt(GetTabCount() - 1)->widget();
    965     } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
    966       int index = view_id - VIEW_ID_TAB_0;
    967       if (index >= 0 && index < GetTabCount()) {
    968         return GetTabAt(index)->widget();
    969       } else {
    970         return NULL;
    971       }
    972     }
    973   }
    974 
    975   return NULL;
    976 }
    977 
    978 ////////////////////////////////////////////////////////////////////////////////
    979 // TabStripGtk, TabStripModelObserver implementation:
    980 
    981 void TabStripGtk::TabInsertedAt(WebContents* contents,
    982                                 int index,
    983                                 bool foreground) {
    984   TRACE_EVENT0("ui::gtk", "TabStripGtk::TabInsertedAt");
    985 
    986   DCHECK(contents);
    987   DCHECK(index == TabStripModel::kNoTab || model_->ContainsIndex(index));
    988 
    989   StopAnimation();
    990 
    991   bool contains_tab = false;
    992   TabGtk* tab = NULL;
    993   // First see if this Tab is one that was dragged out of this TabStrip and is
    994   // now being dragged back in. In this case, the DraggedTabController actually
    995   // has the Tab already constructed and we can just insert it into our list
    996   // again.
    997   if (IsDragSessionActive()) {
    998     tab = drag_controller_->GetDraggedTabForContents(contents);
    999     if (tab) {
   1000       // If the Tab was detached, it would have been animated closed but not
   1001       // removed, so we need to reset this property.
   1002       tab->set_closing(false);
   1003       tab->ValidateLoadingAnimation(TabRendererGtk::ANIMATION_NONE);
   1004       tab->SetVisible(true);
   1005     }
   1006 
   1007     // See if we're already in the list. We don't want to add ourselves twice.
   1008     std::vector<TabData>::const_iterator iter = tab_data_.begin();
   1009     for (; iter != tab_data_.end() && !contains_tab; ++iter) {
   1010       if (iter->tab == tab)
   1011         contains_tab = true;
   1012     }
   1013   }
   1014 
   1015   if (!tab)
   1016     tab = new TabGtk(this);
   1017 
   1018   // Only insert if we're not already in the list.
   1019   if (!contains_tab) {
   1020     TabData d = { tab, gfx::Rect() };
   1021     tab_data_.insert(tab_data_.begin() + index, d);
   1022     tab->UpdateData(contents, model_->IsAppTab(index), false);
   1023   }
   1024   tab->set_mini(model_->IsMiniTab(index));
   1025   tab->set_app(model_->IsAppTab(index));
   1026   tab->SetBlocked(model_->IsTabBlocked(index));
   1027 
   1028   if (gtk_widget_get_parent(tab->widget()) != tabstrip_.get())
   1029     gtk_fixed_put(GTK_FIXED(tabstrip_.get()), tab->widget(), 0, 0);
   1030 
   1031   // Don't animate the first tab; it looks weird.
   1032   if (GetTabCount() > 1) {
   1033     StartInsertTabAnimation(index);
   1034     // We added the tab at 0x0, we need to force an animation step otherwise
   1035     // if GTK paints before the animation event the tab is painted at 0x0
   1036     // which is most likely not where it should be positioned.
   1037     active_animation_->AnimationProgressed(NULL);
   1038   } else {
   1039     Layout();
   1040   }
   1041 
   1042   ReStack();
   1043 }
   1044 
   1045 void TabStripGtk::TabDetachedAt(WebContents* contents, int index) {
   1046   GenerateIdealBounds();
   1047   StartRemoveTabAnimation(index, contents);
   1048   // Have to do this _after_ calling StartRemoveTabAnimation, so that any
   1049   // previous remove is completed fully and index is valid in sync with the
   1050   // model index.
   1051   GetTabAt(index)->set_closing(true);
   1052 }
   1053 
   1054 void TabStripGtk::ActiveTabChanged(WebContents* old_contents,
   1055                                    WebContents* new_contents,
   1056                                    int index,
   1057                                    int reason) {
   1058   TRACE_EVENT0("ui::gtk", "TabStripGtk::ActiveTabChanged");
   1059   ReStack();
   1060 }
   1061 
   1062 void TabStripGtk::TabSelectionChanged(TabStripModel* tab_strip_model,
   1063                                       const ui::ListSelectionModel& old_model) {
   1064   // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
   1065   // a different size to the selected ones.
   1066   bool tiny_tabs = current_unselected_width_ != current_selected_width_;
   1067   if (!IsAnimating() && (!needs_resize_layout_ || tiny_tabs))
   1068     Layout();
   1069 
   1070   if (model_->active_index() >= 0)
   1071     GetTabAt(model_->active_index())->SchedulePaint();
   1072 
   1073   if (old_model.active() >= 0) {
   1074     GetTabAt(old_model.active())->SchedulePaint();
   1075     GetTabAt(old_model.active())->StopMiniTabTitleAnimation();
   1076   }
   1077 
   1078   std::vector<int> indices_affected;
   1079   std::insert_iterator<std::vector<int> > it1(indices_affected,
   1080                                               indices_affected.begin());
   1081   std::set_symmetric_difference(
   1082       old_model.selected_indices().begin(),
   1083       old_model.selected_indices().end(),
   1084       model_->selection_model().selected_indices().begin(),
   1085       model_->selection_model().selected_indices().end(),
   1086       it1);
   1087   for (std::vector<int>::iterator it = indices_affected.begin();
   1088        it != indices_affected.end(); ++it) {
   1089     // SchedulePaint() has already been called for the active tab and
   1090     // the previously active tab (if it still exists).
   1091     if (*it != model_->active_index() && *it != old_model.active())
   1092       GetTabAtAdjustForAnimation(*it)->SchedulePaint();
   1093   }
   1094 
   1095   ui::ListSelectionModel::SelectedIndices no_longer_selected;
   1096   std::insert_iterator<std::vector<int> > it2(no_longer_selected,
   1097                                               no_longer_selected.begin());
   1098   std::set_difference(old_model.selected_indices().begin(),
   1099                       old_model.selected_indices().end(),
   1100                       model_->selection_model().selected_indices().begin(),
   1101                       model_->selection_model().selected_indices().end(),
   1102                       it2);
   1103   for (std::vector<int>::iterator it = no_longer_selected.begin();
   1104        it != no_longer_selected.end(); ++it) {
   1105     GetTabAtAdjustForAnimation(*it)->StopMiniTabTitleAnimation();
   1106   }
   1107 }
   1108 
   1109 void TabStripGtk::TabMoved(WebContents* contents,
   1110                            int from_index,
   1111                            int to_index) {
   1112   gfx::Rect start_bounds = GetIdealBounds(from_index);
   1113   TabGtk* tab = GetTabAt(from_index);
   1114   tab_data_.erase(tab_data_.begin() + from_index);
   1115   TabData data = {tab, gfx::Rect()};
   1116   tab->set_mini(model_->IsMiniTab(to_index));
   1117   tab->SetBlocked(model_->IsTabBlocked(to_index));
   1118   tab_data_.insert(tab_data_.begin() + to_index, data);
   1119   GenerateIdealBounds();
   1120   StartMoveTabAnimation(from_index, to_index);
   1121   ReStack();
   1122 }
   1123 
   1124 void TabStripGtk::TabChangedAt(WebContents* contents,
   1125                                int index,
   1126                                TabChangeType change_type) {
   1127   // Index is in terms of the model. Need to make sure we adjust that index in
   1128   // case we have an animation going.
   1129   TabGtk* tab = GetTabAtAdjustForAnimation(index);
   1130   if (change_type == TITLE_NOT_LOADING) {
   1131     if (tab->mini() && !tab->IsActive())
   1132       tab->StartMiniTabTitleAnimation();
   1133     // We'll receive another notification of the change asynchronously.
   1134     return;
   1135   }
   1136   tab->UpdateData(contents,
   1137                   model_->IsAppTab(index),
   1138                   change_type == LOADING_ONLY);
   1139   tab->UpdateFromModel();
   1140 }
   1141 
   1142 void TabStripGtk::TabReplacedAt(TabStripModel* tab_strip_model,
   1143                                 WebContents* old_contents,
   1144                                 WebContents* new_contents,
   1145                                 int index) {
   1146   TabChangedAt(new_contents, index, ALL);
   1147 }
   1148 
   1149 void TabStripGtk::TabMiniStateChanged(WebContents* contents, int index) {
   1150   // Don't do anything if we've already picked up the change from TabMoved.
   1151   if (GetTabAt(index)->mini() == model_->IsMiniTab(index))
   1152     return;
   1153 
   1154   GetTabAt(index)->set_mini(model_->IsMiniTab(index));
   1155   // Don't animate if the window isn't visible yet. The window won't be visible
   1156   // when dragging a mini-tab to a new window.
   1157   if (window_ && window_->window() &&
   1158       gtk_widget_get_visible(GTK_WIDGET(window_->window()))) {
   1159     StartMiniTabAnimation(index);
   1160   } else {
   1161     Layout();
   1162   }
   1163 }
   1164 
   1165 void TabStripGtk::TabBlockedStateChanged(WebContents* contents, int index) {
   1166   GetTabAt(index)->SetBlocked(model_->IsTabBlocked(index));
   1167 }
   1168 
   1169 ////////////////////////////////////////////////////////////////////////////////
   1170 // TabStripGtk, TabGtk::TabDelegate implementation:
   1171 
   1172 bool TabStripGtk::IsTabActive(const TabGtk* tab) const {
   1173   if (tab->closing())
   1174     return false;
   1175 
   1176   return GetIndexOfTab(tab) == model_->active_index();
   1177 }
   1178 
   1179 bool TabStripGtk::IsTabSelected(const TabGtk* tab) const {
   1180   if (tab->closing())
   1181     return false;
   1182 
   1183   return model_->IsTabSelected(GetIndexOfTab(tab));
   1184 }
   1185 
   1186 bool TabStripGtk::IsTabDetached(const TabGtk* tab) const {
   1187   if (drag_controller_.get())
   1188     return drag_controller_->IsTabDetached(tab);
   1189   return false;
   1190 }
   1191 
   1192 void TabStripGtk::GetCurrentTabWidths(double* unselected_width,
   1193                                       double* selected_width) const {
   1194   *unselected_width = current_unselected_width_;
   1195   *selected_width = current_selected_width_;
   1196 }
   1197 
   1198 bool TabStripGtk::IsTabPinned(const TabGtk* tab) const {
   1199   if (tab->closing())
   1200     return false;
   1201 
   1202   return model_->IsTabPinned(GetIndexOfTab(tab));
   1203 }
   1204 
   1205 void TabStripGtk::ActivateTab(TabGtk* tab) {
   1206   int index = GetIndexOfTab(tab);
   1207   if (model_->ContainsIndex(index))
   1208     model_->ActivateTabAt(index, true);
   1209 }
   1210 
   1211 void TabStripGtk::ToggleTabSelection(TabGtk* tab) {
   1212   int index = GetIndexOfTab(tab);
   1213   model_->ToggleSelectionAt(index);
   1214 }
   1215 
   1216 void TabStripGtk::ExtendTabSelection(TabGtk* tab) {
   1217   int index = GetIndexOfTab(tab);
   1218   if (model_->ContainsIndex(index))
   1219     model_->ExtendSelectionTo(index);
   1220 }
   1221 
   1222 void TabStripGtk::CloseTab(TabGtk* tab) {
   1223   int tab_index = GetIndexOfTab(tab);
   1224   if (model_->ContainsIndex(tab_index)) {
   1225     TabGtk* last_tab = GetTabAt(GetTabCount() - 1);
   1226     // Limit the width available to the TabStrip for laying out Tabs, so that
   1227     // Tabs are not resized until a later time (when the mouse pointer leaves
   1228     // the TabStrip).
   1229     available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
   1230     needs_resize_layout_ = true;
   1231     // We hook into the message loop in order to receive mouse move events when
   1232     // the mouse is outside of the tabstrip.  We unhook once the resize layout
   1233     // animation is started.
   1234     AddMessageLoopObserver();
   1235     model_->CloseWebContentsAt(tab_index,
   1236                                TabStripModel::CLOSE_USER_GESTURE |
   1237                                TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
   1238   }
   1239 }
   1240 
   1241 bool TabStripGtk::IsCommandEnabledForTab(
   1242     TabStripModel::ContextMenuCommand command_id, const TabGtk* tab) const {
   1243   int index = GetIndexOfTab(tab);
   1244   if (model_->ContainsIndex(index))
   1245     return model_->IsContextMenuCommandEnabled(index, command_id);
   1246   return false;
   1247 }
   1248 
   1249 void TabStripGtk::ExecuteCommandForTab(
   1250     TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
   1251   int index = GetIndexOfTab(tab);
   1252   if (model_->ContainsIndex(index))
   1253     model_->ExecuteContextMenuCommand(index, command_id);
   1254 }
   1255 
   1256 void TabStripGtk::StartHighlightTabsForCommand(
   1257     TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
   1258   if (command_id == TabStripModel::CommandCloseOtherTabs ||
   1259       command_id == TabStripModel::CommandCloseTabsToRight) {
   1260     NOTIMPLEMENTED();
   1261   }
   1262 }
   1263 
   1264 void TabStripGtk::StopHighlightTabsForCommand(
   1265     TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
   1266   if (command_id == TabStripModel::CommandCloseTabsToRight ||
   1267       command_id == TabStripModel::CommandCloseOtherTabs) {
   1268     // Just tell all Tabs to stop pulsing - it's safe.
   1269     StopAllHighlighting();
   1270   }
   1271 }
   1272 
   1273 void TabStripGtk::StopAllHighlighting() {
   1274   // TODO(jhawkins): Hook up animations.
   1275   NOTIMPLEMENTED();
   1276 }
   1277 
   1278 void TabStripGtk::MaybeStartDrag(TabGtk* tab, const gfx::Point& point) {
   1279   // Don't accidentally start any drag operations during animations if the
   1280   // mouse is down.
   1281   if (IsAnimating() || tab->closing() || !HasAvailableDragActions())
   1282     return;
   1283 
   1284   std::vector<TabGtk*> tabs;
   1285   for (size_t i = 0; i < model()->selection_model().size(); i++) {
   1286     TabGtk* selected_tab =
   1287         GetTabAtAdjustForAnimation(
   1288             model()->selection_model().selected_indices()[i]);
   1289     if (!selected_tab->closing())
   1290       tabs.push_back(selected_tab);
   1291   }
   1292 
   1293   drag_controller_.reset(new DraggedTabControllerGtk(this, tab, tabs));
   1294   drag_controller_->CaptureDragInfo(point);
   1295 }
   1296 
   1297 void TabStripGtk::ContinueDrag(GdkDragContext* context) {
   1298   // We can get called even if |MaybeStartDrag| wasn't called in the event of
   1299   // a TabStrip animation when the mouse button is down. In this case we should
   1300   // _not_ continue the drag because it can lead to weird bugs.
   1301   if (drag_controller_.get())
   1302     drag_controller_->Drag();
   1303 }
   1304 
   1305 bool TabStripGtk::EndDrag(bool canceled) {
   1306   return drag_controller_.get() ? drag_controller_->EndDrag(canceled) : false;
   1307 }
   1308 
   1309 bool TabStripGtk::HasAvailableDragActions() const {
   1310   return model_->delegate()->GetDragActions() != 0;
   1311 }
   1312 
   1313 GtkThemeService* TabStripGtk::GetThemeProvider() {
   1314   return theme_service_;
   1315 }
   1316 
   1317 TabStripMenuController* TabStripGtk::GetTabStripMenuControllerForTab(
   1318     TabGtk* tab) {
   1319   return new TabStripMenuController(tab, model(), GetIndexOfTab(tab));
   1320 }
   1321 
   1322 ///////////////////////////////////////////////////////////////////////////////
   1323 // TabStripGtk, MessageLoop::Observer implementation:
   1324 
   1325 void TabStripGtk::WillProcessEvent(GdkEvent* event) {
   1326   // Nothing to do.
   1327 }
   1328 
   1329 void TabStripGtk::DidProcessEvent(GdkEvent* event) {
   1330   switch (event->type) {
   1331     case GDK_MOTION_NOTIFY:
   1332     case GDK_LEAVE_NOTIFY:
   1333       HandleGlobalMouseMoveEvent();
   1334       break;
   1335     default:
   1336       break;
   1337   }
   1338 }
   1339 
   1340 ///////////////////////////////////////////////////////////////////////////////
   1341 // TabStripGtk, content::NotificationObserver implementation:
   1342 
   1343 void TabStripGtk::Observe(int type,
   1344                           const content::NotificationSource& source,
   1345                           const content::NotificationDetails& details) {
   1346   DCHECK_EQ(type, chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
   1347   SetNewTabButtonBackground();
   1348 }
   1349 
   1350 ////////////////////////////////////////////////////////////////////////////////
   1351 // TabStripGtk, private:
   1352 
   1353 int TabStripGtk::GetTabCount() const {
   1354   return static_cast<int>(tab_data_.size());
   1355 }
   1356 
   1357 int TabStripGtk::GetMiniTabCount() const {
   1358   int mini_count = 0;
   1359   for (size_t i = 0; i < tab_data_.size(); ++i) {
   1360     if (tab_data_[i].tab->mini())
   1361       mini_count++;
   1362     else
   1363       return mini_count;
   1364   }
   1365   return mini_count;
   1366 }
   1367 
   1368 int TabStripGtk::GetAvailableWidthForTabs(TabGtk* last_tab) const {
   1369   if (!base::i18n::IsRTL())
   1370     return last_tab->x() - bounds_.x() + last_tab->width();
   1371   else
   1372     return bounds_.width() - last_tab->x();
   1373 }
   1374 
   1375 int TabStripGtk::GetIndexOfTab(const TabGtk* tab) const {
   1376   for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
   1377     TabGtk* current_tab = GetTabAt(i);
   1378     if (current_tab->closing()) {
   1379       --index;
   1380     } else if (current_tab == tab) {
   1381       return index;
   1382     }
   1383   }
   1384   return -1;
   1385 }
   1386 
   1387 TabGtk* TabStripGtk::GetTabAt(int index) const {
   1388   DCHECK_GE(index, 0);
   1389   DCHECK_LT(index, GetTabCount());
   1390   return tab_data_.at(index).tab;
   1391 }
   1392 
   1393 TabGtk* TabStripGtk::GetTabAtAdjustForAnimation(int index) const {
   1394   if (active_animation_.get() &&
   1395       active_animation_->type() == TabAnimation::REMOVE &&
   1396       index >=
   1397       static_cast<RemoveTabAnimation*>(active_animation_.get())->index()) {
   1398     index++;
   1399   }
   1400   return GetTabAt(index);
   1401 }
   1402 
   1403 void TabStripGtk::RemoveTabAt(int index) {
   1404   TabGtk* removed = tab_data_.at(index).tab;
   1405 
   1406   // Remove the Tab from the TabStrip's list.
   1407   tab_data_.erase(tab_data_.begin() + index);
   1408 
   1409   if (!removed->dragging()) {
   1410     gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), removed->widget());
   1411     delete removed;
   1412   }
   1413 }
   1414 
   1415 void TabStripGtk::HandleGlobalMouseMoveEvent() {
   1416   if (!IsCursorInTabStripZone()) {
   1417     // Mouse moved outside the tab slop zone, start a timer to do a resize
   1418     // layout after a short while...
   1419     if (!weak_factory_.HasWeakPtrs()) {
   1420       base::MessageLoop::current()->PostDelayedTask(
   1421           FROM_HERE,
   1422           base::Bind(&TabStripGtk::ResizeLayoutTabs,
   1423                      weak_factory_.GetWeakPtr()),
   1424           base::TimeDelta::FromMilliseconds(kResizeTabsTimeMs));
   1425     }
   1426   } else {
   1427     // Mouse moved quickly out of the tab strip and then into it again, so
   1428     // cancel the timer so that the strip doesn't move when the mouse moves
   1429     // back over it.
   1430     weak_factory_.InvalidateWeakPtrs();
   1431   }
   1432 }
   1433 
   1434 void TabStripGtk::GenerateIdealBounds() {
   1435   int tab_count = GetTabCount();
   1436   double unselected, selected;
   1437   GetDesiredTabWidths(tab_count, GetMiniTabCount(), &unselected, &selected);
   1438 
   1439   current_unselected_width_ = unselected;
   1440   current_selected_width_ = selected;
   1441 
   1442   // NOTE: This currently assumes a tab's height doesn't differ based on
   1443   // selected state or the number of tabs in the strip!
   1444   int tab_height = TabGtk::GetStandardSize().height();
   1445   double tab_x = tab_start_x();
   1446   for (int i = 0; i < tab_count; ++i) {
   1447     TabGtk* tab = GetTabAt(i);
   1448     double tab_width = unselected;
   1449     if (tab->mini())
   1450       tab_width = TabGtk::GetMiniWidth();
   1451     else if (tab->IsActive())
   1452       tab_width = selected;
   1453     double end_of_tab = tab_x + tab_width;
   1454     int rounded_tab_x = Round(tab_x);
   1455     gfx::Rect state(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
   1456                     tab_height);
   1457     tab_data_.at(i).ideal_bounds = state;
   1458     tab_x = end_of_tab + GetTabHOffset(i + 1);
   1459   }
   1460 }
   1461 
   1462 void TabStripGtk::LayoutNewTabButton(double last_tab_right,
   1463                                      double unselected_width) {
   1464   GtkWidget* toplevel = gtk_widget_get_ancestor(widget(), GTK_TYPE_WINDOW);
   1465   bool is_maximized = false;
   1466   if (toplevel) {
   1467     GdkWindow* gdk_window = gtk_widget_get_window(toplevel);
   1468     is_maximized = (gdk_window_get_state(gdk_window) &
   1469                     GDK_WINDOW_STATE_MAXIMIZED) != 0;
   1470   }
   1471 
   1472   int y = is_maximized ? 0 : kNewTabButtonVOffset;
   1473   int height = newtab_surface_bounds_.height() + kNewTabButtonVOffset - y;
   1474 
   1475   gfx::Rect bounds(0, y, newtab_surface_bounds_.width(), height);
   1476   int delta = abs(Round(unselected_width) - TabGtk::GetStandardSize().width());
   1477   if (delta > 1 && !needs_resize_layout_) {
   1478     // We're shrinking tabs, so we need to anchor the New Tab button to the
   1479     // right edge of the TabStrip's bounds, rather than the right edge of the
   1480     // right-most Tab, otherwise it'll bounce when animating.
   1481     bounds.set_x(bounds_.width() - newtab_button_->WidgetAllocation().width);
   1482   } else {
   1483     bounds.set_x(Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset);
   1484   }
   1485   bounds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
   1486 
   1487   gtk_fixed_move(GTK_FIXED(tabstrip_.get()), newtab_button_->widget(),
   1488                  bounds.x(), bounds.y());
   1489   gtk_widget_set_size_request(newtab_button_->widget(), bounds.width(),
   1490                               bounds.height());
   1491 }
   1492 
   1493 void TabStripGtk::GetDesiredTabWidths(int tab_count,
   1494                                       int mini_tab_count,
   1495                                       double* unselected_width,
   1496                                       double* selected_width) const {
   1497   DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
   1498   const double min_unselected_width =
   1499       TabGtk::GetMinimumUnselectedSize().width();
   1500   const double min_selected_width =
   1501       TabGtk::GetMinimumSelectedSize().width();
   1502 
   1503   *unselected_width = min_unselected_width;
   1504   *selected_width = min_selected_width;
   1505 
   1506   if (tab_count == 0) {
   1507     // Return immediately to avoid divide-by-zero below.
   1508     return;
   1509   }
   1510 
   1511   // Determine how much space we can actually allocate to tabs.
   1512   GtkAllocation tabstrip_allocation;
   1513   gtk_widget_get_allocation(tabstrip_.get(), &tabstrip_allocation);
   1514   int available_width = tabstrip_allocation.width;
   1515   if (available_width_for_tabs_ < 0) {
   1516     available_width = bounds_.width();
   1517     available_width -=
   1518         (kNewTabButtonHOffset + newtab_button_->WidgetAllocation().width);
   1519   } else {
   1520     // Interesting corner case: if |available_width_for_tabs_| > the result
   1521     // of the calculation in the conditional arm above, the strip is in
   1522     // overflow.  We can either use the specified width or the true available
   1523     // width here; the first preserves the consistent "leave the last tab under
   1524     // the user's mouse so they can close many tabs" behavior at the cost of
   1525     // prolonging the glitchy appearance of the overflow state, while the second
   1526     // gets us out of overflow as soon as possible but forces the user to move
   1527     // their mouse for a few tabs' worth of closing.  We choose visual
   1528     // imperfection over behavioral imperfection and select the first option.
   1529     available_width = available_width_for_tabs_;
   1530   }
   1531 
   1532   if (mini_tab_count > 0) {
   1533     available_width -= mini_tab_count * (TabGtk::GetMiniWidth() + kTabHOffset);
   1534     tab_count -= mini_tab_count;
   1535     if (tab_count == 0) {
   1536       *selected_width = *unselected_width = TabGtk::GetStandardSize().width();
   1537       return;
   1538     }
   1539     // Account for gap between the last mini-tab and first normal tab.
   1540     available_width -= mini_to_non_mini_gap_;
   1541   }
   1542 
   1543   // Calculate the desired tab widths by dividing the available space into equal
   1544   // portions.  Don't let tabs get larger than the "standard width" or smaller
   1545   // than the minimum width for each type, respectively.
   1546   const int total_offset = kTabHOffset * (tab_count - 1);
   1547   const double desired_tab_width = std::min(
   1548       (static_cast<double>(available_width - total_offset) /
   1549        static_cast<double>(tab_count)),
   1550       static_cast<double>(TabGtk::GetStandardSize().width()));
   1551 
   1552   *unselected_width = std::max(desired_tab_width, min_unselected_width);
   1553   *selected_width = std::max(desired_tab_width, min_selected_width);
   1554 
   1555   // When there are multiple tabs, we'll have one selected and some unselected
   1556   // tabs.  If the desired width was between the minimum sizes of these types,
   1557   // try to shrink the tabs with the smaller minimum.  For example, if we have
   1558   // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5.  If
   1559   // selected tabs have a minimum width of 4 and unselected tabs have a minimum
   1560   // width of 1, the above code would set *unselected_width = 2.5,
   1561   // *selected_width = 4, which results in a total width of 11.5.  Instead, we
   1562   // want to set *unselected_width = 2, *selected_width = 4, for a total width
   1563   // of 10.
   1564   if (tab_count > 1) {
   1565     if ((min_unselected_width < min_selected_width) &&
   1566         (desired_tab_width < min_selected_width)) {
   1567       double calc_width =
   1568           static_cast<double>(
   1569               available_width - total_offset - min_selected_width) /
   1570           static_cast<double>(tab_count - 1);
   1571       *unselected_width = std::max(calc_width, min_unselected_width);
   1572     } else if ((min_unselected_width > min_selected_width) &&
   1573                (desired_tab_width < min_unselected_width)) {
   1574       *selected_width = std::max(available_width - total_offset -
   1575           (min_unselected_width * (tab_count - 1)), min_selected_width);
   1576     }
   1577   }
   1578 }
   1579 
   1580 int TabStripGtk::GetTabHOffset(int tab_index) {
   1581   if (tab_index < GetTabCount() && GetTabAt(tab_index - 1)->mini() &&
   1582       !GetTabAt(tab_index)->mini()) {
   1583     return mini_to_non_mini_gap_ + kTabHOffset;
   1584   }
   1585   return kTabHOffset;
   1586 }
   1587 
   1588 int TabStripGtk::tab_start_x() const {
   1589   return 0;
   1590 }
   1591 
   1592 void TabStripGtk::ResizeLayoutTabs() {
   1593   weak_factory_.InvalidateWeakPtrs();
   1594   layout_factory_.InvalidateWeakPtrs();
   1595 
   1596   // It is critically important that this is unhooked here, otherwise we will
   1597   // keep spying on messages forever.
   1598   RemoveMessageLoopObserver();
   1599 
   1600   available_width_for_tabs_ = -1;
   1601   int mini_tab_count = GetMiniTabCount();
   1602   if (mini_tab_count == GetTabCount()) {
   1603     // Only mini tabs, we know the tab widths won't have changed (all mini-tabs
   1604     // have the same width), so there is nothing to do.
   1605     return;
   1606   }
   1607   TabGtk* first_tab = GetTabAt(mini_tab_count);
   1608   double unselected, selected;
   1609   GetDesiredTabWidths(GetTabCount(), mini_tab_count, &unselected, &selected);
   1610   int w = Round(first_tab->IsActive() ? selected : unselected);
   1611 
   1612   // We only want to run the animation if we're not already at the desired
   1613   // size.
   1614   if (abs(first_tab->width() - w) > 1)
   1615     StartResizeLayoutAnimation();
   1616 }
   1617 
   1618 bool TabStripGtk::IsCursorInTabStripZone() const {
   1619   gfx::Point tabstrip_topleft;
   1620   gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &tabstrip_topleft);
   1621 
   1622   gfx::Rect bds = bounds();
   1623   bds.set_origin(tabstrip_topleft);
   1624   bds.set_height(bds.height() + kTabStripAnimationVSlop);
   1625 
   1626   GdkScreen* screen = gdk_screen_get_default();
   1627   GdkDisplay* display = gdk_screen_get_display(screen);
   1628   gint x, y;
   1629   gdk_display_get_pointer(display, NULL, &x, &y, NULL);
   1630   gfx::Point cursor_point(x, y);
   1631 
   1632   return bds.Contains(cursor_point);
   1633 }
   1634 
   1635 void TabStripGtk::ReStack() {
   1636   TRACE_EVENT0("ui::gtk", "TabStripGtk::ReStack");
   1637 
   1638   if (!gtk_widget_get_realized(tabstrip_.get())) {
   1639     // If the window isn't realized yet, we can't stack them yet. It will be
   1640     // done by the OnMap signal handler.
   1641     return;
   1642   }
   1643   int tab_count = GetTabCount();
   1644   TabGtk* active_tab = NULL;
   1645   for (int i = tab_count - 1; i >= 0; --i) {
   1646     TabGtk* tab = GetTabAt(i);
   1647     if (tab->IsActive())
   1648       active_tab = tab;
   1649     else
   1650       tab->Raise();
   1651   }
   1652   if (active_tab)
   1653     active_tab->Raise();
   1654 }
   1655 
   1656 void TabStripGtk::AddMessageLoopObserver() {
   1657   if (!added_as_message_loop_observer_) {
   1658     base::MessageLoopForUI::current()->AddObserver(this);
   1659     added_as_message_loop_observer_ = true;
   1660   }
   1661 }
   1662 
   1663 void TabStripGtk::RemoveMessageLoopObserver() {
   1664   if (added_as_message_loop_observer_) {
   1665     base::MessageLoopForUI::current()->RemoveObserver(this);
   1666     added_as_message_loop_observer_ = false;
   1667   }
   1668 }
   1669 
   1670 gfx::Rect TabStripGtk::GetDropBounds(int drop_index,
   1671                                      bool drop_before,
   1672                                      bool* is_beneath) {
   1673   DCHECK_NE(drop_index, -1);
   1674   int center_x;
   1675   if (drop_index < GetTabCount()) {
   1676     TabGtk* tab = GetTabAt(drop_index);
   1677     gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
   1678     // TODO(sky): update these for pinned tabs.
   1679     if (drop_before)
   1680       center_x = bounds.x() - (kTabHOffset / 2);
   1681     else
   1682       center_x = bounds.x() + (bounds.width() / 2);
   1683   } else {
   1684     TabGtk* last_tab = GetTabAt(drop_index - 1);
   1685     gfx::Rect bounds = last_tab->GetNonMirroredBounds(tabstrip_.get());
   1686     center_x = bounds.x() + bounds.width() + (kTabHOffset / 2);
   1687   }
   1688 
   1689   center_x = gtk_util::MirroredXCoordinate(tabstrip_.get(), center_x);
   1690 
   1691   // Determine the screen bounds.
   1692   gfx::Point drop_loc(center_x - drop_indicator_width / 2,
   1693                       -drop_indicator_height);
   1694   gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &drop_loc);
   1695   gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
   1696                         drop_indicator_height);
   1697 
   1698   // TODO(jhawkins): We always display the arrow underneath the tab because we
   1699   // don't have custom frame support yet.
   1700   *is_beneath = true;
   1701   if (*is_beneath)
   1702     drop_bounds.Offset(0, drop_bounds.height() + bounds().height());
   1703 
   1704   return drop_bounds;
   1705 }
   1706 
   1707 void TabStripGtk::UpdateDropIndex(GdkDragContext* context, gint x, gint y) {
   1708   // If the UI layout is right-to-left, we need to mirror the mouse
   1709   // coordinates since we calculate the drop index based on the
   1710   // original (and therefore non-mirrored) positions of the tabs.
   1711   x = gtk_util::MirroredXCoordinate(tabstrip_.get(), x);
   1712   // We don't allow replacing the urls of mini-tabs.
   1713   for (int i = GetMiniTabCount(); i < GetTabCount(); ++i) {
   1714     TabGtk* tab = GetTabAt(i);
   1715     gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
   1716     const int tab_max_x = bounds.x() + bounds.width();
   1717     const int hot_width = bounds.width() / kTabEdgeRatioInverse;
   1718     if (x < tab_max_x) {
   1719       if (x < bounds.x() + hot_width)
   1720         SetDropIndex(i, true);
   1721       else if (x >= tab_max_x - hot_width)
   1722         SetDropIndex(i + 1, true);
   1723       else
   1724         SetDropIndex(i, false);
   1725       return;
   1726     }
   1727   }
   1728 
   1729   // The drop isn't over a tab, add it to the end.
   1730   SetDropIndex(GetTabCount(), true);
   1731 }
   1732 
   1733 void TabStripGtk::SetDropIndex(int index, bool drop_before) {
   1734   bool is_beneath;
   1735   gfx::Rect drop_bounds = GetDropBounds(index, drop_before, &is_beneath);
   1736 
   1737   // Perform a delayed tab transition if hovering directly over a tab;
   1738   // otherwise, cancel the pending one.
   1739   if (index != -1 && !drop_before)
   1740     hover_tab_selector_.StartTabTransition(index);
   1741   else
   1742     hover_tab_selector_.CancelTabTransition();
   1743 
   1744   if (!drop_info_.get()) {
   1745     drop_info_.reset(new DropInfo(index, drop_before, !is_beneath));
   1746   } else {
   1747     if (!GTK_IS_WIDGET(drop_info_->container)) {
   1748       drop_info_->CreateContainer();
   1749     } else if (drop_info_->drop_index == index &&
   1750                drop_info_->drop_before == drop_before) {
   1751       return;
   1752     }
   1753 
   1754     drop_info_->drop_index = index;
   1755     drop_info_->drop_before = drop_before;
   1756     if (is_beneath == drop_info_->point_down) {
   1757       drop_info_->point_down = !is_beneath;
   1758       drop_info_->drop_arrow = GetDropArrowImage(drop_info_->point_down);
   1759     }
   1760   }
   1761 
   1762   gtk_window_move(GTK_WINDOW(drop_info_->container),
   1763                   drop_bounds.x(), drop_bounds.y());
   1764   gtk_window_resize(GTK_WINDOW(drop_info_->container),
   1765                     drop_bounds.width(), drop_bounds.height());
   1766 }
   1767 
   1768 bool TabStripGtk::CompleteDrop(const guchar* data, bool is_plain_text) {
   1769   if (!drop_info_.get())
   1770     return false;
   1771 
   1772   const int drop_index = drop_info_->drop_index;
   1773   const bool drop_before = drop_info_->drop_before;
   1774 
   1775   // Destroy the drop indicator.
   1776   drop_info_.reset();
   1777 
   1778   // Cancel any pending tab transition.
   1779   hover_tab_selector_.CancelTabTransition();
   1780 
   1781   GURL url;
   1782   if (is_plain_text) {
   1783     AutocompleteMatch match;
   1784     AutocompleteClassifierFactory::GetForProfile(model_->profile())->Classify(
   1785         UTF8ToUTF16(reinterpret_cast<const char*>(data)), false, false, &match,
   1786         NULL);
   1787     url = match.destination_url;
   1788   } else {
   1789     std::string url_string(reinterpret_cast<const char*>(data));
   1790     url = GURL(url_string.substr(0, url_string.find_first_of('\n')));
   1791   }
   1792   if (!url.is_valid())
   1793     return false;
   1794 
   1795   chrome::NavigateParams params(window()->browser(), url,
   1796                                 content::PAGE_TRANSITION_LINK);
   1797   params.tabstrip_index = drop_index;
   1798 
   1799   if (drop_before) {
   1800     params.disposition = NEW_FOREGROUND_TAB;
   1801   } else {
   1802     params.disposition = CURRENT_TAB;
   1803     params.source_contents = model_->GetWebContentsAt(drop_index);
   1804   }
   1805 
   1806   chrome::Navigate(&params);
   1807 
   1808   return true;
   1809 }
   1810 
   1811 // static
   1812 gfx::Image* TabStripGtk::GetDropArrowImage(bool is_down) {
   1813   return &ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
   1814       is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
   1815 }
   1816 
   1817 // TabStripGtk::DropInfo -------------------------------------------------------
   1818 
   1819 TabStripGtk::DropInfo::DropInfo(int drop_index, bool drop_before,
   1820                                 bool point_down)
   1821     : drop_index(drop_index),
   1822       drop_before(drop_before),
   1823       point_down(point_down) {
   1824   CreateContainer();
   1825   drop_arrow = GetDropArrowImage(point_down);
   1826 }
   1827 
   1828 TabStripGtk::DropInfo::~DropInfo() {
   1829   DestroyContainer();
   1830 }
   1831 
   1832 gboolean TabStripGtk::DropInfo::OnExposeEvent(GtkWidget* widget,
   1833                                               GdkEventExpose* event) {
   1834   TRACE_EVENT0("ui::gtk", "TabStripGtk::DropInfo::OnExposeEvent");
   1835 
   1836   if (ui::IsScreenComposited()) {
   1837     SetContainerTransparency();
   1838   } else {
   1839     SetContainerShapeMask();
   1840   }
   1841 
   1842   cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
   1843   gdk_cairo_rectangle(cr, &event->area);
   1844   cairo_clip(cr);
   1845 
   1846   drop_arrow->ToCairo()->SetSource(cr, widget, 0, 0);
   1847   cairo_paint(cr);
   1848 
   1849   cairo_destroy(cr);
   1850 
   1851   return FALSE;
   1852 }
   1853 
   1854 // Sets the color map of the container window to allow the window to be
   1855 // transparent.
   1856 void TabStripGtk::DropInfo::SetContainerColorMap() {
   1857   GdkScreen* screen = gtk_widget_get_screen(container);
   1858   GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
   1859 
   1860   // If rgba is not available, use rgb instead.
   1861   if (!colormap)
   1862     colormap = gdk_screen_get_rgb_colormap(screen);
   1863 
   1864   gtk_widget_set_colormap(container, colormap);
   1865 }
   1866 
   1867 // Sets full transparency for the container window.  This is used if
   1868 // compositing is available for the screen.
   1869 void TabStripGtk::DropInfo::SetContainerTransparency() {
   1870   cairo_t* cairo_context = gdk_cairo_create(gtk_widget_get_window(container));
   1871   if (!cairo_context)
   1872       return;
   1873 
   1874   // Make the background of the dragged tab window fully transparent.  All of
   1875   // the content of the window (child widgets) will be completely opaque.
   1876 
   1877   cairo_scale(cairo_context, static_cast<double>(drop_indicator_width),
   1878               static_cast<double>(drop_indicator_height));
   1879   cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f);
   1880   cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
   1881   cairo_paint(cairo_context);
   1882   cairo_destroy(cairo_context);
   1883 }
   1884 
   1885 // Sets the shape mask for the container window to emulate a transparent
   1886 // container window.  This is used if compositing is not available for the
   1887 // screen.
   1888 void TabStripGtk::DropInfo::SetContainerShapeMask() {
   1889   // Create a 1bpp bitmap the size of |container|.
   1890   GdkPixmap* pixmap = gdk_pixmap_new(NULL,
   1891                                      drop_indicator_width,
   1892                                      drop_indicator_height, 1);
   1893   cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE(pixmap));
   1894 
   1895   // Set the transparency.
   1896   cairo_set_source_rgba(cairo_context, 1, 1, 1, 0);
   1897 
   1898   // Blit the rendered bitmap into a pixmap.  Any pixel set in the pixmap will
   1899   // be opaque in the container window.
   1900   cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
   1901   // We don't use CairoCachedSurface::SetSource() here because we're not
   1902   // rendering on a display server.
   1903   gdk_cairo_set_source_pixbuf(cairo_context, drop_arrow->ToGdkPixbuf(), 0, 0);
   1904   cairo_paint(cairo_context);
   1905   cairo_destroy(cairo_context);
   1906 
   1907   // Set the shape mask.
   1908   GdkWindow* gdk_window = gtk_widget_get_window(container);
   1909   gdk_window_shape_combine_mask(gdk_window, pixmap, 0, 0);
   1910   g_object_unref(pixmap);
   1911 }
   1912 
   1913 void TabStripGtk::DropInfo::CreateContainer() {
   1914   container = gtk_window_new(GTK_WINDOW_POPUP);
   1915   SetContainerColorMap();
   1916   gtk_widget_set_app_paintable(container, TRUE);
   1917   g_signal_connect(container, "expose-event",
   1918                    G_CALLBACK(OnExposeEventThunk), this);
   1919   gtk_widget_add_events(container, GDK_STRUCTURE_MASK);
   1920   gtk_window_move(GTK_WINDOW(container), 0, 0);
   1921   gtk_window_resize(GTK_WINDOW(container),
   1922                     drop_indicator_width, drop_indicator_height);
   1923   gtk_widget_show_all(container);
   1924 }
   1925 
   1926 void TabStripGtk::DropInfo::DestroyContainer() {
   1927   if (GTK_IS_WIDGET(container))
   1928     gtk_widget_destroy(container);
   1929 }
   1930 
   1931 void TabStripGtk::StopAnimation() {
   1932   if (active_animation_.get())
   1933     active_animation_->Stop();
   1934 }
   1935 
   1936 // Called from:
   1937 // - animation tick
   1938 void TabStripGtk::AnimationLayout(double unselected_width) {
   1939   int tab_height = TabGtk::GetStandardSize().height();
   1940   double tab_x = tab_start_x();
   1941   for (int i = 0; i < GetTabCount(); ++i) {
   1942     TabAnimation* animation = active_animation_.get();
   1943     if (animation)
   1944       tab_x += animation->GetGapWidth(i);
   1945     double tab_width = TabAnimation::GetCurrentTabWidth(this, animation, i);
   1946     double end_of_tab = tab_x + tab_width;
   1947     int rounded_tab_x = Round(tab_x);
   1948     TabGtk* tab = GetTabAt(i);
   1949     gfx::Rect bounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
   1950                      tab_height);
   1951     SetTabBounds(tab, bounds);
   1952     tab_x = end_of_tab + GetTabHOffset(i + 1);
   1953   }
   1954   LayoutNewTabButton(tab_x, unselected_width);
   1955 }
   1956 
   1957 void TabStripGtk::StartInsertTabAnimation(int index) {
   1958   // The TabStrip can now use its entire width to lay out Tabs.
   1959   available_width_for_tabs_ = -1;
   1960   StopAnimation();
   1961   active_animation_.reset(new InsertTabAnimation(this, index));
   1962   active_animation_->Start();
   1963 }
   1964 
   1965 void TabStripGtk::StartRemoveTabAnimation(int index, WebContents* contents) {
   1966   if (active_animation_.get()) {
   1967     // Some animations (e.g. MoveTabAnimation) cause there to be a Layout when
   1968     // they're completed (which includes canceled). Since |tab_data_| is now
   1969     // inconsistent with TabStripModel, doing this Layout will crash now, so
   1970     // we ask the MoveTabAnimation to skip its Layout (the state will be
   1971     // corrected by the RemoveTabAnimation we're about to initiate).
   1972     active_animation_->set_layout_on_completion(false);
   1973     active_animation_->Stop();
   1974   }
   1975 
   1976   active_animation_.reset(new RemoveTabAnimation(this, index, contents));
   1977   active_animation_->Start();
   1978 }
   1979 
   1980 void TabStripGtk::StartMoveTabAnimation(int from_index, int to_index) {
   1981   StopAnimation();
   1982   active_animation_.reset(new MoveTabAnimation(this, from_index, to_index));
   1983   active_animation_->Start();
   1984 }
   1985 
   1986 void TabStripGtk::StartResizeLayoutAnimation() {
   1987   StopAnimation();
   1988   active_animation_.reset(new ResizeLayoutAnimation(this));
   1989   active_animation_->Start();
   1990 }
   1991 
   1992 void TabStripGtk::StartMiniTabAnimation(int index) {
   1993   StopAnimation();
   1994   active_animation_.reset(new MiniTabAnimation(this, index));
   1995   active_animation_->Start();
   1996 }
   1997 
   1998 void TabStripGtk::StartMiniMoveTabAnimation(int from_index,
   1999                                             int to_index,
   2000                                             const gfx::Rect& start_bounds) {
   2001   StopAnimation();
   2002   active_animation_.reset(
   2003       new MiniMoveAnimation(this, from_index, to_index, start_bounds));
   2004   active_animation_->Start();
   2005 }
   2006 
   2007 void TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation,
   2008                                   bool layout) {
   2009   active_animation_.reset(NULL);
   2010 
   2011   // Reset the animation state of each tab.
   2012   for (int i = 0, count = GetTabCount(); i < count; ++i)
   2013     GetTabAt(i)->set_animating_mini_change(false);
   2014 
   2015   if (layout)
   2016     Layout();
   2017 }
   2018 
   2019 void TabStripGtk::OnMap(GtkWidget* widget) {
   2020   ReStack();
   2021 }
   2022 
   2023 gboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event) {
   2024   TRACE_EVENT0("ui::gtk", "TabStripGtk::OnExpose");
   2025 
   2026   if (gdk_region_empty(event->region))
   2027     return TRUE;
   2028 
   2029   // If we're only repainting favicons, optimize the paint path and only draw
   2030   // the favicons.
   2031   GdkRectangle* rects;
   2032   gint num_rects;
   2033   gdk_region_get_rectangles(event->region, &rects, &num_rects);
   2034   qsort(rects, num_rects, sizeof(GdkRectangle), CompareGdkRectangles);
   2035   std::vector<int> tabs_to_repaint;
   2036   if (!IsDragSessionActive() &&
   2037       CanPaintOnlyFavicons(rects, num_rects, &tabs_to_repaint)) {
   2038     PaintOnlyFavicons(event, tabs_to_repaint);
   2039     g_free(rects);
   2040     return TRUE;
   2041   }
   2042   g_free(rects);
   2043 
   2044   // Ideally we'd like to only draw what's needed in the damage rect, but the
   2045   // tab widgets overlap each other. To get the proper visual look, we need to
   2046   // draw tabs from the rightmost to the leftmost tab. So if we have a dirty
   2047   // rectangle in the center of the tabstrip, we'll have to draw all the tabs
   2048   // to the left of it.
   2049   //
   2050   // TODO(erg): Figure out why we can't have clip rects that don't start from
   2051   // x=0. jhawkins had a big comment here about how painting on one widget will
   2052   // cause an expose-event to be sent to the widgets underneath, but that
   2053   // should still obey clip rects, but doesn't seem to.
   2054   if (active_animation_.get() || drag_controller_.get()) {
   2055     // If we have an active animation or the tab is being dragged, no matter
   2056     // what GTK tells us our dirty rectangles are, we need to redraw the entire
   2057     // tabstrip.
   2058     event->area.width = bounds_.width();
   2059   } else {
   2060     // Expand whatever dirty rectangle we were given to the area from the
   2061     // leftmost edge of the tabstrip to the rightmost edge of the dirty
   2062     // rectangle given.
   2063     //
   2064     // Doing this frees up CPU when redrawing the tabstrip with throbbing
   2065     // tabs. The most likely tabs to throb are pinned or minitabs which live on
   2066     // the very leftmost of the tabstrip.
   2067     event->area.width += event->area.x;
   2068   }
   2069 
   2070   event->area.x = 0;
   2071   event->area.y = 0;
   2072   event->area.height = bounds_.height();
   2073   gdk_region_union_with_rect(event->region, &event->area);
   2074 
   2075   // Paint the New Tab button.
   2076   gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
   2077       newtab_button_->widget(), event);
   2078 
   2079   // Paint the tabs in reverse order, so they stack to the left.
   2080   TabGtk* selected_tab = NULL;
   2081   int tab_count = GetTabCount();
   2082   for (int i = tab_count - 1; i >= 0; --i) {
   2083     TabGtk* tab = GetTabAt(i);
   2084     // We must ask the _Tab's_ model, not ourselves, because in some situations
   2085     // the model will be different to this object, e.g. when a Tab is being
   2086     // removed after its WebContents has been destroyed.
   2087     if (!tab->IsActive()) {
   2088       gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
   2089                                      tab->widget(), event);
   2090     } else {
   2091       selected_tab = tab;
   2092     }
   2093   }
   2094 
   2095   // Paint the selected tab last, so it overlaps all the others.
   2096   if (selected_tab) {
   2097     gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
   2098                                    selected_tab->widget(), event);
   2099   }
   2100 
   2101   return TRUE;
   2102 }
   2103 
   2104 void TabStripGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
   2105   TRACE_EVENT0("ui::gtk", "TabStripGtk::OnSizeAllocate");
   2106 
   2107   gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y,
   2108       allocation->width, allocation->height);
   2109 
   2110   // Nothing to do if the bounds are the same.  If we don't catch this, we'll
   2111   // get an infinite loop of size-allocate signals.
   2112   if (bounds_ == bounds)
   2113     return;
   2114 
   2115   SetBounds(bounds);
   2116 
   2117   // No tabs, nothing to layout.  This happens when a browser window is created
   2118   // and shown before tabs are added (as in a popup window).
   2119   if (GetTabCount() == 0)
   2120     return;
   2121 
   2122   // When there is only one tab, Layout() so we don't animate it. With more
   2123   // tabs, we should always attempt a resize unless we already have one coming
   2124   // up in our message loop.
   2125   if (GetTabCount() == 1) {
   2126     Layout();
   2127   } else if (!layout_factory_.HasWeakPtrs()) {
   2128     base::MessageLoop::current()->PostDelayedTask(
   2129         FROM_HERE,
   2130         base::Bind(&TabStripGtk::Layout, layout_factory_.GetWeakPtr()),
   2131         base::TimeDelta::FromMilliseconds(kLayoutAfterSizeAllocateMs));
   2132   }
   2133 }
   2134 
   2135 gboolean TabStripGtk::OnDragMotion(GtkWidget* widget, GdkDragContext* context,
   2136                                    gint x, gint y, guint time) {
   2137   UpdateDropIndex(context, x, y);
   2138   return TRUE;
   2139 }
   2140 
   2141 gboolean TabStripGtk::OnDragDrop(GtkWidget* widget, GdkDragContext* context,
   2142                                  gint x, gint y, guint time) {
   2143   if (!drop_info_.get())
   2144     return FALSE;
   2145 
   2146   GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL);
   2147   if (target != GDK_NONE)
   2148     gtk_drag_finish(context, FALSE, FALSE, time);
   2149   else
   2150     gtk_drag_get_data(widget, context, target, time);
   2151 
   2152   return TRUE;
   2153 }
   2154 
   2155 gboolean TabStripGtk::OnDragLeave(GtkWidget* widget, GdkDragContext* context,
   2156                                   guint time) {
   2157   // Destroy the drop indicator.
   2158   drop_info_->DestroyContainer();
   2159 
   2160   // Cancel any pending tab transition.
   2161   hover_tab_selector_.CancelTabTransition();
   2162 
   2163   return FALSE;
   2164 }
   2165 
   2166 gboolean TabStripGtk::OnDragDataReceived(GtkWidget* widget,
   2167                                          GdkDragContext* context,
   2168                                          gint x, gint y,
   2169                                          GtkSelectionData* data,
   2170                                          guint info, guint time) {
   2171   bool success = false;
   2172 
   2173   if (info == ui::TEXT_URI_LIST ||
   2174       info == ui::NETSCAPE_URL ||
   2175       info == ui::TEXT_PLAIN) {
   2176     success = CompleteDrop(gtk_selection_data_get_data(data),
   2177                            info == ui::TEXT_PLAIN);
   2178   }
   2179 
   2180   gtk_drag_finish(context, success, FALSE, time);
   2181   return TRUE;
   2182 }
   2183 
   2184 void TabStripGtk::OnNewTabClicked(GtkWidget* widget) {
   2185   GdkEvent* event = gtk_get_current_event();
   2186   DCHECK_EQ(event->type, GDK_BUTTON_RELEASE);
   2187   int mouse_button = event->button.button;
   2188   gdk_event_free(event);
   2189 
   2190   switch (mouse_button) {
   2191     case 1:
   2192       content::RecordAction(UserMetricsAction("NewTab_Button"));
   2193       UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_BUTTON,
   2194                                 TabStripModel::NEW_TAB_ENUM_COUNT);
   2195       model_->delegate()->AddBlankTabAt(-1, true);
   2196       break;
   2197     case 2: {
   2198       // On middle-click, try to parse the PRIMARY selection as a URL and load
   2199       // it instead of creating a blank page.
   2200       GURL url;
   2201       if (!gtk_util::URLFromPrimarySelection(model_->profile(), &url))
   2202         return;
   2203 
   2204       Browser* browser = window_->browser();
   2205       DCHECK(browser);
   2206       chrome::AddSelectedTabWithURL(
   2207           browser, url, content::PAGE_TRANSITION_TYPED);
   2208       break;
   2209     }
   2210     default:
   2211       NOTREACHED() << "Got click on new tab button with unhandled mouse "
   2212                    << "button " << mouse_button;
   2213   }
   2214 }
   2215 
   2216 void TabStripGtk::SetTabBounds(TabGtk* tab, const gfx::Rect& bounds) {
   2217   gfx::Rect bds = bounds;
   2218   bds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
   2219   tab->SetBounds(bds);
   2220   gtk_fixed_move(GTK_FIXED(tabstrip_.get()), tab->widget(),
   2221                  bds.x(), bds.y());
   2222 }
   2223 
   2224 bool TabStripGtk::CanPaintOnlyFavicons(const GdkRectangle* rects,
   2225     int num_rects, std::vector<int>* tabs_to_paint) {
   2226   // |rects| are sorted so we just need to scan from left to right and compare
   2227   // it to the tab favicon positions from left to right.
   2228   int t = 0;
   2229   for (int r = 0; r < num_rects; ++r) {
   2230     while (t < GetTabCount()) {
   2231       TabGtk* tab = GetTabAt(t);
   2232       if (GdkRectMatchesTabFaviconBounds(rects[r], tab) &&
   2233           tab->ShouldShowIcon()) {
   2234         tabs_to_paint->push_back(t);
   2235         ++t;
   2236         break;
   2237       }
   2238       ++t;
   2239     }
   2240   }
   2241   return static_cast<int>(tabs_to_paint->size()) == num_rects;
   2242 }
   2243 
   2244 void TabStripGtk::PaintOnlyFavicons(GdkEventExpose* event,
   2245                                     const std::vector<int>& tabs_to_paint) {
   2246   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
   2247   for (size_t i = 0; i < tabs_to_paint.size(); ++i) {
   2248     cairo_save(cr);
   2249     GetTabAt(tabs_to_paint[i])->PaintFaviconArea(tabstrip_.get(), cr);
   2250     cairo_restore(cr);
   2251   }
   2252 
   2253   cairo_destroy(cr);
   2254 }
   2255 
   2256 CustomDrawButton* TabStripGtk::MakeNewTabButton() {
   2257   CustomDrawButton* button = new CustomDrawButton(IDR_NEWTAB_BUTTON,
   2258       IDR_NEWTAB_BUTTON_P, IDR_NEWTAB_BUTTON_H, 0);
   2259 
   2260   gtk_widget_set_tooltip_text(button->widget(),
   2261       l10n_util::GetStringUTF8(IDS_TOOLTIP_NEW_TAB).c_str());
   2262 
   2263   // Let the middle mouse button initiate clicks as well.
   2264   gtk_util::SetButtonTriggersNavigation(button->widget());
   2265   g_signal_connect(button->widget(), "clicked",
   2266                    G_CALLBACK(OnNewTabClickedThunk), this);
   2267   gtk_widget_set_can_focus(button->widget(), FALSE);
   2268   gtk_fixed_put(GTK_FIXED(tabstrip_.get()), button->widget(), 0, 0);
   2269 
   2270   return button;
   2271 }
   2272 
   2273 void TabStripGtk::SetNewTabButtonBackground() {
   2274   SkColor color = theme_service_->GetColor(
   2275       ThemeProperties::COLOR_BUTTON_BACKGROUND);
   2276   SkBitmap background = theme_service_->GetImageNamed(
   2277       IDR_THEME_WINDOW_CONTROL_BACKGROUND).AsBitmap();
   2278   SkBitmap mask = theme_service_->GetImageNamed(
   2279       IDR_NEWTAB_BUTTON_MASK).AsBitmap();
   2280   newtab_button_->SetBackground(color, background, mask);
   2281 }
   2282