| // 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); |
| } |