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