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