blob: 7b94642aa9a0812fb1dad20352954699dfc3e1c7 [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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_H_
#define CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_H_
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/supports_user_data.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/resources/ui_resource_client.h"
#include "components/performance_manager/scenario_api/performance_scenario_observer.h"
#include "components/viz/common/gpu/context_lost_observer.h"
#include "content/browser/renderer_host/navigation_transitions/navigation_transition_data.h"
#include "content/common/content_export.h"
class SkBitmap;
namespace gpu {
class ClientSharedImage;
}
namespace viz {
class RasterContextProvider;
} // namespace viz
namespace content {
class NavigationEntryScreenshotCache;
// Wraps around a `cc::UIResourceBitmap`, which is used to show the user a
// preview of the previous page. This class is stored as user data on
// `NavigationEntry`.
//
// The screenshot is captured for the leaving page when the navigation is about
// to commit (see `CommitDeferringCondition`), subsequently stashed into the
// `NavigationEntry` that this screenshot is captured for. The capture is done
// in the browser process. The pixel data includes sensitive cross-origin data,
// so it must never be leaked to a renderer process.
//
// The screenshot is taken out of the `NavigationEntry` when it will be used for
// an animated transition for a gestured navigation.
// - If the screenshot ends up being used, or deemed invalid (i.e. mismatches
// with the current viewport size) for a preview, the caller is responsible
// for destroying the screenshot.
// - If the screenshot is not used for a preview but still valid (e.g. user
// gesture cancels the animation thus no navigation, or the user initiates a
// gesture to go back to multiple entries), the caller is responsible for
// putting the screenshot back into the `NavigationEntryScreenshotCache`.
//
// If the user clears the navigation history, the screenshot is deleted when
// its owning `NavigationEntry` is destroyed. The screenshot is never recreated
// or cloned even when its `NavigationEntry` is cloned (tab clone) or restored
// (i.e., by restoring the last closed tab), because
// `base::SupportsUserData::Data::Clone()` is not implemented by
// `NavigationEntryScreenshot`.
class CONTENT_EXPORT NavigationEntryScreenshot
: public cc::UIResourceClient,
public base::SupportsUserData::Data,
public performance_scenarios::MatchingScenarioObserver,
public viz::ContextLostObserver {
public:
using ScreenshotCallback = base::RepeatingCallback<
void(const SkBitmap& bitmap, bool requested, SkBitmap& out_override)>;
const static void* const kUserDataKey;
static void SetDisableCompressionForTesting(bool disable);
NavigationEntryScreenshot(const SkBitmap& bitmap,
NavigationTransitionData::UniqueId unique_id,
bool supports_etc_non_power_of_two);
NavigationEntryScreenshot(
scoped_refptr<gpu::ClientSharedImage> shared_image_holder,
NavigationTransitionData::UniqueId unique_id,
bool supports_etc_non_power_of_two,
scoped_refptr<viz::RasterContextProvider> context_provider,
ScreenshotCallback callback);
NavigationEntryScreenshot(const NavigationEntryScreenshot&) = delete;
NavigationEntryScreenshot& operator=(const NavigationEntryScreenshot&) =
delete;
~NavigationEntryScreenshot() override;
// Returns true when a bitmap (compressed or not) is ready for consumption.
// A bitmap isn't ready when a read back is still pending or it failed.
bool IsBitmapReady() const;
// `cc::UIResourceClient`:
cc::UIResourceBitmap GetBitmap(cc::UIResourceId uid,
bool resource_lost) override;
// Sets the `cache` managing the memory for this screenshot. When set, the
// screenshot is stored on its associated NavigationEntry and is guaranteed to
// not be displayed in the UI.
//
// Returns the memory occupied by the bitmap in bytes.
size_t SetCache(NavigationEntryScreenshotCache* cache);
void OnScenarioMatchChanged(performance_scenarios::ScenarioScope scope,
bool matches_pattern) override;
void OnContextLost() override;
// Returns true if the screenshot is being managed by a cache. This is not the
// case when it's being displayed in the UI.
bool is_cached() const { return cache_ != nullptr; }
// Returns the bounds of the uncompressed bitmap.
gfx::Size dimensions_without_compression() const {
return dimensions_without_compression_;
}
NavigationTransitionData::UniqueId unique_id() const { return unique_id_; }
SkBitmap GetBitmapForTesting() const;
size_t CompressedSizeForTesting() const;
private:
void ReadBack();
void OnReadBack(bool success);
void OnCompressionFinished(sk_sp<SkPixelRef> compressed_bitmap);
void SetupCompressionTask(const SkBitmap& bitmap,
bool supports_etc_non_power_of_two);
void StartCompression();
void ResetContextProvider();
const cc::UIResourceBitmap& GetBitmap() const;
// The uncompressed bitmap cached when navigating away from this navigation
// entry.
std::optional<cc::UIResourceBitmap> bitmap_;
std::optional<SkBitmap> read_back_bitmap_;
scoped_refptr<gpu::ClientSharedImage> shared_image_;
// The compressed bitmap generated on a worker thread. `bitmap_` is discarded
// when the compressed bitmap is available and this screenshot is no longer
// being displayed in the UI.
std::optional<cc::UIResourceBitmap> compressed_bitmap_;
// Set if this screenshot is being tracked by the `cache_`. The cache is
// guaranteed to outlive the screenshot, if the screenshot is tracked.
//
// Since `this` is never restored/cloned (unlike its owning `NavigationEntry`,
// per the class-level comments), we will never have a screenshot tracked in a
// cache from a different `NavigationController`.
raw_ptr<NavigationEntryScreenshotCache> cache_ = nullptr;
// This screenshot is cached for the navigation entry, whose
// `navigation_transition_data()` has `unique_id_`.
const NavigationTransitionData::UniqueId unique_id_;
const gfx::Size dimensions_without_compression_;
// Whether a readback is needed and wasn't issued.
bool read_back_needed_ = false;
const bool supports_etc_non_power_of_two_;
scoped_refptr<viz::RasterContextProvider> context_provider_;
base::OnceClosure compression_task_;
ScreenshotCallback screenshot_callback_;
base::WeakPtrFactory<NavigationEntryScreenshot> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_H_