blob: ed0a40ae4966de198bf7c6212c8a99894e6e659a [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// 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"
#include "base/time/time.h"
// static
constexpr base::TimeDelta ThumbnailCaptureDriver::kCooldownDelay;
// static
constexpr size_t ThumbnailCaptureDriver::kMaxCooldownRetries;
ThumbnailCaptureDriver::ThumbnailCaptureDriver(Client* client,
ThumbnailScheduler* scheduler)
: client_(client), scheduler_(scheduler) {
scheduler_->AddTab(this);
}
ThumbnailCaptureDriver::~ThumbnailCaptureDriver() {
scheduler_->RemoveTab(this);
}
void ThumbnailCaptureDriver::UpdatePageReadiness(PageReadiness page_readiness) {
page_readiness_ = page_readiness;
UpdateSchedulingPriority();
UpdateCaptureState();
}
void ThumbnailCaptureDriver::UpdatePageVisibility(bool page_visible) {
page_visible_ = page_visible;
UpdateSchedulingPriority();
}
void ThumbnailCaptureDriver::UpdateThumbnailVisibility(bool thumbnail_visible) {
thumbnail_visible_ = thumbnail_visible;
UpdateSchedulingPriority();
}
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::SetCapturePermittedByScheduler(bool scheduled) {
scheduled_ = scheduled;
UpdateCaptureState();
}
void ThumbnailCaptureDriver::UpdateCaptureState() {
// If there was a final thumbnail but the page has changed, get set up
// for a new capture.
if (page_readiness_ < PageReadiness::kReadyForFinalCapture &&
capture_state_ == CaptureState::kHaveFinalCapture) {
client_->StopCapture();
capture_state_ = CaptureState::kNoCapture;
}
// If de-scheduled, stop any ongoing capture.
if (!scheduled_) {
client_->StopCapture();
if (capture_state_ < CaptureState::kHaveFinalCapture) {
capture_state_ = CaptureState::kNoCapture;
}
return;
}
// Request to capture if we haven't done so.
if (capture_state_ < CaptureState::kCaptureRequested) {
client_->RequestCapture();
capture_state_ = 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_.Stop();
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::UpdateSchedulingPriority() {
if (page_readiness_ == PageReadiness::kNotReady) {
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kNone);
return;
}
// For now don't force-load background pages, or the current page if the
// thumbnail isn't being requested. 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/40686155): 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_) {
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kNone);
return;
}
// 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) {
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kNone);
return;
}
if (page_readiness_ == PageReadiness::kReadyForInitialCapture) {
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kLow);
return;
}
DCHECK_EQ(page_readiness_, PageReadiness::kReadyForFinalCapture);
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kHigh);
}
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();
scheduler_->SetTabCapturePriority(
this, ThumbnailScheduler::TabCapturePriority::kNone);
}