Commit 923f6277 authored by Peter Boström's avatar Peter Boström Committed by Commit Bot

Rewrite tab animation to use TimeTicks

Avoids having additional high-frequency post tasking for no-op tasks in
the background.

To keep track of when to stop animating without an AnimationEnded
callback, we keep track of the last-drawn animation state. This also has
the side effect of allowing us to avoid repainting the loading-progress
bar if would paint the same pixels we drew last time. This should avoid
a lot of repaints for slow-loading sites.

This also ends up fixing backwards-jumping indicators. Probably because
the logic for clamping the loading-progress timer got easier.

Bug: chromium:907044, chromium:905020, chromium:905452
Change-Id: Ic83748bad83115ef2283c881a89ed65e3d3938ab
Reviewed-on: https://chromium-review.googlesource.com/c/1346831Reviewed-by: default avatarSidney San Martín <sdy@chromium.org>
Commit-Queue: Peter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611234}
parent bc5163ce
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/ui/views/tabs/tab_icon.h" #include "chrome/browser/ui/views/tabs/tab_icon.h"
#include "base/time/default_tick_clock.h"
#include "cc/paint/paint_flags.h" #include "cc/paint/paint_flags.h"
#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/layout_constants.h"
...@@ -26,12 +27,9 @@ ...@@ -26,12 +27,9 @@
namespace { namespace {
// Note: The excess (part above 1.0) is used as a fade-out effect, so if this constexpr int kLoadingProgressTimeMs = 400;
// value is 1.5, then 50% of the progress indicator animation will be used for constexpr int kLoadingProgressFadeOutMs = 200;
// fade-out time after the loading sequence is complete. constexpr int kFaviconFadeInMs = 200;
// Also note that the progress indicator animation is capped to the actual load
// progress.
constexpr double kProgressIndicatorAnimationDuration = 1.5;
bool UseNewLoadingAnimation() { bool UseNewLoadingAnimation() {
return base::FeatureList::IsEnabled(features::kNewTabLoadingAnimation); return base::FeatureList::IsEnabled(features::kNewTabLoadingAnimation);
...@@ -88,18 +86,16 @@ class TabIcon::CrashAnimation : public gfx::LinearAnimation, ...@@ -88,18 +86,16 @@ class TabIcon::CrashAnimation : public gfx::LinearAnimation,
DISALLOW_COPY_AND_ASSIGN(CrashAnimation); DISALLOW_COPY_AND_ASSIGN(CrashAnimation);
}; };
TabIcon::TabIcon() TabIcon::TabIcon() : clock_(base::DefaultTickClock::GetInstance()) {
: loading_progress_timer_(base::TimeDelta::FromMilliseconds(600),
gfx::LinearAnimation::kDefaultFrameRate,
this),
favicon_fade_in_animation_(base::TimeDelta::FromMilliseconds(200),
gfx::LinearAnimation::kDefaultFrameRate,
this) {
set_can_process_events_within_subtree(false); set_can_process_events_within_subtree(false);
// The minimum size to avoid clipping the attention indicator. // The minimum size to avoid clipping the attention indicator.
SetPreferredSize(gfx::Size(gfx::kFaviconSize + kAttentionIndicatorRadius, SetPreferredSize(gfx::Size(gfx::kFaviconSize + kAttentionIndicatorRadius,
gfx::kFaviconSize + kAttentionIndicatorRadius)); gfx::kFaviconSize + kAttentionIndicatorRadius));
UpdateLoadingAnimationState();
// Initial state (before any data) should not be animating.
DCHECK(!ShowingLoadingAnimation());
} }
TabIcon::~TabIcon() = default; TabIcon::~TabIcon() = default;
...@@ -118,7 +114,7 @@ void TabIcon::SetData(const TabRendererData& data) { ...@@ -118,7 +114,7 @@ void TabIcon::SetData(const TabRendererData& data) {
if (was_showing_load && !showing_load) { if (was_showing_load && !showing_load) {
// Loading animation transitioning from on to off. // Loading animation transitioning from on to off.
loading_start_time_ = base::TimeTicks(); old_animation_loading_start_time_ = base::TimeTicks();
waiting_state_ = gfx::ThrobberWaitingState(); waiting_state_ = gfx::ThrobberWaitingState();
SchedulePaint(); SchedulePaint();
} else if (!was_showing_load && showing_load) { } else if (!was_showing_load && showing_load) {
...@@ -143,13 +139,34 @@ bool TabIcon::ShowingLoadingAnimation() const { ...@@ -143,13 +139,34 @@ bool TabIcon::ShowingLoadingAnimation() const {
if (inhibit_loading_animation_) if (inhibit_loading_animation_)
return false; return false;
if (loading_progress_timer_.is_animating() || if (network_state_ != TabNetworkState::kNone &&
favicon_fade_in_animation_.is_animating()) { network_state_ != TabNetworkState::kError)
return true;
const base::TimeTicks now = clock_->NowTicks();
if (loading_progress_timer_ &&
*loading_progress_timer_ +
base::TimeDelta::FromMilliseconds(kLoadingProgressTimeMs +
kLoadingProgressFadeOutMs) >=
now) {
return true;
}
if (favicon_fade_in_animation_ &&
*favicon_fade_in_animation_ +
base::TimeDelta::FromMilliseconds(kFaviconFadeInMs) >=
now) {
return true; return true;
} }
return network_state_ != TabNetworkState::kNone && // If the last frame painted still displays the loading indicator or favicon
network_state_ != TabNetworkState::kError; // in less than full opacity we need to paint the last frame.
if (animation_state_.favicon_alpha < SK_AlphaOPAQUE ||
animation_state_.loading_progress_alpha > 0) {
return true;
}
return false;
} }
bool TabIcon::ShowingAttentionIndicator() const { bool TabIcon::ShowingAttentionIndicator() const {
...@@ -165,8 +182,11 @@ void TabIcon::SetCanPaintToLayer(bool can_paint_to_layer) { ...@@ -165,8 +182,11 @@ void TabIcon::SetCanPaintToLayer(bool can_paint_to_layer) {
void TabIcon::StepLoadingAnimation(const base::TimeDelta& elapsed_time) { void TabIcon::StepLoadingAnimation(const base::TimeDelta& elapsed_time) {
waiting_state_.elapsed_time = elapsed_time; waiting_state_.elapsed_time = elapsed_time;
if (ShowingLoadingAnimation())
if (LoadingAnimationNeedsRepaint())
SchedulePaint(); SchedulePaint();
RefreshLayer();
} }
void TabIcon::SetBackgroundColor(SkColor bg_color) { void TabIcon::SetBackgroundColor(SkColor bg_color) {
...@@ -191,6 +211,8 @@ void TabIcon::OnPaint(gfx::Canvas* canvas) { ...@@ -191,6 +211,8 @@ void TabIcon::OnPaint(gfx::Canvas* canvas) {
return; return;
} }
UpdateLoadingAnimationState();
if (ShowingAttentionIndicator() && !should_display_crashed_favicon_) { if (ShowingAttentionIndicator() && !should_display_crashed_favicon_) {
PaintAttentionIndicatorAndIcon(canvas, GetIconToPaint(), icon_bounds); PaintAttentionIndicatorAndIcon(canvas, GetIconToPaint(), icon_bounds);
} else if (!MaybePaintFavicon(canvas, GetIconToPaint(), icon_bounds)) { } else if (!MaybePaintFavicon(canvas, GetIconToPaint(), icon_bounds)) {
...@@ -201,20 +223,80 @@ void TabIcon::OnPaint(gfx::Canvas* canvas) { ...@@ -201,20 +223,80 @@ void TabIcon::OnPaint(gfx::Canvas* canvas) {
PaintLoadingAnimation(canvas, icon_bounds); PaintLoadingAnimation(canvas, icon_bounds);
} }
void TabIcon::UpdateLoadingAnimationState() {
animation_state_ = GetLoadingAnimationState();
}
TabIcon::LoadingAnimationState TabIcon::GetLoadingAnimationState() const {
const base::TimeTicks now = clock_->NowTicks();
LoadingAnimationState state;
state.elapsed_time = waiting_state_.elapsed_time;
if (!loading_progress_timer_) {
state.loading_progress = 0;
state.loading_progress_alpha = 0;
} else {
base::TimeDelta loading_progress_time = now - *loading_progress_timer_;
state.loading_progress = std::min(
loading_progress_time.InMillisecondsF() / kLoadingProgressTimeMs,
target_loading_progress_);
state.loading_progress_alpha = SK_AlphaOPAQUE;
if (network_state_ != TabNetworkState::kLoading &&
loading_progress_time.InMilliseconds() > kLoadingProgressTimeMs) {
// When we're no longer loading, translate loading progress time above
// kLoadingProgressTimeMs to the fade-out effect.
state.loading_progress_alpha =
SK_AlphaOPAQUE *
std::max(1.0 - (loading_progress_time.InMillisecondsF() -
kLoadingProgressTimeMs) /
kLoadingProgressFadeOutMs,
0.0);
}
}
// In the waiting/loading state we initially show no favicon.
state.favicon_alpha = (network_state_ == TabNetworkState::kWaiting ||
network_state_ == TabNetworkState::kLoading)
? 0
: SK_AlphaOPAQUE;
if (favicon_fade_in_animation_) {
base::TimeDelta favicon_fade_in_time = now - *favicon_fade_in_animation_;
state.favicon_alpha =
SK_AlphaOPAQUE *
std::min(favicon_fade_in_time.InMillisecondsF() / kFaviconFadeInMs,
1.0);
}
return state;
}
bool TabIcon::LoadingAnimationNeedsRepaint() const {
if (!UseNewLoadingAnimation())
return ShowingLoadingAnimation();
// Throbber always needs a repaint.
if (network_state_ == TabNetworkState::kWaiting)
return true;
LoadingAnimationState new_state = GetLoadingAnimationState();
// Compare without |elapsed_time| as it's only used in the waiting state.
auto tie = [](const LoadingAnimationState& state) {
return std::tie(state.loading_progress, state.loading_progress_alpha,
state.favicon_alpha);
};
return tie(new_state) != tie(animation_state_);
}
void TabIcon::OnThemeChanged() { void TabIcon::OnThemeChanged() {
crashed_icon_ = gfx::ImageSkia(); // Force recomputation if crashed. crashed_icon_ = gfx::ImageSkia(); // Force recomputation if crashed.
if (!themed_favicon_.isNull()) if (!themed_favicon_.isNull())
themed_favicon_ = ThemeImage(favicon_); themed_favicon_ = ThemeImage(favicon_);
} }
void TabIcon::AnimationEnded(const gfx::Animation* animation) {
// After the last animation ends it's possible we should not paint to a layer
// anymore.
RefreshLayer();
// Make sure that the final frame of the animation gets painted.
SchedulePaint();
}
void TabIcon::PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas, void TabIcon::PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas,
const gfx::ImageSkia& icon, const gfx::ImageSkia& icon,
const gfx::Rect& bounds) { const gfx::Rect& bounds) {
...@@ -251,30 +333,14 @@ void TabIcon::PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas, ...@@ -251,30 +333,14 @@ void TabIcon::PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas,
void TabIcon::PaintLoadingProgressIndicator(gfx::Canvas* canvas, void TabIcon::PaintLoadingProgressIndicator(gfx::Canvas* canvas,
gfx::RectF bounds, gfx::RectF bounds,
SkColor color) { SkColor color) {
double animation_value = loading_progress_timer_.GetCurrentValue() * bounds.set_width(bounds.height() + animation_state_.loading_progress *
kProgressIndicatorAnimationDuration; (bounds.width() - bounds.height()));
if (network_state_ == TabNetworkState::kLoading) {
// While we're loading, clamp to the loading process (which cannot go above
// 1.0) to make sure that we don't start the fade-out during kLoading, even
// if loading progress has previously been reported as 1.0.
animation_value = std::min(loading_progress_, animation_value);
}
const double loading_progress = std::min(animation_value, 1.0);
bounds.set_width(bounds.height() +
loading_progress * (bounds.width() - bounds.height()));
cc::PaintFlags flags; cc::PaintFlags flags;
flags.setColor(color); flags.setColor(color);
flags.setStyle(cc::PaintFlags::kFill_Style); flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true); flags.setAntiAlias(true);
if (animation_value > 1.0) { flags.setAlpha(animation_state_.loading_progress_alpha);
// The part of kProgressIndicatorAnimationDuration above 1.0 is translated
// into an alpha value used as a linear fade-out effect.
flags.setAlpha(SK_AlphaOPAQUE *
(1.0 - (animation_value - 1.0) /
(kProgressIndicatorAnimationDuration - 1.0)));
}
canvas->DrawRect(bounds, flags); canvas->DrawRect(bounds, flags);
} }
...@@ -290,7 +356,7 @@ void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas, ...@@ -290,7 +356,7 @@ void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas,
tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING); tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING);
if (network_state_ == TabNetworkState::kWaiting) { if (network_state_ == TabNetworkState::kWaiting) {
gfx::PaintNewThrobberWaiting(canvas, throbber_bounds, loading_color, gfx::PaintNewThrobberWaiting(canvas, throbber_bounds, loading_color,
waiting_state_.elapsed_time); animation_state_.elapsed_time);
} else { } else {
PaintLoadingProgressIndicator(canvas, throbber_bounds, loading_color); PaintLoadingProgressIndicator(canvas, throbber_bounds, loading_color);
} }
...@@ -301,16 +367,16 @@ void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas, ...@@ -301,16 +367,16 @@ void TabIcon::PaintLoadingAnimation(gfx::Canvas* canvas,
tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING), tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING),
waiting_state_.elapsed_time); waiting_state_.elapsed_time);
} else { } else {
const base::TimeTicks current_time = base::TimeTicks::Now(); const base::TimeTicks current_time = clock_->NowTicks();
if (loading_start_time_ == base::TimeTicks()) if (old_animation_loading_start_time_.is_null())
loading_start_time_ = current_time; old_animation_loading_start_time_ = current_time;
waiting_state_.color = waiting_state_.color =
tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING); tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING);
gfx::PaintThrobberSpinningAfterWaiting( gfx::PaintThrobberSpinningAfterWaiting(
canvas, bounds, canvas, bounds,
tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING), tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING),
current_time - loading_start_time_, &waiting_state_); current_time - old_animation_loading_start_time_, &waiting_state_);
} }
} }
} }
...@@ -344,20 +410,14 @@ bool TabIcon::MaybePaintFavicon(gfx::Canvas* canvas, ...@@ -344,20 +410,14 @@ bool TabIcon::MaybePaintFavicon(gfx::Canvas* canvas,
const gfx::Rect& bounds) { const gfx::Rect& bounds) {
// While loading, the favicon (or placeholder) isn't drawn until it has // While loading, the favicon (or placeholder) isn't drawn until it has
// started fading in. // started fading in.
if (ShowingLoadingAnimation() && if (animation_state_.favicon_alpha == 0)
favicon_fade_in_animation_.GetCurrentValue() == 0.0) {
return false; return false;
}
if (icon.isNull()) if (icon.isNull())
return false; return false;
cc::PaintFlags flags; cc::PaintFlags flags;
// If we're loading and the favicon is fading in, draw with transparency. flags.setAlpha(animation_state_.favicon_alpha);
flags.setAlpha(ShowingLoadingAnimation()
? favicon_fade_in_animation_.GetCurrentValue() *
SK_AlphaOPAQUE
: SK_AlphaOPAQUE);
canvas->DrawImageInt(icon, 0, 0, bounds.width(), bounds.height(), bounds.x(), canvas->DrawImageInt(icon, 0, 0, bounds.width(), bounds.height(), bounds.x(),
bounds.y(), bounds.width(), bounds.height(), false, bounds.y(), bounds.width(), bounds.height(), false,
...@@ -375,10 +435,10 @@ void TabIcon::SetIcon(const GURL& url, const gfx::ImageSkia& icon) { ...@@ -375,10 +435,10 @@ void TabIcon::SetIcon(const GURL& url, const gfx::ImageSkia& icon) {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
const bool is_default_favicon = const bool is_default_favicon =
icon.BackedBySameObjectAs(*rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON)); icon.BackedBySameObjectAs(*rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON));
if (favicon_fade_in_animation_.GetCurrentValue() == 0.0 &&
!is_default_favicon) { if (!is_default_favicon && !favicon_fade_in_animation_)
favicon_fade_in_animation_.Start(); favicon_fade_in_animation_ = clock_->NowTicks();
}
if (is_default_favicon || ShouldThemifyFaviconForUrl(url)) { if (is_default_favicon || ShouldThemifyFaviconForUrl(url)) {
themed_favicon_ = ThemeImage(icon); themed_favicon_ = ThemeImage(icon);
} else { } else {
...@@ -395,25 +455,26 @@ void TabIcon::SetNetworkState(TabNetworkState network_state, ...@@ -395,25 +455,26 @@ void TabIcon::SetNetworkState(TabNetworkState network_state,
if (network_state_ == TabNetworkState::kLoading) { if (network_state_ == TabNetworkState::kLoading) {
// When transitioning to loading, reset the progress indicatator + timer. // When transitioning to loading, reset the progress indicatator + timer.
loading_progress_ = 0.0; target_loading_progress_ = 0.0;
loading_progress_timer_.SetCurrentValue(0); loading_progress_timer_ = clock_->NowTicks();
} }
if (old_state == TabNetworkState::kLoading) { if (old_state == TabNetworkState::kLoading) {
loading_progress_ = 1.0; target_loading_progress_ = 1.0;
// This fades in the placeholder favicon if no favicon has loaded so far. // Start fading in placeholder favicon if no favicon has loaded so far.
if (!favicon_fade_in_animation_.is_animating() && const base::TimeTicks now = clock_->NowTicks();
favicon_fade_in_animation_.GetCurrentValue() == 0.0) { if (!favicon_fade_in_animation_)
favicon_fade_in_animation_.Start(); favicon_fade_in_animation_ = now;
}
// Rewind the progress timer back to 100% if necessary. This prevents
// parts of the fade-out animation to be skipped.
RewindLoadingProgressTimerIfNecessary(1.0f);
} }
if (network_state_ == TabNetworkState::kWaiting) { if (network_state_ == TabNetworkState::kWaiting) {
// Reset favicon and tab-loading animations // Reset favicon and tab-loading animations
favicon_fade_in_animation_.Stop(); favicon_fade_in_animation_.reset();
favicon_fade_in_animation_.SetCurrentValue(0.0); loading_progress_timer_.reset();
loading_progress_timer_.Stop();
loading_progress_timer_.SetCurrentValue(0.0);
} }
SchedulePaint(); SchedulePaint();
} }
...@@ -421,18 +482,23 @@ void TabIcon::SetNetworkState(TabNetworkState network_state, ...@@ -421,18 +482,23 @@ void TabIcon::SetNetworkState(TabNetworkState network_state,
// The loading progress looks really weird if it ever jumps backwards, so make // The loading progress looks really weird if it ever jumps backwards, so make
// sure it only increases. // sure it only increases.
if (network_state_ == TabNetworkState::kLoading && if (network_state_ == TabNetworkState::kLoading &&
loading_progress_ < load_progress) { target_loading_progress_ < load_progress) {
// Clamp the loading progress timer to the currently visible value. Note DCHECK(loading_progress_timer_);
// that the animation plays a fade-out effect after loading_progress_ is
// full, so filling the bar only uses a fraction of the timer. RewindLoadingProgressTimerIfNecessary(target_loading_progress_);
loading_progress_timer_.SetCurrentValue(
std::min(loading_progress_timer_.GetCurrentValue(), target_loading_progress_ = load_progress;
loading_progress_ / kProgressIndicatorAnimationDuration));
loading_progress_ = load_progress;
loading_progress_timer_.Start();
} }
} }
void TabIcon::RewindLoadingProgressTimerIfNecessary(double progress) {
base::TimeTicks clamped_start =
clock_->NowTicks() -
base::TimeDelta::FromMilliseconds(progress * kLoadingProgressTimeMs);
if (clamped_start > loading_progress_timer_)
loading_progress_timer_ = clamped_start;
}
void TabIcon::SetIsCrashed(bool is_crashed) { void TabIcon::SetIsCrashed(bool is_crashed) {
if (is_crashed == is_crashed_) if (is_crashed == is_crashed_)
return; return;
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/ui/tabs/tab_network_state.h" #include "chrome/browser/ui/tabs/tab_network_state.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "ui/gfx/paint_throbber.h" #include "ui/gfx/paint_throbber.h"
#include "ui/views/view.h" #include "ui/views/view.h"
...@@ -25,7 +23,7 @@ struct TabRendererData; ...@@ -25,7 +23,7 @@ struct TabRendererData;
// the width is TabIcon::GetIdealWidth(), and the height goes down to the // the width is TabIcon::GetIdealWidth(), and the height goes down to the
// bottom of the enclosing view (this is so the crashed tab can animate out of // bottom of the enclosing view (this is so the crashed tab can animate out of
// the bottom). // the bottom).
class TabIcon : public views::View, public gfx::AnimationDelegate { class TabIcon : public views::View {
public: public:
// Attention indicator types (use as a bitmask). There is only one visual // Attention indicator types (use as a bitmask). There is only one visual
// representation, but the state of each of these is tracked separately and // representation, but the state of each of these is tracked separately and
...@@ -67,13 +65,20 @@ class TabIcon : public views::View, public gfx::AnimationDelegate { ...@@ -67,13 +65,20 @@ class TabIcon : public views::View, public gfx::AnimationDelegate {
friend CrashAnimation; friend CrashAnimation;
friend class TabTest; friend class TabTest;
// State used to draw the tab-loading animation. Also used to store the
// last-painted state to know to redraw the final frame as the animation
// finishes.
struct LoadingAnimationState {
base::TimeDelta elapsed_time;
double loading_progress;
SkAlpha loading_progress_alpha;
SkAlpha favicon_alpha;
};
// views::View: // views::View:
void OnPaint(gfx::Canvas* canvas) override; void OnPaint(gfx::Canvas* canvas) override;
void OnThemeChanged() override; void OnThemeChanged() override;
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
// Paints the attention indicator and |favicon_| at the given location. // Paints the attention indicator and |favicon_| at the given location.
void PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas, void PaintAttentionIndicatorAndIcon(gfx::Canvas* canvas,
const gfx::ImageSkia& icon, const gfx::ImageSkia& icon,
...@@ -89,6 +94,15 @@ class TabIcon : public views::View, public gfx::AnimationDelegate { ...@@ -89,6 +94,15 @@ class TabIcon : public views::View, public gfx::AnimationDelegate {
// current tab state. // current tab state.
void PaintLoadingAnimation(gfx::Canvas* canvas, const gfx::Rect& bounds); void PaintLoadingAnimation(gfx::Canvas* canvas, const gfx::Rect& bounds);
void UpdateLoadingAnimationState();
LoadingAnimationState GetLoadingAnimationState() const;
void RewindLoadingProgressTimerIfNecessary(double progress);
// Returns false if painting the loading animation would paint the same thing
// that's already painted.
bool LoadingAnimationNeedsRepaint() const;
// Gets either the crashed icon or favicon to be rendered for the tab. // Gets either the crashed icon or favicon to be rendered for the tab.
const gfx::ImageSkia& GetIconToPaint(); const gfx::ImageSkia& GetIconToPaint();
...@@ -118,6 +132,8 @@ class TabIcon : public views::View, public gfx::AnimationDelegate { ...@@ -118,6 +132,8 @@ class TabIcon : public views::View, public gfx::AnimationDelegate {
gfx::ImageSkia ThemeImage(const gfx::ImageSkia& source); gfx::ImageSkia ThemeImage(const gfx::ImageSkia& source);
const base::TickClock* clock_;
gfx::ImageSkia favicon_; gfx::ImageSkia favicon_;
TabNetworkState network_state_ = TabNetworkState::kNone; TabNetworkState network_state_ = TabNetworkState::kNone;
bool is_crashed_ = false; bool is_crashed_ = false;
...@@ -128,7 +144,8 @@ class TabIcon : public views::View, public gfx::AnimationDelegate { ...@@ -128,7 +144,8 @@ class TabIcon : public views::View, public gfx::AnimationDelegate {
bool inhibit_loading_animation_ = false; bool inhibit_loading_animation_ = false;
// The point in time when the tab icon was first painted in the loading state. // The point in time when the tab icon was first painted in the loading state.
base::TimeTicks loading_start_time_; // TODO(pbos): Remove after |kNewTabLoadingAnimation| launches.
base::TimeTicks old_animation_loading_start_time_;
// Paint state for the loading animation after the most recent waiting paint. // Paint state for the loading animation after the most recent waiting paint.
// TODO(pbos): After |kNewTabLoadingAnimation| launches, remove the need for // TODO(pbos): After |kNewTabLoadingAnimation| launches, remove the need for
...@@ -152,12 +169,15 @@ class TabIcon : public views::View, public gfx::AnimationDelegate { ...@@ -152,12 +169,15 @@ class TabIcon : public views::View, public gfx::AnimationDelegate {
double hiding_fraction_ = 0.0; double hiding_fraction_ = 0.0;
// Loading progress used for drawing the progress indicator. // Loading progress used for drawing the progress indicator.
double loading_progress_ = 1.0; double target_loading_progress_ = 1.0;
gfx::LinearAnimation loading_progress_timer_;
LoadingAnimationState animation_state_;
base::Optional<base::TimeTicks> loading_progress_timer_;
// Fade-in animation for the favicon. Starts when a favicon loads or the tab // Fade-in animation for the favicon. Starts when a favicon loads or the tab
// is no longer loading. The latter case will fade into a placeholder icon. // is no longer loading. The latter case will fade into a placeholder icon.
gfx::LinearAnimation favicon_fade_in_animation_; base::Optional<base::TimeTicks> favicon_fade_in_animation_;
// Crash animation (in place of favicon). Lazily created since most of the // Crash animation (in place of favicon). Lazily created since most of the
// time it will be unneeded. // time it will be unneeded.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/simple_test_tick_clock.h"
#include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/views/tabs/alert_indicator.h" #include "chrome/browser/ui/views/tabs/alert_indicator.h"
...@@ -305,18 +306,17 @@ class TabTest : public ChromeViewsTestBase { ...@@ -305,18 +306,17 @@ class TabTest : public ChromeViewsTestBase {
fade_animation->Stop(); fade_animation->Stop();
} }
static void FinishRunningLoadingAnimations(TabIcon* icon) { void SetupFakeClock(TabIcon* icon) { icon->clock_ = &fake_clock_; }
for (auto* animation :
{&icon->loading_progress_timer_, &icon->favicon_fade_in_animation_}) { void FinishRunningLoadingAnimations(TabIcon* icon) {
if (!animation->is_animating()) // Forward the clock enough for any running animations to finish.
continue; DCHECK(icon->clock_ == &fake_clock_);
animation->Stop(); fake_clock_.Advance(base::TimeDelta::FromMilliseconds(2000));
animation->SetCurrentValue(1.0); icon->UpdateLoadingAnimationState();
}
} }
static float GetLoadingProgress(TabIcon* icon) { static float GetLoadingProgress(TabIcon* icon) {
return icon->loading_progress_; return icon->target_loading_progress_;
} }
protected: protected:
...@@ -337,6 +337,7 @@ class TabTest : public ChromeViewsTestBase { ...@@ -337,6 +337,7 @@ class TabTest : public ChromeViewsTestBase {
} }
std::string original_locale_; std::string original_locale_;
base::SimpleTestTickClock fake_clock_;
}; };
class AlertIndicatorTest : public ChromeViewsTestBase { class AlertIndicatorTest : public ChromeViewsTestBase {
...@@ -585,6 +586,7 @@ TEST_F(TabTest, LayeredThrobber) { ...@@ -585,6 +586,7 @@ TEST_F(TabTest, LayeredThrobber) {
tab.SizeToPreferredSize(); tab.SizeToPreferredSize();
TabIcon* icon = GetTabIcon(tab); TabIcon* icon = GetTabIcon(tab);
SetupFakeClock(icon);
TabRendererData data; TabRendererData data;
data.url = GURL("http://example.com"); data.url = GURL("http://example.com");
EXPECT_FALSE(icon->ShowingLoadingAnimation()); EXPECT_FALSE(icon->ShowingLoadingAnimation());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment