Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / tab.cc
index fa9e9d8..be43953 100644 (file)
@@ -24,9 +24,9 @@
 #include "grit/theme_resources.h"
 #include "grit/ui_resources.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
-#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/accessibility/ax_view_state.h"
+#include "ui/aura/env.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/layout.h"
 #include "ui/base/models/list_selection_model.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/favicon_size.h"
-#include "ui/gfx/font.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/rect_conversions.h"
 #include "ui/gfx/skia_util.h"
-#include "ui/gfx/text_elider.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/rect_based_targeting_utils.h"
+#include "ui/views/view_targeter.h"
 #include "ui/views/widget/tooltip_manager.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/non_client_view.h"
 
-#if defined(OS_WIN)
-#include "win8/util/win8_util.h"
-#endif
-
-#if defined(USE_ASH)
-#include "ui/aura/env.h"
-#endif
-
 namespace {
 
 // Padding around the "content" of a tab, occupied by the tab border graphics.
-
-int left_padding() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = 22;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 30;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
-
-int top_padding() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = 7;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 10;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
-
-int right_padding() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = 17;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 21;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
-
-int bottom_padding() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = 5;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 7;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
+const int kLeftPadding = 22;
+const int kTopPadding = 4;
+const int kRightPadding = 17;
+const int kBottomPadding = 2;
 
 // Height of the shadow at the top of the tab image assets.
-int drop_shadow_height() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = 4;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 5;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
-
-// Size of icon used for throbber and favicon next to tab title.
-int tab_icon_size() {
-  static int value = -1;
-  if (value == -1) {
-    switch (ui::GetDisplayLayout()) {
-      case ui::LAYOUT_DESKTOP:
-        value = gfx::kFaviconSize;
-        break;
-      case ui::LAYOUT_TOUCH:
-        value = 20;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  return value;
-}
+const int kDropShadowHeight = 4;
 
 // How long the pulse throb takes.
 const int kPulseDurationMs = 200;
@@ -173,18 +68,8 @@ static const int kTouchWidth = 120;
 
 static const int kToolbarOverlap = 1;
 static const int kFaviconTitleSpacing = 4;
-// Additional vertical offset for title text relative to top of tab.
-// Ash text rendering may be different than Windows.
-static const int kTitleTextOffsetYAsh = 1;
-static const int kTitleTextOffsetY = 0;
-static const int kTitleCloseButtonSpacing = 3;
+static const int kViewSpacing = 3;
 static const int kStandardTitleWidth = 175;
-// Additional vertical offset for close button relative to top of tab.
-// Ash needs this to match the text vertical position.
-static const int kCloseButtonVertFuzzAsh = 1;
-static const int kCloseButtonVertFuzz = 0;
-// Additional horizontal offset for close button relative to title text.
-static const int kCloseButtonHorzFuzz = 3;
 
 // When a non-mini-tab becomes a mini-tab the width of the tab animates. If
 // the width of a mini-tab is >= kMiniTabRendererAsNormalTabWidth then the tab
@@ -290,6 +175,13 @@ chrome::HostDesktopType GetHostDesktopType(views::View* view) {
       widget ? widget->GetNativeView() : NULL);
 }
 
+// Stop()s |animation| and then deletes it. We do this rather than just deleting
+// so that the delegate is notified before the destruction.
+void StopAndDeleteAnimation(scoped_ptr<gfx::Animation> animation) {
+  if (animation)
+    animation->Stop();
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -336,35 +228,19 @@ class Tab::FaviconCrashAnimation : public gfx::LinearAnimation,
 //
 //  This is a Button subclass that causes middle clicks to be forwarded to the
 //  parent View by explicitly not handling them in OnMousePressed.
-class Tab::TabCloseButton : public views::ImageButton {
+class Tab::TabCloseButton : public views::ImageButton,
+                            public views::MaskedTargeterDelegate {
  public:
-  explicit TabCloseButton(Tab* tab) : views::ImageButton(tab), tab_(tab) {}
-  virtual ~TabCloseButton() {}
-
-  // Overridden from views::View.
-  virtual View* GetEventHandlerForRect(const gfx::Rect& rect) OVERRIDE {
-    if (!views::UsePointBasedTargeting(rect))
-      return View::GetEventHandlerForRect(rect);
-
-    // Ignore the padding set on the button.
-    gfx::Rect contents_bounds = GetContentsBounds();
-    contents_bounds.set_x(GetMirroredXForRect(contents_bounds));
-
-    // TODO(tdanderson): Remove this ifdef if rect-based targeting
-    // is turned on by default.
-#if defined(USE_ASH)
-    // Include the padding in hit-test for touch events.
-    if (aura::Env::GetInstance()->is_touch_down())
-      contents_bounds = GetLocalBounds();
-#elif defined(OS_WIN)
-    // TODO(sky): Use local-bounds if a touch-point is active.
-    // http://crbug.com/145258
-#endif
-
-    return contents_bounds.Intersects(rect) ? this : parent();
+  explicit TabCloseButton(Tab* tab)
+      : views::ImageButton(tab),
+        tab_(tab) {
+    SetEventTargeter(
+        scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
   }
 
-  // Overridden from views::View.
+  virtual ~TabCloseButton() {}
+
+  // views::View:
   virtual View* GetTooltipHandlerForPoint(const gfx::Point& point) OVERRIDE {
     // Tab close button has no children, so tooltip handler should be the same
     // as the event handler.
@@ -376,8 +252,7 @@ class Tab::TabCloseButton : public views::ImageButton {
   }
 
   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
-    if (tab_->controller())
-      tab_->controller()->OnMouseEventInTab(this, event);
+    tab_->controller_->OnMouseEventInTab(this, event);
 
     bool handled = ImageButton::OnMousePressed(event);
     // Explicitly mark midle-mouse clicks as non-handled to ensure the tab
@@ -386,14 +261,12 @@ class Tab::TabCloseButton : public views::ImageButton {
   }
 
   virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE {
-    if (tab_->controller())
-      tab_->controller()->OnMouseEventInTab(this, event);
+    tab_->controller_->OnMouseEventInTab(this, event);
     CustomButton::OnMouseMoved(event);
   }
 
   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
-    if (tab_->controller())
-      tab_->controller()->OnMouseEventInTab(this, event);
+    tab_->controller_->OnMouseEventInTab(this, event);
     CustomButton::OnMouseReleased(event);
   }
 
@@ -404,42 +277,101 @@ class Tab::TabCloseButton : public views::ImageButton {
     event->SetHandled();
   }
 
-  virtual bool HasHitTestMask() const OVERRIDE {
-    return true;
+  virtual const char* GetClassName() const OVERRIDE {
+    return kTabCloseButtonName;
   }
 
-  virtual void GetHitTestMask(HitTestSource source,
-                              gfx::Path* path) const OVERRIDE {
-    // Use the button's contents bounds (which does not include padding)
-    // and the hit test mask of our parent |tab_| to determine if the
-    // button is hidden behind another tab.
+ private:
+  // Returns the rectangular bounds of parent tab's visible region in the
+  // local coordinate space of |this|.
+  gfx::Rect GetTabBounds() const {
     gfx::Path tab_mask;
-    tab_->GetHitTestMask(source, &tab_mask);
+    tab_->GetHitTestMask(&tab_mask);
 
-    gfx::Rect button_bounds(GetContentsBounds());
-    button_bounds.set_x(GetMirroredXForRect(button_bounds));
     gfx::RectF tab_bounds_f(gfx::SkRectToRectF(tab_mask.getBounds()));
     views::View::ConvertRectToTarget(tab_, this, &tab_bounds_f);
-    gfx::Rect tab_bounds = gfx::ToEnclosingRect(tab_bounds_f);
-
-    // If the button is hidden behind another tab, the hit test mask is empty.
-    // Otherwise set the hit test mask to be the contents bounds.
-    path->reset();
-    if (tab_bounds.Intersects(button_bounds)) {
-      // Include the padding in the hit test mask for touch events.
-      if (source == HIT_TEST_SOURCE_TOUCH)
-        button_bounds = GetLocalBounds();
-
-      // TODO: this isn't quite right, really need to intersect the two regions.
-      path->addRect(RectToSkRect(button_bounds));
+    return gfx::ToEnclosingRect(tab_bounds_f);
+  }
+
+  // Returns the rectangular bounds of the tab close button in the local
+  // coordinate space of |this|, not including clipped regions on the top
+  // or bottom of the button. |tab_bounds| is the rectangular bounds of
+  // the parent tab's visible region in the local coordinate space of |this|.
+  gfx::Rect GetTabCloseButtonBounds(const gfx::Rect& tab_bounds) const {
+    gfx::Rect button_bounds(GetContentsBounds());
+    button_bounds.set_x(GetMirroredXForRect(button_bounds));
+
+    int top_overflow = tab_bounds.y() - button_bounds.y();
+    int bottom_overflow = button_bounds.bottom() - tab_bounds.bottom();
+    if (top_overflow > 0)
+      button_bounds.set_y(tab_bounds.y());
+    else if (bottom_overflow > 0)
+      button_bounds.set_height(button_bounds.height() - bottom_overflow);
+
+    return button_bounds;
+  }
+
+  // views::ViewTargeterDelegate:
+  virtual View* TargetForRect(View* root, const gfx::Rect& rect) OVERRIDE {
+    CHECK_EQ(root, this);
+
+    if (!views::UsePointBasedTargeting(rect))
+      return ViewTargeterDelegate::TargetForRect(root, rect);
+
+    // Ignore the padding set on the button.
+    gfx::Rect contents_bounds = GetContentsBounds();
+    contents_bounds.set_x(GetMirroredXForRect(contents_bounds));
+
+    // Include the padding in hit-test for touch events.
+    if (aura::Env::GetInstance()->is_touch_down())
+      contents_bounds = GetLocalBounds();
+
+    return contents_bounds.Intersects(rect) ? this : parent();
+  }
+
+  // views:MaskedTargeterDelegate:
+  virtual bool GetHitTestMask(gfx::Path* mask) const OVERRIDE {
+    DCHECK(mask);
+    mask->reset();
+
+    // The parent tab may be partially occluded by another tab if we are
+    // in stacked tab mode, which means that the tab close button may also
+    // be partially occluded. Define the hit test mask of the tab close
+    // button to be the intersection of the parent tab's visible bounds
+    // and the bounds of the tab close button.
+    gfx::Rect tab_bounds(GetTabBounds());
+    gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds));
+    gfx::Rect intersection(gfx::IntersectRects(tab_bounds, button_bounds));
+
+    if (!intersection.IsEmpty()) {
+      mask->addRect(RectToSkRect(intersection));
+      return true;
     }
+
+    return false;
   }
 
-  virtual const char* GetClassName() const OVERRIDE {
-    return kTabCloseButtonName;
+  virtual bool DoesIntersectRect(const View* target,
+                                 const gfx::Rect& rect) const OVERRIDE {
+    CHECK_EQ(target, this);
+
+    // If the request is not made in response to a gesture, use the
+    // default implementation.
+    if (views::UsePointBasedTargeting(rect))
+      return MaskedTargeterDelegate::DoesIntersectRect(target, rect);
+
+    // The hit test request is in response to a gesture. Return false if any
+    // part of the tab close button is hidden from the user.
+    // TODO(tdanderson): Consider always returning the intersection if the
+    //                   non-rectangular shape of the tab can be accounted for.
+    gfx::Rect tab_bounds(GetTabBounds());
+    gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds));
+    if (!tab_bounds.Contains(button_bounds))
+      return false;
+
+    return MaskedTargeterDelegate::DoesIntersectRect(target, rect);
   }
 
- private:
   Tab* tab_;
 
   DISALLOW_COPY_AND_ASSIGN(TabCloseButton);
@@ -460,16 +392,9 @@ Tab::ImageCacheEntry::~ImageCacheEntry() {}
 
 // static
 const char Tab::kViewClassName[] = "Tab";
-
-// static
-Tab::TabImage Tab::tab_alpha_ = {0};
 Tab::TabImage Tab::tab_active_ = {0};
 Tab::TabImage Tab::tab_inactive_ = {0};
-// static
-gfx::Font* Tab::font_ = NULL;
-// static
-int Tab::font_height_ = 0;
-// static
+Tab::TabImage Tab::tab_alpha_ = {0};
 Tab::ImageCache* Tab::image_cache_ = NULL;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -479,18 +404,21 @@ Tab::Tab(TabController* controller)
     : controller_(controller),
       closing_(false),
       dragging_(false),
+      detached_(false),
       favicon_hiding_offset_(0),
       loading_animation_frame_(0),
       immersive_loading_step_(0),
       should_display_crashed_favicon_(false),
       animating_media_state_(TAB_MEDIA_STATE_NONE),
-      theme_provider_(NULL),
-      tab_activated_with_last_gesture_begin_(false),
+      close_button_(NULL),
+      title_(new views::Label()),
+      tab_activated_with_last_tap_down_(false),
       hover_controller_(this),
       showing_icon_(false),
       showing_media_indicator_(false),
       showing_close_button_(false),
       close_button_color_(0) {
+  DCHECK(controller);
   InitTabResources();
 
   // So we get don't get enter/exit on children and don't prematurely stop the
@@ -499,6 +427,15 @@ Tab::Tab(TabController* controller)
 
   set_id(VIEW_ID_TAB);
 
+  title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
+  title_->SetElideBehavior(gfx::FADE_TAIL);
+  title_->SetAutoColorReadabilityEnabled(false);
+  title_->SetText(CoreTabHelper::GetDefaultTitle());
+  AddChildView(title_);
+
+  SetEventTargeter(
+      scoped_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
+
   // Add the Close Button.
   close_button_ = new TabCloseButton(this);
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
@@ -527,11 +464,11 @@ void Tab::set_animation_container(gfx::AnimationContainer* container) {
 }
 
 bool Tab::IsActive() const {
-  return controller() ? controller()->IsActiveTab(this) : true;
+  return controller_->IsActiveTab(this);
 }
 
 bool Tab::IsSelected() const {
-  return controller() ? controller()->IsTabSelected(this) : true;
+  return controller_->IsTabSelected(this);
 }
 
 void Tab::SetData(const TabRendererData& data) {
@@ -541,6 +478,16 @@ void Tab::SetData(const TabRendererData& data) {
   TabRendererData old(data_);
   data_ = data;
 
+  base::string16 title = data_.title;
+  if (title.empty()) {
+    title = data_.loading ?
+        l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
+        CoreTabHelper::GetDefaultTitle();
+  } else {
+    Browser::FormatTitleForDisplay(&title);
+  }
+  title_->SetText(title);
+
   if (data_.IsCrashed()) {
     if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) {
       data_.media_state = TAB_MEDIA_STATE_NONE;
@@ -569,10 +516,8 @@ void Tab::SetData(const TabRendererData& data) {
   }
 
   if (old.mini != data_.mini) {
-    if (tab_animation_.get() && tab_animation_->is_animating()) {
-      tab_animation_->Stop();
-      tab_animation_.reset(NULL);
-    }
+    StopAndDeleteAnimation(
+        mini_title_change_animation_.PassAs<gfx::Animation>());
   }
 
   DataChanged(old);
@@ -595,27 +540,21 @@ void Tab::UpdateLoadingAnimation(TabRendererData::NetworkState state) {
 }
 
 void Tab::StartPulse() {
-  gfx::ThrobAnimation* animation = new gfx::ThrobAnimation(this);
-  animation->SetSlideDuration(kPulseDurationMs);
-  if (animation_container_.get())
-    animation->SetContainer(animation_container_.get());
-  animation->StartThrobbing(std::numeric_limits<int>::max());
-  tab_animation_.reset(animation);
+  pulse_animation_.reset(new gfx::ThrobAnimation(this));
+  pulse_animation_->SetSlideDuration(kPulseDurationMs);
+  if (animation_container_)
+    pulse_animation_->SetContainer(animation_container_.get());
+  pulse_animation_->StartThrobbing(std::numeric_limits<int>::max());
 }
 
 void Tab::StopPulse() {
-  if (!tab_animation_.get())
-    return;
-  tab_animation_->Stop();
-  tab_animation_.reset(NULL);
+  StopAndDeleteAnimation(pulse_animation_.PassAs<gfx::Animation>());
 }
 
 void Tab::StartMiniTabTitleAnimation() {
-  // We can only do this animation if the tab is mini because we will
-  // upcast tab_animation back to MultiAnimation when we draw.
   if (!data().mini)
     return;
-  if (!tab_animation_.get()) {
+  if (!mini_title_change_animation_) {
     gfx::MultiAnimation::Parts parts;
     parts.push_back(
         gfx::MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS,
@@ -632,20 +571,16 @@ void Tab::StartMiniTabTitleAnimation() {
     parts[2].end_time_ms = kMiniTitleChangeAnimationEnd3MS;
     base::TimeDelta timeout =
         base::TimeDelta::FromMilliseconds(kMiniTitleChangeAnimationIntervalMS);
-    gfx::MultiAnimation* animation = new gfx::MultiAnimation(parts, timeout);
-    if (animation_container_.get())
-      animation->SetContainer(animation_container_.get());
-    animation->set_delegate(this);
-    tab_animation_.reset(animation);
+    mini_title_change_animation_.reset(new gfx::MultiAnimation(parts, timeout));
+    if (animation_container_)
+      mini_title_change_animation_->SetContainer(animation_container_.get());
+    mini_title_change_animation_->set_delegate(this);
   }
-  tab_animation_->Start();
+  mini_title_change_animation_->Start();
 }
 
 void Tab::StopMiniTabTitleAnimation() {
-  if (!tab_animation_.get())
-    return;
-  tab_animation_->Stop();
-  tab_animation_.reset(NULL);
+  StopAndDeleteAnimation(mini_title_change_animation_.PassAs<gfx::Animation>());
 }
 
 // static
@@ -653,7 +588,7 @@ gfx::Size Tab::GetBasicMinimumUnselectedSize() {
   InitTabResources();
 
   gfx::Size minimum_size;
-  minimum_size.set_width(left_padding() + right_padding());
+  minimum_size.set_width(kLeftPadding + kRightPadding);
   // Since we use image images, the real minimum height of the image is
   // defined most accurately by the height of the end cap images.
   minimum_size.set_height(tab_active_.image_l->height());
@@ -668,7 +603,7 @@ gfx::Size Tab::GetMinimumUnselectedSize() {
 gfx::Size Tab::GetMinimumSelectedSize() {
   gfx::Size minimum_size = GetBasicMinimumUnselectedSize();
   minimum_size.set_width(
-      left_padding() + gfx::kFaviconSize + right_padding());
+      kLeftPadding + gfx::kFaviconSize + kRightPadding);
   return minimum_size;
 }
 
@@ -701,7 +636,7 @@ int Tab::GetImmersiveHeight() {
 void Tab::AnimationProgressed(const gfx::Animation* animation) {
   // Ignore if the pulse animation is being performed on active tab because
   // it repaints the same image. See |Tab::PaintTabBackground()|.
-  if (animation == tab_animation_.get() && IsActive())
+  if (animation == pulse_animation_.get() && IsActive())
     return;
   SchedulePaint();
 }
@@ -727,7 +662,7 @@ void Tab::ButtonPressed(views::Button* sender, const ui::Event& event) {
        (event.flags() & ui::EF_FROM_TOUCH) == 0) ? CLOSE_TAB_FROM_MOUSE :
       CLOSE_TAB_FROM_TOUCH;
   DCHECK_EQ(close_button_, sender);
-  controller()->CloseTab(this, source);
+  controller_->CloseTab(this, source);
   if (event.type() == ui::ET_GESTURE_TAP)
     TouchUMA::RecordGestureAction(TouchUMA::GESTURE_TABCLOSE_TAP);
 }
@@ -738,8 +673,37 @@ void Tab::ButtonPressed(views::Button* sender, const ui::Event& event) {
 void Tab::ShowContextMenuForView(views::View* source,
                                  const gfx::Point& point,
                                  ui::MenuSourceType source_type) {
-  if (controller() && !closing())
-    controller()->ShowContextMenuForTab(this, point, source_type);
+  if (!closing())
+    controller_->ShowContextMenuForTab(this, point, source_type);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tab, views::MaskedTargeterDelegate overrides:
+
+bool Tab::GetHitTestMask(gfx::Path* mask) const {
+  DCHECK(mask);
+
+  // When the window is maximized we don't want to shave off the edges or top
+  // shadow of the tab, such that the user can click anywhere along the top
+  // edge of the screen to select a tab. Ditto for immersive fullscreen.
+  const views::Widget* widget = GetWidget();
+  bool include_top_shadow =
+      widget && (widget->IsMaximized() || widget->IsFullscreen());
+  TabResources::GetHitTestMask(width(), height(), include_top_shadow, mask);
+
+  // It is possible for a portion of the tab to be occluded if tabs are
+  // stacked, so modify the hit test mask to only include the visible
+  // region of the tab.
+  gfx::Rect clip;
+  controller_->ShouldPaintTab(this, &clip);
+  if (clip.size().GetArea()) {
+    SkRect intersection(mask->getBounds());
+    intersection.intersect(RectToSkRect(clip));
+    mask->reset();
+    mask->addRect(intersection);
+  }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -752,16 +716,14 @@ void Tab::OnPaint(gfx::Canvas* canvas) {
     return;
 
   gfx::Rect clip;
-  if (controller()) {
-    if (!controller()->ShouldPaintTab(this, &clip))
-      return;
-    if (!clip.IsEmpty()) {
-      canvas->Save();
-      canvas->ClipRect(clip);
-    }
+  if (!controller_->ShouldPaintTab(this, &clip))
+    return;
+  if (!clip.IsEmpty()) {
+    canvas->Save();
+    canvas->ClipRect(clip);
   }
 
-  if (controller() && controller()->IsImmersiveStyle())
+  if (controller_->IsImmersiveStyle())
     PaintImmersiveTab(canvas);
   else
     PaintTab(canvas);
@@ -772,40 +734,20 @@ void Tab::OnPaint(gfx::Canvas* canvas) {
 
 void Tab::Layout() {
   gfx::Rect lb = GetContentsBounds();
+  lb.Inset(kLeftPadding, kTopPadding, kRightPadding, kBottomPadding);
   if (lb.IsEmpty())
     return;
-  lb.Inset(
-      left_padding(), top_padding(), right_padding(), bottom_padding());
 
-  // The height of the content of the Tab is the largest of the favicon,
-  // the title text and the close button graphic.
-  int content_height = std::max(tab_icon_size(), font_height_);
-  close_button_->SetBorder(views::Border::NullBorder());
-  gfx::Size close_button_size(close_button_->GetPreferredSize());
-  content_height = std::max(content_height, close_button_size.height());
-
-  // Size the Favicon.
   showing_icon_ = ShouldShowIcon();
+  favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0);
   if (showing_icon_) {
-    // Use the size of the favicon as apps use a bigger favicon size.
-    int favicon_top = top_padding() + content_height / 2 - tab_icon_size() / 2;
-    int favicon_left = lb.x();
-    favicon_bounds_.SetRect(favicon_left, favicon_top,
-                            tab_icon_size(), tab_icon_size());
+    favicon_bounds_.set_size(gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize));
+    favicon_bounds_.set_y(lb.y() + (lb.height() - gfx::kFaviconSize + 1) / 2);
     MaybeAdjustLeftForMiniTab(&favicon_bounds_);
-  } else {
-    favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0);
   }
 
-  // Size the Close button.
   showing_close_button_ = ShouldShowCloseBox();
-  const bool is_host_desktop_type_ash =
-      GetHostDesktopType(this) == chrome::HOST_DESKTOP_TYPE_ASH;
   if (showing_close_button_) {
-    const int close_button_vert_fuzz = is_host_desktop_type_ash ?
-        kCloseButtonVertFuzzAsh : kCloseButtonVertFuzz;
-    int close_button_top = top_padding() + close_button_vert_fuzz +
-        (content_height - close_button_size.height()) / 2;
     // If the ratio of the close button size to tab width exceeds the maximum.
     // The close button should be as large as possible so that there is a larger
     // hit-target for touch events. So the close button bounds extends to the
@@ -814,81 +756,57 @@ void Tab::Layout() {
     // So a border is added to the button with necessary padding. The close
     // button (BaseTab::TabCloseButton) makes sure the padding is a hit-target
     // only for touch events.
-    int top_border = close_button_top;
-    int bottom_border = height() - (close_button_size.height() + top_border);
-    int left_border = kCloseButtonHorzFuzz;
-    int right_border = width() - (lb.width() + close_button_size.width() +
-        left_border);
-    close_button_->SetBorder(views::Border::CreateEmptyBorder(
-        top_border, left_border, bottom_border, right_border));
+    close_button_->SetBorder(views::Border::NullBorder());
+    const gfx::Size close_button_size(close_button_->GetPreferredSize());
+    const int top = lb.y() + (lb.height() - close_button_size.height() + 1) / 2;
+    const int bottom = height() - (close_button_size.height() + top);
+    const int left = kViewSpacing;
+    const int right = width() - (lb.width() + close_button_size.width() + left);
+    close_button_->SetBorder(
+        views::Border::CreateEmptyBorder(top, left, bottom, right));
     close_button_->SetPosition(gfx::Point(lb.width(), 0));
     close_button_->SizeToPreferredSize();
-    close_button_->SetVisible(true);
-  } else {
-    close_button_->SetBounds(0, 0, 0, 0);
-    close_button_->SetVisible(false);
   }
+  close_button_->SetVisible(showing_close_button_);
 
   showing_media_indicator_ = ShouldShowMediaIndicator();
+  media_indicator_bounds_.SetRect(lb.x(), lb.y(), 0, 0);
   if (showing_media_indicator_) {
     const gfx::Image& media_indicator_image =
         chrome::GetTabMediaIndicatorImage(animating_media_state_);
     media_indicator_bounds_.set_width(media_indicator_image.Width());
     media_indicator_bounds_.set_height(media_indicator_image.Height());
     media_indicator_bounds_.set_y(
-        top_padding() +
-            (content_height - media_indicator_bounds_.height()) / 2);
+        lb.y() + (lb.height() - media_indicator_bounds_.height() + 1) / 2);
     const int right = showing_close_button_ ?
         close_button_->x() + close_button_->GetInsets().left() : lb.right();
     media_indicator_bounds_.set_x(
         std::max(lb.x(), right - media_indicator_bounds_.width()));
     MaybeAdjustLeftForMiniTab(&media_indicator_bounds_);
-  } else {
-    media_indicator_bounds_.SetRect(lb.x(), lb.y(), 0, 0);
   }
 
-  const int title_text_offset = is_host_desktop_type_ash ?
-      kTitleTextOffsetYAsh : kTitleTextOffsetY;
-  int title_left = favicon_bounds_.right() + kFaviconTitleSpacing;
-  int title_top = top_padding() + title_text_offset +
-      (content_height - font_height_) / 2;
-  // Size the Title text to fill the remaining space.
-  if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) {
-    // If the user has big fonts, the title will appear rendered too far down
-    // on the y-axis if we use the regular top padding, so we need to adjust it
-    // so that the text appears centered.
-    gfx::Size minimum_size = GetMinimumUnselectedSize();
-    int text_height = title_top + font_height_ + bottom_padding();
-    if (text_height > minimum_size.height())
-      title_top -= (text_height - minimum_size.height()) / 2;
-
-    int title_width;
+  // Size the title to fill the remaining width and use all available height.
+  bool show_title = !data().mini || width() >= kMiniTabRendererAsNormalTabWidth;
+  if (show_title) {
+    int title_left = favicon_bounds_.right() + kFaviconTitleSpacing;
+    int title_width = lb.width() - title_left;
     if (showing_media_indicator_) {
-      title_width = media_indicator_bounds_.x() - kTitleCloseButtonSpacing -
-          title_left;
+      title_width = media_indicator_bounds_.x() - kViewSpacing - title_left;
     } else if (close_button_->visible()) {
-      // The close button has an empty border with some padding (see details
-      // above where the close-button's bounds is set). Allow the title to
-      // overlap the empty padding.
+      // Allow the title to overlay the close button's empty border padding.
       title_width = close_button_->x() + close_button_->GetInsets().left() -
-          kTitleCloseButtonSpacing - title_left;
-    } else {
-      title_width = lb.width() - title_left;
+          kViewSpacing - title_left;
     }
-    title_width = std::max(title_width, 0);
-    title_bounds_.SetRect(title_left, title_top, title_width, font_height_);
-  } else {
-    title_bounds_.SetRect(title_left, title_top, 0, 0);
+    gfx::Rect rect(title_left, lb.y(), std::max(title_width, 0), lb.height());
+    const int title_height = title_->GetPreferredSize().height();
+    if (title_height > rect.height()) {
+      rect.set_y(lb.y() - (title_height - rect.height()) / 2);
+      rect.set_height(title_height);
+    }
+    rect.set_x(GetMirroredXForRect(rect));
+    title_->SetBoundsRect(rect);
   }
-
-  // Certain UI elements within the Tab (the favicon, etc.) are not represented
-  // as child Views (which is the preferred method).  Instead, these UI elements
-  // are drawn directly on the canvas from within Tab::OnPaint(). The Tab's
-  // child Views (for example, the Tab's close button which is a views::Button
-  // instance) are automatically mirrored by the mirroring infrastructure in
-  // views. The elements Tab draws directly on the canvas need to be manually
-  // mirrored if the View's layout is right-to-left.
-  title_bounds_.set_x(GetMirroredXForRect(title_bounds_));
+  title_->SetVisible(show_title);
 }
 
 void Tab::OnThemeChanged() {
@@ -899,34 +817,6 @@ const char* Tab::GetClassName() const {
   return kViewClassName;
 }
 
-bool Tab::HasHitTestMask() const {
-  return true;
-}
-
-void Tab::GetHitTestMask(HitTestSource source, gfx::Path* path) const {
-  // When the window is maximized we don't want to shave off the edges or top
-  // shadow of the tab, such that the user can click anywhere along the top
-  // edge of the screen to select a tab. Ditto for immersive fullscreen.
-  const views::Widget* widget = GetWidget();
-  bool include_top_shadow =
-      widget && (widget->IsMaximized() || widget->IsFullscreen());
-  TabResources::GetHitTestMask(width(), height(), include_top_shadow, path);
-
-  // It is possible for a portion of the tab to be occluded if tabs are
-  // stacked, so modify the hit test mask to only include the visible
-  // region of the tab.
-  if (controller()) {
-    gfx::Rect clip;
-    controller()->ShouldPaintTab(this, &clip);
-    if (clip.size().GetArea()) {
-      SkRect intersection(path->getBounds());
-      intersection.intersect(RectToSkRect(clip));
-      path->reset();
-      path->addRect(intersection);
-    }
-  }
-}
-
 bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
   // Note: Anything that affects the tooltip text should be accounted for when
   // calling TooltipTextChanged() from Tab::DataChanged().
@@ -935,74 +825,62 @@ bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
 }
 
 bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) const {
-  origin->set_x(title_bounds_.x() + 10);
+  origin->set_x(title_->x() + 10);
   origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4);
   return true;
 }
 
-ui::ThemeProvider* Tab::GetThemeProvider() const {
-  ui::ThemeProvider* tp = View::GetThemeProvider();
-  return tp ? tp : theme_provider_;
-}
-
 bool Tab::OnMousePressed(const ui::MouseEvent& event) {
-  if (!controller())
-    return false;
-
-  controller()->OnMouseEventInTab(this, event);
+  controller_->OnMouseEventInTab(this, event);
 
   // Allow a right click from touch to drag, which corresponds to a long click.
   if (event.IsOnlyLeftMouseButton() ||
       (event.IsOnlyRightMouseButton() && event.flags() & ui::EF_FROM_TOUCH)) {
     ui::ListSelectionModel original_selection;
-    original_selection.Copy(controller()->GetSelectionModel());
+    original_selection.Copy(controller_->GetSelectionModel());
     // Changing the selection may cause our bounds to change. If that happens
     // the location of the event may no longer be valid. Create a copy of the
     // event in the parents coordinate, which won't change, and recreate an
     // event after changing so the coordinates are correct.
     ui::MouseEvent event_in_parent(event, static_cast<View*>(this), parent());
-    if (controller()->SupportsMultipleSelection()) {
+    if (controller_->SupportsMultipleSelection()) {
       if (event.IsShiftDown() && event.IsControlDown()) {
-        controller()->AddSelectionFromAnchorTo(this);
+        controller_->AddSelectionFromAnchorTo(this);
       } else if (event.IsShiftDown()) {
-        controller()->ExtendSelectionTo(this);
+        controller_->ExtendSelectionTo(this);
       } else if (event.IsControlDown()) {
-        controller()->ToggleSelected(this);
+        controller_->ToggleSelected(this);
         if (!IsSelected()) {
           // Don't allow dragging non-selected tabs.
           return false;
         }
       } else if (!IsSelected()) {
-        controller()->SelectTab(this);
+        controller_->SelectTab(this);
       }
     } else if (!IsSelected()) {
-      controller()->SelectTab(this);
+      controller_->SelectTab(this);
     }
     ui::MouseEvent cloned_event(event_in_parent, parent(),
                                 static_cast<View*>(this));
-    controller()->MaybeStartDrag(this, cloned_event, original_selection);
+    controller_->MaybeStartDrag(this, cloned_event, original_selection);
   }
   return true;
 }
 
 bool Tab::OnMouseDragged(const ui::MouseEvent& event) {
-  if (controller())
-    controller()->ContinueDrag(this, event);
+  controller_->ContinueDrag(this, event);
   return true;
 }
 
 void Tab::OnMouseReleased(const ui::MouseEvent& event) {
-  if (!controller())
-    return;
-
-  controller()->OnMouseEventInTab(this, event);
+  controller_->OnMouseEventInTab(this, event);
 
   // Notify the drag helper that we're done with any potential drag operations.
   // Clean up the drag helper, which is re-created on the next mouse press.
   // In some cases, ending the drag will schedule the tab for destruction; if
   // so, bail immediately, since our members are already dead and we shouldn't
   // do anything else except drop the tab where it is.
-  if (controller()->EndDrag(END_DRAG_COMPLETE))
+  if (controller_->EndDrag(END_DRAG_COMPLETE))
     return;
 
   // Close tab on middle click, but only if the button is released over the tab
@@ -1010,28 +888,27 @@ void Tab::OnMouseReleased(const ui::MouseEvent& event) {
   // releases happen off the element).
   if (event.IsMiddleMouseButton()) {
     if (HitTestPoint(event.location())) {
-      controller()->CloseTab(this, CLOSE_TAB_FROM_MOUSE);
+      controller_->CloseTab(this, CLOSE_TAB_FROM_MOUSE);
     } else if (closing_) {
       // We're animating closed and a middle mouse button was pushed on us but
       // we don't contain the mouse anymore. We assume the user is clicking
       // quicker than the animation and we should close the tab that falls under
       // the mouse.
-      Tab* closest_tab = controller()->GetTabAt(this, event.location());
+      Tab* closest_tab = controller_->GetTabAt(this, event.location());
       if (closest_tab)
-        controller()->CloseTab(closest_tab, CLOSE_TAB_FROM_MOUSE);
+        controller_->CloseTab(closest_tab, CLOSE_TAB_FROM_MOUSE);
     }
   } else if (event.IsOnlyLeftMouseButton() && !event.IsShiftDown() &&
              !event.IsControlDown()) {
     // If the tab was already selected mouse pressed doesn't change the
     // selection. Reset it now to handle the case where multiple tabs were
     // selected.
-    controller()->SelectTab(this);
+    controller_->SelectTab(this);
   }
 }
 
 void Tab::OnMouseCaptureLost() {
-  if (controller())
-    controller()->EndDrag(END_DRAG_CAPTURE_LOST);
+  controller_->EndDrag(END_DRAG_CAPTURE_LOST);
 }
 
 void Tab::OnMouseEntered(const ui::MouseEvent& event) {
@@ -1040,8 +917,7 @@ void Tab::OnMouseEntered(const ui::MouseEvent& event) {
 
 void Tab::OnMouseMoved(const ui::MouseEvent& event) {
   hover_controller_.SetLocation(event.location());
-  if (controller())
-    controller()->OnMouseEventInTab(this, event);
+  controller_->OnMouseEventInTab(this, event);
 }
 
 void Tab::OnMouseExited(const ui::MouseEvent& event) {
@@ -1049,38 +925,33 @@ void Tab::OnMouseExited(const ui::MouseEvent& event) {
 }
 
 void Tab::OnGestureEvent(ui::GestureEvent* event) {
-  if (!controller()) {
-    event->SetHandled();
-    return;
-  }
-
   switch (event->type()) {
-    case ui::ET_GESTURE_BEGIN: {
-      if (event->details().touch_points() != 1)
-        return;
+    case ui::ET_GESTURE_TAP_DOWN: {
+      // TAP_DOWN is only dispatched for the first touch point.
+      DCHECK_EQ(1, event->details().touch_points());
 
       // See comment in OnMousePressed() as to why we copy the event.
       ui::GestureEvent event_in_parent(*event, static_cast<View*>(this),
                                        parent());
       ui::ListSelectionModel original_selection;
-      original_selection.Copy(controller()->GetSelectionModel());
-      tab_activated_with_last_gesture_begin_ = !IsActive();
+      original_selection.Copy(controller_->GetSelectionModel());
+      tab_activated_with_last_tap_down_ = !IsActive();
       if (!IsSelected())
-        controller()->SelectTab(this);
+        controller_->SelectTab(this);
       gfx::Point loc(event->location());
       views::View::ConvertPointToScreen(this, &loc);
       ui::GestureEvent cloned_event(event_in_parent, parent(),
                                     static_cast<View*>(this));
-      controller()->MaybeStartDrag(this, cloned_event, original_selection);
+      controller_->MaybeStartDrag(this, cloned_event, original_selection);
       break;
     }
 
     case ui::ET_GESTURE_END:
-      controller()->EndDrag(END_DRAG_COMPLETE);
+      controller_->EndDrag(END_DRAG_COMPLETE);
       break;
 
     case ui::ET_GESTURE_SCROLL_UPDATE:
-      controller()->ContinueDrag(this, *event);
+      controller_->ContinueDrag(this, *event);
       break;
 
     default:
@@ -1089,22 +960,19 @@ void Tab::OnGestureEvent(ui::GestureEvent* event) {
   event->SetHandled();
 }
 
-void Tab::GetAccessibleState(ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_PAGETAB;
+void Tab::GetAccessibleState(ui::AXViewState* state) {
+  state->role = ui::AX_ROLE_TAB;
   state->name = data_.title;
+  state->AddStateFlag(ui::AX_STATE_MULTISELECTABLE);
+  state->AddStateFlag(ui::AX_STATE_SELECTABLE);
+  controller_->UpdateTabAccessibilityState(this, state);
+  if (IsSelected())
+    state->AddStateFlag(ui::AX_STATE_SELECTED);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Tab, private
 
-const gfx::Rect& Tab::GetTitleBounds() const {
-  return title_bounds_;
-}
-
-const gfx::Rect& Tab::GetIconBounds() const {
-  return favicon_bounds_;
-}
-
 void Tab::MaybeAdjustLeftForMiniTab(gfx::Rect* bounds) const {
   if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth)
     return;
@@ -1142,13 +1010,12 @@ void Tab::PaintTab(gfx::Canvas* canvas) {
 
   PaintTabBackground(canvas);
 
-  SkColor title_color = GetThemeProvider()->
-      GetColor(IsSelected() ?
-          ThemeProperties::COLOR_TAB_TEXT :
-          ThemeProperties::COLOR_BACKGROUND_TAB_TEXT);
-
-  if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth)
-    PaintTitle(canvas, title_color);
+  const SkColor title_color = GetThemeProvider()->GetColor(IsSelected() ?
+      ThemeProperties::COLOR_TAB_TEXT :
+      ThemeProperties::COLOR_BACKGROUND_TAB_TEXT);
+  title_->SetVisible(!data().mini ||
+                     width() > kMiniTabRendererAsNormalTabWidth);
+  title_->SetEnabledColor(title_color);
 
   if (show_icon)
     PaintIcon(canvas);
@@ -1169,10 +1036,8 @@ void Tab::PaintTab(gfx::Canvas* canvas) {
 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) {
   // Use transparency for the draw-attention animation.
   int alpha = 255;
-  if (tab_animation_ &&
-      tab_animation_->is_animating() &&
-      !data().mini) {
-    alpha = tab_animation_->CurrentValueBetween(
+  if (pulse_animation_ && pulse_animation_->is_animating() && !data().mini) {
+    alpha = pulse_animation_->CurrentValueBetween(
         255, static_cast<int>(255 * kImmersiveTabMinThrobOpacity));
   }
 
@@ -1216,12 +1081,9 @@ void Tab::PaintTabBackground(gfx::Canvas* canvas) {
   if (IsActive()) {
     PaintActiveTabBackground(canvas);
   } else {
-    if (tab_animation_.get() &&
-        tab_animation_->is_animating() &&
-        data().mini) {
-      gfx::MultiAnimation* animation =
-          static_cast<gfx::MultiAnimation*>(tab_animation_.get());
-      PaintInactiveTabBackgroundWithTitleChange(canvas, animation);
+    if (mini_title_change_animation_ &&
+        mini_title_change_animation_->is_animating()) {
+      PaintInactiveTabBackgroundWithTitleChange(canvas);
     } else {
       PaintInactiveTabBackground(canvas);
     }
@@ -1236,9 +1098,7 @@ void Tab::PaintTabBackground(gfx::Canvas* canvas) {
   }
 }
 
-void Tab::PaintInactiveTabBackgroundWithTitleChange(
-    gfx::Canvas* canvas,
-    gfx::MultiAnimation* animation) {
+void Tab::PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas) {
   // Render the inactive tab background. We'll use this for clipping.
   gfx::Canvas background_canvas(size(), canvas->image_scale(), false);
   PaintInactiveTabBackground(&background_canvas);
@@ -1252,12 +1112,12 @@ void Tab::PaintInactiveTabBackgroundWithTitleChange(
   int x1 = radius;
   int x2 = -radius;
   int x;
-  if (animation->current_part_index() == 0) {
-    x = animation->CurrentValueBetween(x0, x1);
-  } else if (animation->current_part_index() == 1) {
+  if (mini_title_change_animation_->current_part_index() == 0) {
+    x = mini_title_change_animation_->CurrentValueBetween(x0, x1);
+  } else if (mini_title_change_animation_->current_part_index() == 1) {
     x = x1;
   } else {
-    x = animation->CurrentValueBetween(x1, x2);
+    x = mini_title_change_animation_->CurrentValueBetween(x1, x2);
   }
   SkPoint center_point;
   center_point.iset(x, 0);
@@ -1280,8 +1140,8 @@ void Tab::PaintInactiveTabBackgroundWithTitleChange(
   canvas->DrawImageInt(background_image, 0, 0);
 
   // And then the gradient on top of that.
-  if (animation->current_part_index() == 2) {
-    uint8 alpha = animation->CurrentValueBetween(255, 0);
+  if (mini_title_change_animation_->current_part_index() == 2) {
+    uint8 alpha = mini_title_change_animation_->CurrentValueBetween(255, 0);
     canvas->DrawImageInt(hover_image, 0, 0, alpha);
   } else {
     canvas->DrawImageInt(hover_image, 0, 0);
@@ -1378,11 +1238,11 @@ void Tab::PaintInactiveTabBackgroundUsingResourceId(gfx::Canvas* canvas,
   // rectangle. And again, don't draw over the toolbar.
   background_canvas.TileImageInt(*tab_bg,
      offset + tab_image->l_width,
-     bg_offset_y + drop_shadow_height(),
+     bg_offset_y + kDropShadowHeight,
      tab_image->l_width,
-     drop_shadow_height(),
+     kDropShadowHeight,
      width() - tab_image->l_width - tab_image->r_width,
-     height() - drop_shadow_height() - kToolbarOverlap);
+     height() - kDropShadowHeight - kToolbarOverlap);
 
   canvas->DrawImageInt(
       gfx::ImageSkia(background_canvas.ExtractImageRep()), 0, 0);
@@ -1431,11 +1291,11 @@ void Tab::PaintActiveTabBackground(gfx::Canvas* canvas) {
   // by incrementing by GetDropShadowHeight(), since it's a simple rectangle.
   canvas->TileImageInt(*tab_background,
      offset + tab_image->l_width,
-     drop_shadow_height(),
+     kDropShadowHeight,
      tab_image->l_width,
-     drop_shadow_height(),
+     kDropShadowHeight,
      width() - tab_image->l_width - tab_image->r_width,
-     height() - drop_shadow_height());
+     height() - kDropShadowHeight);
 
   // Now draw the highlights/shadows around the tab edge.
   canvas->DrawImageInt(*tab_image->image_l, 0, 0);
@@ -1445,7 +1305,7 @@ void Tab::PaintActiveTabBackground(gfx::Canvas* canvas) {
 }
 
 void Tab::PaintIcon(gfx::Canvas* canvas) {
-  gfx::Rect bounds = GetIconBounds();
+  gfx::Rect bounds = favicon_bounds_;
   if (bounds.IsEmpty())
     return;
 
@@ -1469,9 +1329,9 @@ void Tab::PaintIcon(gfx::Canvas* canvas) {
     gfx::ImageSkia crashed_favicon(*rb.GetImageSkiaNamed(IDR_SAD_FAVICON));
     bounds.set_y(bounds.y() + favicon_hiding_offset_);
     DrawIconCenter(canvas, crashed_favicon, 0,
-                    crashed_favicon.width(),
-                    crashed_favicon.height(),
-                    bounds, true, SkPaint());
+                   crashed_favicon.width(),
+                   crashed_favicon.height(),
+                   bounds, true, SkPaint());
   } else if (!data().favicon.isNull()) {
     // Paint the normal favicon.
     DrawIconCenter(canvas, data().favicon, 0,
@@ -1503,21 +1363,6 @@ void Tab::PaintMediaIndicator(gfx::Canvas* canvas) {
                      media_indicator_image.height(), true, paint);
 }
 
-void Tab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) {
-  // Paint the Title.
-  base::string16 title = data().title;
-  if (title.empty()) {
-    title = data().loading ?
-        l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
-        CoreTabHelper::GetDefaultTitle();
-  } else {
-    Browser::FormatTitleForDisplay(&title);
-  }
-
-  canvas->DrawFadeTruncatingStringRect(title, gfx::Canvas::TruncateFadeTail,
-      gfx::FontList(*font_), title_color, GetTitleBounds());
-}
-
 void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state,
                                   TabRendererData::NetworkState state) {
   static bool initialized = false;
@@ -1571,7 +1416,7 @@ void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state,
     loading_animation_frame_ = 0;
     immersive_loading_step_ = 0;
   }
-  if (controller() && controller()->IsImmersiveStyle())
+  if (controller_->IsImmersiveStyle())
     SchedulePaintInRect(GetImmersiveBarRect());
   else
     ScheduleIconPaint();
@@ -1581,8 +1426,8 @@ int Tab::IconCapacity() const {
   if (height() < GetMinimumUnselectedSize().height())
     return 0;
   const int available_width =
-      std::max(0, width() - left_padding() - right_padding());
-  const int width_per_icon = tab_icon_size();
+      std::max(0, width() - kLeftPadding - kRightPadding);
+  const int width_per_icon = gfx::kFaviconSize;
   const int kPaddingBetweenIcons = 2;
   if (available_width >= width_per_icon &&
       available_width < (width_per_icon + kPaddingBetweenIcons)) {
@@ -1609,13 +1454,16 @@ bool Tab::ShouldShowCloseBox() const {
 }
 
 double Tab::GetThrobValue() {
-  bool is_selected = IsSelected();
-  double min = is_selected ? kSelectedTabOpacity : 0;
-  double scale = is_selected ? kSelectedTabThrobScale : 1;
-
-  if (!data().mini) {
-    if (tab_animation_.get() && tab_animation_->is_animating())
-      return tab_animation_->GetCurrentValue() * kHoverOpacity * scale + min;
+  const bool is_selected = IsSelected();
+  const double min = is_selected ? kSelectedTabOpacity : 0;
+  const double scale = is_selected ? kSelectedTabThrobScale : 1;
+
+  // Showing both the pulse and title change animation at the same time is too
+  // much.
+  if (pulse_animation_ && pulse_animation_->is_animating() &&
+      (!mini_title_change_animation_ ||
+       !mini_title_change_animation_->is_animating())) {
+    return pulse_animation_->GetCurrentValue() * kHoverOpacity * scale + min;
   }
 
   if (hover_controller_.ShouldDraw()) {
@@ -1660,12 +1508,11 @@ void Tab::StartMediaIndicatorAnimation() {
 }
 
 void Tab::ScheduleIconPaint() {
-  gfx::Rect bounds = GetIconBounds();
+  gfx::Rect bounds = favicon_bounds_;
   if (bounds.IsEmpty())
     return;
 
-  // Extends the area to the bottom when sad_favicon is
-  // animating.
+  // Extends the area to the bottom when sad_favicon is animating.
   if (IsPerformingCrashAnimation())
     bounds.set_height(height() - bounds.y());
   bounds.set_x(GetMirroredXForRect(bounds));
@@ -1686,17 +1533,13 @@ gfx::Rect Tab::GetImmersiveBarRect() const {
 void Tab::GetTabIdAndFrameId(views::Widget* widget,
                              int* tab_id,
                              int* frame_id) const {
-  if (widget && widget->GetTopLevelWidget()->ShouldUseNativeFrame()) {
+  if (widget &&
+      widget->GetTopLevelWidget()->ShouldWindowContentsBeTransparent()) {
     *tab_id = IDR_THEME_TAB_BACKGROUND_V;
     *frame_id = 0;
   } else if (data().incognito) {
     *tab_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO;
     *frame_id = IDR_THEME_FRAME_INCOGNITO;
-#if defined(OS_WIN)
-  } else if (win8::IsSingleWindowMetroMode()) {
-    *tab_id = IDR_THEME_TAB_BACKGROUND_V;
-    *frame_id = 0;
-#endif
   } else {
     *tab_id = IDR_THEME_TAB_BACKGROUND;
     *frame_id = IDR_THEME_FRAME;
@@ -1713,11 +1556,6 @@ void Tab::InitTabResources() {
     return;
 
   initialized = true;
-
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  font_ = new gfx::Font(rb.GetFont(ui::ResourceBundle::BaseFont));
-  font_height_ = font_->GetHeight();
-
   image_cache_ = new ImageCache();
 
   // Load the tab images once now, and maybe again later if the theme changes.