blob: 4ca2e82ac4d3d6b38e6f7fb62ea732b40f8dc563 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/thumbnails/thumbnail_capture_driver.h"
#include "base/check_op.h"
// static
constexpr base::TimeDelta ThumbnailCaptureDriver::kCooldownDelay;
// static
constexpr size_t ThumbnailCaptureDriver::kMaxCooldownRetries;
ThumbnailCaptureDriver::ThumbnailCaptureDriver(Client* client)
: client_(client) {}
ThumbnailCaptureDriver::~ThumbnailCaptureDriver() = default;
void ThumbnailCaptureDriver::UpdatePageReadiness(PageReadiness page_readiness) {
page_readiness_ = page_readiness;
UpdateCaptureState();
}
void ThumbnailCaptureDriver::UpdatePageVisibility(bool page_visible) {
page_visible_ = page_visible;
UpdateCaptureState();
}
void ThumbnailCaptureDriver::UpdateThumbnailVisibility(bool thumbnail_visible) {
thumbnail_visible_ = thumbnail_visible;
UpdateCaptureState();
}
void ThumbnailCaptureDriver::SetCanCapture(bool can_capture) {
can_capture_ = can_capture;
UpdateCaptureState();
}
void ThumbnailCaptureDriver::GotFrame() {
if (capture_state_ == CaptureState::kCooldown)
captured_cooldown_frame_ = true;
}
void ThumbnailCaptureDriver::UpdateCaptureState() {
// Stop any existing capture and return if the page is not ready.
if (page_readiness_ == PageReadiness::kNotReady) {
client_->StopCapture();
capture_state_ = CaptureState::kNoCapture;
return;
}
// Don't capture when the page is visible and the thumbnail is not
// requested.
if (!thumbnail_visible_ && page_visible_) {
client_->StopCapture();
if (capture_state_ < CaptureState::kHaveFinalCapture)
capture_state_ = CaptureState::kNoCapture;
return;
}
// For now don't force-load background pages. This is not ideal. We would
// like to grab frames from background pages to make hover cards and the
// "Mohnstrudel" touch/tablet tabstrip more responsive by pre-loading
// thumbnails from those pages. However, this currently results in a number
// of test failures and a possible violation of an assumption made by the
// renderer. TODO(crbug.com/1073141): Figure out how to force-render
// background tabs. This bug has detailed descriptions of steps we might
// take to make capture more flexible in this area.
if (!thumbnail_visible_ && !page_visible_) {
client_->StopCapture();
if (capture_state_ < CaptureState::kHaveFinalCapture)
capture_state_ = CaptureState::kNoCapture;
return;
}
// Now we know the page is ready for capture and the thumbnail is
// visible.
// If the page is in its final state and we already have a good
// thumbnail, don't need to anything.
if (page_readiness_ == PageReadiness::kReadyForFinalCapture &&
capture_state_ == CaptureState::kHaveFinalCapture) {
return;
}
// Now we know the page is a candidate for capture.
// Request to capture if we haven't done so.
if (capture_state_ < CaptureState::kCaptureRequested) {
capture_state_ = CaptureState::kCaptureRequested;
client_->RequestCapture();
}
// Now, our |capture_state_| is at least |CaptureState::kCaptureRequested|.
// Wait until our client is able to capture.
if (!can_capture_) {
// It is possible we were actively capturing and the client reported
// it can no longer capture. Reset our state to re-request capture
// later.
capture_state_ = CaptureState::kCaptureRequested;
cooldown_timer_.AbandonAndStop();
return;
}
// The client is ready so start capturing. Continue below in case the
// page is fully loaded, in which case we will wrap things up
// immediately.
if (capture_state_ == CaptureState::kCaptureRequested) {
capture_state_ = CaptureState::kCapturing;
client_->StartCapture();
}
// If the page is finalized, enter cooldown if we haven't yet.
if (page_readiness_ == PageReadiness::kReadyForFinalCapture &&
capture_state_ == CaptureState::kCapturing) {
StartCooldown();
return;
}
// If the page is finalized and we are in cooldown capture mode, we
// don't need to do anything. The cooldown timer callback will
// finalize everything.
if (page_readiness_ == PageReadiness::kReadyForFinalCapture &&
capture_state_ == CaptureState::kCooldown) {
return;
}
// If we aren't actively capturing, we should've handled this above.
DCHECK_EQ(capture_state_, CaptureState::kCapturing)
<< "page_readiness_ = " << static_cast<int>(page_readiness_);
}
void ThumbnailCaptureDriver::StartCooldown() {
DCHECK_EQ(page_readiness_, PageReadiness::kReadyForFinalCapture);
DCHECK_EQ(capture_state_, CaptureState::kCapturing);
capture_state_ = CaptureState::kCooldown;
captured_cooldown_frame_ = false;
cooldown_retry_count_ = 0U;
if (cooldown_timer_.IsRunning()) {
cooldown_timer_.Reset();
} else {
cooldown_timer_.Start(
FROM_HERE, kCooldownDelay,
base::BindRepeating(&ThumbnailCaptureDriver::OnCooldownEnded,
weak_ptr_factory_.GetWeakPtr()));
}
}
void ThumbnailCaptureDriver::OnCooldownEnded() {
if (capture_state_ < CaptureState::kCooldown)
return;
if (!captured_cooldown_frame_ &&
cooldown_retry_count_ < kMaxCooldownRetries) {
++cooldown_retry_count_;
cooldown_timer_.Reset();
return;
}
capture_state_ = CaptureState::kHaveFinalCapture;
client_->StopCapture();
}