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