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