Home | History | Annotate | Download | only in tabs
      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 
     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 
    106 Tab::~Tab() {
    107 }
    108 
    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 
    132 void Tab::StopMiniTabTitleAnimation() {
    133   if (mini_title_animation_.get())
    134     mini_title_animation_->Stop();
    135 }
    136 
    137 // static
    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
    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
    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
    165 int Tab::GetMiniWidth() {
    166   return browser_defaults::kMiniTabWidth;
    167 }
    168 
    169 ////////////////////////////////////////////////////////////////////////////////
    170 // Tab, protected:
    171 
    172 const gfx::Rect& Tab::GetTitleBounds() const {
    173   return title_bounds_;
    174 }
    175 
    176 const gfx::Rect& Tab::GetIconBounds() const {
    177   return favicon_bounds_;
    178 }
    179 
    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 
    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 
    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 
    315 void Tab::OnThemeChanged() {
    316   LoadTabImages();
    317 }
    318 
    319 std::string Tab::GetClassName() const {
    320   return kViewClassName;
    321 }
    322 
    323 bool Tab::HasHitTestMask() const {
    324   return true;
    325 }
    326 
    327 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    613 int Tab::IconCapacity() const {
    614   if (height() < GetMinimumUnselectedSize().height())
    615     return 0;
    616   return (width() - kLeftPadding - kRightPadding) / kFaviconSize;
    617 }
    618 
    619 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 
    632 bool Tab::ShouldShowCloseBox() const {
    633   // The active tab never clips close button.
    634   return !data().mini && IsCloseable() && (IsActive() || IconCapacity() >= 3);
    635 }
    636 
    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
    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
    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