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