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/views/tabs/tab_strip.h"
      6 
      7 #if defined(OS_WIN)
      8 #include <windowsx.h>
      9 #endif
     10 
     11 #include <algorithm>
     12 #include <iterator>
     13 #include <string>
     14 #include <vector>
     15 
     16 #include "base/compiler_specific.h"
     17 #include "base/metrics/histogram.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "chrome/browser/defaults.h"
     21 #include "chrome/browser/ui/host_desktop.h"
     22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     23 #include "chrome/browser/ui/view_ids.h"
     24 #include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
     25 #include "chrome/browser/ui/views/tabs/tab.h"
     26 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
     27 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
     28 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h"
     29 #include "chrome/browser/ui/views/touch_uma/touch_uma.h"
     30 #include "content/public/browser/user_metrics.h"
     31 #include "grit/generated_resources.h"
     32 #include "grit/theme_resources.h"
     33 #include "ui/base/accessibility/accessible_view_state.h"
     34 #include "ui/base/default_theme_provider.h"
     35 #include "ui/base/dragdrop/drag_drop_types.h"
     36 #include "ui/base/l10n/l10n_util.h"
     37 #include "ui/base/layout.h"
     38 #include "ui/base/models/list_selection_model.h"
     39 #include "ui/base/resource/resource_bundle.h"
     40 #include "ui/gfx/animation/animation_container.h"
     41 #include "ui/gfx/animation/throb_animation.h"
     42 #include "ui/gfx/canvas.h"
     43 #include "ui/gfx/display.h"
     44 #include "ui/gfx/image/image_skia.h"
     45 #include "ui/gfx/image/image_skia_operations.h"
     46 #include "ui/gfx/path.h"
     47 #include "ui/gfx/rect_conversions.h"
     48 #include "ui/gfx/screen.h"
     49 #include "ui/gfx/size.h"
     50 #include "ui/views/controls/image_view.h"
     51 #include "ui/views/mouse_watcher_view_host.h"
     52 #include "ui/views/rect_based_targeting_utils.h"
     53 #include "ui/views/view_model_utils.h"
     54 #include "ui/views/widget/root_view.h"
     55 #include "ui/views/widget/widget.h"
     56 #include "ui/views/window/non_client_view.h"
     57 
     58 #if defined(OS_WIN)
     59 #include "ui/gfx/win/hwnd_util.h"
     60 #include "ui/views/widget/monitor_win.h"
     61 #include "ui/views/win/hwnd_util.h"
     62 #include "win8/util/win8_util.h"
     63 #endif
     64 
     65 using content::UserMetricsAction;
     66 using ui::DropTargetEvent;
     67 
     68 namespace {
     69 
     70 static const int kTabStripAnimationVSlop = 40;
     71 // Inactive tabs in a native frame are slightly transparent.
     72 static const int kNativeFrameInactiveTabAlpha = 200;
     73 // If there are multiple tabs selected then make non-selected inactive tabs
     74 // even more transparent.
     75 static const int kNativeFrameInactiveTabAlphaMultiSelection = 150;
     76 
     77 // Alpha applied to all elements save the selected tabs.
     78 static const int kInactiveTabAndNewTabButtonAlphaAsh = 230;
     79 static const int kInactiveTabAndNewTabButtonAlpha = 255;
     80 
     81 // Inverse ratio of the width of a tab edge to the width of the tab. When
     82 // hovering over the left or right edge of a tab, the drop indicator will
     83 // point between tabs.
     84 static const int kTabEdgeRatioInverse = 4;
     85 
     86 // Size of the drop indicator.
     87 static int drop_indicator_width;
     88 static int drop_indicator_height;
     89 
     90 static inline int Round(double x) {
     91   // Why oh why is this not in a standard header?
     92   return static_cast<int>(floor(x + 0.5));
     93 }
     94 
     95 // Max number of stacked tabs.
     96 static const int kMaxStackedCount = 4;
     97 
     98 // Padding between stacked tabs.
     99 static const int kStackedPadding = 6;
    100 
    101 // See UpdateLayoutTypeFromMouseEvent() for a description of these.
    102 #if !defined(USE_ASH)
    103 const int kMouseMoveTimeMS = 200;
    104 const int kMouseMoveCountBeforeConsiderReal = 3;
    105 #endif
    106 
    107 // Amount of time we delay before resizing after a close from a touch.
    108 const int kTouchResizeLayoutTimeMS = 2000;
    109 
    110 // Horizontal offset for the new tab button to bring it closer to the
    111 // rightmost tab.
    112 int newtab_button_h_offset() {
    113   static int value = -1;
    114   if (value == -1) {
    115     switch (ui::GetDisplayLayout()) {
    116       case ui::LAYOUT_DESKTOP:
    117         value = -11;
    118         break;
    119       case ui::LAYOUT_TOUCH:
    120         value = -13;
    121         break;
    122       default:
    123         NOTREACHED();
    124     }
    125   }
    126   return value;
    127 }
    128 
    129 // Vertical offset for the new tab button to bring it closer to the
    130 // rightmost tab.
    131 int newtab_button_v_offset() {
    132   static int value = -1;
    133   if (value == -1) {
    134     switch (ui::GetDisplayLayout()) {
    135       case ui::LAYOUT_DESKTOP:
    136         value = 7;
    137         break;
    138       case ui::LAYOUT_TOUCH:
    139         value = 8;
    140         break;
    141       default:
    142         NOTREACHED();
    143     }
    144   }
    145   return value;
    146 }
    147 
    148 // Amount the left edge of a tab is offset from the rectangle of the tab's
    149 // favicon/title/close box.  Related to the width of IDR_TAB_ACTIVE_LEFT.
    150 // Affects the size of the "V" between adjacent tabs.
    151 int tab_h_offset() {
    152   static int value = -1;
    153   if (value == -1) {
    154     switch (ui::GetDisplayLayout()) {
    155       case ui::LAYOUT_DESKTOP:
    156         value = -26;
    157         break;
    158       case ui::LAYOUT_TOUCH:
    159         value = -34;
    160         break;
    161       default:
    162         NOTREACHED();
    163     }
    164   }
    165   return value;
    166 }
    167 
    168 // The size of the new tab button must be hardcoded because we need to be
    169 // able to lay it out before we are able to get its image from the
    170 // ui::ThemeProvider.  It also makes sense to do this, because the size of the
    171 // new tab button should not need to be calculated dynamically.
    172 int newtab_button_asset_width() {
    173   static int value = -1;
    174   if (value == -1) {
    175     switch (ui::GetDisplayLayout()) {
    176       case ui::LAYOUT_DESKTOP:
    177         value = 34;
    178         break;
    179       case ui::LAYOUT_TOUCH:
    180         value = 46;
    181         break;
    182       default:
    183         NOTREACHED();
    184     }
    185   }
    186   return value;
    187 }
    188 
    189 int newtab_button_asset_height() {
    190   static int value = -1;
    191   if (value == -1) {
    192     switch (ui::GetDisplayLayout()) {
    193       case ui::LAYOUT_DESKTOP:
    194         value = 18;
    195         break;
    196       case ui::LAYOUT_TOUCH:
    197         value = 24;
    198         break;
    199       default:
    200         NOTREACHED();
    201     }
    202   }
    203   return value;
    204 }
    205 
    206 // Amount to adjust the clip by when the tab is stacked before the active index.
    207 int stacked_tab_left_clip() {
    208   static int value = -1;
    209   if (value == -1) {
    210     switch (ui::GetDisplayLayout()) {
    211       case ui::LAYOUT_DESKTOP:
    212         value = 20;
    213         break;
    214       case ui::LAYOUT_TOUCH:
    215         value = 26;
    216         break;
    217       default:
    218         NOTREACHED();
    219     }
    220   }
    221   return value;
    222 }
    223 
    224 // Amount to adjust the clip by when the tab is stacked after the active index.
    225 int stacked_tab_right_clip() {
    226   static int value = -1;
    227   if (value == -1) {
    228     switch (ui::GetDisplayLayout()) {
    229       case ui::LAYOUT_DESKTOP:
    230         value = 20;
    231         break;
    232       case ui::LAYOUT_TOUCH:
    233         value = 26;
    234         break;
    235       default:
    236         NOTREACHED();
    237     }
    238   }
    239   return value;
    240 }
    241 
    242 base::string16 GetClipboardText() {
    243   if (!ui::Clipboard::IsSupportedClipboardType(ui::CLIPBOARD_TYPE_SELECTION))
    244     return base::string16();
    245   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
    246   CHECK(clipboard);
    247   base::string16 clipboard_text;
    248   clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &clipboard_text);
    249   return clipboard_text;
    250 }
    251 
    252 // Animation delegate used when a dragged tab is released. When done sets the
    253 // dragging state to false.
    254 class ResetDraggingStateDelegate
    255     : public views::BoundsAnimator::OwnedAnimationDelegate {
    256  public:
    257   explicit ResetDraggingStateDelegate(Tab* tab) : tab_(tab) {
    258   }
    259 
    260   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
    261     tab_->set_dragging(false);
    262   }
    263 
    264   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
    265     tab_->set_dragging(false);
    266   }
    267 
    268  private:
    269   Tab* tab_;
    270 
    271   DISALLOW_COPY_AND_ASSIGN(ResetDraggingStateDelegate);
    272 };
    273 
    274 // If |dest| contains the point |point_in_source| the event handler from |dest|
    275 // is returned. Otherwise NULL is returned.
    276 views::View* ConvertPointToViewAndGetEventHandler(
    277     views::View* source,
    278     views::View* dest,
    279     const gfx::Point& point_in_source) {
    280   gfx::Point dest_point(point_in_source);
    281   views::View::ConvertPointToTarget(source, dest, &dest_point);
    282   return dest->HitTestPoint(dest_point) ?
    283       dest->GetEventHandlerForPoint(dest_point) : NULL;
    284 }
    285 
    286 // Gets a tooltip handler for |point_in_source| from |dest|. Note that |dest|
    287 // should return NULL if it does not contain the point.
    288 views::View* ConvertPointToViewAndGetTooltipHandler(
    289     views::View* source,
    290     views::View* dest,
    291     const gfx::Point& point_in_source) {
    292   gfx::Point dest_point(point_in_source);
    293   views::View::ConvertPointToTarget(source, dest, &dest_point);
    294   return dest->GetTooltipHandlerForPoint(dest_point);
    295 }
    296 
    297 TabDragController::EventSource EventSourceFromEvent(
    298     const ui::LocatedEvent& event) {
    299   return event.IsGestureEvent() ? TabDragController::EVENT_SOURCE_TOUCH :
    300       TabDragController::EVENT_SOURCE_MOUSE;
    301 }
    302 
    303 }  // namespace
    304 
    305 ///////////////////////////////////////////////////////////////////////////////
    306 // NewTabButton
    307 //
    308 //  A subclass of button that hit-tests to the shape of the new tab button and
    309 //  does custom drawing.
    310 
    311 class NewTabButton : public views::ImageButton {
    312  public:
    313   NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener);
    314   virtual ~NewTabButton();
    315 
    316   // Set the background offset used to match the background image to the frame
    317   // image.
    318   void set_background_offset(const gfx::Point& offset) {
    319     background_offset_ = offset;
    320   }
    321 
    322  protected:
    323   // Overridden from views::View:
    324   virtual bool HasHitTestMask() const OVERRIDE;
    325   virtual void GetHitTestMask(HitTestSource source,
    326                               gfx::Path* path) const OVERRIDE;
    327 #if defined(OS_WIN)
    328   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
    329 #endif
    330   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
    331 
    332   // Overridden from ui::EventHandler:
    333   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
    334 
    335  private:
    336   bool ShouldUseNativeFrame() const;
    337   gfx::ImageSkia GetBackgroundImage(views::CustomButton::ButtonState state,
    338                                     ui::ScaleFactor scale_factor) const;
    339   gfx::ImageSkia GetImageForState(views::CustomButton::ButtonState state,
    340                                   ui::ScaleFactor scale_factor) const;
    341   gfx::ImageSkia GetImageForScale(ui::ScaleFactor scale_factor) const;
    342 
    343   // Tab strip that contains this button.
    344   TabStrip* tab_strip_;
    345 
    346   // The offset used to paint the background image.
    347   gfx::Point background_offset_;
    348 
    349   // were we destroyed?
    350   bool* destroyed_;
    351 
    352   DISALLOW_COPY_AND_ASSIGN(NewTabButton);
    353 };
    354 
    355 NewTabButton::NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener)
    356     : views::ImageButton(listener),
    357       tab_strip_(tab_strip),
    358       destroyed_(NULL) {
    359 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    360   set_triggerable_event_flags(triggerable_event_flags() |
    361                               ui::EF_MIDDLE_MOUSE_BUTTON);
    362 #endif
    363 }
    364 
    365 NewTabButton::~NewTabButton() {
    366   if (destroyed_)
    367     *destroyed_ = true;
    368 }
    369 
    370 bool NewTabButton::HasHitTestMask() const {
    371   // When the button is sized to the top of the tab strip we want the user to
    372   // be able to click on complete bounds, and so don't return a custom hit
    373   // mask.
    374   return !tab_strip_->SizeTabButtonToTopOfTabStrip();
    375 }
    376 
    377 void NewTabButton::GetHitTestMask(HitTestSource source, gfx::Path* path) const {
    378   DCHECK(path);
    379 
    380   SkScalar w = SkIntToScalar(width());
    381   SkScalar v_offset = SkIntToScalar(newtab_button_v_offset());
    382 
    383   // These values are defined by the shape of the new tab image. Should that
    384   // image ever change, these values will need to be updated. They're so
    385   // custom it's not really worth defining constants for.
    386   // These values are correct for regular and USE_ASH versions of the image.
    387   path->moveTo(0, v_offset + 1);
    388   path->lineTo(w - 7, v_offset + 1);
    389   path->lineTo(w - 4, v_offset + 4);
    390   path->lineTo(w, v_offset + 16);
    391   path->lineTo(w - 1, v_offset + 17);
    392   path->lineTo(7, v_offset + 17);
    393   path->lineTo(4, v_offset + 13);
    394   path->lineTo(0, v_offset + 1);
    395   path->close();
    396 }
    397 
    398 #if defined(OS_WIN)
    399 void NewTabButton::OnMouseReleased(const ui::MouseEvent& event) {
    400   if (event.IsOnlyRightMouseButton()) {
    401     gfx::Point point = event.location();
    402     views::View::ConvertPointToScreen(this, &point);
    403     bool destroyed = false;
    404     destroyed_ = &destroyed;
    405     gfx::ShowSystemMenuAtPoint(views::HWNDForView(this), point);
    406     if (destroyed)
    407       return;
    408 
    409     destroyed_ = NULL;
    410     SetState(views::CustomButton::STATE_NORMAL);
    411     return;
    412   }
    413   views::ImageButton::OnMouseReleased(event);
    414 }
    415 #endif
    416 
    417 void NewTabButton::OnPaint(gfx::Canvas* canvas) {
    418   gfx::ImageSkia image =
    419       GetImageForScale(ui::GetSupportedScaleFactor(canvas->image_scale()));
    420   canvas->DrawImageInt(image, 0, height() - image.height());
    421 }
    422 
    423 void NewTabButton::OnGestureEvent(ui::GestureEvent* event) {
    424   // Consume all gesture events here so that the parent (Tab) does not
    425   // start consuming gestures.
    426   views::ImageButton::OnGestureEvent(event);
    427   event->SetHandled();
    428 }
    429 
    430 bool NewTabButton::ShouldUseNativeFrame() const {
    431   return GetWidget() &&
    432     GetWidget()->GetTopLevelWidget()->ShouldUseNativeFrame();
    433 }
    434 
    435 gfx::ImageSkia NewTabButton::GetBackgroundImage(
    436     views::CustomButton::ButtonState state,
    437     ui::ScaleFactor scale_factor) const {
    438   int background_id = 0;
    439   if (ShouldUseNativeFrame()) {
    440     background_id = IDR_THEME_TAB_BACKGROUND_V;
    441   } else if (tab_strip_->controller()->IsIncognito()) {
    442     background_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO;
    443 #if defined(OS_WIN)
    444   } else if (win8::IsSingleWindowMetroMode()) {
    445     background_id = IDR_THEME_TAB_BACKGROUND_V;
    446 #endif
    447   } else {
    448     background_id = IDR_THEME_TAB_BACKGROUND;
    449   }
    450 
    451   int alpha = 0;
    452   switch (state) {
    453     case views::CustomButton::STATE_NORMAL:
    454     case views::CustomButton::STATE_HOVERED:
    455       alpha = ShouldUseNativeFrame() ? kNativeFrameInactiveTabAlpha : 255;
    456       break;
    457     case views::CustomButton::STATE_PRESSED:
    458       alpha = 145;
    459       break;
    460     default:
    461       NOTREACHED();
    462       break;
    463   }
    464 
    465   gfx::ImageSkia* mask =
    466       GetThemeProvider()->GetImageSkiaNamed(IDR_NEWTAB_BUTTON_MASK);
    467   int height = mask->height();
    468   int width = mask->width();
    469   float scale = ui::GetImageScale(scale_factor);
    470   // The canvas and mask has to use the same scale factor.
    471   if (!mask->HasRepresentation(scale))
    472     scale_factor = ui::SCALE_FACTOR_100P;
    473 
    474   gfx::Canvas canvas(gfx::Size(width, height), scale, false);
    475 
    476   // For custom images the background starts at the top of the tab strip.
    477   // Otherwise the background starts at the top of the frame.
    478   gfx::ImageSkia* background =
    479       GetThemeProvider()->GetImageSkiaNamed(background_id);
    480   int offset_y = GetThemeProvider()->HasCustomImage(background_id) ?
    481       0 : background_offset_.y();
    482 
    483   // The new tab background is mirrored in RTL mode, but the theme background
    484   // should never be mirrored. Mirror it here to compensate.
    485   float x_scale = 1.0f;
    486   int x = GetMirroredX() + background_offset_.x();
    487   if (base::i18n::IsRTL()) {
    488     x_scale = -1.0f;
    489     // Offset by |width| such that the same region is painted as if there was no
    490     // flip.
    491     x += width;
    492   }
    493   canvas.TileImageInt(*background, x, newtab_button_v_offset() + offset_y,
    494                       x_scale, 1.0f, 0, 0, width, height);
    495 
    496   if (alpha != 255) {
    497     SkPaint paint;
    498     paint.setColor(SkColorSetARGB(alpha, 255, 255, 255));
    499     paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
    500     paint.setStyle(SkPaint::kFill_Style);
    501     canvas.DrawRect(gfx::Rect(0, 0, width, height), paint);
    502   }
    503 
    504   // White highlight on hover.
    505   if (state == views::CustomButton::STATE_HOVERED)
    506     canvas.FillRect(GetLocalBounds(), SkColorSetARGB(64, 255, 255, 255));
    507 
    508   return gfx::ImageSkiaOperations::CreateMaskedImage(
    509       gfx::ImageSkia(canvas.ExtractImageRep()), *mask);
    510 }
    511 
    512 gfx::ImageSkia NewTabButton::GetImageForState(
    513     views::CustomButton::ButtonState state,
    514     ui::ScaleFactor scale_factor) const {
    515   const int overlay_id = state == views::CustomButton::STATE_PRESSED ?
    516         IDR_NEWTAB_BUTTON_P : IDR_NEWTAB_BUTTON;
    517   gfx::ImageSkia* overlay = GetThemeProvider()->GetImageSkiaNamed(overlay_id);
    518 
    519   gfx::Canvas canvas(
    520       gfx::Size(overlay->width(), overlay->height()),
    521       ui::GetImageScale(scale_factor),
    522       false);
    523   canvas.DrawImageInt(GetBackgroundImage(state, scale_factor), 0, 0);
    524 
    525   // Draw the button border with a slight alpha.
    526   const int kNativeFrameOverlayAlpha = 178;
    527   const int kOpaqueFrameOverlayAlpha = 230;
    528   uint8 alpha = ShouldUseNativeFrame() ?
    529       kNativeFrameOverlayAlpha : kOpaqueFrameOverlayAlpha;
    530   canvas.DrawImageInt(*overlay, 0, 0, alpha);
    531 
    532   return gfx::ImageSkia(canvas.ExtractImageRep());
    533 }
    534 
    535 gfx::ImageSkia NewTabButton::GetImageForScale(
    536     ui::ScaleFactor scale_factor) const {
    537   if (!hover_animation_->is_animating())
    538     return GetImageForState(state(), scale_factor);
    539   return gfx::ImageSkiaOperations::CreateBlendedImage(
    540       GetImageForState(views::CustomButton::STATE_NORMAL, scale_factor),
    541       GetImageForState(views::CustomButton::STATE_HOVERED, scale_factor),
    542       hover_animation_->GetCurrentValue());
    543 }
    544 
    545 ///////////////////////////////////////////////////////////////////////////////
    546 // TabStrip::RemoveTabDelegate
    547 //
    548 // AnimationDelegate used when removing a tab. Does the necessary cleanup when
    549 // done.
    550 class TabStrip::RemoveTabDelegate
    551     : public views::BoundsAnimator::OwnedAnimationDelegate {
    552  public:
    553   RemoveTabDelegate(TabStrip* tab_strip, Tab* tab);
    554 
    555   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
    556   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE;
    557 
    558  private:
    559   void CompleteRemove();
    560 
    561   // When the animation completes, we send the Container a message to simulate
    562   // a mouse moved event at the current mouse position. This tickles the Tab
    563   // the mouse is currently over to show the "hot" state of the close button.
    564   void HighlightCloseButton();
    565 
    566   TabStrip* tabstrip_;
    567   Tab* tab_;
    568 
    569   DISALLOW_COPY_AND_ASSIGN(RemoveTabDelegate);
    570 };
    571 
    572 TabStrip::RemoveTabDelegate::RemoveTabDelegate(TabStrip* tab_strip,
    573                                                Tab* tab)
    574     : tabstrip_(tab_strip),
    575       tab_(tab) {
    576 }
    577 
    578 void TabStrip::RemoveTabDelegate::AnimationEnded(
    579     const gfx::Animation* animation) {
    580   CompleteRemove();
    581 }
    582 
    583 void TabStrip::RemoveTabDelegate::AnimationCanceled(
    584     const gfx::Animation* animation) {
    585   CompleteRemove();
    586 }
    587 
    588 void TabStrip::RemoveTabDelegate::CompleteRemove() {
    589   DCHECK(tab_->closing());
    590   tabstrip_->RemoveAndDeleteTab(tab_);
    591   HighlightCloseButton();
    592 }
    593 
    594 void TabStrip::RemoveTabDelegate::HighlightCloseButton() {
    595   if (tabstrip_->IsDragSessionActive() ||
    596       !tabstrip_->ShouldHighlightCloseButtonAfterRemove()) {
    597     // This function is not required (and indeed may crash!) for removes
    598     // spawned by non-mouse closes and drag-detaches.
    599     return;
    600   }
    601 
    602   views::Widget* widget = tabstrip_->GetWidget();
    603   // This can be null during shutdown. See http://crbug.com/42737.
    604   if (!widget)
    605     return;
    606 
    607   widget->SynthesizeMouseMoveEvent();
    608 }
    609 
    610 ///////////////////////////////////////////////////////////////////////////////
    611 // TabStrip, public:
    612 
    613 // static
    614 const char TabStrip::kViewClassName[] = "TabStrip";
    615 
    616 // static
    617 const int TabStrip::kMiniToNonMiniGap = 3;
    618 
    619 TabStrip::TabStrip(TabStripController* controller)
    620     : controller_(controller),
    621       newtab_button_(NULL),
    622       current_unselected_width_(Tab::GetStandardSize().width()),
    623       current_selected_width_(Tab::GetStandardSize().width()),
    624       available_width_for_tabs_(-1),
    625       in_tab_close_(false),
    626       animation_container_(new gfx::AnimationContainer()),
    627       bounds_animator_(this),
    628       layout_type_(TAB_STRIP_LAYOUT_SHRINK),
    629       adjust_layout_(false),
    630       reset_to_shrink_on_exit_(false),
    631       mouse_move_count_(0),
    632       immersive_style_(false) {
    633   Init();
    634 }
    635 
    636 TabStrip::~TabStrip() {
    637   FOR_EACH_OBSERVER(TabStripObserver, observers_,
    638                     TabStripDeleted(this));
    639 
    640   // The animations may reference the tabs. Shut down the animation before we
    641   // delete the tabs.
    642   StopAnimating(false);
    643 
    644   DestroyDragController();
    645 
    646   // Make sure we unhook ourselves as a message loop observer so that we don't
    647   // crash in the case where the user closes the window after closing a tab
    648   // but before moving the mouse.
    649   RemoveMessageLoopObserver();
    650 
    651   // The children (tabs) may callback to us from their destructor. Delete them
    652   // so that if they call back we aren't in a weird state.
    653   RemoveAllChildViews(true);
    654 }
    655 
    656 void TabStrip::AddObserver(TabStripObserver* observer) {
    657   observers_.AddObserver(observer);
    658 }
    659 
    660 void TabStrip::RemoveObserver(TabStripObserver* observer) {
    661   observers_.RemoveObserver(observer);
    662 }
    663 
    664 void TabStrip::SetLayoutType(TabStripLayoutType layout_type,
    665                              bool adjust_layout) {
    666   adjust_layout_ = adjust_layout;
    667   if (layout_type == layout_type_)
    668     return;
    669 
    670   const int active_index = controller_->GetActiveIndex();
    671   int active_center = 0;
    672   if (active_index != -1) {
    673     active_center = ideal_bounds(active_index).x() +
    674         ideal_bounds(active_index).width() / 2;
    675   }
    676   layout_type_ = layout_type;
    677   SetResetToShrinkOnExit(false);
    678   SwapLayoutIfNecessary();
    679   // When transitioning to stacked try to keep the active tab centered.
    680   if (touch_layout_.get() && active_index != -1) {
    681     touch_layout_->SetActiveTabLocation(
    682         active_center - ideal_bounds(active_index).width() / 2);
    683     AnimateToIdealBounds();
    684   }
    685 }
    686 
    687 gfx::Rect TabStrip::GetNewTabButtonBounds() {
    688   return newtab_button_->bounds();
    689 }
    690 
    691 bool TabStrip::SizeTabButtonToTopOfTabStrip() {
    692   // Extend the button to the screen edge in maximized and immersive fullscreen.
    693   views::Widget* widget = GetWidget();
    694   return browser_defaults::kSizeTabButtonToTopOfTabStrip ||
    695       (widget && (widget->IsMaximized() || widget->IsFullscreen()));
    696 }
    697 
    698 void TabStrip::StartHighlight(int model_index) {
    699   tab_at(model_index)->StartPulse();
    700 }
    701 
    702 void TabStrip::StopAllHighlighting() {
    703   for (int i = 0; i < tab_count(); ++i)
    704     tab_at(i)->StopPulse();
    705 }
    706 
    707 void TabStrip::AddTabAt(int model_index,
    708                         const TabRendererData& data,
    709                         bool is_active) {
    710   // Stop dragging when a new tab is added and dragging a window. Doing
    711   // otherwise results in a confusing state if the user attempts to reattach. We
    712   // could allow this and make TabDragController update itself during the add,
    713   // but this comes up infrequently enough that it's not work the complexity.
    714   if (drag_controller_.get() && !drag_controller_->is_mutating() &&
    715       drag_controller_->is_dragging_window()) {
    716     EndDrag(END_DRAG_COMPLETE);
    717   }
    718   Tab* tab = CreateTab();
    719   tab->SetData(data);
    720   UpdateTabsClosingMap(model_index, 1);
    721   tabs_.Add(tab, model_index);
    722   AddChildView(tab);
    723 
    724   if (touch_layout_.get()) {
    725     GenerateIdealBoundsForMiniTabs(NULL);
    726     int add_types = 0;
    727     if (data.mini)
    728       add_types |= StackedTabStripLayout::kAddTypeMini;
    729     if (is_active)
    730       add_types |= StackedTabStripLayout::kAddTypeActive;
    731     touch_layout_->AddTab(model_index, add_types, GetStartXForNormalTabs());
    732   }
    733 
    734   // Don't animate the first tab, it looks weird, and don't animate anything
    735   // if the containing window isn't visible yet.
    736   if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible())
    737     StartInsertTabAnimation(model_index);
    738   else
    739     DoLayout();
    740 
    741   SwapLayoutIfNecessary();
    742 
    743   FOR_EACH_OBSERVER(TabStripObserver, observers_,
    744                     TabStripAddedTabAt(this, model_index));
    745 }
    746 
    747 void TabStrip::MoveTab(int from_model_index,
    748                        int to_model_index,
    749                        const TabRendererData& data) {
    750   DCHECK_GT(tabs_.view_size(), 0);
    751   Tab* last_tab = tab_at(tab_count() - 1);
    752   tab_at(from_model_index)->SetData(data);
    753   if (touch_layout_.get()) {
    754     tabs_.MoveViewOnly(from_model_index, to_model_index);
    755     int mini_count = 0;
    756     GenerateIdealBoundsForMiniTabs(&mini_count);
    757     touch_layout_->MoveTab(
    758         from_model_index, to_model_index, controller_->GetActiveIndex(),
    759         GetStartXForNormalTabs(), mini_count);
    760   } else {
    761     tabs_.Move(from_model_index, to_model_index);
    762   }
    763   StartMoveTabAnimation();
    764   if (TabDragController::IsAttachedTo(this) &&
    765       (last_tab != tab_at(tab_count() - 1) || last_tab->dragging())) {
    766     newtab_button_->SetVisible(false);
    767   }
    768   SwapLayoutIfNecessary();
    769 
    770   FOR_EACH_OBSERVER(TabStripObserver, observers_,
    771                     TabStripMovedTab(this, from_model_index, to_model_index));
    772 }
    773 
    774 void TabStrip::RemoveTabAt(int model_index) {
    775   if (touch_layout_.get()) {
    776     Tab* tab = tab_at(model_index);
    777     tab->set_closing(true);
    778     int old_x = tabs_.ideal_bounds(model_index).x();
    779     // We still need to paint the tab until we actually remove it. Put it in
    780     // tabs_closing_map_ so we can find it.
    781     RemoveTabFromViewModel(model_index);
    782     touch_layout_->RemoveTab(model_index, GenerateIdealBoundsForMiniTabs(NULL),
    783                              old_x);
    784     ScheduleRemoveTabAnimation(tab);
    785   } else if (in_tab_close_ && model_index != GetModelCount()) {
    786     StartMouseInitiatedRemoveTabAnimation(model_index);
    787   } else {
    788     StartRemoveTabAnimation(model_index);
    789   }
    790   SwapLayoutIfNecessary();
    791 
    792   FOR_EACH_OBSERVER(TabStripObserver, observers_,
    793                     TabStripRemovedTabAt(this, model_index));
    794 }
    795 
    796 void TabStrip::SetTabData(int model_index, const TabRendererData& data) {
    797   Tab* tab = tab_at(model_index);
    798   bool mini_state_changed = tab->data().mini != data.mini;
    799   tab->SetData(data);
    800 
    801   if (mini_state_changed) {
    802     if (touch_layout_.get()) {
    803       int mini_tab_count = 0;
    804       int start_x = GenerateIdealBoundsForMiniTabs(&mini_tab_count);
    805       touch_layout_->SetXAndMiniCount(start_x, mini_tab_count);
    806     }
    807     if (GetWidget() && GetWidget()->IsVisible())
    808       StartMiniTabAnimation();
    809     else
    810       DoLayout();
    811   }
    812   SwapLayoutIfNecessary();
    813 }
    814 
    815 void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) {
    816   if (!in_tab_close_ && IsAnimating()) {
    817     // Cancel any current animations. We do this as remove uses the current
    818     // ideal bounds and we need to know ideal bounds is in a good state.
    819     StopAnimating(true);
    820   }
    821 
    822   if (!GetWidget())
    823     return;
    824 
    825   int model_count = GetModelCount();
    826   if (model_index + 1 != model_count && model_count > 1) {
    827     // The user is about to close a tab other than the last tab. Set
    828     // available_width_for_tabs_ so that if we do a layout we don't position a
    829     // tab past the end of the second to last tab. We do this so that as the
    830     // user closes tabs with the mouse a tab continues to fall under the mouse.
    831     Tab* last_tab = tab_at(model_count - 1);
    832     Tab* tab_being_removed = tab_at(model_index);
    833     available_width_for_tabs_ = last_tab->x() + last_tab->width() -
    834         tab_being_removed->width() - tab_h_offset();
    835     if (model_index == 0 && tab_being_removed->data().mini &&
    836         !tab_at(1)->data().mini) {
    837       available_width_for_tabs_ -= kMiniToNonMiniGap;
    838     }
    839   }
    840 
    841   in_tab_close_ = true;
    842   resize_layout_timer_.Stop();
    843   if (source == CLOSE_TAB_FROM_TOUCH) {
    844     StartResizeLayoutTabsFromTouchTimer();
    845   } else {
    846     AddMessageLoopObserver();
    847   }
    848 }
    849 
    850 void TabStrip::SetSelection(const ui::ListSelectionModel& old_selection,
    851                             const ui::ListSelectionModel& new_selection) {
    852   if (touch_layout_.get()) {
    853     touch_layout_->SetActiveIndex(new_selection.active());
    854     // Only start an animation if we need to. Otherwise clicking on an
    855     // unselected tab and dragging won't work because dragging is only allowed
    856     // if not animating.
    857     if (!views::ViewModelUtils::IsAtIdealBounds(tabs_))
    858       AnimateToIdealBounds();
    859     SchedulePaint();
    860   } else {
    861     // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
    862     // a different size to the selected ones.
    863     bool tiny_tabs = current_unselected_width_ != current_selected_width_;
    864     if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
    865       DoLayout();
    866     } else {
    867       SchedulePaint();
    868     }
    869   }
    870 
    871   ui::ListSelectionModel::SelectedIndices no_longer_selected =
    872       base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>(
    873           old_selection.selected_indices(),
    874           new_selection.selected_indices());
    875   for (size_t i = 0; i < no_longer_selected.size(); ++i)
    876     tab_at(no_longer_selected[i])->StopMiniTabTitleAnimation();
    877 }
    878 
    879 void TabStrip::TabTitleChangedNotLoading(int model_index) {
    880   Tab* tab = tab_at(model_index);
    881   if (tab->data().mini && !tab->IsActive())
    882     tab->StartMiniTabTitleAnimation();
    883 }
    884 
    885 Tab* TabStrip::tab_at(int index) const {
    886   return static_cast<Tab*>(tabs_.view_at(index));
    887 }
    888 
    889 int TabStrip::GetModelIndexOfTab(const Tab* tab) const {
    890   return tabs_.GetIndexOfView(tab);
    891 }
    892 
    893 int TabStrip::GetModelCount() const {
    894   return controller_->GetCount();
    895 }
    896 
    897 bool TabStrip::IsValidModelIndex(int model_index) const {
    898   return controller_->IsValidIndex(model_index);
    899 }
    900 
    901 Tab* TabStrip::CreateTabForDragging() {
    902   Tab* tab = new Tab(NULL);
    903   // Make sure the dragged tab shares our theme provider. We need to explicitly
    904   // do this as during dragging there isn't a theme provider.
    905   tab->set_theme_provider(GetThemeProvider());
    906   return tab;
    907 }
    908 
    909 bool TabStrip::IsDragSessionActive() const {
    910   return drag_controller_.get() != NULL;
    911 }
    912 
    913 bool TabStrip::IsActiveDropTarget() const {
    914   for (int i = 0; i < tab_count(); ++i) {
    915     Tab* tab = tab_at(i);
    916     if (tab->dragging())
    917       return true;
    918   }
    919   return false;
    920 }
    921 
    922 bool TabStrip::IsTabStripEditable() const {
    923   return !IsDragSessionActive() && !IsActiveDropTarget();
    924 }
    925 
    926 bool TabStrip::IsTabStripCloseable() const {
    927   return !IsDragSessionActive();
    928 }
    929 
    930 void TabStrip::UpdateLoadingAnimations() {
    931   controller_->UpdateLoadingAnimations();
    932 }
    933 
    934 bool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
    935   return IsRectInWindowCaption(gfx::Rect(point, gfx::Size(1, 1)));
    936 }
    937 
    938 bool TabStrip::IsRectInWindowCaption(const gfx::Rect& rect) {
    939   views::View* v = GetEventHandlerForRect(rect);
    940 
    941   // If there is no control at this location, claim the hit was in the title
    942   // bar to get a move action.
    943   if (v == this)
    944     return true;
    945 
    946   // Check to see if the rect intersects the non-button parts of the new tab
    947   // button. The button has a non-rectangular shape, so if it's not in the
    948   // visual portions of the button we treat it as a click to the caption.
    949   gfx::RectF rect_in_newtab_coords_f(rect);
    950   View::ConvertRectToTarget(this, newtab_button_, &rect_in_newtab_coords_f);
    951   gfx::Rect rect_in_newtab_coords = gfx::ToEnclosingRect(
    952       rect_in_newtab_coords_f);
    953   if (newtab_button_->GetLocalBounds().Intersects(rect_in_newtab_coords) &&
    954       !newtab_button_->HitTestRect(rect_in_newtab_coords))
    955     return true;
    956 
    957   // All other regions, including the new Tab button, should be considered part
    958   // of the containing Window's client area so that regular events can be
    959   // processed for them.
    960   return false;
    961 }
    962 
    963 void TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
    964   for (int i = 0; i < tab_count(); ++i)
    965     tab_at(i)->set_background_offset(offset);
    966   newtab_button_->set_background_offset(offset);
    967 }
    968 
    969 views::View* TabStrip::newtab_button() {
    970   return newtab_button_;
    971 }
    972 
    973 void TabStrip::SetImmersiveStyle(bool enable) {
    974   if (immersive_style_ == enable)
    975     return;
    976   immersive_style_ = enable;
    977 }
    978 
    979 bool TabStrip::IsAnimating() const {
    980   return bounds_animator_.IsAnimating();
    981 }
    982 
    983 void TabStrip::StopAnimating(bool layout) {
    984   if (!IsAnimating())
    985     return;
    986 
    987   bounds_animator_.Cancel();
    988 
    989   if (layout)
    990     DoLayout();
    991 }
    992 
    993 void TabStrip::FileSupported(const GURL& url, bool supported) {
    994   if (drop_info_.get() && drop_info_->url == url)
    995     drop_info_->file_supported = supported;
    996 }
    997 
    998 const ui::ListSelectionModel& TabStrip::GetSelectionModel() {
    999   return controller_->GetSelectionModel();
   1000 }
   1001 
   1002 bool TabStrip::SupportsMultipleSelection() {
   1003   // TODO: currently only allow single selection in touch layout mode.
   1004   return touch_layout_.get() == NULL;
   1005 }
   1006 
   1007 void TabStrip::SelectTab(Tab* tab) {
   1008   int model_index = GetModelIndexOfTab(tab);
   1009   if (IsValidModelIndex(model_index))
   1010     controller_->SelectTab(model_index);
   1011 }
   1012 
   1013 void TabStrip::ExtendSelectionTo(Tab* tab) {
   1014   int model_index = GetModelIndexOfTab(tab);
   1015   if (IsValidModelIndex(model_index))
   1016     controller_->ExtendSelectionTo(model_index);
   1017 }
   1018 
   1019 void TabStrip::ToggleSelected(Tab* tab) {
   1020   int model_index = GetModelIndexOfTab(tab);
   1021   if (IsValidModelIndex(model_index))
   1022     controller_->ToggleSelected(model_index);
   1023 }
   1024 
   1025 void TabStrip::AddSelectionFromAnchorTo(Tab* tab) {
   1026   int model_index = GetModelIndexOfTab(tab);
   1027   if (IsValidModelIndex(model_index))
   1028     controller_->AddSelectionFromAnchorTo(model_index);
   1029 }
   1030 
   1031 void TabStrip::CloseTab(Tab* tab, CloseTabSource source) {
   1032   if (tab->closing()) {
   1033     // If the tab is already closing, close the next tab. We do this so that the
   1034     // user can rapidly close tabs by clicking the close button and not have
   1035     // the animations interfere with that.
   1036     for (TabsClosingMap::const_iterator i(tabs_closing_map_.begin());
   1037          i != tabs_closing_map_.end(); ++i) {
   1038       std::vector<Tab*>::const_iterator j =
   1039           std::find(i->second.begin(), i->second.end(), tab);
   1040       if (j != i->second.end()) {
   1041         if (i->first + 1 < GetModelCount())
   1042           controller_->CloseTab(i->first + 1, source);
   1043         return;
   1044       }
   1045     }
   1046     // If we get here, it means a tab has been marked as closing but isn't in
   1047     // the set of known closing tabs.
   1048     NOTREACHED();
   1049     return;
   1050   }
   1051   int model_index = GetModelIndexOfTab(tab);
   1052   if (IsValidModelIndex(model_index))
   1053     controller_->CloseTab(model_index, source);
   1054 }
   1055 
   1056 void TabStrip::ShowContextMenuForTab(Tab* tab,
   1057                                      const gfx::Point& p,
   1058                                      ui::MenuSourceType source_type) {
   1059   controller_->ShowContextMenuForTab(tab, p, source_type);
   1060 }
   1061 
   1062 bool TabStrip::IsActiveTab(const Tab* tab) const {
   1063   int model_index = GetModelIndexOfTab(tab);
   1064   return IsValidModelIndex(model_index) &&
   1065       controller_->IsActiveTab(model_index);
   1066 }
   1067 
   1068 bool TabStrip::IsTabSelected(const Tab* tab) const {
   1069   int model_index = GetModelIndexOfTab(tab);
   1070   return IsValidModelIndex(model_index) &&
   1071       controller_->IsTabSelected(model_index);
   1072 }
   1073 
   1074 bool TabStrip::IsTabPinned(const Tab* tab) const {
   1075   if (tab->closing())
   1076     return false;
   1077 
   1078   int model_index = GetModelIndexOfTab(tab);
   1079   return IsValidModelIndex(model_index) &&
   1080       controller_->IsTabPinned(model_index);
   1081 }
   1082 
   1083 void TabStrip::MaybeStartDrag(
   1084     Tab* tab,
   1085     const ui::LocatedEvent& event,
   1086     const ui::ListSelectionModel& original_selection) {
   1087   // Don't accidentally start any drag operations during animations if the
   1088   // mouse is down... during an animation tabs are being resized automatically,
   1089   // so the View system can misinterpret this easily if the mouse is down that
   1090   // the user is dragging.
   1091   if (IsAnimating() || tab->closing() ||
   1092       controller_->HasAvailableDragActions() == 0) {
   1093     return;
   1094   }
   1095 
   1096   // Do not do any dragging of tabs when using the super short immersive style.
   1097   if (IsImmersiveStyle())
   1098     return;
   1099 
   1100   int model_index = GetModelIndexOfTab(tab);
   1101   if (!IsValidModelIndex(model_index)) {
   1102     CHECK(false);
   1103     return;
   1104   }
   1105   std::vector<Tab*> tabs;
   1106   int size_to_selected = 0;
   1107   int x = tab->GetMirroredXInView(event.x());
   1108   int y = event.y();
   1109   // Build the set of selected tabs to drag and calculate the offset from the
   1110   // first selected tab.
   1111   for (int i = 0; i < tab_count(); ++i) {
   1112     Tab* other_tab = tab_at(i);
   1113     if (IsTabSelected(other_tab)) {
   1114       tabs.push_back(other_tab);
   1115       if (other_tab == tab) {
   1116         size_to_selected = GetSizeNeededForTabs(tabs);
   1117         x = size_to_selected - tab->width() + x;
   1118       }
   1119     }
   1120   }
   1121   DCHECK(!tabs.empty());
   1122   DCHECK(std::find(tabs.begin(), tabs.end(), tab) != tabs.end());
   1123   ui::ListSelectionModel selection_model;
   1124   if (!original_selection.IsSelected(model_index))
   1125     selection_model.Copy(original_selection);
   1126   // Delete the existing DragController before creating a new one. We do this as
   1127   // creating the DragController remembers the WebContents delegates and we need
   1128   // to make sure the existing DragController isn't still a delegate.
   1129   drag_controller_.reset();
   1130   TabDragController::DetachBehavior detach_behavior =
   1131       TabDragController::DETACHABLE;
   1132   TabDragController::MoveBehavior move_behavior =
   1133       TabDragController::REORDER;
   1134   // Use MOVE_VISIBILE_TABS in the following conditions:
   1135   // . Mouse event generated from touch and the left button is down (the right
   1136   //   button corresponds to a long press, which we want to reorder).
   1137   // . Gesture begin and control key isn't down.
   1138   // . Real mouse event and control is down. This is mostly for testing.
   1139   DCHECK(event.type() == ui::ET_MOUSE_PRESSED ||
   1140          event.type() == ui::ET_GESTURE_BEGIN);
   1141   if (touch_layout_.get() &&
   1142       ((event.type() == ui::ET_MOUSE_PRESSED &&
   1143         (((event.flags() & ui::EF_FROM_TOUCH) &&
   1144           static_cast<const ui::MouseEvent&>(event).IsLeftMouseButton()) ||
   1145          (!(event.flags() & ui::EF_FROM_TOUCH) &&
   1146           static_cast<const ui::MouseEvent&>(event).IsControlDown()))) ||
   1147        (event.type() == ui::ET_GESTURE_BEGIN && !event.IsControlDown()))) {
   1148     move_behavior = TabDragController::MOVE_VISIBILE_TABS;
   1149   }
   1150 
   1151   views::Widget* widget = GetWidget();
   1152 #if defined(OS_WIN)
   1153   // It doesn't make sense to drag tabs out on Win8's single window Metro mode.
   1154   if (win8::IsSingleWindowMetroMode())
   1155     detach_behavior = TabDragController::NOT_DETACHABLE;
   1156 #endif
   1157   // Gestures don't automatically do a capture. We don't allow multiple drags at
   1158   // the same time, so we explicitly capture.
   1159   if (event.type() == ui::ET_GESTURE_BEGIN)
   1160     widget->SetCapture(this);
   1161   drag_controller_.reset(new TabDragController);
   1162   drag_controller_->Init(
   1163       this, tab, tabs, gfx::Point(x, y), event.x(), selection_model,
   1164       detach_behavior, move_behavior, EventSourceFromEvent(event));
   1165 }
   1166 
   1167 void TabStrip::ContinueDrag(views::View* view, const ui::LocatedEvent& event) {
   1168   if (drag_controller_.get() &&
   1169       drag_controller_->event_source() == EventSourceFromEvent(event)) {
   1170     gfx::Point screen_location(event.location());
   1171     views::View::ConvertPointToScreen(view, &screen_location);
   1172     drag_controller_->Drag(screen_location);
   1173   }
   1174 }
   1175 
   1176 bool TabStrip::EndDrag(EndDragReason reason) {
   1177   if (!drag_controller_.get())
   1178     return false;
   1179   bool started_drag = drag_controller_->started_drag();
   1180   drag_controller_->EndDrag(reason);
   1181   return started_drag;
   1182 }
   1183 
   1184 Tab* TabStrip::GetTabAt(Tab* tab, const gfx::Point& tab_in_tab_coordinates) {
   1185   gfx::Point local_point = tab_in_tab_coordinates;
   1186   ConvertPointToTarget(tab, this, &local_point);
   1187 
   1188   views::View* view = GetEventHandlerForPoint(local_point);
   1189   if (!view)
   1190     return NULL;  // No tab contains the point.
   1191 
   1192   // Walk up the view hierarchy until we find a tab, or the TabStrip.
   1193   while (view && view != this && view->id() != VIEW_ID_TAB)
   1194     view = view->parent();
   1195 
   1196   return view && view->id() == VIEW_ID_TAB ? static_cast<Tab*>(view) : NULL;
   1197 }
   1198 
   1199 void TabStrip::OnMouseEventInTab(views::View* source,
   1200                                  const ui::MouseEvent& event) {
   1201   UpdateLayoutTypeFromMouseEvent(source, event);
   1202 }
   1203 
   1204 bool TabStrip::ShouldPaintTab(const Tab* tab, gfx::Rect* clip) {
   1205   // Only touch layout needs to restrict the clip.
   1206   if (!(touch_layout_.get() || IsStackingDraggedTabs()))
   1207     return true;
   1208 
   1209   int index = GetModelIndexOfTab(tab);
   1210   if (index == -1)
   1211     return true;  // Tab is closing, paint it all.
   1212 
   1213   int active_index = IsStackingDraggedTabs() ?
   1214       controller_->GetActiveIndex() : touch_layout_->active_index();
   1215   if (active_index == tab_count())
   1216     active_index--;
   1217 
   1218   if (index < active_index) {
   1219     if (tab_at(index)->x() == tab_at(index + 1)->x())
   1220       return false;
   1221 
   1222     if (tab_at(index)->x() > tab_at(index + 1)->x())
   1223       return true;  // Can happen during dragging.
   1224 
   1225     clip->SetRect(0, 0, tab_at(index + 1)->x() - tab_at(index)->x() +
   1226                       stacked_tab_left_clip(),
   1227                   tab_at(index)->height());
   1228   } else if (index > active_index && index > 0) {
   1229     const gfx::Rect& tab_bounds(tab_at(index)->bounds());
   1230     const gfx::Rect& previous_tab_bounds(tab_at(index - 1)->bounds());
   1231     if (tab_bounds.x() == previous_tab_bounds.x())
   1232       return false;
   1233 
   1234     if (tab_bounds.x() < previous_tab_bounds.x())
   1235       return true;  // Can happen during dragging.
   1236 
   1237     if (previous_tab_bounds.right() + tab_h_offset() != tab_bounds.x()) {
   1238       int x = previous_tab_bounds.right() - tab_bounds.x() -
   1239           stacked_tab_right_clip();
   1240       clip->SetRect(x, 0, tab_bounds.width() - x, tab_bounds.height());
   1241     }
   1242   }
   1243   return true;
   1244 }
   1245 
   1246 bool TabStrip::IsImmersiveStyle() const {
   1247   return immersive_style_;
   1248 }
   1249 
   1250 void TabStrip::MouseMovedOutOfHost() {
   1251   ResizeLayoutTabs();
   1252   if (reset_to_shrink_on_exit_) {
   1253     reset_to_shrink_on_exit_ = false;
   1254     SetLayoutType(TAB_STRIP_LAYOUT_SHRINK, true);
   1255     controller_->LayoutTypeMaybeChanged();
   1256   }
   1257 }
   1258 
   1259 ///////////////////////////////////////////////////////////////////////////////
   1260 // TabStrip, views::View overrides:
   1261 
   1262 void TabStrip::Layout() {
   1263   // Only do a layout if our size changed.
   1264   if (last_layout_size_ == size())
   1265     return;
   1266   if (IsDragSessionActive())
   1267     return;
   1268   DoLayout();
   1269 }
   1270 
   1271 void TabStrip::PaintChildren(gfx::Canvas* canvas) {
   1272   // The view order doesn't match the paint order (tabs_ contains the tab
   1273   // ordering). Additionally we need to paint the tabs that are closing in
   1274   // |tabs_closing_map_|.
   1275   Tab* active_tab = NULL;
   1276   std::vector<Tab*> tabs_dragging;
   1277   std::vector<Tab*> selected_tabs;
   1278   int selected_tab_count = 0;
   1279   bool is_dragging = false;
   1280   int active_tab_index = -1;
   1281   // Since |touch_layout_| is created based on number of tabs and width we use
   1282   // the ideal state to determine if we should paint stacked. This minimizes
   1283   // painting changes as we switch between the two.
   1284   const bool stacking = (layout_type_ == TAB_STRIP_LAYOUT_STACKED) ||
   1285       IsStackingDraggedTabs();
   1286 
   1287   const chrome::HostDesktopType host_desktop_type =
   1288       chrome::GetHostDesktopTypeForNativeView(GetWidget()->GetNativeView());
   1289   const int inactive_tab_alpha =
   1290       host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH ?
   1291       kInactiveTabAndNewTabButtonAlphaAsh :
   1292       kInactiveTabAndNewTabButtonAlpha;
   1293 
   1294   if (inactive_tab_alpha < 255)
   1295     canvas->SaveLayerAlpha(inactive_tab_alpha);
   1296 
   1297   PaintClosingTabs(canvas, tab_count());
   1298 
   1299   for (int i = tab_count() - 1; i >= 0; --i) {
   1300     Tab* tab = tab_at(i);
   1301     if (tab->IsSelected())
   1302       selected_tab_count++;
   1303     if (tab->dragging() && !stacking) {
   1304       is_dragging = true;
   1305       if (tab->IsActive()) {
   1306         active_tab = tab;
   1307         active_tab_index = i;
   1308       } else {
   1309         tabs_dragging.push_back(tab);
   1310       }
   1311     } else if (!tab->IsActive()) {
   1312       if (!tab->IsSelected()) {
   1313         if (!stacking)
   1314           tab->Paint(canvas);
   1315       } else {
   1316         selected_tabs.push_back(tab);
   1317       }
   1318     } else {
   1319       active_tab = tab;
   1320       active_tab_index = i;
   1321     }
   1322     PaintClosingTabs(canvas, i);
   1323   }
   1324 
   1325   // Draw from the left and then the right if we're in touch mode.
   1326   if (stacking && active_tab_index >= 0) {
   1327     for (int i = 0; i < active_tab_index; ++i) {
   1328       Tab* tab = tab_at(i);
   1329       tab->Paint(canvas);
   1330     }
   1331 
   1332     for (int i = tab_count() - 1; i > active_tab_index; --i) {
   1333       Tab* tab = tab_at(i);
   1334       tab->Paint(canvas);
   1335     }
   1336   }
   1337   if (inactive_tab_alpha < 255)
   1338     canvas->Restore();
   1339 
   1340   if (GetWidget()->ShouldUseNativeFrame()) {
   1341     // Make sure non-active tabs are somewhat transparent.
   1342     SkPaint paint;
   1343     // If there are multiple tabs selected, fade non-selected tabs more to make
   1344     // the selected tabs more noticable.
   1345     int alpha = selected_tab_count > 1 ?
   1346         kNativeFrameInactiveTabAlphaMultiSelection :
   1347         kNativeFrameInactiveTabAlpha;
   1348     paint.setColor(SkColorSetARGB(alpha, 255, 255, 255));
   1349     paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
   1350     paint.setStyle(SkPaint::kFill_Style);
   1351     // The tabstrip area overlaps the toolbar area by 2 px.
   1352     canvas->DrawRect(gfx::Rect(0, 0, width(), height() - 2), paint);
   1353   }
   1354 
   1355   // Now selected but not active. We don't want these dimmed if using native
   1356   // frame, so they're painted after initial pass.
   1357   for (size_t i = 0; i < selected_tabs.size(); ++i)
   1358     selected_tabs[i]->Paint(canvas);
   1359 
   1360   // Next comes the active tab.
   1361   if (active_tab && !is_dragging)
   1362     active_tab->Paint(canvas);
   1363 
   1364   // Paint the New Tab button.
   1365   if (inactive_tab_alpha < 255)
   1366     canvas->SaveLayerAlpha(inactive_tab_alpha);
   1367   newtab_button_->Paint(canvas);
   1368   if (inactive_tab_alpha < 255)
   1369     canvas->Restore();
   1370 
   1371   // And the dragged tabs.
   1372   for (size_t i = 0; i < tabs_dragging.size(); ++i)
   1373     tabs_dragging[i]->Paint(canvas);
   1374 
   1375   // If the active tab is being dragged, it goes last.
   1376   if (active_tab && is_dragging)
   1377     active_tab->Paint(canvas);
   1378 }
   1379 
   1380 const char* TabStrip::GetClassName() const {
   1381   return kViewClassName;
   1382 }
   1383 
   1384 gfx::Size TabStrip::GetPreferredSize() {
   1385   // For stacked tabs the minimum size is calculated as the size needed to
   1386   // handle showing any number of tabs. Otherwise report the minimum width as
   1387   // the size required for a single selected tab plus the new tab button. Don't
   1388   // base it on the actual number of tabs because it's undesirable to have the
   1389   // minimum window size change when a new tab is opened.
   1390   int needed_width;
   1391   if (touch_layout_.get() || adjust_layout_) {
   1392     needed_width = Tab::GetTouchWidth() +
   1393         (2 * kStackedPadding * kMaxStackedCount);
   1394   } else {
   1395     needed_width = Tab::GetMinimumSelectedSize().width();
   1396   }
   1397   needed_width += new_tab_button_width();
   1398   if (immersive_style_)
   1399     return gfx::Size(needed_width, Tab::GetImmersiveHeight());
   1400   return gfx::Size(needed_width, Tab::GetMinimumUnselectedSize().height());
   1401 }
   1402 
   1403 void TabStrip::OnDragEntered(const DropTargetEvent& event) {
   1404   // Force animations to stop, otherwise it makes the index calculation tricky.
   1405   StopAnimating(true);
   1406 
   1407   UpdateDropIndex(event);
   1408 
   1409   GURL url;
   1410   base::string16 title;
   1411 
   1412   // Check whether the event data includes supported drop data.
   1413   if (event.data().GetURLAndTitle(
   1414           ui::OSExchangeData::CONVERT_FILENAMES, &url, &title) &&
   1415       url.is_valid()) {
   1416     drop_info_->url = url;
   1417 
   1418     // For file:// URLs, kick off a MIME type request in case they're dropped.
   1419     if (url.SchemeIsFile())
   1420       controller()->CheckFileSupported(url);
   1421   }
   1422 }
   1423 
   1424 int TabStrip::OnDragUpdated(const DropTargetEvent& event) {
   1425   // Update the drop index even if the file is unsupported, to allow
   1426   // dragging a file to the contents of another tab.
   1427   UpdateDropIndex(event);
   1428 
   1429   if (!drop_info_->file_supported)
   1430     return ui::DragDropTypes::DRAG_NONE;
   1431 
   1432   return GetDropEffect(event);
   1433 }
   1434 
   1435 void TabStrip::OnDragExited() {
   1436   SetDropIndex(-1, false);
   1437 }
   1438 
   1439 int TabStrip::OnPerformDrop(const DropTargetEvent& event) {
   1440   if (!drop_info_.get())
   1441     return ui::DragDropTypes::DRAG_NONE;
   1442 
   1443   const int drop_index = drop_info_->drop_index;
   1444   const bool drop_before = drop_info_->drop_before;
   1445   const bool file_supported = drop_info_->file_supported;
   1446 
   1447   // Hide the drop indicator.
   1448   SetDropIndex(-1, false);
   1449 
   1450   // Do nothing if the file was unsupported or the URL is invalid. The URL may
   1451   // have been changed after |drop_info_| was created.
   1452   GURL url;
   1453   base::string16 title;
   1454   if (!file_supported ||
   1455       !event.data().GetURLAndTitle(
   1456            ui::OSExchangeData::CONVERT_FILENAMES, &url, &title) ||
   1457       !url.is_valid())
   1458     return ui::DragDropTypes::DRAG_NONE;
   1459 
   1460   controller()->PerformDrop(drop_before, drop_index, url);
   1461 
   1462   return GetDropEffect(event);
   1463 }
   1464 
   1465 void TabStrip::GetAccessibleState(ui::AccessibleViewState* state) {
   1466   state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST;
   1467 }
   1468 
   1469 views::View* TabStrip::GetEventHandlerForRect(const gfx::Rect& rect) {
   1470   if (!views::UsePointBasedTargeting(rect))
   1471     return View::GetEventHandlerForRect(rect);
   1472   const gfx::Point point(rect.CenterPoint());
   1473 
   1474   if (!touch_layout_.get()) {
   1475     // Return any view that isn't a Tab or this TabStrip immediately. We don't
   1476     // want to interfere.
   1477     views::View* v = View::GetEventHandlerForRect(rect);
   1478     if (v && v != this && strcmp(v->GetClassName(), Tab::kViewClassName))
   1479       return v;
   1480 
   1481     views::View* tab = FindTabHitByPoint(point);
   1482     if (tab)
   1483       return tab;
   1484   } else {
   1485     if (newtab_button_->visible()) {
   1486       views::View* view =
   1487           ConvertPointToViewAndGetEventHandler(this, newtab_button_, point);
   1488       if (view)
   1489         return view;
   1490     }
   1491     Tab* tab = FindTabForEvent(point);
   1492     if (tab)
   1493       return ConvertPointToViewAndGetEventHandler(this, tab, point);
   1494   }
   1495   return this;
   1496 }
   1497 
   1498 views::View* TabStrip::GetTooltipHandlerForPoint(const gfx::Point& point) {
   1499   if (!HitTestPoint(point))
   1500     return NULL;
   1501 
   1502   if (!touch_layout_.get()) {
   1503     // Return any view that isn't a Tab or this TabStrip immediately. We don't
   1504     // want to interfere.
   1505     views::View* v = View::GetTooltipHandlerForPoint(point);
   1506     if (v && v != this && strcmp(v->GetClassName(), Tab::kViewClassName))
   1507       return v;
   1508 
   1509     views::View* tab = FindTabHitByPoint(point);
   1510     if (tab)
   1511       return tab;
   1512   } else {
   1513     if (newtab_button_->visible()) {
   1514       views::View* view =
   1515           ConvertPointToViewAndGetTooltipHandler(this, newtab_button_, point);
   1516       if (view)
   1517         return view;
   1518     }
   1519     Tab* tab = FindTabForEvent(point);
   1520     if (tab)
   1521       return ConvertPointToViewAndGetTooltipHandler(this, tab, point);
   1522   }
   1523   return this;
   1524 }
   1525 
   1526 // static
   1527 int TabStrip::GetImmersiveHeight() {
   1528   return Tab::GetImmersiveHeight();
   1529 }
   1530 
   1531 int TabStrip::GetMiniTabCount() const {
   1532   int mini_count = 0;
   1533   while (mini_count < tab_count() && tab_at(mini_count)->data().mini)
   1534     mini_count++;
   1535   return mini_count;
   1536 }
   1537 
   1538 ///////////////////////////////////////////////////////////////////////////////
   1539 // TabStrip, views::ButtonListener implementation:
   1540 
   1541 void TabStrip::ButtonPressed(views::Button* sender, const ui::Event& event) {
   1542   if (sender == newtab_button_) {
   1543     content::RecordAction(UserMetricsAction("NewTab_Button"));
   1544     UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_BUTTON,
   1545                               TabStripModel::NEW_TAB_ENUM_COUNT);
   1546     if (event.IsMouseEvent()) {
   1547       const ui::MouseEvent& mouse = static_cast<const ui::MouseEvent&>(event);
   1548       if (mouse.IsOnlyMiddleMouseButton()) {
   1549         base::string16 clipboard_text = GetClipboardText();
   1550         if (!clipboard_text.empty())
   1551           controller()->CreateNewTabWithLocation(clipboard_text);
   1552         return;
   1553       }
   1554     }
   1555 
   1556     controller()->CreateNewTab();
   1557     if (event.type() == ui::ET_GESTURE_TAP)
   1558       TouchUMA::RecordGestureAction(TouchUMA::GESTURE_NEWTAB_TAP);
   1559   }
   1560 }
   1561 
   1562 ///////////////////////////////////////////////////////////////////////////////
   1563 // TabStrip, protected:
   1564 
   1565 // Overridden to support automation. See automation_proxy_uitest.cc.
   1566 const views::View* TabStrip::GetViewByID(int view_id) const {
   1567   if (tab_count() > 0) {
   1568     if (view_id == VIEW_ID_TAB_LAST) {
   1569       return tab_at(tab_count() - 1);
   1570     } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
   1571       int index = view_id - VIEW_ID_TAB_0;
   1572       if (index >= 0 && index < tab_count()) {
   1573         return tab_at(index);
   1574       } else {
   1575         return NULL;
   1576       }
   1577     }
   1578   }
   1579 
   1580   return View::GetViewByID(view_id);
   1581 }
   1582 
   1583 bool TabStrip::OnMousePressed(const ui::MouseEvent& event) {
   1584   UpdateLayoutTypeFromMouseEvent(this, event);
   1585   // We can't return true here, else clicking in an empty area won't drag the
   1586   // window.
   1587   return false;
   1588 }
   1589 
   1590 bool TabStrip::OnMouseDragged(const ui::MouseEvent& event) {
   1591   ContinueDrag(this, event);
   1592   return true;
   1593 }
   1594 
   1595 void TabStrip::OnMouseReleased(const ui::MouseEvent& event) {
   1596   EndDrag(END_DRAG_COMPLETE);
   1597   UpdateLayoutTypeFromMouseEvent(this, event);
   1598 }
   1599 
   1600 void TabStrip::OnMouseCaptureLost() {
   1601   EndDrag(END_DRAG_CAPTURE_LOST);
   1602 }
   1603 
   1604 void TabStrip::OnMouseMoved(const ui::MouseEvent& event) {
   1605   UpdateLayoutTypeFromMouseEvent(this, event);
   1606 }
   1607 
   1608 void TabStrip::OnMouseEntered(const ui::MouseEvent& event) {
   1609   SetResetToShrinkOnExit(true);
   1610 }
   1611 
   1612 void TabStrip::OnGestureEvent(ui::GestureEvent* event) {
   1613   SetResetToShrinkOnExit(false);
   1614   switch (event->type()) {
   1615     case ui::ET_GESTURE_SCROLL_END:
   1616     case ui::ET_SCROLL_FLING_START:
   1617     case ui::ET_GESTURE_END:
   1618       EndDrag(END_DRAG_COMPLETE);
   1619       if (adjust_layout_) {
   1620         SetLayoutType(TAB_STRIP_LAYOUT_STACKED, true);
   1621         controller_->LayoutTypeMaybeChanged();
   1622       }
   1623       break;
   1624 
   1625     case ui::ET_GESTURE_LONG_PRESS:
   1626       if (drag_controller_.get())
   1627         drag_controller_->SetMoveBehavior(TabDragController::REORDER);
   1628       break;
   1629 
   1630     case ui::ET_GESTURE_LONG_TAP: {
   1631       EndDrag(END_DRAG_CANCEL);
   1632       gfx::Point local_point = event->location();
   1633       Tab* tab = FindTabForEvent(local_point);
   1634       if (tab) {
   1635         ConvertPointToScreen(this, &local_point);
   1636         ShowContextMenuForTab(tab, local_point, ui::MENU_SOURCE_TOUCH);
   1637       }
   1638       break;
   1639     }
   1640 
   1641     case ui::ET_GESTURE_SCROLL_UPDATE:
   1642       ContinueDrag(this, *event);
   1643       break;
   1644 
   1645     case ui::ET_GESTURE_BEGIN:
   1646       EndDrag(END_DRAG_CANCEL);
   1647       break;
   1648 
   1649     case ui::ET_GESTURE_TAP: {
   1650       const int active_index = controller_->GetActiveIndex();
   1651       DCHECK_NE(-1, active_index);
   1652       Tab* active_tab = tab_at(active_index);
   1653       TouchUMA::GestureActionType action = TouchUMA::GESTURE_TABNOSWITCH_TAP;
   1654       if (active_tab->tab_activated_with_last_gesture_begin())
   1655         action = TouchUMA::GESTURE_TABSWITCH_TAP;
   1656       TouchUMA::RecordGestureAction(action);
   1657       break;
   1658     }
   1659 
   1660     default:
   1661       break;
   1662   }
   1663   event->SetHandled();
   1664 }
   1665 
   1666 ///////////////////////////////////////////////////////////////////////////////
   1667 // TabStrip, private:
   1668 
   1669 void TabStrip::Init() {
   1670   set_id(VIEW_ID_TAB_STRIP);
   1671   // So we get enter/exit on children to switch layout type.
   1672   set_notify_enter_exit_on_child(true);
   1673   newtab_button_bounds_.SetRect(0,
   1674                                 0,
   1675                                 newtab_button_asset_width(),
   1676                                 newtab_button_asset_height() +
   1677                                     newtab_button_v_offset());
   1678   newtab_button_ = new NewTabButton(this, this);
   1679   newtab_button_->SetTooltipText(
   1680       l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB));
   1681   newtab_button_->SetAccessibleName(
   1682       l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB));
   1683   newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
   1684                                     views::ImageButton::ALIGN_BOTTOM);
   1685   AddChildView(newtab_button_);
   1686   if (drop_indicator_width == 0) {
   1687     // Direction doesn't matter, both images are the same size.
   1688     gfx::ImageSkia* drop_image = GetDropArrowImage(true);
   1689     drop_indicator_width = drop_image->width();
   1690     drop_indicator_height = drop_image->height();
   1691   }
   1692 }
   1693 
   1694 Tab* TabStrip::CreateTab() {
   1695   Tab* tab = new Tab(this);
   1696   tab->set_animation_container(animation_container_.get());
   1697   return tab;
   1698 }
   1699 
   1700 void TabStrip::StartInsertTabAnimation(int model_index) {
   1701   PrepareForAnimation();
   1702 
   1703   // The TabStrip can now use its entire width to lay out Tabs.
   1704   in_tab_close_ = false;
   1705   available_width_for_tabs_ = -1;
   1706 
   1707   GenerateIdealBounds();
   1708 
   1709   Tab* tab = tab_at(model_index);
   1710   if (model_index == 0) {
   1711     tab->SetBounds(0, ideal_bounds(model_index).y(), 0,
   1712                    ideal_bounds(model_index).height());
   1713   } else {
   1714     Tab* last_tab = tab_at(model_index - 1);
   1715     tab->SetBounds(last_tab->bounds().right() + tab_h_offset(),
   1716                    ideal_bounds(model_index).y(), 0,
   1717                    ideal_bounds(model_index).height());
   1718   }
   1719 
   1720   AnimateToIdealBounds();
   1721 }
   1722 
   1723 void TabStrip::StartMoveTabAnimation() {
   1724   PrepareForAnimation();
   1725   GenerateIdealBounds();
   1726   AnimateToIdealBounds();
   1727 }
   1728 
   1729 void TabStrip::StartRemoveTabAnimation(int model_index) {
   1730   PrepareForAnimation();
   1731 
   1732   // Mark the tab as closing.
   1733   Tab* tab = tab_at(model_index);
   1734   tab->set_closing(true);
   1735 
   1736   RemoveTabFromViewModel(model_index);
   1737 
   1738   ScheduleRemoveTabAnimation(tab);
   1739 }
   1740 
   1741 void TabStrip::ScheduleRemoveTabAnimation(Tab* tab) {
   1742   // Start an animation for the tabs.
   1743   GenerateIdealBounds();
   1744   AnimateToIdealBounds();
   1745 
   1746   // Animate the tab being closed to 0x0.
   1747   gfx::Rect tab_bounds = tab->bounds();
   1748   tab_bounds.set_width(0);
   1749   bounds_animator_.AnimateViewTo(tab, tab_bounds);
   1750 
   1751   // Register delegate to do cleanup when done, BoundsAnimator takes
   1752   // ownership of RemoveTabDelegate.
   1753   bounds_animator_.SetAnimationDelegate(tab, new RemoveTabDelegate(this, tab),
   1754                                         true);
   1755 
   1756   // Don't animate the new tab button when dragging tabs. Otherwise it looks
   1757   // like the new tab button magically appears from beyond the end of the tab
   1758   // strip.
   1759   if (TabDragController::IsAttachedTo(this)) {
   1760     bounds_animator_.StopAnimatingView(newtab_button_);
   1761     newtab_button_->SetBoundsRect(newtab_button_bounds_);
   1762   }
   1763 }
   1764 
   1765 void TabStrip::AnimateToIdealBounds() {
   1766   for (int i = 0; i < tab_count(); ++i) {
   1767     Tab* tab = tab_at(i);
   1768     if (!tab->dragging())
   1769       bounds_animator_.AnimateViewTo(tab, ideal_bounds(i));
   1770   }
   1771 
   1772   bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_);
   1773 }
   1774 
   1775 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
   1776   return in_tab_close_;
   1777 }
   1778 
   1779 void TabStrip::DoLayout() {
   1780   last_layout_size_ = size();
   1781 
   1782   StopAnimating(false);
   1783 
   1784   SwapLayoutIfNecessary();
   1785 
   1786   if (touch_layout_.get())
   1787     touch_layout_->SetWidth(size().width() - new_tab_button_width());
   1788 
   1789   GenerateIdealBounds();
   1790 
   1791   views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
   1792 
   1793   SchedulePaint();
   1794 
   1795   bounds_animator_.StopAnimatingView(newtab_button_);
   1796   newtab_button_->SetBoundsRect(newtab_button_bounds_);
   1797 }
   1798 
   1799 void TabStrip::DragActiveTab(const std::vector<int>& initial_positions,
   1800                              int delta) {
   1801   DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size()));
   1802   if (!touch_layout_.get()) {
   1803     StackDraggedTabs(delta);
   1804     return;
   1805   }
   1806   SetIdealBoundsFromPositions(initial_positions);
   1807   touch_layout_->DragActiveTab(delta);
   1808   DoLayout();
   1809 }
   1810 
   1811 void TabStrip::SetIdealBoundsFromPositions(const std::vector<int>& positions) {
   1812   if (static_cast<size_t>(tab_count()) != positions.size())
   1813     return;
   1814 
   1815   for (int i = 0; i < tab_count(); ++i) {
   1816     gfx::Rect bounds(ideal_bounds(i));
   1817     bounds.set_x(positions[i]);
   1818     set_ideal_bounds(i, bounds);
   1819   }
   1820 }
   1821 
   1822 void TabStrip::StackDraggedTabs(int delta) {
   1823   DCHECK(!touch_layout_.get());
   1824   GenerateIdealBounds();
   1825   const int active_index = controller_->GetActiveIndex();
   1826   DCHECK_NE(-1, active_index);
   1827   if (delta < 0) {
   1828     // Drag the tabs to the left, stacking tabs before the active tab.
   1829     const int adjusted_delta =
   1830         std::min(ideal_bounds(active_index).x() -
   1831                      kStackedPadding * std::min(active_index, kMaxStackedCount),
   1832                  -delta);
   1833     for (int i = 0; i <= active_index; ++i) {
   1834       const int min_x = std::min(i, kMaxStackedCount) * kStackedPadding;
   1835       gfx::Rect new_bounds(ideal_bounds(i));
   1836       new_bounds.set_x(std::max(min_x, new_bounds.x() - adjusted_delta));
   1837       set_ideal_bounds(i, new_bounds);
   1838     }
   1839     const bool is_active_mini = tab_at(active_index)->data().mini;
   1840     const int active_width = ideal_bounds(active_index).width();
   1841     for (int i = active_index + 1; i < tab_count(); ++i) {
   1842       const int max_x = ideal_bounds(active_index).x() +
   1843           (kStackedPadding * std::min(i - active_index, kMaxStackedCount));
   1844       gfx::Rect new_bounds(ideal_bounds(i));
   1845       int new_x = std::max(new_bounds.x() + delta, max_x);
   1846       if (new_x == max_x && !tab_at(i)->data().mini && !is_active_mini &&
   1847           new_bounds.width() != active_width)
   1848         new_x += (active_width - new_bounds.width());
   1849       new_bounds.set_x(new_x);
   1850       set_ideal_bounds(i, new_bounds);
   1851     }
   1852   } else {
   1853     // Drag the tabs to the right, stacking tabs after the active tab.
   1854     const int last_tab_width = ideal_bounds(tab_count() - 1).width();
   1855     const int last_tab_x = width() - new_tab_button_width() - last_tab_width;
   1856     if (active_index == tab_count() - 1 &&
   1857         ideal_bounds(tab_count() - 1).x() == last_tab_x)
   1858       return;
   1859     const int adjusted_delta =
   1860         std::min(last_tab_x -
   1861                      kStackedPadding * std::min(tab_count() - active_index - 1,
   1862                                                 kMaxStackedCount) -
   1863                      ideal_bounds(active_index).x(),
   1864                  delta);
   1865     for (int last_index = tab_count() - 1, i = last_index; i >= active_index;
   1866          --i) {
   1867       const int max_x = last_tab_x -
   1868           std::min(tab_count() - i - 1, kMaxStackedCount) * kStackedPadding;
   1869       gfx::Rect new_bounds(ideal_bounds(i));
   1870       int new_x = std::min(max_x, new_bounds.x() + adjusted_delta);
   1871       // Because of rounding not all tabs are the same width. Adjust the
   1872       // position to accommodate this, otherwise the stacking is off.
   1873       if (new_x == max_x && !tab_at(i)->data().mini &&
   1874           new_bounds.width() != last_tab_width)
   1875         new_x += (last_tab_width - new_bounds.width());
   1876       new_bounds.set_x(new_x);
   1877       set_ideal_bounds(i, new_bounds);
   1878     }
   1879     for (int i = active_index - 1; i >= 0; --i) {
   1880       const int min_x = ideal_bounds(active_index).x() -
   1881           std::min(active_index - i, kMaxStackedCount) * kStackedPadding;
   1882       gfx::Rect new_bounds(ideal_bounds(i));
   1883       new_bounds.set_x(std::min(min_x, new_bounds.x() + delta));
   1884       set_ideal_bounds(i, new_bounds);
   1885     }
   1886     if (ideal_bounds(tab_count() - 1).right() >= newtab_button_->x())
   1887       newtab_button_->SetVisible(false);
   1888   }
   1889   views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
   1890   SchedulePaint();
   1891 }
   1892 
   1893 bool TabStrip::IsStackingDraggedTabs() const {
   1894   return drag_controller_.get() && drag_controller_->started_drag() &&
   1895       (drag_controller_->move_behavior() ==
   1896        TabDragController::MOVE_VISIBILE_TABS);
   1897 }
   1898 
   1899 void TabStrip::LayoutDraggedTabsAt(const std::vector<Tab*>& tabs,
   1900                                    Tab* active_tab,
   1901                                    const gfx::Point& location,
   1902                                    bool initial_drag) {
   1903   // Immediately hide the new tab button if the last tab is being dragged.
   1904   if (tab_at(tab_count() - 1)->dragging())
   1905     newtab_button_->SetVisible(false);
   1906   std::vector<gfx::Rect> bounds;
   1907   CalculateBoundsForDraggedTabs(tabs, &bounds);
   1908   DCHECK_EQ(tabs.size(), bounds.size());
   1909   int active_tab_model_index = GetModelIndexOfTab(active_tab);
   1910   int active_tab_index = static_cast<int>(
   1911       std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin());
   1912   for (size_t i = 0; i < tabs.size(); ++i) {
   1913     Tab* tab = tabs[i];
   1914     gfx::Rect new_bounds = bounds[i];
   1915     new_bounds.Offset(location.x(), location.y());
   1916     int consecutive_index =
   1917         active_tab_model_index - (active_tab_index - static_cast<int>(i));
   1918     // If this is the initial layout during a drag and the tabs aren't
   1919     // consecutive animate the view into position. Do the same if the tab is
   1920     // already animating (which means we previously caused it to animate).
   1921     if ((initial_drag &&
   1922          GetModelIndexOfTab(tabs[i]) != consecutive_index) ||
   1923         bounds_animator_.IsAnimating(tabs[i])) {
   1924       bounds_animator_.SetTargetBounds(tabs[i], new_bounds);
   1925     } else {
   1926       tab->SetBoundsRect(new_bounds);
   1927     }
   1928   }
   1929 }
   1930 
   1931 void TabStrip::CalculateBoundsForDraggedTabs(const std::vector<Tab*>& tabs,
   1932                                              std::vector<gfx::Rect>* bounds) {
   1933   int x = 0;
   1934   for (size_t i = 0; i < tabs.size(); ++i) {
   1935     Tab* tab = tabs[i];
   1936     if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
   1937       x += kMiniToNonMiniGap;
   1938     gfx::Rect new_bounds = tab->bounds();
   1939     new_bounds.set_origin(gfx::Point(x, 0));
   1940     bounds->push_back(new_bounds);
   1941     x += tab->width() + tab_h_offset();
   1942   }
   1943 }
   1944 
   1945 int TabStrip::GetSizeNeededForTabs(const std::vector<Tab*>& tabs) {
   1946   int width = 0;
   1947   for (size_t i = 0; i < tabs.size(); ++i) {
   1948     Tab* tab = tabs[i];
   1949     width += tab->width();
   1950     if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
   1951       width += kMiniToNonMiniGap;
   1952   }
   1953   if (tabs.size() > 0)
   1954     width += tab_h_offset() * static_cast<int>(tabs.size() - 1);
   1955   return width;
   1956 }
   1957 
   1958 void TabStrip::RemoveTabFromViewModel(int index) {
   1959   // We still need to paint the tab until we actually remove it. Put it
   1960   // in tabs_closing_map_ so we can find it.
   1961   tabs_closing_map_[index].push_back(tab_at(index));
   1962   UpdateTabsClosingMap(index + 1, -1);
   1963   tabs_.Remove(index);
   1964 }
   1965 
   1966 void TabStrip::RemoveAndDeleteTab(Tab* tab) {
   1967   scoped_ptr<Tab> deleter(tab);
   1968   for (TabsClosingMap::iterator i(tabs_closing_map_.begin());
   1969        i != tabs_closing_map_.end(); ++i) {
   1970     std::vector<Tab*>::iterator j =
   1971         std::find(i->second.begin(), i->second.end(), tab);
   1972     if (j != i->second.end()) {
   1973       i->second.erase(j);
   1974       if (i->second.empty())
   1975         tabs_closing_map_.erase(i);
   1976       return;
   1977     }
   1978   }
   1979   NOTREACHED();
   1980 }
   1981 
   1982 void TabStrip::UpdateTabsClosingMap(int index, int delta) {
   1983   if (tabs_closing_map_.empty())
   1984     return;
   1985 
   1986   if (delta == -1 &&
   1987       tabs_closing_map_.find(index - 1) != tabs_closing_map_.end() &&
   1988       tabs_closing_map_.find(index) != tabs_closing_map_.end()) {
   1989     const std::vector<Tab*>& tabs(tabs_closing_map_[index]);
   1990     tabs_closing_map_[index - 1].insert(
   1991         tabs_closing_map_[index - 1].end(), tabs.begin(), tabs.end());
   1992   }
   1993   TabsClosingMap updated_map;
   1994   for (TabsClosingMap::iterator i(tabs_closing_map_.begin());
   1995        i != tabs_closing_map_.end(); ++i) {
   1996     if (i->first > index)
   1997       updated_map[i->first + delta] = i->second;
   1998     else if (i->first < index)
   1999       updated_map[i->first] = i->second;
   2000   }
   2001   if (delta > 0 && tabs_closing_map_.find(index) != tabs_closing_map_.end())
   2002     updated_map[index + delta] = tabs_closing_map_[index];
   2003   tabs_closing_map_.swap(updated_map);
   2004 }
   2005 
   2006 void TabStrip::StartedDraggingTabs(const std::vector<Tab*>& tabs) {
   2007   // Let the controller know that the user started dragging tabs.
   2008   controller()->OnStartedDraggingTabs();
   2009 
   2010   // Hide the new tab button immediately if we didn't originate the drag.
   2011   if (!drag_controller_.get())
   2012     newtab_button_->SetVisible(false);
   2013 
   2014   PrepareForAnimation();
   2015 
   2016   // Reset dragging state of existing tabs.
   2017   for (int i = 0; i < tab_count(); ++i)
   2018     tab_at(i)->set_dragging(false);
   2019 
   2020   for (size_t i = 0; i < tabs.size(); ++i) {
   2021     tabs[i]->set_dragging(true);
   2022     bounds_animator_.StopAnimatingView(tabs[i]);
   2023   }
   2024 
   2025   // Move the dragged tabs to their ideal bounds.
   2026   GenerateIdealBounds();
   2027 
   2028   // Sets the bounds of the dragged tabs.
   2029   for (size_t i = 0; i < tabs.size(); ++i) {
   2030     int tab_data_index = GetModelIndexOfTab(tabs[i]);
   2031     DCHECK_NE(-1, tab_data_index);
   2032     tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index));
   2033   }
   2034   SchedulePaint();
   2035 }
   2036 
   2037 void TabStrip::DraggedTabsDetached() {
   2038   // Let the controller know that the user is not dragging this tabstrip's tabs
   2039   // anymore.
   2040   controller()->OnStoppedDraggingTabs();
   2041   newtab_button_->SetVisible(true);
   2042 }
   2043 
   2044 void TabStrip::StoppedDraggingTabs(const std::vector<Tab*>& tabs,
   2045                                    const std::vector<int>& initial_positions,
   2046                                    bool move_only,
   2047                                    bool completed) {
   2048   // Let the controller know that the user stopped dragging tabs.
   2049   controller()->OnStoppedDraggingTabs();
   2050 
   2051   newtab_button_->SetVisible(true);
   2052   if (move_only && touch_layout_.get()) {
   2053     if (completed) {
   2054       touch_layout_->SizeToFit();
   2055     } else {
   2056       SetIdealBoundsFromPositions(initial_positions);
   2057     }
   2058   }
   2059   bool is_first_tab = true;
   2060   for (size_t i = 0; i < tabs.size(); ++i)
   2061     StoppedDraggingTab(tabs[i], &is_first_tab);
   2062 }
   2063 
   2064 void TabStrip::StoppedDraggingTab(Tab* tab, bool* is_first_tab) {
   2065   int tab_data_index = GetModelIndexOfTab(tab);
   2066   if (tab_data_index == -1) {
   2067     // The tab was removed before the drag completed. Don't do anything.
   2068     return;
   2069   }
   2070 
   2071   if (*is_first_tab) {
   2072     *is_first_tab = false;
   2073     PrepareForAnimation();
   2074 
   2075     // Animate the view back to its correct position.
   2076     GenerateIdealBounds();
   2077     AnimateToIdealBounds();
   2078   }
   2079   bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index));
   2080   // Install a delegate to reset the dragging state when done. We have to leave
   2081   // dragging true for the tab otherwise it'll draw beneath the new tab button.
   2082   bounds_animator_.SetAnimationDelegate(
   2083       tab, new ResetDraggingStateDelegate(tab), true);
   2084 }
   2085 
   2086 void TabStrip::OwnDragController(TabDragController* controller) {
   2087   // Typically, ReleaseDragController() and OwnDragController() calls are paired
   2088   // via corresponding calls to TabDragController::Detach() and
   2089   // TabDragController::Attach(). There is one exception to that rule: when a
   2090   // drag might start, we create a TabDragController that is owned by the
   2091   // potential source tabstrip in MaybeStartDrag(). If a drag actually starts,
   2092   // we then call Attach() on the source tabstrip, but since the source tabstrip
   2093   // already owns the TabDragController, so we don't need to do anything.
   2094   if (controller != drag_controller_.get())
   2095     drag_controller_.reset(controller);
   2096 }
   2097 
   2098 void TabStrip::DestroyDragController() {
   2099   newtab_button_->SetVisible(true);
   2100   drag_controller_.reset();
   2101 }
   2102 
   2103 TabDragController* TabStrip::ReleaseDragController() {
   2104   return drag_controller_.release();
   2105 }
   2106 
   2107 void TabStrip::PaintClosingTabs(gfx::Canvas* canvas, int index) {
   2108   if (tabs_closing_map_.find(index) == tabs_closing_map_.end())
   2109     return;
   2110 
   2111   const std::vector<Tab*>& tabs = tabs_closing_map_[index];
   2112   for (std::vector<Tab*>::const_reverse_iterator i(tabs.rbegin());
   2113        i != tabs.rend(); ++i) {
   2114     (*i)->Paint(canvas);
   2115   }
   2116 }
   2117 
   2118 void TabStrip::UpdateLayoutTypeFromMouseEvent(views::View* source,
   2119                                               const ui::MouseEvent& event) {
   2120   if (!GetAdjustLayout())
   2121     return;
   2122 
   2123   // The following code attempts to switch to TAB_STRIP_LAYOUT_SHRINK when the
   2124   // mouse exits the tabstrip (or the mouse is pressed on a stacked tab) and
   2125   // TAB_STRIP_LAYOUT_STACKED when a touch device is used. This is made
   2126   // problematic by windows generating mouse move events that do not clearly
   2127   // indicate the move is the result of a touch device. This assumes a real
   2128   // mouse is used if |kMouseMoveCountBeforeConsiderReal| mouse move events are
   2129   // received within the time window |kMouseMoveTimeMS|.  At the time we get a
   2130   // mouse press we know whether its from a touch device or not, but we don't
   2131   // layout then else everything shifts. Instead we wait for the release.
   2132   //
   2133   // TODO(sky): revisit this when touch events are really plumbed through.
   2134 
   2135   switch (event.type()) {
   2136     case ui::ET_MOUSE_PRESSED:
   2137       mouse_move_count_ = 0;
   2138       last_mouse_move_time_ = base::TimeTicks();
   2139       SetResetToShrinkOnExit((event.flags() & ui::EF_FROM_TOUCH) == 0);
   2140       if (reset_to_shrink_on_exit_ && touch_layout_.get()) {
   2141         gfx::Point tab_strip_point(event.location());
   2142         views::View::ConvertPointToTarget(source, this, &tab_strip_point);
   2143         Tab* tab = FindTabForEvent(tab_strip_point);
   2144         if (tab && touch_layout_->IsStacked(GetModelIndexOfTab(tab))) {
   2145           SetLayoutType(TAB_STRIP_LAYOUT_SHRINK, true);
   2146           controller_->LayoutTypeMaybeChanged();
   2147         }
   2148       }
   2149       break;
   2150 
   2151     case ui::ET_MOUSE_MOVED: {
   2152 #if defined(USE_ASH)
   2153       // Ash does not synthesize mouse events from touch events.
   2154       SetResetToShrinkOnExit(true);
   2155 #else
   2156       gfx::Point location(event.location());
   2157       ConvertPointToTarget(source, this, &location);
   2158       if (location == last_mouse_move_location_)
   2159         return;  // Ignore spurious moves.
   2160       last_mouse_move_location_ = location;
   2161       if ((event.flags() & ui::EF_FROM_TOUCH) == 0 &&
   2162           (event.flags() & ui::EF_IS_SYNTHESIZED) == 0) {
   2163         if ((base::TimeTicks::Now() - last_mouse_move_time_).InMilliseconds() <
   2164             kMouseMoveTimeMS) {
   2165           if (mouse_move_count_++ == kMouseMoveCountBeforeConsiderReal)
   2166             SetResetToShrinkOnExit(true);
   2167         } else {
   2168           mouse_move_count_ = 1;
   2169           last_mouse_move_time_ = base::TimeTicks::Now();
   2170         }
   2171       } else {
   2172         last_mouse_move_time_ = base::TimeTicks();
   2173       }
   2174 #endif
   2175       break;
   2176     }
   2177 
   2178     case ui::ET_MOUSE_RELEASED: {
   2179       gfx::Point location(event.location());
   2180       ConvertPointToTarget(source, this, &location);
   2181       last_mouse_move_location_ = location;
   2182       mouse_move_count_ = 0;
   2183       last_mouse_move_time_ = base::TimeTicks();
   2184       if ((event.flags() & ui::EF_FROM_TOUCH) == ui::EF_FROM_TOUCH) {
   2185         SetLayoutType(TAB_STRIP_LAYOUT_STACKED, true);
   2186         controller_->LayoutTypeMaybeChanged();
   2187       }
   2188       break;
   2189     }
   2190 
   2191     default:
   2192       break;
   2193   }
   2194 }
   2195 
   2196 void TabStrip::GetCurrentTabWidths(double* unselected_width,
   2197                                    double* selected_width) const {
   2198   *unselected_width = current_unselected_width_;
   2199   *selected_width = current_selected_width_;
   2200 }
   2201 
   2202 void TabStrip::GetDesiredTabWidths(int tab_count,
   2203                                    int mini_tab_count,
   2204                                    double* unselected_width,
   2205                                    double* selected_width) const {
   2206   DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
   2207   const double min_unselected_width = Tab::GetMinimumUnselectedSize().width();
   2208   const double min_selected_width = Tab::GetMinimumSelectedSize().width();
   2209 
   2210   *unselected_width = min_unselected_width;
   2211   *selected_width = min_selected_width;
   2212 
   2213   if (tab_count == 0) {
   2214     // Return immediately to avoid divide-by-zero below.
   2215     return;
   2216   }
   2217 
   2218   // Determine how much space we can actually allocate to tabs.
   2219   int available_width;
   2220   if (available_width_for_tabs_ < 0) {
   2221     available_width = width() - new_tab_button_width();
   2222   } else {
   2223     // Interesting corner case: if |available_width_for_tabs_| > the result
   2224     // of the calculation in the conditional arm above, the strip is in
   2225     // overflow.  We can either use the specified width or the true available
   2226     // width here; the first preserves the consistent "leave the last tab under
   2227     // the user's mouse so they can close many tabs" behavior at the cost of
   2228     // prolonging the glitchy appearance of the overflow state, while the second
   2229     // gets us out of overflow as soon as possible but forces the user to move
   2230     // their mouse for a few tabs' worth of closing.  We choose visual
   2231     // imperfection over behavioral imperfection and select the first option.
   2232     available_width = available_width_for_tabs_;
   2233   }
   2234 
   2235   if (mini_tab_count > 0) {
   2236     available_width -= mini_tab_count * (Tab::GetMiniWidth() + tab_h_offset());
   2237     tab_count -= mini_tab_count;
   2238     if (tab_count == 0) {
   2239       *selected_width = *unselected_width = Tab::GetStandardSize().width();
   2240       return;
   2241     }
   2242     // Account for gap between the last mini-tab and first non-mini-tab.
   2243     available_width -= kMiniToNonMiniGap;
   2244   }
   2245 
   2246   // Calculate the desired tab widths by dividing the available space into equal
   2247   // portions.  Don't let tabs get larger than the "standard width" or smaller
   2248   // than the minimum width for each type, respectively.
   2249   const int total_offset = tab_h_offset() * (tab_count - 1);
   2250   const double desired_tab_width = std::min((static_cast<double>(
   2251       available_width - total_offset) / static_cast<double>(tab_count)),
   2252       static_cast<double>(Tab::GetStandardSize().width()));
   2253   *unselected_width = std::max(desired_tab_width, min_unselected_width);
   2254   *selected_width = std::max(desired_tab_width, min_selected_width);
   2255 
   2256   // When there are multiple tabs, we'll have one selected and some unselected
   2257   // tabs.  If the desired width was between the minimum sizes of these types,
   2258   // try to shrink the tabs with the smaller minimum.  For example, if we have
   2259   // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5.  If
   2260   // selected tabs have a minimum width of 4 and unselected tabs have a minimum
   2261   // width of 1, the above code would set *unselected_width = 2.5,
   2262   // *selected_width = 4, which results in a total width of 11.5.  Instead, we
   2263   // want to set *unselected_width = 2, *selected_width = 4, for a total width
   2264   // of 10.
   2265   if (tab_count > 1) {
   2266     if ((min_unselected_width < min_selected_width) &&
   2267         (desired_tab_width < min_selected_width)) {
   2268       // Unselected width = (total width - selected width) / (num_tabs - 1)
   2269       *unselected_width = std::max(static_cast<double>(
   2270           available_width - total_offset - min_selected_width) /
   2271           static_cast<double>(tab_count - 1), min_unselected_width);
   2272     } else if ((min_unselected_width > min_selected_width) &&
   2273                (desired_tab_width < min_unselected_width)) {
   2274       // Selected width = (total width - (unselected width * (num_tabs - 1)))
   2275       *selected_width = std::max(available_width - total_offset -
   2276           (min_unselected_width * (tab_count - 1)), min_selected_width);
   2277     }
   2278   }
   2279 }
   2280 
   2281 void TabStrip::ResizeLayoutTabs() {
   2282   // We've been called back after the TabStrip has been emptied out (probably
   2283   // just prior to the window being destroyed). We need to do nothing here or
   2284   // else GetTabAt below will crash.
   2285   if (tab_count() == 0)
   2286     return;
   2287 
   2288   // It is critically important that this is unhooked here, otherwise we will
   2289   // keep spying on messages forever.
   2290   RemoveMessageLoopObserver();
   2291 
   2292   in_tab_close_ = false;
   2293   available_width_for_tabs_ = -1;
   2294   int mini_tab_count = GetMiniTabCount();
   2295   if (mini_tab_count == tab_count()) {
   2296     // Only mini-tabs, we know the tab widths won't have changed (all
   2297     // mini-tabs have the same width), so there is nothing to do.
   2298     return;
   2299   }
   2300   // Don't try and avoid layout based on tab sizes. If tabs are small enough
   2301   // then the width of the active tab may not change, but other widths may
   2302   // have. This is particularly important if we've overflowed (all tabs are at
   2303   // the min).
   2304   StartResizeLayoutAnimation();
   2305 }
   2306 
   2307 void TabStrip::ResizeLayoutTabsFromTouch() {
   2308   // Don't resize if the user is interacting with the tabstrip.
   2309   if (!drag_controller_.get())
   2310     ResizeLayoutTabs();
   2311   else
   2312     StartResizeLayoutTabsFromTouchTimer();
   2313 }
   2314 
   2315 void TabStrip::StartResizeLayoutTabsFromTouchTimer() {
   2316   resize_layout_timer_.Stop();
   2317   resize_layout_timer_.Start(
   2318       FROM_HERE, base::TimeDelta::FromMilliseconds(kTouchResizeLayoutTimeMS),
   2319       this, &TabStrip::ResizeLayoutTabsFromTouch);
   2320 }
   2321 
   2322 void TabStrip::SetTabBoundsForDrag(const std::vector<gfx::Rect>& tab_bounds) {
   2323   StopAnimating(false);
   2324   DCHECK_EQ(tab_count(), static_cast<int>(tab_bounds.size()));
   2325   for (int i = 0; i < tab_count(); ++i)
   2326     tab_at(i)->SetBoundsRect(tab_bounds[i]);
   2327 }
   2328 
   2329 void TabStrip::AddMessageLoopObserver() {
   2330   if (!mouse_watcher_.get()) {
   2331     mouse_watcher_.reset(
   2332         new views::MouseWatcher(
   2333             new views::MouseWatcherViewHost(
   2334                 this, gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)),
   2335             this));
   2336   }
   2337   mouse_watcher_->Start();
   2338 }
   2339 
   2340 void TabStrip::RemoveMessageLoopObserver() {
   2341   mouse_watcher_.reset(NULL);
   2342 }
   2343 
   2344 gfx::Rect TabStrip::GetDropBounds(int drop_index,
   2345                                   bool drop_before,
   2346                                   bool* is_beneath) {
   2347   DCHECK_NE(drop_index, -1);
   2348   int center_x;
   2349   if (drop_index < tab_count()) {
   2350     Tab* tab = tab_at(drop_index);
   2351     if (drop_before)
   2352       center_x = tab->x() - (tab_h_offset() / 2);
   2353     else
   2354       center_x = tab->x() + (tab->width() / 2);
   2355   } else {
   2356     Tab* last_tab = tab_at(drop_index - 1);
   2357     center_x = last_tab->x() + last_tab->width() + (tab_h_offset() / 2);
   2358   }
   2359 
   2360   // Mirror the center point if necessary.
   2361   center_x = GetMirroredXInView(center_x);
   2362 
   2363   // Determine the screen bounds.
   2364   gfx::Point drop_loc(center_x - drop_indicator_width / 2,
   2365                       -drop_indicator_height);
   2366   ConvertPointToScreen(this, &drop_loc);
   2367   gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
   2368                         drop_indicator_height);
   2369 
   2370   // If the rect doesn't fit on the monitor, push the arrow to the bottom.
   2371   gfx::Screen* screen = gfx::Screen::GetScreenFor(GetWidget()->GetNativeView());
   2372   gfx::Display display = screen->GetDisplayMatching(drop_bounds);
   2373   *is_beneath = !display.bounds().Contains(drop_bounds);
   2374   if (*is_beneath)
   2375     drop_bounds.Offset(0, drop_bounds.height() + height());
   2376 
   2377   return drop_bounds;
   2378 }
   2379 
   2380 void TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
   2381   // If the UI layout is right-to-left, we need to mirror the mouse
   2382   // coordinates since we calculate the drop index based on the
   2383   // original (and therefore non-mirrored) positions of the tabs.
   2384   const int x = GetMirroredXInView(event.x());
   2385   // We don't allow replacing the urls of mini-tabs.
   2386   for (int i = GetMiniTabCount(); i < tab_count(); ++i) {
   2387     Tab* tab = tab_at(i);
   2388     const int tab_max_x = tab->x() + tab->width();
   2389     const int hot_width = tab->width() / kTabEdgeRatioInverse;
   2390     if (x < tab_max_x) {
   2391       if (x < tab->x() + hot_width)
   2392         SetDropIndex(i, true);
   2393       else if (x >= tab_max_x - hot_width)
   2394         SetDropIndex(i + 1, true);
   2395       else
   2396         SetDropIndex(i, false);
   2397       return;
   2398     }
   2399   }
   2400 
   2401   // The drop isn't over a tab, add it to the end.
   2402   SetDropIndex(tab_count(), true);
   2403 }
   2404 
   2405 void TabStrip::SetDropIndex(int tab_data_index, bool drop_before) {
   2406   // Let the controller know of the index update.
   2407   controller()->OnDropIndexUpdate(tab_data_index, drop_before);
   2408 
   2409   if (tab_data_index == -1) {
   2410     if (drop_info_.get())
   2411       drop_info_.reset(NULL);
   2412     return;
   2413   }
   2414 
   2415   if (drop_info_.get() && drop_info_->drop_index == tab_data_index &&
   2416       drop_info_->drop_before == drop_before) {
   2417     return;
   2418   }
   2419 
   2420   bool is_beneath;
   2421   gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before,
   2422                                         &is_beneath);
   2423 
   2424   if (!drop_info_.get()) {
   2425     drop_info_.reset(
   2426         new DropInfo(tab_data_index, drop_before, !is_beneath, GetWidget()));
   2427   } else {
   2428     drop_info_->drop_index = tab_data_index;
   2429     drop_info_->drop_before = drop_before;
   2430     if (is_beneath == drop_info_->point_down) {
   2431       drop_info_->point_down = !is_beneath;
   2432       drop_info_->arrow_view->SetImage(
   2433           GetDropArrowImage(drop_info_->point_down));
   2434     }
   2435   }
   2436 
   2437   // Reposition the window. Need to show it too as the window is initially
   2438   // hidden.
   2439   drop_info_->arrow_window->SetBounds(drop_bounds);
   2440   drop_info_->arrow_window->Show();
   2441 }
   2442 
   2443 int TabStrip::GetDropEffect(const ui::DropTargetEvent& event) {
   2444   const int source_ops = event.source_operations();
   2445   if (source_ops & ui::DragDropTypes::DRAG_COPY)
   2446     return ui::DragDropTypes::DRAG_COPY;
   2447   if (source_ops & ui::DragDropTypes::DRAG_LINK)
   2448     return ui::DragDropTypes::DRAG_LINK;
   2449   return ui::DragDropTypes::DRAG_MOVE;
   2450 }
   2451 
   2452 // static
   2453 gfx::ImageSkia* TabStrip::GetDropArrowImage(bool is_down) {
   2454   return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
   2455       is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
   2456 }
   2457 
   2458 // TabStrip::DropInfo ----------------------------------------------------------
   2459 
   2460 TabStrip::DropInfo::DropInfo(int drop_index,
   2461                              bool drop_before,
   2462                              bool point_down,
   2463                              views::Widget* context)
   2464     : drop_index(drop_index),
   2465       drop_before(drop_before),
   2466       point_down(point_down),
   2467       file_supported(true) {
   2468   arrow_view = new views::ImageView;
   2469   arrow_view->SetImage(GetDropArrowImage(point_down));
   2470 
   2471   arrow_window = new views::Widget;
   2472   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
   2473   params.keep_on_top = true;
   2474   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   2475   params.accept_events = false;
   2476   params.can_activate = false;
   2477   params.bounds = gfx::Rect(drop_indicator_width, drop_indicator_height);
   2478   params.context = context->GetNativeView();
   2479   arrow_window->Init(params);
   2480   arrow_window->SetContentsView(arrow_view);
   2481 }
   2482 
   2483 TabStrip::DropInfo::~DropInfo() {
   2484   // Close eventually deletes the window, which deletes arrow_view too.
   2485   arrow_window->Close();
   2486 }
   2487 
   2488 ///////////////////////////////////////////////////////////////////////////////
   2489 
   2490 void TabStrip::PrepareForAnimation() {
   2491   if (!IsDragSessionActive() && !TabDragController::IsAttachedTo(this)) {
   2492     for (int i = 0; i < tab_count(); ++i)
   2493       tab_at(i)->set_dragging(false);
   2494   }
   2495 }
   2496 
   2497 void TabStrip::GenerateIdealBounds() {
   2498   int new_tab_y = 0;
   2499 
   2500   if (touch_layout_.get()) {
   2501     if (tabs_.view_size() == 0)
   2502       return;
   2503 
   2504     int new_tab_x = tabs_.ideal_bounds(tabs_.view_size() - 1).right() +
   2505         newtab_button_h_offset();
   2506     newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
   2507     return;
   2508   }
   2509 
   2510   double unselected, selected;
   2511   GetDesiredTabWidths(tab_count(), GetMiniTabCount(), &unselected, &selected);
   2512   current_unselected_width_ = unselected;
   2513   current_selected_width_ = selected;
   2514 
   2515   // NOTE: This currently assumes a tab's height doesn't differ based on
   2516   // selected state or the number of tabs in the strip!
   2517   int tab_height = Tab::GetStandardSize().height();
   2518   int first_non_mini_index = 0;
   2519   double tab_x = GenerateIdealBoundsForMiniTabs(&first_non_mini_index);
   2520   for (int i = first_non_mini_index; i < tab_count(); ++i) {
   2521     Tab* tab = tab_at(i);
   2522     DCHECK(!tab->data().mini);
   2523     double tab_width = tab->IsActive() ? selected : unselected;
   2524     double end_of_tab = tab_x + tab_width;
   2525     int rounded_tab_x = Round(tab_x);
   2526     set_ideal_bounds(
   2527         i,
   2528         gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
   2529                   tab_height));
   2530     tab_x = end_of_tab + tab_h_offset();
   2531   }
   2532 
   2533   // Update bounds of new tab button.
   2534   int new_tab_x;
   2535   if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
   2536       !in_tab_close_) {
   2537     // We're shrinking tabs, so we need to anchor the New Tab button to the
   2538     // right edge of the TabStrip's bounds, rather than the right edge of the
   2539     // right-most Tab, otherwise it'll bounce when animating.
   2540     new_tab_x = width() - newtab_button_bounds_.width();
   2541   } else {
   2542     new_tab_x = Round(tab_x - tab_h_offset()) + newtab_button_h_offset();
   2543   }
   2544   newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
   2545 }
   2546 
   2547 int TabStrip::GenerateIdealBoundsForMiniTabs(int* first_non_mini_index) {
   2548   int next_x = 0;
   2549   int mini_width = Tab::GetMiniWidth();
   2550   int tab_height = Tab::GetStandardSize().height();
   2551   int index = 0;
   2552   for (; index < tab_count() && tab_at(index)->data().mini; ++index) {
   2553     set_ideal_bounds(index,
   2554                      gfx::Rect(next_x, 0, mini_width, tab_height));
   2555     next_x += mini_width + tab_h_offset();
   2556   }
   2557   if (index > 0 && index < tab_count())
   2558     next_x += kMiniToNonMiniGap;
   2559   if (first_non_mini_index)
   2560     *first_non_mini_index = index;
   2561   return next_x;
   2562 }
   2563 
   2564 // static
   2565 int TabStrip::new_tab_button_width() {
   2566   return newtab_button_asset_width() + newtab_button_h_offset();
   2567 }
   2568 
   2569 // static
   2570 int TabStrip::button_v_offset() {
   2571   return newtab_button_v_offset();
   2572 }
   2573 
   2574 int TabStrip::tab_area_width() const {
   2575   return width() - new_tab_button_width();
   2576 }
   2577 
   2578 void TabStrip::StartResizeLayoutAnimation() {
   2579   PrepareForAnimation();
   2580   GenerateIdealBounds();
   2581   AnimateToIdealBounds();
   2582 }
   2583 
   2584 void TabStrip::StartMiniTabAnimation() {
   2585   in_tab_close_ = false;
   2586   available_width_for_tabs_ = -1;
   2587 
   2588   PrepareForAnimation();
   2589 
   2590   GenerateIdealBounds();
   2591   AnimateToIdealBounds();
   2592 }
   2593 
   2594 void TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
   2595   // The user initiated the close. We want to persist the bounds of all the
   2596   // existing tabs, so we manually shift ideal_bounds then animate.
   2597   Tab* tab_closing = tab_at(model_index);
   2598   int delta = tab_closing->width() + tab_h_offset();
   2599   // If the tab being closed is a mini-tab next to a non-mini-tab, be sure to
   2600   // add the extra padding.
   2601   DCHECK_NE(model_index + 1, tab_count());
   2602   if (tab_closing->data().mini && model_index + 1 < tab_count() &&
   2603       !tab_at(model_index + 1)->data().mini) {
   2604     delta += kMiniToNonMiniGap;
   2605   }
   2606 
   2607   for (int i = model_index + 1; i < tab_count(); ++i) {
   2608     gfx::Rect bounds = ideal_bounds(i);
   2609     bounds.set_x(bounds.x() - delta);
   2610     set_ideal_bounds(i, bounds);
   2611   }
   2612 
   2613   newtab_button_bounds_.set_x(newtab_button_bounds_.x() - delta);
   2614 
   2615   PrepareForAnimation();
   2616 
   2617   tab_closing->set_closing(true);
   2618 
   2619   // We still need to paint the tab until we actually remove it. Put it in
   2620   // tabs_closing_map_ so we can find it.
   2621   RemoveTabFromViewModel(model_index);
   2622 
   2623   AnimateToIdealBounds();
   2624 
   2625   gfx::Rect tab_bounds = tab_closing->bounds();
   2626   tab_bounds.set_width(0);
   2627   bounds_animator_.AnimateViewTo(tab_closing, tab_bounds);
   2628 
   2629   // Register delegate to do cleanup when done, BoundsAnimator takes
   2630   // ownership of RemoveTabDelegate.
   2631   bounds_animator_.SetAnimationDelegate(
   2632       tab_closing,
   2633       new RemoveTabDelegate(this, tab_closing),
   2634       true);
   2635 }
   2636 
   2637 bool TabStrip::IsPointInTab(Tab* tab,
   2638                             const gfx::Point& point_in_tabstrip_coords) {
   2639   gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
   2640   View::ConvertPointToTarget(this, tab, &point_in_tab_coords);
   2641   return tab->HitTestPoint(point_in_tab_coords);
   2642 }
   2643 
   2644 int TabStrip::GetStartXForNormalTabs() const {
   2645   int mini_tab_count = GetMiniTabCount();
   2646   if (mini_tab_count == 0)
   2647     return 0;
   2648   return mini_tab_count * (Tab::GetMiniWidth() + tab_h_offset()) +
   2649       kMiniToNonMiniGap;
   2650 }
   2651 
   2652 Tab* TabStrip::FindTabForEvent(const gfx::Point& point) {
   2653   if (touch_layout_.get()) {
   2654     int active_tab_index = touch_layout_->active_index();
   2655     if (active_tab_index != -1) {
   2656       Tab* tab = FindTabForEventFrom(point, active_tab_index, -1);
   2657       if (!tab)
   2658         tab = FindTabForEventFrom(point, active_tab_index + 1, 1);
   2659       return tab;
   2660     } else if (tab_count()) {
   2661       return FindTabForEventFrom(point, 0, 1);
   2662     }
   2663   } else {
   2664     for (int i = 0; i < tab_count(); ++i) {
   2665       if (IsPointInTab(tab_at(i), point))
   2666         return tab_at(i);
   2667     }
   2668   }
   2669   return NULL;
   2670 }
   2671 
   2672 Tab* TabStrip::FindTabForEventFrom(const gfx::Point& point,
   2673                                    int start,
   2674                                    int delta) {
   2675   // |start| equals tab_count() when there are only pinned tabs.
   2676   if (start == tab_count())
   2677     start += delta;
   2678   for (int i = start; i >= 0 && i < tab_count(); i += delta) {
   2679     if (IsPointInTab(tab_at(i), point))
   2680       return tab_at(i);
   2681   }
   2682   return NULL;
   2683 }
   2684 
   2685 views::View* TabStrip::FindTabHitByPoint(const gfx::Point& point) {
   2686   // The display order doesn't necessarily match the child list order, so we
   2687   // walk the display list hit-testing Tabs. Since the active tab always
   2688   // renders on top of adjacent tabs, it needs to be hit-tested before any
   2689   // left-adjacent Tab, so we look ahead for it as we walk.
   2690   for (int i = 0; i < tab_count(); ++i) {
   2691     Tab* next_tab = i < (tab_count() - 1) ? tab_at(i + 1) : NULL;
   2692     if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point))
   2693       return next_tab;
   2694     if (IsPointInTab(tab_at(i), point))
   2695       return tab_at(i);
   2696   }
   2697 
   2698   return NULL;
   2699 }
   2700 
   2701 std::vector<int> TabStrip::GetTabXCoordinates() {
   2702   std::vector<int> results;
   2703   for (int i = 0; i < tab_count(); ++i)
   2704     results.push_back(ideal_bounds(i).x());
   2705   return results;
   2706 }
   2707 
   2708 void TabStrip::SwapLayoutIfNecessary() {
   2709   bool needs_touch = NeedsTouchLayout();
   2710   bool using_touch = touch_layout_.get() != NULL;
   2711   if (needs_touch == using_touch)
   2712     return;
   2713 
   2714   if (needs_touch) {
   2715     gfx::Size tab_size(Tab::GetMinimumSelectedSize());
   2716     tab_size.set_width(Tab::GetTouchWidth());
   2717     touch_layout_.reset(new StackedTabStripLayout(
   2718                             tab_size,
   2719                             tab_h_offset(),
   2720                             kStackedPadding,
   2721                             kMaxStackedCount,
   2722                             &tabs_));
   2723     touch_layout_->SetWidth(width() - new_tab_button_width());
   2724     // This has to be after SetWidth() as SetWidth() is going to reset the
   2725     // bounds of the mini-tabs (since StackedTabStripLayout doesn't yet know how
   2726     // many mini-tabs there are).
   2727     GenerateIdealBoundsForMiniTabs(NULL);
   2728     touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(),
   2729                                     GetMiniTabCount());
   2730     touch_layout_->SetActiveIndex(controller_->GetActiveIndex());
   2731   } else {
   2732     touch_layout_.reset();
   2733   }
   2734   PrepareForAnimation();
   2735   GenerateIdealBounds();
   2736   AnimateToIdealBounds();
   2737 }
   2738 
   2739 bool TabStrip::NeedsTouchLayout() const {
   2740   if (layout_type_ == TAB_STRIP_LAYOUT_SHRINK)
   2741     return false;
   2742 
   2743   int mini_tab_count = GetMiniTabCount();
   2744   int normal_count = tab_count() - mini_tab_count;
   2745   if (normal_count <= 1 || normal_count == mini_tab_count)
   2746     return false;
   2747   int x = GetStartXForNormalTabs();
   2748   int available_width = width() - x - new_tab_button_width();
   2749   return (Tab::GetTouchWidth() * normal_count +
   2750           tab_h_offset() * (normal_count - 1)) > available_width;
   2751 }
   2752 
   2753 void TabStrip::SetResetToShrinkOnExit(bool value) {
   2754   if (!GetAdjustLayout())
   2755     return;
   2756 
   2757   if (value && layout_type_ == TAB_STRIP_LAYOUT_SHRINK)
   2758     value = false;  // We're already at TAB_STRIP_LAYOUT_SHRINK.
   2759 
   2760   if (value == reset_to_shrink_on_exit_)
   2761     return;
   2762 
   2763   reset_to_shrink_on_exit_ = value;
   2764   // Add an observer so we know when the mouse moves out of the tabstrip.
   2765   if (reset_to_shrink_on_exit_)
   2766     AddMessageLoopObserver();
   2767   else
   2768     RemoveMessageLoopObserver();
   2769 }
   2770 
   2771 bool TabStrip::GetAdjustLayout() const {
   2772   if (!adjust_layout_)
   2773     return false;
   2774 
   2775 #if defined(USE_AURA)
   2776   return chrome::GetHostDesktopTypeForNativeView(
   2777       GetWidget()->GetNativeView()) == chrome::HOST_DESKTOP_TYPE_ASH;
   2778 #else
   2779   if (ui::GetDisplayLayout() != ui::LAYOUT_TOUCH)
   2780     return false;
   2781 #endif
   2782 
   2783   return true;
   2784 }
   2785