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