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