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