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