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