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