• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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.h"
6 
7 #include <limits>
8 
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/defaults.h"
11 #include "chrome/browser/themes/theme_service.h"
12 #include "grit/app_resources.h"
13 #include "grit/generated_resources.h"
14 #include "grit/theme_resources.h"
15 #include "third_party/skia/include/effects/SkGradientShader.h"
16 #include "ui/base/animation/multi_animation.h"
17 #include "ui/base/animation/slide_animation.h"
18 #include "ui/base/animation/throb_animation.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/canvas_skia.h"
21 #include "ui/gfx/favicon_size.h"
22 #include "ui/gfx/font.h"
23 #include "ui/gfx/path.h"
24 #include "ui/gfx/skbitmap_operations.h"
25 #include "views/controls/button/image_button.h"
26 #include "views/widget/tooltip_manager.h"
27 #include "views/widget/widget.h"
28 #include "views/window/non_client_view.h"
29 #include "views/window/window.h"
30 
31 static const int kLeftPadding = 16;
32 static const int kTopPadding = 6;
33 static const int kRightPadding = 15;
34 static const int kBottomPadding = 5;
35 static const int kDropShadowHeight = 2;
36 static const int kToolbarOverlap = 1;
37 static const int kFaviconTitleSpacing = 4;
38 static const int kTitleCloseButtonSpacing = 5;
39 static const int kStandardTitleWidth = 175;
40 static const int kCloseButtonVertFuzz = 0;
41 static const int kCloseButtonHorzFuzz = 5;
42 
43 // Vertical adjustment to the favicon when the tab has a large icon.
44 static const int kAppTapFaviconVerticalAdjustment = 2;
45 
46 // When a non-mini-tab becomes a mini-tab the width of the tab animates. If
47 // the width of a mini-tab is >= kMiniTabRendererAsNormalTabWidth then the tab
48 // is rendered as a normal tab. This is done to avoid having the title
49 // immediately disappear when transitioning a tab from normal to mini-tab.
50 static const int kMiniTabRendererAsNormalTabWidth =
51     browser_defaults::kMiniTabWidth + 30;
52 
53 // How opaque to make the hover state (out of 1).
54 static const double kHoverOpacity = 0.33;
55 static const double kHoverSlideOpacity = 0.5;
56 
57 // Opacity for non-active selected tabs.
58 static const double kSelectedTabOpacity = .45;
59 
60 // Selected (but not active) tabs have their throb value scaled down by this.
61 static const double kSelectedTabThrobScale = .5;
62 
63 Tab::TabImage Tab::tab_alpha_ = {0};
64 Tab::TabImage Tab::tab_active_ = {0};
65 Tab::TabImage Tab::tab_inactive_ = {0};
66 
67 // Durations for the various parts of the mini tab title animation.
68 static const int kMiniTitleChangeAnimationDuration1MS = 1600;
69 static const int kMiniTitleChangeAnimationStart1MS = 0;
70 static const int kMiniTitleChangeAnimationEnd1MS = 1900;
71 static const int kMiniTitleChangeAnimationDuration2MS = 0;
72 static const int kMiniTitleChangeAnimationDuration3MS = 550;
73 static const int kMiniTitleChangeAnimationStart3MS = 150;
74 static const int kMiniTitleChangeAnimationEnd3MS = 800;
75 
76 // Offset from the right edge for the start of the mini title change animation.
77 static const int kMiniTitleChangeInitialXOffset = 6;
78 
79 // Radius of the radial gradient used for mini title change animation.
80 static const int kMiniTitleChangeGradientRadius = 20;
81 
82 // Colors of the gradient used during the mini title change animation.
83 static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE;
84 static const SkColor kMiniTitleChangeGradientColor2 =
85     SkColorSetARGB(0, 255, 255, 255);
86 
87 // Hit mask constants.
88 static const SkScalar kTabCapWidth = 15;
89 static const SkScalar kTabTopCurveWidth = 4;
90 static const SkScalar kTabBottomCurveWidth = 3;
91 
92 // static
93 const char Tab::kViewClassName[] = "browser/tabs/Tab";
94 
95 ////////////////////////////////////////////////////////////////////////////////
96 // Tab, public:
97 
Tab(TabController * controller)98 Tab::Tab(TabController* controller)
99     : BaseTab(controller),
100       showing_icon_(false),
101       showing_close_button_(false),
102       close_button_color_(0) {
103   InitTabResources();
104 }
105 
~Tab()106 Tab::~Tab() {
107 }
108 
StartMiniTabTitleAnimation()109 void Tab::StartMiniTabTitleAnimation() {
110   if (!mini_title_animation_.get()) {
111     ui::MultiAnimation::Parts parts;
112     parts.push_back(
113         ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS,
114                                  ui::Tween::EASE_OUT));
115     parts.push_back(
116         ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration2MS,
117                                  ui::Tween::ZERO));
118     parts.push_back(
119         ui::MultiAnimation::Part(kMiniTitleChangeAnimationDuration3MS,
120                                  ui::Tween::EASE_IN));
121     parts[0].start_time_ms = kMiniTitleChangeAnimationStart1MS;
122     parts[0].end_time_ms = kMiniTitleChangeAnimationEnd1MS;
123     parts[2].start_time_ms = kMiniTitleChangeAnimationStart3MS;
124     parts[2].end_time_ms = kMiniTitleChangeAnimationEnd3MS;
125     mini_title_animation_.reset(new ui::MultiAnimation(parts));
126     mini_title_animation_->SetContainer(animation_container());
127     mini_title_animation_->set_delegate(this);
128   }
129   mini_title_animation_->Start();
130 }
131 
StopMiniTabTitleAnimation()132 void Tab::StopMiniTabTitleAnimation() {
133   if (mini_title_animation_.get())
134     mini_title_animation_->Stop();
135 }
136 
137 // static
GetMinimumUnselectedSize()138 gfx::Size Tab::GetMinimumUnselectedSize() {
139   InitTabResources();
140 
141   gfx::Size minimum_size;
142   minimum_size.set_width(kLeftPadding + kRightPadding);
143   // Since we use bitmap images, the real minimum height of the image is
144   // defined most accurately by the height of the end cap images.
145   minimum_size.set_height(tab_active_.image_l->height());
146   return minimum_size;
147 }
148 
149 // static
GetMinimumSelectedSize()150 gfx::Size Tab::GetMinimumSelectedSize() {
151   gfx::Size minimum_size = GetMinimumUnselectedSize();
152   minimum_size.set_width(kLeftPadding + kFaviconSize + kRightPadding);
153   return minimum_size;
154 }
155 
156 // static
GetStandardSize()157 gfx::Size Tab::GetStandardSize() {
158   gfx::Size standard_size = GetMinimumUnselectedSize();
159   standard_size.set_width(
160       standard_size.width() + kFaviconTitleSpacing + kStandardTitleWidth);
161   return standard_size;
162 }
163 
164 // static
GetMiniWidth()165 int Tab::GetMiniWidth() {
166   return browser_defaults::kMiniTabWidth;
167 }
168 
169 ////////////////////////////////////////////////////////////////////////////////
170 // Tab, protected:
171 
GetTitleBounds() const172 const gfx::Rect& Tab::GetTitleBounds() const {
173   return title_bounds_;
174 }
175 
GetIconBounds() const176 const gfx::Rect& Tab::GetIconBounds() const {
177   return favicon_bounds_;
178 }
179 
DataChanged(const TabRendererData & old)180 void Tab::DataChanged(const TabRendererData& old) {
181   if (data().blocked == old.blocked)
182     return;
183 
184   if (data().blocked)
185     StartPulse();
186   else
187     StopPulse();
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 // Tab, views::View overrides:
192 
OnPaint(gfx::Canvas * canvas)193 void Tab::OnPaint(gfx::Canvas* canvas) {
194   // Don't paint if we're narrower than we can render correctly. (This should
195   // only happen during animations).
196   if (width() < GetMinimumUnselectedSize().width() && !data().mini)
197     return;
198 
199   // See if the model changes whether the icons should be painted.
200   const bool show_icon = ShouldShowIcon();
201   const bool show_close_button = ShouldShowCloseBox();
202   if (show_icon != showing_icon_ || show_close_button != showing_close_button_)
203     Layout();
204 
205   PaintTabBackground(canvas);
206 
207   SkColor title_color = GetThemeProvider()->
208       GetColor(IsSelected() ?
209           ThemeService::COLOR_TAB_TEXT :
210           ThemeService::COLOR_BACKGROUND_TAB_TEXT);
211 
212   if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth)
213     PaintTitle(canvas, title_color);
214 
215   if (show_icon)
216     PaintIcon(canvas);
217 
218   // If the close button color has changed, generate a new one.
219   if (!close_button_color_ || title_color != close_button_color_) {
220     close_button_color_ = title_color;
221     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
222     close_button()->SetBackground(close_button_color_,
223         rb.GetBitmapNamed(IDR_TAB_CLOSE),
224         rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK));
225   }
226 }
227 
Layout()228 void Tab::Layout() {
229   gfx::Rect lb = GetContentsBounds();
230   if (lb.IsEmpty())
231     return;
232   lb.Inset(kLeftPadding, kTopPadding, kRightPadding, kBottomPadding);
233 
234   // The height of the content of the Tab is the largest of the favicon,
235   // the title text and the close button graphic.
236   int content_height = std::max(kFaviconSize, font_height());
237   gfx::Size close_button_size(close_button()->GetPreferredSize());
238   content_height = std::max(content_height, close_button_size.height());
239 
240   // Size the Favicon.
241   showing_icon_ = ShouldShowIcon();
242   if (showing_icon_) {
243     // Use the size of the favicon as apps use a bigger favicon size.
244     int favicon_top = kTopPadding + content_height / 2 - kFaviconSize / 2;
245     int favicon_left = lb.x();
246     favicon_bounds_.SetRect(favicon_left, favicon_top,
247                             kFaviconSize, kFaviconSize);
248     if (data().mini && width() < kMiniTabRendererAsNormalTabWidth) {
249       // Adjust the location of the favicon when transitioning from a normal
250       // tab to a mini-tab.
251       int mini_delta = kMiniTabRendererAsNormalTabWidth - GetMiniWidth();
252       int ideal_delta = width() - GetMiniWidth();
253       if (ideal_delta < mini_delta) {
254         int ideal_x = (GetMiniWidth() - kFaviconSize) / 2;
255         int x = favicon_bounds_.x() + static_cast<int>(
256             (1 - static_cast<float>(ideal_delta) /
257              static_cast<float>(mini_delta)) *
258             (ideal_x - favicon_bounds_.x()));
259         favicon_bounds_.set_x(x);
260       }
261     }
262   } else {
263     favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0);
264   }
265 
266   // Size the Close button.
267   showing_close_button_ = ShouldShowCloseBox();
268   if (showing_close_button_) {
269     int close_button_top = kTopPadding + kCloseButtonVertFuzz +
270         (content_height - close_button_size.height()) / 2;
271     // If the ratio of the close button size to tab width exceeds the maximum.
272     close_button()->SetBounds(lb.width() + kCloseButtonHorzFuzz,
273                               close_button_top, close_button_size.width(),
274                               close_button_size.height());
275     close_button()->SetVisible(true);
276   } else {
277     close_button()->SetBounds(0, 0, 0, 0);
278     close_button()->SetVisible(false);
279   }
280 
281   int title_left = favicon_bounds_.right() + kFaviconTitleSpacing;
282   int title_top = kTopPadding + (content_height - font_height()) / 2;
283   // Size the Title text to fill the remaining space.
284   if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) {
285     // If the user has big fonts, the title will appear rendered too far down
286     // on the y-axis if we use the regular top padding, so we need to adjust it
287     // so that the text appears centered.
288     gfx::Size minimum_size = GetMinimumUnselectedSize();
289     int text_height = title_top + font_height() + kBottomPadding;
290     if (text_height > minimum_size.height())
291       title_top -= (text_height - minimum_size.height()) / 2;
292 
293     int title_width;
294     if (close_button()->IsVisible()) {
295       title_width = std::max(close_button()->x() -
296                              kTitleCloseButtonSpacing - title_left, 0);
297     } else {
298       title_width = std::max(lb.width() - title_left, 0);
299     }
300     title_bounds_.SetRect(title_left, title_top, title_width, font_height());
301   } else {
302     title_bounds_.SetRect(title_left, title_top, 0, 0);
303   }
304 
305   // Certain UI elements within the Tab (the favicon, etc.) are not represented
306   // as child Views (which is the preferred method).  Instead, these UI elements
307   // are drawn directly on the canvas from within Tab::OnPaint(). The Tab's
308   // child Views (for example, the Tab's close button which is a views::Button
309   // instance) are automatically mirrored by the mirroring infrastructure in
310   // views. The elements Tab draws directly on the canvas need to be manually
311   // mirrored if the View's layout is right-to-left.
312   title_bounds_.set_x(GetMirroredXForRect(title_bounds_));
313 }
314 
OnThemeChanged()315 void Tab::OnThemeChanged() {
316   LoadTabImages();
317 }
318 
GetClassName() const319 std::string Tab::GetClassName() const {
320   return kViewClassName;
321 }
322 
HasHitTestMask() const323 bool Tab::HasHitTestMask() const {
324   return true;
325 }
326 
GetHitTestMask(gfx::Path * path) const327 void Tab::GetHitTestMask(gfx::Path* path) const {
328   DCHECK(path);
329 
330   SkScalar h = SkIntToScalar(height());
331   SkScalar w = SkIntToScalar(width());
332 
333   path->moveTo(0, h);
334 
335   // Left end cap.
336   path->lineTo(kTabBottomCurveWidth, h - kTabBottomCurveWidth);
337   path->lineTo(kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth);
338   path->lineTo(kTabCapWidth, 0);
339 
340   // Connect to the right cap.
341   path->lineTo(w - kTabCapWidth, 0);
342 
343   // Right end cap.
344   path->lineTo(w - kTabCapWidth + kTabTopCurveWidth, kTabTopCurveWidth);
345   path->lineTo(w - kTabBottomCurveWidth, h - kTabBottomCurveWidth);
346   path->lineTo(w, h);
347 
348   // Close out the path.
349   path->lineTo(0, h);
350   path->close();
351 }
352 
GetTooltipTextOrigin(const gfx::Point & p,gfx::Point * origin)353 bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) {
354   origin->set_x(title_bounds_.x() + 10);
355   origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4);
356   return true;
357 }
358 
OnMouseMoved(const views::MouseEvent & event)359 void Tab::OnMouseMoved(const views::MouseEvent& event) {
360   hover_point_ = event.location();
361   // We need to redraw here because otherwise the hover glow does not update
362   // and follow the new mouse position.
363   SchedulePaint();
364 }
365 
366 ////////////////////////////////////////////////////////////////////////////////
367 // Tab, private
368 
PaintTabBackground(gfx::Canvas * canvas)369 void Tab::PaintTabBackground(gfx::Canvas* canvas) {
370   if (IsActive()) {
371     PaintActiveTabBackground(canvas);
372   } else {
373     if (mini_title_animation_.get() && mini_title_animation_->is_animating())
374       PaintInactiveTabBackgroundWithTitleChange(canvas);
375     else
376       PaintInactiveTabBackground(canvas);
377 
378     double throb_value = GetThrobValue();
379     if (throb_value > 0) {
380       canvas->SaveLayerAlpha(static_cast<int>(throb_value * 0xff),
381                              gfx::Rect(width(), height()));
382       canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255,
383                                        SkXfermode::kClear_Mode);
384       PaintActiveTabBackground(canvas);
385       canvas->Restore();
386     }
387   }
388 }
389 
PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas * canvas)390 void Tab::PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas) {
391   // Render the inactive tab background. We'll use this for clipping.
392   gfx::CanvasSkia background_canvas(width(), height(), false);
393   PaintInactiveTabBackground(&background_canvas);
394 
395   SkBitmap background_image = background_canvas.ExtractBitmap();
396 
397   // Draw a radial gradient to hover_canvas.
398   gfx::CanvasSkia hover_canvas(width(), height(), false);
399   int radius = kMiniTitleChangeGradientRadius;
400   int x0 = width() + radius - kMiniTitleChangeInitialXOffset;
401   int x1 = radius;
402   int x2 = -radius;
403   int x;
404   if (mini_title_animation_->current_part_index() == 0) {
405     x = mini_title_animation_->CurrentValueBetween(x0, x1);
406   } else if (mini_title_animation_->current_part_index() == 1) {
407     x = x1;
408   } else {
409     x = mini_title_animation_->CurrentValueBetween(x1, x2);
410   }
411   SkPaint paint;
412   SkPoint loc = { SkIntToScalar(x), SkIntToScalar(0) };
413   SkColor colors[2];
414   colors[0] = kMiniTitleChangeGradientColor1;
415   colors[1] = kMiniTitleChangeGradientColor2;
416   SkShader* shader = SkGradientShader::CreateRadial(
417       loc,
418       SkIntToScalar(radius),
419       colors,
420       NULL,
421       2,
422       SkShader::kClamp_TileMode);
423   paint.setShader(shader);
424   shader->unref();
425   hover_canvas.DrawRectInt(x - radius, -radius, radius * 2, radius * 2, paint);
426 
427   // Draw the radial gradient clipped to the background into hover_image.
428   SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap(
429       hover_canvas.ExtractBitmap(), background_image);
430 
431   // Draw the tab background to the canvas.
432   canvas->DrawBitmapInt(background_image, 0, 0);
433 
434   // And then the gradient on top of that.
435   if (mini_title_animation_->current_part_index() == 2) {
436     canvas->SaveLayerAlpha(mini_title_animation_->CurrentValueBetween(255, 0));
437     canvas->DrawBitmapInt(hover_image, 0, 0);
438     canvas->Restore();
439   } else {
440     canvas->DrawBitmapInt(hover_image, 0, 0);
441   }
442 }
443 
PaintInactiveTabBackground(gfx::Canvas * canvas)444 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) {
445   // The tab image needs to be lined up with the background image
446   // so that it feels partially transparent.  These offsets represent the tab
447   // position within the frame background image.
448   int offset = GetMirroredX() + background_offset_.x();
449 
450   int tab_id;
451   if (GetWidget() &&
452       GetWidget()->GetWindow()->non_client_view()->UseNativeFrame()) {
453     tab_id = IDR_THEME_TAB_BACKGROUND_V;
454   } else {
455     tab_id = data().incognito ? IDR_THEME_TAB_BACKGROUND_INCOGNITO :
456                                 IDR_THEME_TAB_BACKGROUND;
457   }
458 
459   SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id);
460 
461   TabImage* tab_image = &tab_active_;
462   TabImage* tab_inactive_image = &tab_inactive_;
463   TabImage* alpha = &tab_alpha_;
464 
465   // If the theme is providing a custom background image, then its top edge
466   // should be at the top of the tab. Otherwise, we assume that the background
467   // image is a composited foreground + frame image.
468   int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ?
469       0 : background_offset_.y();
470 
471   // We need a CanvasSkia object to be able to extract the bitmap from.
472   // We draw everything to this canvas and then output it to the canvas
473   // parameter in addition to using it to mask the hover glow if needed.
474   gfx::CanvasSkia background_canvas(width(), height(), false);
475 
476   // Draw left edge.  Don't draw over the toolbar, as we're not the foreground
477   // tab.
478   SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap(
479       *tab_bg, offset, bg_offset_y, tab_image->l_width, height());
480   SkBitmap theme_l =
481       SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l);
482   background_canvas.DrawBitmapInt(theme_l,
483       0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap,
484       0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap,
485       false);
486 
487   // Draw right edge.  Again, don't draw over the toolbar.
488   SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg,
489       offset + width() - tab_image->r_width, bg_offset_y,
490       tab_image->r_width, height());
491   SkBitmap theme_r =
492       SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r);
493   background_canvas.DrawBitmapInt(theme_r,
494       0, 0, theme_r.width(), theme_r.height() - kToolbarOverlap,
495       width() - theme_r.width(), 0, theme_r.width(),
496       theme_r.height() - kToolbarOverlap, false);
497 
498   // Draw center.  Instead of masking out the top portion we simply skip over
499   // it by incrementing by kDropShadowHeight, since it's a simple rectangle.
500   // And again, don't draw over the toolbar.
501   background_canvas.TileImageInt(*tab_bg,
502      offset + tab_image->l_width,
503      bg_offset_y + kDropShadowHeight + tab_image->y_offset,
504      tab_image->l_width,
505      kDropShadowHeight + tab_image->y_offset,
506      width() - tab_image->l_width - tab_image->r_width,
507      height() - kDropShadowHeight - kToolbarOverlap - tab_image->y_offset);
508 
509   canvas->DrawBitmapInt(background_canvas.ExtractBitmap(), 0, 0);
510 
511   if (!GetThemeProvider()->HasCustomImage(tab_id) &&
512       hover_animation() &&
513       (hover_animation()->IsShowing() || hover_animation()->is_animating())) {
514     SkBitmap hover_glow = DrawHoverGlowBitmap(width(), height());
515     // Draw the hover glow clipped to the background into hover_image.
516     SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap(
517         hover_glow, background_canvas.ExtractBitmap());
518     canvas->DrawBitmapInt(hover_image, 0, 0);
519   }
520 
521   // Now draw the highlights/shadows around the tab edge.
522   canvas->DrawBitmapInt(*tab_inactive_image->image_l, 0, 0);
523   canvas->TileImageInt(*tab_inactive_image->image_c,
524                        tab_inactive_image->l_width, 0,
525                        width() - tab_inactive_image->l_width -
526                            tab_inactive_image->r_width,
527                        height());
528   canvas->DrawBitmapInt(*tab_inactive_image->image_r,
529                         width() - tab_inactive_image->r_width, 0);
530 }
531 
PaintActiveTabBackground(gfx::Canvas * canvas)532 void Tab::PaintActiveTabBackground(gfx::Canvas* canvas) {
533   int offset = GetMirroredX() + background_offset_.x();
534   ui::ThemeProvider* tp = GetThemeProvider();
535   DCHECK(tp) << "Unable to get theme provider";
536 
537   SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR);
538 
539   TabImage* tab_image = &tab_active_;
540   TabImage* alpha = &tab_alpha_;
541 
542   // Draw left edge.
543   SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap(
544       *tab_bg, offset, 0, tab_image->l_width, height());
545   SkBitmap theme_l =
546       SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l);
547   canvas->DrawBitmapInt(theme_l, 0, 0);
548 
549   // Draw right edge.
550   SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg,
551       offset + width() - tab_image->r_width, 0, tab_image->r_width, height());
552   SkBitmap theme_r =
553       SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r);
554   canvas->DrawBitmapInt(theme_r, width() - tab_image->r_width, 0);
555 
556   // Draw center.  Instead of masking out the top portion we simply skip over it
557   // by incrementing by kDropShadowHeight, since it's a simple rectangle.
558   canvas->TileImageInt(*tab_bg,
559      offset + tab_image->l_width,
560      kDropShadowHeight + tab_image->y_offset,
561      tab_image->l_width,
562      kDropShadowHeight + tab_image->y_offset,
563      width() - tab_image->l_width - tab_image->r_width,
564      height() - kDropShadowHeight - tab_image->y_offset);
565 
566   // Now draw the highlights/shadows around the tab edge.
567   canvas->DrawBitmapInt(*tab_image->image_l, 0, 0);
568   canvas->TileImageInt(*tab_image->image_c, tab_image->l_width, 0,
569       width() - tab_image->l_width - tab_image->r_width, height());
570   canvas->DrawBitmapInt(*tab_image->image_r, width() - tab_image->r_width, 0);
571 }
572 
DrawHoverGlowBitmap(int width_input,int height_input)573 SkBitmap Tab::DrawHoverGlowBitmap(int width_input, int height_input) {
574   // Draw a radial gradient to hover_canvas so we can export the bitmap.
575   gfx::CanvasSkia hover_canvas(width_input, height_input, false);
576 
577   // Draw a radial gradient to hover_canvas.
578   int radius = width() / 3;
579 
580   SkPaint paint;
581   paint.setStyle(SkPaint::kFill_Style);
582   paint.setFlags(SkPaint::kAntiAlias_Flag);
583   SkPoint loc = { SkIntToScalar(hover_point_.x()),
584                   SkIntToScalar(hover_point_.y()) };
585   SkColor colors[2];
586   const ui::SlideAnimation* hover_slide = hover_animation();
587   int hover_alpha = 0;
588   if (hover_slide) {
589     hover_alpha = static_cast<int>(255 * kHoverSlideOpacity *
590                                    hover_slide->GetCurrentValue());
591   }
592   colors[0] = SkColorSetARGB(hover_alpha, 255, 255, 255);
593   colors[1] = SkColorSetARGB(0, 255, 255, 255);
594   SkShader* shader = SkGradientShader::CreateRadial(
595       loc,
596       SkIntToScalar(radius),
597       colors,
598       NULL,
599       2,
600       SkShader::kClamp_TileMode);
601   // Shader can end up null when radius = 0.
602   // If so, this results in default full tab glow behavior.
603   if (shader) {
604     paint.setShader(shader);
605     shader->unref();
606     hover_canvas.DrawRectInt(hover_point_.x() - radius,
607                              hover_point_.y() - radius,
608                              radius * 2, radius * 2, paint);
609   }
610   return hover_canvas.ExtractBitmap();
611 }
612 
IconCapacity() const613 int Tab::IconCapacity() const {
614   if (height() < GetMinimumUnselectedSize().height())
615     return 0;
616   return (width() - kLeftPadding - kRightPadding) / kFaviconSize;
617 }
618 
ShouldShowIcon() const619 bool Tab::ShouldShowIcon() const {
620   if (data().mini && height() >= GetMinimumUnselectedSize().height())
621     return true;
622   if (!data().show_icon) {
623     return false;
624   } else if (IsActive()) {
625     // The active tab clips favicon before close button.
626     return IconCapacity() >= 2;
627   }
628   // Non-selected tabs clip close button before favicon.
629   return IconCapacity() >= 1;
630 }
631 
ShouldShowCloseBox() const632 bool Tab::ShouldShowCloseBox() const {
633   // The active tab never clips close button.
634   return !data().mini && IsCloseable() && (IsActive() || IconCapacity() >= 3);
635 }
636 
GetThrobValue()637 double Tab::GetThrobValue() {
638   bool is_selected = IsSelected();
639   double min = is_selected ? kSelectedTabOpacity : 0;
640   double scale = is_selected ? kSelectedTabThrobScale : 1;
641 
642   if (pulse_animation() && pulse_animation()->is_animating())
643     return pulse_animation()->GetCurrentValue() * kHoverOpacity * scale + min;
644 
645   if (hover_animation())
646     return kHoverOpacity * hover_animation()->GetCurrentValue() * scale + min;
647 
648   return is_selected ? kSelectedTabOpacity : 0;
649 }
650 
651 ////////////////////////////////////////////////////////////////////////////////
652 // Tab, private:
653 
654 // static
InitTabResources()655 void Tab::InitTabResources() {
656   static bool initialized = false;
657   if (initialized)
658     return;
659 
660   initialized = true;
661 
662   // Load the tab images once now, and maybe again later if the theme changes.
663   LoadTabImages();
664 }
665 
666 // static
LoadTabImages()667 void Tab::LoadTabImages() {
668   // We're not letting people override tab images just yet.
669   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
670 
671   tab_alpha_.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT);
672   tab_alpha_.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT);
673 
674   tab_active_.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT);
675   tab_active_.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER);
676   tab_active_.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT);
677   tab_active_.l_width = tab_active_.image_l->width();
678   tab_active_.r_width = tab_active_.image_r->width();
679 
680   tab_inactive_.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT);
681   tab_inactive_.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER);
682   tab_inactive_.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT);
683   tab_inactive_.l_width = tab_inactive_.image_l->width();
684   tab_inactive_.r_width = tab_inactive_.image_r->width();
685 }
686