blob: 3284009fb29882b6e1d4da3f378a085c959b4299 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot.h"
#include "base/feature_list.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/bind_post_task.h"
#include "base/task/thread_pool.h"
#include "components/performance_manager/scenario_api/performance_scenario_observer.h"
#include "components/performance_manager/scenario_api/performance_scenarios.h"
#include "content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_cache.h"
#include "content/browser/renderer_host/navigation_transitions/navigation_transition_config.h"
#if BUILDFLAG(IS_ANDROID)
#include "ui/android/resources/etc1_utils.h"
#endif
namespace content {
#if BUILDFLAG(IS_ANDROID)
namespace {
BASE_FEATURE(NavigationEntryScreenshotCompression,
base::FEATURE_ENABLED_BY_DEFAULT);
static bool g_disable_compression_for_testing = false;
using CompressionDoneCallback = base::OnceCallback<void(sk_sp<SkPixelRef>)>;
void CompressNavigationScreenshotOnWorkerThread(
SkBitmap bitmap,
bool supports_etc_non_power_of_two,
CompressionDoneCallback done_callback) {
SCOPED_UMA_HISTOGRAM_TIMER("Navigation.GestureTransition.CompressionTime");
TRACE_EVENT0("navigation", "CompressNavigationScreenshotOnWorkerThread");
sk_sp<SkPixelRef> compressed_bitmap = nullptr;
if (base::FeatureList::IsEnabled(ui::kCompressBitmapAtBackgroundPriority)) {
compressed_bitmap = ui::Etc1::CompressBitmapAtBackgroundPriority(
bitmap, supports_etc_non_power_of_two);
} else {
compressed_bitmap =
ui::Etc1::CompressBitmap(bitmap, supports_etc_non_power_of_two);
}
if (compressed_bitmap) {
std::move(done_callback).Run(std::move(compressed_bitmap));
}
}
} // namespace
#endif
// static
const void* const NavigationEntryScreenshot::kUserDataKey =
&NavigationEntryScreenshot::kUserDataKey;
// static
void NavigationEntryScreenshot::SetDisableCompressionForTesting(bool disable) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if BUILDFLAG(IS_ANDROID)
g_disable_compression_for_testing = disable;
#endif
}
NavigationEntryScreenshot::NavigationEntryScreenshot(
const SkBitmap& bitmap,
NavigationTransitionData::UniqueId unique_id,
bool supports_etc_non_power_of_two)
: performance_scenarios::MatchingScenarioObserver(
performance_scenarios::kDefaultIdleScenarios),
bitmap_(cc::UIResourceBitmap(bitmap)),
unique_id_(unique_id),
dimensions_without_compression_(bitmap_->GetSize()) {
CHECK(NavigationTransitionConfig::AreBackForwardTransitionsEnabled());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
compression_task_ = CompressionTask(bitmap, supports_etc_non_power_of_two);
if (!compression_task_) {
return;
}
if (NavigationTransitionConfig::ShouldCompressScreenshotWhenQuiet()) {
auto observer_list =
performance_scenarios::PerformanceScenarioObserverList::GetForScope(
performance_scenarios::ScenarioScope::kGlobal);
if (observer_list) {
observer_list->AddMatchingObserver(this);
return;
}
}
StartCompression();
}
NavigationEntryScreenshot::~NavigationEntryScreenshot() {
if (cache_) {
cache_->OnNavigationEntryGone(unique_id_);
}
if (compression_task_) {
auto observer_list =
performance_scenarios::PerformanceScenarioObserverList::GetForScope(
performance_scenarios::ScenarioScope::kGlobal);
if (observer_list) {
observer_list->RemoveMatchingObserver(this);
}
}
}
cc::UIResourceBitmap NavigationEntryScreenshot::GetBitmap(cc::UIResourceId uid,
bool resource_lost) {
// TODO(liuwilliam): Currently none of the impls of `GetBitmap` uses `uid` or
// `resource_lost`. Consider deleting them from the interface.
return GetBitmap();
}
size_t NavigationEntryScreenshot::SetCache(
NavigationEntryScreenshotCache* cache) {
CHECK(!cache_ || !cache);
cache_ = cache;
if (cache_ && compressed_bitmap_) {
bitmap_.reset();
}
return GetBitmap().SizeInBytes();
}
void NavigationEntryScreenshot::OnScenarioMatchChanged(
performance_scenarios::ScenarioScope scope,
bool matches_pattern) {
if (matches_pattern && compression_task_) {
StartCompression();
performance_scenarios::PerformanceScenarioObserverList::GetForScope(
performance_scenarios::ScenarioScope::kGlobal)
->RemoveMatchingObserver(this);
}
}
SkBitmap NavigationEntryScreenshot::GetBitmapForTesting() const {
return GetBitmap().GetBitmapForTesting(); // IN-TEST
}
size_t NavigationEntryScreenshot::CompressedSizeForTesting() const {
return !bitmap_ ? compressed_bitmap_->SizeInBytes() : 0u;
}
base::OnceClosure NavigationEntryScreenshot::CompressionTask(
const SkBitmap& bitmap,
bool supports_etc_non_power_of_two) {
#if BUILDFLAG(IS_ANDROID)
if (!base::FeatureList::IsEnabled(kNavigationEntryScreenshotCompression) ||
g_disable_compression_for_testing) {
return base::OnceClosure();
}
CompressionDoneCallback done_callback = base::BindPostTask(
GetUIThreadTaskRunner(),
base::BindOnce(&NavigationEntryScreenshot::OnCompressionFinished,
weak_factory_.GetWeakPtr()));
return base::BindOnce(&CompressNavigationScreenshotOnWorkerThread, bitmap,
supports_etc_non_power_of_two,
std::move(done_callback));
#else
return base::OnceClosure();
#endif
}
void NavigationEntryScreenshot::StartCompression() {
base::ThreadPool::PostTask(FROM_HERE,
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
std::move(compression_task_));
}
void NavigationEntryScreenshot::OnCompressionFinished(
sk_sp<SkPixelRef> compressed_bitmap) {
CHECK(!compressed_bitmap_);
CHECK(bitmap_);
CHECK(compressed_bitmap);
const auto size =
gfx::Size(compressed_bitmap->width(), compressed_bitmap->height());
compressed_bitmap_ = cc::UIResourceBitmap(std::move(compressed_bitmap), size);
TRACE_EVENT2("navigation", "NavigationEntryScreenshot::OnCompressionFinished",
"old_size", bitmap_->SizeInBytes(), "new_size",
compressed_bitmap_->SizeInBytes());
// We defer discarding the uncompressed bitmap if there is no cache since it
// may still be in use in the UI.
if (cache_) {
bitmap_.reset();
cache_->OnScreenshotCompressed(unique_id_, GetBitmap().SizeInBytes());
}
}
const cc::UIResourceBitmap& NavigationEntryScreenshot::GetBitmap() const {
return bitmap_ ? *bitmap_ : *compressed_bitmap_;
}
} // namespace content