172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/tabs/base_tab.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <limits>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/command_line.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/view_ids.h"
1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/tabs/tab_controller.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h"
16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/app_resources.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/accessibility/accessible_view_state.h"
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/animation/animation_container.h"
223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h"
233f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/throb_animation.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/text/text_elider.h"
2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/theme_provider.h"
2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h"
2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/favicon_size.h"
3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/font.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/button/image_button.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How long the pulse throb takes.
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kPulseDurationMs = 200;
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How long the hover state takes.
3721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstatic const int kHoverDurationMs = 400;
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabCloseButton
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//  This is a Button subclass that causes middle clicks to be forwarded to the
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//  parent View by explicitly not handling them in OnMousePressed.
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass TabCloseButton : public views::ImageButton {
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit TabCloseButton(views::ButtonListener* listener)
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : views::ImageButton(listener) {
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~TabCloseButton() {}
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool handled = ImageButton::OnMousePressed(event);
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Explicitly mark midle-mouse clicks as non-handled to ensure the tab
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // sees them.
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return event.IsOnlyMiddleMouseButton() ? false : handled;
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to let the parent know about mouse state so that it
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // can highlight itself appropriately. Note that Exit events
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // fire before Enter events, so this works.
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE {
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CustomButton::OnMouseEntered(event);
6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    parent()->OnMouseEntered(event);
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CustomButton::OnMouseExited(event);
7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    parent()->OnMouseExited(event);
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(TabCloseButton);
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Draws the icon image at the center of |bounds|.
78dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid DrawIconCenter(gfx::Canvas* canvas,
79dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    const SkBitmap& image,
80dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    int image_offset,
81dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    int icon_width,
82dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    int icon_height,
83dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    const gfx::Rect& bounds,
84dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                    bool filter) {
85dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Center the image within bounds.
86dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  int dst_x = bounds.x() - (icon_width - bounds.width()) / 2;
87dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  int dst_y = bounds.y() - (icon_height - bounds.height()) / 2;
88dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // NOTE: the clipping is a work around for 69528, it shouldn't be necessary.
89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  canvas->Save();
90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  canvas->ClipRectInt(dst_x, dst_y, icon_width, icon_height);
91dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  canvas->DrawBitmapInt(image,
92dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                        image_offset, 0, icon_width, icon_height,
93dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                        dst_x, dst_y, icon_width, icon_height,
94dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                        filter);
95dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  canvas->Restore();
96dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
97dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Font* BaseTab::font_ = NULL;
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BaseTab::font_height_ = 0;
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// FaviconCrashAnimation
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//  A custom animation subclass to manage the favicon crash animation.
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass BaseTab::FaviconCrashAnimation : public ui::LinearAnimation,
1103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                       public ui::AnimationDelegate {
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  explicit FaviconCrashAnimation(BaseTab* target)
1133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      : ALLOW_THIS_IN_INITIALIZER_LIST(ui::LinearAnimation(1000, 25, this)),
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        target_(target) {
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual ~FaviconCrashAnimation() {}
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // ui::Animation overrides:
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void AnimateToState(double state) {
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const double kHidingOffset = 27;
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (state < .5) {
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      target_->SetFaviconHidingOffset(
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<int>(floor(kHidingOffset * 2.0 * state)));
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      target_->DisplayCrashedFavicon();
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      target_->SetFaviconHidingOffset(
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<int>(
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              floor(kHidingOffset - ((state - .5) * 2.0 * kHidingOffset))));
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1333f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // ui::AnimationDelegate overrides:
1343f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationCanceled(const ui::Animation* animation) {
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    target_->SetFaviconHidingOffset(0);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BaseTab* target_;
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DISALLOW_COPY_AND_ASSIGN(FaviconCrashAnimation);
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab::BaseTab(TabController* controller)
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : controller_(controller),
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      closing_(false),
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dragging_(false),
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      favicon_hiding_offset_(0),
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      loading_animation_frame_(0),
15072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      should_display_crashed_favicon_(false),
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      throbber_disabled_(false),
15272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      theme_provider_(NULL) {
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BaseTab::InitResources();
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetID(VIEW_ID_TAB);
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add the Close Button.
1583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_ = new TabCloseButton(this);
1593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_->SetImage(views::CustomButton::BS_NORMAL,
1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                          rb.GetBitmapNamed(IDR_TAB_CLOSE));
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_->SetImage(views::CustomButton::BS_HOT,
1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                          rb.GetBitmapNamed(IDR_TAB_CLOSE_H));
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_->SetImage(views::CustomButton::BS_PUSHED,
1653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                          rb.GetBitmapNamed(IDR_TAB_CLOSE_P));
1663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  close_button_->SetTooltipText(
1673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_CLOSE_TAB)));
1683f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  close_button_->SetAccessibleName(
16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Disable animation so that the red danger sign shows up immediately
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // to help avoid mis-clicks.
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  close_button_->SetAnimationDuration(0);
1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  AddChildView(close_button_);
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetContextMenuController(this);
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab::~BaseTab() {
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::SetData(const TabRendererData& data) {
182dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (data_.Equals(data))
183dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return;
184dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabRendererData old(data_);
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  data_ = data;
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (data_.IsCrashed()) {
18972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) {
19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // When --reload-killed-tabs is specified, then the idea is that
19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // when tab is killed, the tab has no visual indication that it
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // died and should reload when the tab is next focused without
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // the user seeing the killed tab page.
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      //
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // The only exception to this is when the tab is in the
19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // foreground (i.e. when it's the selected tab), because we
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // don't want to go into an infinite loop reloading a page that
19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // will constantly get killed, or if it's the only tab.  So this
19972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // code makes it so that the favicon will only be shown for
20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // killed tabs when the tab is currently selected.
20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (CommandLine::ForCurrentProcess()->
20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          HasSwitch(switches::kReloadKilledTabs) && !IsSelected()) {
20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        // If we're reloading killed tabs, we don't want to display
20472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        // the crashed animation at all if the process was killed and
20572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        // the tab wasn't the current tab.
20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        if (data_.crashed_status != base::TERMINATION_STATUS_PROCESS_WAS_KILLED)
20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          StartCrashAnimation();
20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      } else {
20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        StartCrashAnimation();
21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
21172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (IsPerformingCrashAnimation())
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      StopCrashAnimation();
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ResetCrashedFavicon();
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DataChanged(old);
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Layout();
221dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  SchedulePaint();
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::UpdateLoadingAnimation(TabRendererData::NetworkState state) {
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If this is an extension app and a command line flag is set,
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // then disable the throbber.
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  throbber_disabled_ = data().app &&
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsNoThrob);
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (throbber_disabled_)
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state == data_.network_state &&
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      state == TabRendererData::NETWORK_STATE_NONE) {
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the network state is none and hasn't changed, do nothing. Otherwise we
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // need to advance the animation frame.
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabRendererData::NetworkState old_state = data_.network_state;
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  data_.network_state = state;
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AdvanceLoadingAnimation(old_state, state);
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::StartPulse() {
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!pulse_animation_.get()) {
2473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    pulse_animation_.reset(new ui::ThrobAnimation(this));
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    pulse_animation_->SetSlideDuration(kPulseDurationMs);
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (animation_container_.get())
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      pulse_animation_->SetContainer(animation_container_.get());
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  pulse_animation_->Reset();
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  pulse_animation_->StartThrobbing(std::numeric_limits<int>::max());
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::StopPulse() {
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!pulse_animation_.get())
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  pulse_animation_->Stop();  // Do stop so we get notified.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  pulse_animation_.reset(NULL);
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::set_animation_container(ui::AnimationContainer* container) {
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  animation_container_ = container;
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BaseTab::IsCloseable() const {
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return controller() ? controller()->IsTabCloseable(this) : true;
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool BaseTab::IsActive() const {
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return controller() ? controller()->IsActiveTab(this) : true;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool BaseTab::IsSelected() const {
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return controller() ? controller()->IsTabSelected(this) : true;
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenui::ThemeProvider* BaseTab::GetThemeProvider() const {
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ThemeProvider* tp = View::GetThemeProvider();
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return tp ? tp : theme_provider_;
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BaseTab::OnMousePressed(const views::MouseEvent& event) {
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!controller())
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (event.IsOnlyLeftMouseButton()) {
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (event.IsShiftDown() && event.IsControlDown()) {
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      controller()->AddSelectionFromAnchorTo(this);
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (event.IsShiftDown()) {
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      controller()->ExtendSelectionTo(this);
294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (event.IsControlDown()) {
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      controller()->ToggleSelected(this);
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!IsSelected()) {
297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        // Don't allow dragging non-selected tabs.
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        return false;
299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (!IsSelected()) {
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      controller()->SelectTab(this);
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    controller()->MaybeStartDrag(this, event);
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BaseTab::OnMouseDragged(const views::MouseEvent& event) {
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (controller())
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    controller()->ContinueDrag(event);
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::OnMouseReleased(const views::MouseEvent& event) {
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!controller())
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Notify the drag helper that we're done with any potential drag operations.
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Clean up the drag helper, which is re-created on the next mouse press.
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // In some cases, ending the drag will schedule the tab for destruction; if
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // so, bail immediately, since our members are already dead and we shouldn't
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // do anything else except drop the tab where it is.
323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (controller()->EndDrag(false))
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Close tab on middle click, but only if the button is released over the tab
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // (normal windows behavior is to discard presses of a UI element where the
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // releases happen off the element).
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (event.IsMiddleMouseButton()) {
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (HitTest(event.location())) {
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      controller()->CloseTab(this);
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (closing_) {
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We're animating closed and a middle mouse button was pushed on us but
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // we don't contain the mouse anymore. We assume the user is clicking
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // quicker than the animation and we should close the tab that falls under
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // the mouse.
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      BaseTab* closest_tab = controller()->GetTabAt(this, event.location());
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (closest_tab)
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        controller()->CloseTab(closest_tab);
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
341ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else if (event.IsOnlyLeftMouseButton() && !event.IsShiftDown() &&
342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen             !event.IsControlDown()) {
343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // If the tab was already selected mouse pressed doesn't change the
344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // selection. Reset it now to handle the case where multiple tabs were
345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // selected.
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    controller()->SelectTab(this);
347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::OnMouseCaptureLost() {
351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (controller())
352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    controller()->EndDrag(true);
353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::OnMouseEntered(const views::MouseEvent& event) {
356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!hover_animation_.get()) {
357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    hover_animation_.reset(new ui::SlideAnimation(this));
358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    hover_animation_->SetContainer(animation_container_.get());
359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    hover_animation_->SetSlideDuration(kHoverDurationMs);
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  hover_animation_->SetTweenType(ui::Tween::EASE_OUT);
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  hover_animation_->Show();
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::OnMouseExited(const views::MouseEvent& event) {
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  hover_animation_->SetTweenType(ui::Tween::EASE_IN);
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  hover_animation_->Hide();
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BaseTab::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (data_.title.empty())
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Only show the tooltip if the title is truncated.
375dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (font_->GetStringWidth(data_.title) > GetTitleBounds().width()) {
3763f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    *tooltip = UTF16ToWide(data_.title);
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::GetAccessibleState(ui::AccessibleViewState* state) {
383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  state->role = ui::AccessibilityTypes::ROLE_PAGETAB;
384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  state->name = data_.title;
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state,
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      TabRendererData::NetworkState state) {
3893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  static bool initialized = false;
3903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  static int loading_animation_frame_count = 0;
3913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  static int waiting_animation_frame_count = 0;
3923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  static int waiting_to_loading_frame_count_ratio = 0;
3933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!initialized) {
3943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    initialized = true;
3953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
3963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    SkBitmap loading_animation(*rb.GetBitmapNamed(IDR_THROBBER));
3973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    loading_animation_frame_count =
3983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        loading_animation.width() / loading_animation.height();
3993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    SkBitmap waiting_animation(*rb.GetBitmapNamed(IDR_THROBBER_WAITING));
4003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    waiting_animation_frame_count =
4013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        waiting_animation.width() / waiting_animation.height();
4023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    waiting_to_loading_frame_count_ratio =
4033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        waiting_animation_frame_count / loading_animation_frame_count;
4043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
4053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The waiting animation is the reverse of the loading animation, but at a
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // different rate - the following reverses and scales the animation_frame_
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // so that the frame is at an equivalent position when going from one
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // animation to the other.
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state != old_state) {
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    loading_animation_frame_ = loading_animation_frame_count -
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (loading_animation_frame_ / waiting_to_loading_frame_count_ratio);
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (state != TabRendererData::NETWORK_STATE_NONE) {
416731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    loading_animation_frame_ = (loading_animation_frame_ + 1) %
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ((state == TabRendererData::NETWORK_STATE_WAITING) ?
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            waiting_animation_frame_count : loading_animation_frame_count);
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    loading_animation_frame_ = 0;
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
422dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ScheduleIconPaint();
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
425dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid BaseTab::PaintIcon(gfx::Canvas* canvas) {
426dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect bounds = GetIconBounds();
427dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (bounds.IsEmpty())
428dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return;
429dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // The size of bounds has to be kFaviconSize x kFaviconSize.
431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK_EQ(kFaviconSize, bounds.width());
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK_EQ(kFaviconSize, bounds.height());
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
434dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  bounds.set_x(GetMirroredXForRect(bounds));
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (data().network_state != TabRendererData::NETWORK_STATE_NONE) {
43772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ui::ThemeProvider* tp = GetThemeProvider();
4383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    SkBitmap frames(*tp->GetBitmapNamed(
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ?
4403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        IDR_THROBBER_WAITING : IDR_THROBBER));
441dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
442dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    int icon_size = frames.height();
443dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    int image_offset = loading_animation_frame_ * icon_size;
444dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    DrawIconCenter(canvas, frames, image_offset,
445dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                   icon_size, icon_size, bounds, false);
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    canvas->Save();
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    canvas->ClipRectInt(0, 0, width(), height());
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (should_display_crashed_favicon_) {
4503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      ResourceBundle& rb = ResourceBundle::GetSharedInstance();
451ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      SkBitmap crashed_favicon(*rb.GetBitmapNamed(IDR_SAD_FAVICON));
452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      bounds.set_y(bounds.y() + favicon_hiding_offset_);
453ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      DrawIconCenter(canvas, crashed_favicon, 0,
454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     crashed_favicon.width(),
455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     crashed_favicon.height(), bounds, true);
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!data().favicon.isNull()) {
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // TODO(pkasting): Use code in tab_icon_view.cc:PaintIcon() (or switch
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // to using that class to render the favicon).
460dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        DrawIconCenter(canvas, data().favicon, 0,
461dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       data().favicon.width(),
462dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       data().favicon.height(),
463dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                       bounds, true);
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    canvas->Restore();
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) {
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Paint the Title.
472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const gfx::Rect& title_bounds = GetTitleBounds();
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string16 title = data().title;
474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (title.empty()) {
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    title = data().loading ?
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
478dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        TabContentsWrapper::GetDefaultTitle();
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Browser::FormatTitleForDisplay(&title);
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  canvas->AsCanvasSkia()->DrawFadeTruncatingString(title,
485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      gfx::CanvasSkia::TruncateFadeTail, 0, *font_, title_color, title_bounds);
486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#else
48772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  canvas->DrawStringInt(title, *font_, title_color,
488dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                        title_bounds.x(), title_bounds.y(),
489dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                        title_bounds.width(), title_bounds.height());
490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4933f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid BaseTab::AnimationProgressed(const ui::Animation* animation) {
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4973f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid BaseTab::AnimationCanceled(const ui::Animation* animation) {
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5013f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid BaseTab::AnimationEnded(const ui::Animation* animation) {
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SchedulePaint();
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::ButtonPressed(views::Button* sender, const views::Event& event) {
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(sender == close_button_);
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller()->CloseTab(this);
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
510dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid BaseTab::ShowContextMenuForView(views::View* source,
511dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                     const gfx::Point& p,
512dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                     bool is_mouse_gesture) {
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (controller())
514dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    controller()->ShowContextMenuForTab(this, p);
515dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
516dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
517dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenint BaseTab::loading_animation_frame() const {
518dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return loading_animation_frame_;
519dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
520dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
521dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenbool BaseTab::should_display_crashed_favicon() const {
522dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return should_display_crashed_favicon_;
523dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
524dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint BaseTab::favicon_hiding_offset() const {
526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return favicon_hiding_offset_;
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::SetFaviconHidingOffset(int offset) {
530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  favicon_hiding_offset_ = offset;
531dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ScheduleIconPaint();
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::DisplayCrashedFavicon() {
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  should_display_crashed_favicon_ = true;
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BaseTab::ResetCrashedFavicon() {
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  should_display_crashed_favicon_ = false;
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::StartCrashAnimation() {
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!crash_animation_.get())
544ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    crash_animation_.reset(new FaviconCrashAnimation(this));
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  crash_animation_->Stop();
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  crash_animation_->Start();
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::StopCrashAnimation() {
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!crash_animation_.get())
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  crash_animation_->Stop();
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BaseTab::IsPerformingCrashAnimation() const {
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return crash_animation_.get() && crash_animation_->is_animating();
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
559dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid BaseTab::ScheduleIconPaint() {
560dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect bounds = GetIconBounds();
561dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (bounds.IsEmpty())
562dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return;
563dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
564dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Extends the area to the bottom when sad_favicon is
565dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // animating.
566dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (IsPerformingCrashAnimation())
567dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    bounds.set_height(height() - bounds.y());
568dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  bounds.set_x(GetMirroredXForRect(bounds));
569dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  SchedulePaintInRect(bounds);
570dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
571dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BaseTab::InitResources() {
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static bool initialized = false;
5753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!initialized) {
5763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    initialized = true;
5773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    font_ = new gfx::Font(
5783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont));
5793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    font_height_ = font_->GetHeight();
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
582