1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// 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/layers/texture_layer_client.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 "components/viz/common/resources/transferable_resource.h"
#include "content/browser/renderer_host/navigation_transitions/navigation_transition_data.h"
#include "content/common/content_export.h"
class SkBitmap;
namespace cc::slim {
class TextureLayer;
}
namespace gpu {
class ClientSharedImage;
} // namespace gpu
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,
private cc::TextureLayerClient {
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,
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;
// Whether the bitmap is ready or there is a shared image pending read back.
bool IsValid() const;
// 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;
// Creates a texture layer that uses the shared image in this screenshot.
// This can't be called again until the returned closure runs.
std::pair<scoped_refptr<cc::slim::TextureLayer>, base::ScopedClosureRunner>
CreateTextureLayer();
// 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(SkBitmap bitmap, 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;
// cc::TextureLayerClient
// Prepares a transferable resource for the shared image in this screenshot.
// This can only be called after running CreateTextureLayer and before the
// returned closure runs.
bool PrepareTransferableResource(
viz::TransferableResource* transferable_resource,
viz::ReleaseCallback* release_callback) override;
void OnTextureLayerToBeDeleted();
// The uncompressed bitmap cached when navigating away from this navigation
// entry.
std::optional<cc::UIResourceBitmap> 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_;
viz::TransferableResource texture_transferable_resource_;
viz::ReleaseCallback texture_release_callback_;
base::WeakPtrFactory<NavigationEntryScreenshot> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_TRANSITIONS_NAVIGATION_ENTRY_SCREENSHOT_H_
|