diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-03 13:32:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 14:31:55 +0200 |
commit | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (patch) | |
tree | 91be119f694044dfc1ff9fdc054459e925de9df0 /chromium/components/paint_preview | |
parent | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (diff) | |
download | qtwebengine-chromium-21ba0c5d4bf8fba15dddd97cd693bad2358b77fd.tar.gz |
BASELINE: Update Chromium to 92.0.4515.166
Change-Id: I42a050486714e9e54fc271f2a8939223a02ae364
Diffstat (limited to 'chromium/components/paint_preview')
40 files changed, 635 insertions, 234 deletions
diff --git a/chromium/components/paint_preview/OWNERS b/chromium/components/paint_preview/OWNERS index f14326f08eb..a56f4c78c0c 100644 --- a/chromium/components/paint_preview/OWNERS +++ b/chromium/components/paint_preview/OWNERS @@ -1,4 +1,3 @@ ckitagawa@chromium.org -mahmoudi@chromium.org -vollick@chromium.org +fredmello@chromium.org yfriedman@chromium.org diff --git a/chromium/components/paint_preview/browser/file_manager.cc b/chromium/components/paint_preview/browser/file_manager.cc index 1d84ee21d11..4a84cdafb39 100644 --- a/chromium/components/paint_preview/browser/file_manager.cc +++ b/chromium/components/paint_preview/browser/file_manager.cc @@ -63,16 +63,16 @@ size_t FileManager::GetSizeOfArtifacts(const DirectoryKey& key) const { } } -base::Optional<base::File::Info> FileManager::GetInfo( +absl::optional<base::File::Info> FileManager::GetInfo( const DirectoryKey& key) const { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); base::FilePath path; StorageType storage_type = GetPathForKey(key, &path); if (storage_type == FileManager::StorageType::kNone) - return base::nullopt; + return absl::nullopt; base::File::Info info; if (!base::GetFileInfo(path, &info)) - return base::nullopt; + return absl::nullopt; return info; } @@ -101,7 +101,7 @@ bool FileManager::CaptureExists(const DirectoryKey& key) const { } } -base::Optional<base::FilePath> FileManager::CreateOrGetDirectory( +absl::optional<base::FilePath> FileManager::CreateOrGetDirectory( const DirectoryKey& key, bool clear) const { DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); @@ -119,7 +119,7 @@ base::Optional<base::FilePath> FileManager::CreateOrGetDirectory( } DVLOG(1) << "ERROR: failed to create directory: " << path << " with error code " << error; - return base::nullopt; + return absl::nullopt; } case kDirectory: return path; @@ -129,17 +129,17 @@ base::Optional<base::FilePath> FileManager::CreateOrGetDirectory( if (!base::CreateDirectoryAndGetError(dst_path, &error)) { DVLOG(1) << "ERROR: failed to create directory: " << path << " with error code " << error; - return base::nullopt; + return absl::nullopt; } if (!zip::Unzip(path, dst_path)) { DVLOG(1) << "ERROR: failed to unzip: " << path << " to " << dst_path; - return base::nullopt; + return absl::nullopt; } base::DeletePathRecursively(path); return dst_path; } default: - return base::nullopt; + return absl::nullopt; } } diff --git a/chromium/components/paint_preview/browser/file_manager.h b/chromium/components/paint_preview/browser/file_manager.h index 5fa5c55761e..67a0c6dec2e 100644 --- a/chromium/components/paint_preview/browser/file_manager.h +++ b/chromium/components/paint_preview/browser/file_manager.h @@ -10,7 +10,6 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" -#include "base/time/time.h" #include "components/paint_preview/browser/directory_key.h" #include "components/paint_preview/common/proto/paint_preview.pb.h" #include "url/gurl.h" @@ -52,7 +51,7 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> { // Get statistics about the time of creation and size of artifacts. size_t GetSizeOfArtifacts(const DirectoryKey& key) const; - base::Optional<base::File::Info> GetInfo(const DirectoryKey& key) const; + absl::optional<base::File::Info> GetInfo(const DirectoryKey& key) const; // Returns the total disk usage of all paint previews. size_t GetTotalDiskUsage() const; @@ -68,7 +67,7 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> { // assigns it to |directory|. The directory will be wiped if |clear| is true. // Returns a path on success or nullopt on failure. If the directory was // compressed then it will be uncompressed automatically. - base::Optional<base::FilePath> CreateOrGetDirectory(const DirectoryKey& key, + absl::optional<base::FilePath> CreateOrGetDirectory(const DirectoryKey& key, bool clear) const; // Compresses the directory associated with |key|. Returns true on success or diff --git a/chromium/components/paint_preview/browser/paint_preview_base_service.cc b/chromium/components/paint_preview/browser/paint_preview_base_service.cc index 3181d624f78..9d12df92980 100644 --- a/chromium/components/paint_preview/browser/paint_preview_base_service.cc +++ b/chromium/components/paint_preview/browser/paint_preview_base_service.cc @@ -60,6 +60,10 @@ void PaintPreviewBaseService::CapturePaintPreview(CaptureParams capture_params, (render_frame_host == web_contents->GetMainFrame()); params.inner.capture_links = capture_params.capture_links; params.inner.max_capture_size = capture_params.max_per_capture_size; + params.inner.max_decoded_image_size_bytes = + capture_params.max_decoded_image_size_bytes; + params.inner.skip_accelerated_content = + capture_params.skip_accelerated_content; // TODO(crbug/1064253): Consider moving to client so that this always happens. // Although, it is harder to get this right in the client due to its diff --git a/chromium/components/paint_preview/browser/paint_preview_base_service.h b/chromium/components/paint_preview/browser/paint_preview_base_service.h index 84818fab236..2d742add21d 100644 --- a/chromium/components/paint_preview/browser/paint_preview_base_service.h +++ b/chromium/components/paint_preview/browser/paint_preview_base_service.h @@ -11,7 +11,6 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/optional.h" #include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "base/unguessable_token.h" @@ -24,6 +23,7 @@ #include "components/paint_preview/common/proto/paint_preview.pb.h" #include "components/paint_preview/common/serialized_recording.h" #include "content/public/browser/web_contents.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace paint_preview { @@ -85,6 +85,22 @@ class PaintPreviewBaseService : public KeyedService { // Cap the perframe SkPicture size to |max_per_capture_size| if non-zero. size_t max_per_capture_size; + + // Limit on the maximum size of a decoded image that can be serialized. + // Any images with a decoded size exceeding this value will be discarded. + // This can be used to reduce the chance of an OOM during serialization and + // later during playback. + uint64_t max_decoded_image_size_bytes{std::numeric_limits<uint64_t>::max()}; + + // This flag will skip GPU accelerated content where applicable when + // capturing. This reduces hangs, capture time and may also reduce OOM + // crashes, but results in a lower fideltiy capture (i.e. the contents + // captured may not accurately reflect the content visible to the user at + // time of capture). + // + // At present this flag: + // - Shows a poster or blank space instead of live video frames. + bool skip_accelerated_content{false}; }; using OnCapturedCallback = @@ -132,8 +148,8 @@ class PaintPreviewBaseService : public KeyedService { mojom::PaintPreviewStatus status, std::unique_ptr<CaptureResult> result); - std::unique_ptr<PaintPreviewFileMixin> file_mixin_ = nullptr; - std::unique_ptr<PaintPreviewPolicy> policy_ = nullptr; + std::unique_ptr<PaintPreviewFileMixin> file_mixin_; + std::unique_ptr<PaintPreviewPolicy> policy_; bool is_off_the_record_; base::WeakPtrFactory<PaintPreviewBaseService> weak_ptr_factory_{this}; diff --git a/chromium/components/paint_preview/browser/paint_preview_base_service_unittest.cc b/chromium/components/paint_preview/browser/paint_preview_base_service_unittest.cc index 002447566c7..dcbde0b22ed 100644 --- a/chromium/components/paint_preview/browser/paint_preview_base_service_unittest.cc +++ b/chromium/components/paint_preview/browser/paint_preview_base_service_unittest.cc @@ -64,7 +64,7 @@ base::FilePath CreateDir(scoped_refptr<FileManager> manager, base::BindOnce(&FileManager::CreateOrGetDirectory, manager, key, false), base::BindOnce( [](base::OnceClosure quit, base::FilePath* out, - const base::Optional<base::FilePath>& path) { + const absl::optional<base::FilePath>& path) { EXPECT_TRUE(path.has_value()); EXPECT_FALSE(path->empty()); *out = path.value(); @@ -178,7 +178,8 @@ class PaintPreviewBaseServiceTest RecordingPersistence persistence, gfx::Rect clip_rect, bool capture_links, - size_t max_per_capture_size) { + size_t max_per_capture_size, + uint64_t max_decoded_image_size_bytes) { PaintPreviewBaseService::CaptureParams capture_params; capture_params.web_contents = web_contents; capture_params.root_dir = root_dir; @@ -186,12 +187,13 @@ class PaintPreviewBaseServiceTest capture_params.clip_rect = clip_rect; capture_params.capture_links = capture_links; capture_params.max_per_capture_size = max_per_capture_size; + capture_params.max_decoded_image_size_bytes = max_decoded_image_size_bytes; return capture_params; } private: - std::unique_ptr<SimpleFactoryKey> key_ = nullptr; - std::unique_ptr<SimpleFactoryKey> rejection_policy_key_ = nullptr; + std::unique_ptr<SimpleFactoryKey> key_; + std::unique_ptr<SimpleFactoryKey> rejection_policy_key_; }; TEST_P(PaintPreviewBaseServiceTest, CaptureMainFrame) { @@ -200,9 +202,10 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureMainFrame) { params->clip_rect = gfx::Rect(0, 0, 0, 0); params->is_main_frame = true; params->max_capture_size = 50; + params->max_decoded_image_size_bytes = 1000; recorder.SetExpectedParams(std::move(params)); auto response = mojom::PaintPreviewCaptureResponse::New(); - response->embedding_token = base::nullopt; + response->embedding_token = absl::nullopt; if (GetParam() == RecordingPersistence::kMemoryBuffer) { response->skp.emplace(mojo_base::BigBuffer()); } @@ -218,7 +221,7 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureMainFrame) { base::RunLoop loop; service->CapturePaintPreview( CreateCaptureParams(web_contents(), &path, GetParam(), - gfx::Rect(0, 0, 0, 0), true, 50), + gfx::Rect(0, 0, 0, 0), true, 50, 1000), base::BindOnce( [](base::OnceClosure quit_closure, PaintPreviewBaseService::CaptureStatus expected_status, @@ -273,7 +276,7 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureFailed) { params->max_capture_size = 0; recorder.SetExpectedParams(std::move(params)); auto response = mojom::PaintPreviewCaptureResponse::New(); - response->embedding_token = base::nullopt; + response->embedding_token = absl::nullopt; recorder.SetResponse(mojom::PaintPreviewStatus::kFailed, std::move(response)); OverrideInterface(&recorder); @@ -286,7 +289,8 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureFailed) { base::RunLoop loop; service->CapturePaintPreview( CreateCaptureParams(web_contents(), &path, GetParam(), - gfx::Rect(0, 0, 0, 0), true, 0), + gfx::Rect(0, 0, 0, 0), true, 0, + std::numeric_limits<uint64_t>::max()), base::BindOnce( [](base::OnceClosure quit_closure, PaintPreviewBaseService::CaptureStatus expected_status, @@ -309,7 +313,7 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureDisallowed) { params->max_capture_size = 0; recorder.SetExpectedParams(std::move(params)); auto response = mojom::PaintPreviewCaptureResponse::New(); - response->embedding_token = base::nullopt; + response->embedding_token = absl::nullopt; recorder.SetResponse(mojom::PaintPreviewStatus::kFailed, std::move(response)); OverrideInterface(&recorder); @@ -322,7 +326,8 @@ TEST_P(PaintPreviewBaseServiceTest, CaptureDisallowed) { base::RunLoop loop; service->CapturePaintPreview( CreateCaptureParams(web_contents(), &path, GetParam(), - gfx::Rect(0, 0, 0, 0), true, 0), + gfx::Rect(0, 0, 0, 0), true, 0, + std::numeric_limits<uint64_t>::max()), base::BindOnce( [](base::OnceClosure quit_closure, PaintPreviewBaseService::CaptureStatus expected_status, diff --git a/chromium/components/paint_preview/browser/paint_preview_client.cc b/chromium/components/paint_preview/browser/paint_preview_client.cc index d51d2ae76a3..7232b9be21b 100644 --- a/chromium/components/paint_preview/browser/paint_preview_client.cc +++ b/chromium/components/paint_preview/browser/paint_preview_client.cc @@ -15,6 +15,7 @@ #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" +#include "base/trace_event/trace_event.h" #include "base/unguessable_token.h" #include "components/paint_preview/common/capture_result.h" #include "components/paint_preview/common/mojom/paint_preview_recorder.mojom-forward.h" @@ -117,8 +118,12 @@ mojom::PaintPreviewCaptureParamsPtr CreateRecordingRequestParams( // when clip_rects are used intentionally to limit capture time. mojo_params->clip_rect_is_hint = true; mojo_params->is_main_frame = capture_params.is_main_frame; + mojo_params->skip_accelerated_content = + capture_params.skip_accelerated_content; mojo_params->file = std::move(file); mojo_params->max_capture_size = capture_params.max_capture_size; + mojo_params->max_decoded_image_size_bytes = + capture_params.max_decoded_image_size_bytes; return mojo_params; } @@ -313,6 +318,10 @@ void PaintPreviewClient::CapturePaintPreview( document_data.accepted_tokens = CreateAcceptedTokenList(render_frame_host); document_data.capture_links = params.inner.capture_links; document_data.max_per_capture_size = params.inner.max_capture_size; + document_data.max_decoded_image_size_bytes = + params.inner.max_decoded_image_size_bytes; + document_data.skip_accelerated_content = + params.inner.skip_accelerated_content; all_document_data_.insert( {params.inner.document_guid, std::move(document_data)}); TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( @@ -337,6 +346,8 @@ void PaintPreviewClient::CaptureSubframePaintPreview( params.is_main_frame = false; params.capture_links = it->second.capture_links; params.max_capture_size = it->second.max_per_capture_size; + params.max_decoded_image_size_bytes = it->second.max_decoded_image_size_bytes; + params.skip_accelerated_content = it->second.skip_accelerated_content; CapturePaintPreviewInternal(params, render_subframe_host); } diff --git a/chromium/components/paint_preview/browser/paint_preview_client.h b/chromium/components/paint_preview/browser/paint_preview_client.h index 1b3537afcc5..7d7e2559ccf 100644 --- a/chromium/components/paint_preview/browser/paint_preview_client.h +++ b/chromium/components/paint_preview/browser/paint_preview_client.h @@ -103,10 +103,11 @@ class PaintPreviewClient // This corresponds to RenderFrameHost::EmbeddingToken. base::UnguessableToken root_frame_token; - // Got from the first recording params. Whether to capture links and the + // From the first recording params. Whether to capture links and the // size limit per capture respectively. bool capture_links = true; size_t max_per_capture_size = 0; + uint64_t max_decoded_image_size_bytes{std::numeric_limits<uint64_t>::max()}; // UKM Source ID of the WebContent. ukm::SourceId source_id; @@ -143,6 +144,14 @@ class PaintPreviewClient // destruction bool should_clean_up_files = false; + // This flag will skip GPU accelerated content where applicable when + // capturing. This reduces hangs, capture time and may also reduce OOM + // crashes, but results in a lower fideltiy capture (i.e. the contents + // captured may not accurately reflect the content visible to the user at + // time of capture). See PaintPreviewBaseService::CaptureParams for a + // description of the effects of this flag. + bool skip_accelerated_content = false; + // Generates a file path based off |root_dir| and |frame_guid|. Will be in // the form "{hexadecimal}.skp". base::FilePath FilePathForFrame(const base::UnguessableToken& frame_guid); diff --git a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc index 8718a99d4e9..47c17055719 100644 --- a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc +++ b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc @@ -104,6 +104,7 @@ mojom::PaintPreviewCaptureParamsPtr ToMojoParams( params_ptr->guid = params.inner.document_guid; params_ptr->is_main_frame = params.inner.is_main_frame; params_ptr->clip_rect = params.inner.clip_rect; + params_ptr->skip_accelerated_content = params.inner.skip_accelerated_content; return params_ptr; } @@ -161,7 +162,7 @@ TEST_P(PaintPreviewClientRenderViewHostTest, CaptureMainFrameMock) { GURL expected_url = rfh->GetLastCommittedURL(); auto response = NewMockPaintPreviewCaptureResponse(); - response->embedding_token = base::nullopt; + response->embedding_token = absl::nullopt; response->scroll_offsets = gfx::Size(5, 10); PaintPreviewProto expected_proto; @@ -290,11 +291,12 @@ TEST_P(PaintPreviewClientRenderViewHostTest, RenderFrameDeletedDuringCapture) { PaintPreviewClient::PaintPreviewParams params(GetParam()); params.root_dir = temp_dir_.GetPath(); params.inner.is_main_frame = true; + params.inner.skip_accelerated_content = true; content::RenderFrameHost* rfh = main_rfh(); auto response = NewMockPaintPreviewCaptureResponse(); - response->embedding_token = base::nullopt; + response->embedding_token = absl::nullopt; base::RunLoop loop; auto callback = base::BindOnce( diff --git a/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.cc b/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.cc index a4a008015ff..271386c74a2 100644 --- a/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.cc +++ b/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.cc @@ -25,7 +25,7 @@ PaintPreviewCompositorClientImpl::~PaintPreviewCompositorClientImpl() { NotifyServiceOfInvalidation(); } -const base::Optional<base::UnguessableToken>& +const absl::optional<base::UnguessableToken>& PaintPreviewCompositorClientImpl::Token() const { DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); return token_; diff --git a/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.h b/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.h index b8ec2b510db..4cf62e155df 100644 --- a/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.h +++ b/chromium/components/paint_preview/browser/paint_preview_compositor_client_impl.h @@ -6,12 +6,12 @@ #define COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_COMPOSITOR_CLIENT_IMPL_H_ #include "base/callback_forward.h" -#include "base/optional.h" #include "base/unguessable_token.h" #include "components/paint_preview/browser/paint_preview_compositor_service_impl.h" #include "components/paint_preview/public/paint_preview_compositor_client.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" @@ -36,7 +36,7 @@ class PaintPreviewCompositorClientImpl : public PaintPreviewCompositorClient { ~PaintPreviewCompositorClientImpl() override; // PaintPreviewCompositorClient implementation. - const base::Optional<base::UnguessableToken>& Token() const override; + const absl::optional<base::UnguessableToken>& Token() const override; void SetDisconnectHandler(base::OnceClosure closure) override; void BeginSeparatedFrameComposite( mojom::PaintPreviewBeginCompositeRequestPtr request, @@ -89,7 +89,7 @@ class PaintPreviewCompositorClientImpl : public PaintPreviewCompositorClient { base::WeakPtr<PaintPreviewCompositorServiceImpl> service_; CompositorPtr compositor_; - base::Optional<base::UnguessableToken> token_; + absl::optional<base::UnguessableToken> token_; base::OnceClosure user_disconnect_closure_; base::WeakPtrFactory<PaintPreviewCompositorClientImpl> weak_ptr_factory_{ diff --git a/chromium/components/paint_preview/browser/paint_preview_file_mixin.cc b/chromium/components/paint_preview/browser/paint_preview_file_mixin.cc index 3ad349dc92f..54ad65f5bd9 100644 --- a/chromium/components/paint_preview/browser/paint_preview_file_mixin.cc +++ b/chromium/components/paint_preview/browser/paint_preview_file_mixin.cc @@ -35,13 +35,13 @@ PaintPreviewFileMixin::~PaintPreviewFileMixin() = default; void PaintPreviewFileMixin::GetCapturedPaintPreviewProto( const DirectoryKey& key, - base::Optional<base::TimeDelta> expiry_horizon, + absl::optional<base::TimeDelta> expiry_horizon, OnReadProtoCallback on_read_proto_callback) { task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce( [](scoped_refptr<FileManager> file_manager, const DirectoryKey& key, - base::Optional<base::TimeDelta> expiry_horizon) + absl::optional<base::TimeDelta> expiry_horizon) -> std::pair<PaintPreviewFileMixin::ProtoReadStatus, std::unique_ptr<PaintPreviewProto>> { if (expiry_horizon.has_value()) { diff --git a/chromium/components/paint_preview/browser/paint_preview_file_mixin.h b/chromium/components/paint_preview/browser/paint_preview_file_mixin.h index 0af1317ccd7..5a5b6fd14e0 100644 --- a/chromium/components/paint_preview/browser/paint_preview_file_mixin.h +++ b/chromium/components/paint_preview/browser/paint_preview_file_mixin.h @@ -57,7 +57,7 @@ class PaintPreviewFileMixin { // will return the kExpired status. virtual void GetCapturedPaintPreviewProto( const DirectoryKey& key, - base::Optional<base::TimeDelta> expiry_horizon, + absl::optional<base::TimeDelta> expiry_horizon, OnReadProtoCallback on_read_proto_callback); // Writes an Accessibility Tree snapshot to the directory listed in key. @@ -76,4 +76,4 @@ class PaintPreviewFileMixin { } // namespace paint_preview -#endif // COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_FILE_HELPER_H_ +#endif // COMPONENTS_PAINT_PREVIEW_BROWSER_PAINT_PREVIEW_FILE_MIXIN_H_ diff --git a/chromium/components/paint_preview/browser/test_paint_preview_policy.cc b/chromium/components/paint_preview/browser/test_paint_preview_policy.cc index 986582ff6a7..8f29dab029c 100644 --- a/chromium/components/paint_preview/browser/test_paint_preview_policy.cc +++ b/chromium/components/paint_preview/browser/test_paint_preview_policy.cc @@ -5,9 +5,9 @@ #include "components/paint_preview/browser/test_paint_preview_policy.h" #include "base/callback.h" -#include "base/optional.h" #include "components/paint_preview/browser/paint_preview_policy.h" #include "content/public/browser/web_contents.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace paint_preview { diff --git a/chromium/components/paint_preview/common/capture_result.h b/chromium/components/paint_preview/common/capture_result.h index 8cebc54aa3d..0ccdaa014fd 100644 --- a/chromium/components/paint_preview/common/capture_result.h +++ b/chromium/components/paint_preview/common/capture_result.h @@ -41,6 +41,20 @@ struct RecordingParams { // async ordering of captures from different frames making it hard to keep // track of available headroom at the time of each capture triggering. size_t max_capture_size; + + // Limit on the maximum size of a decoded image that can be serialized. + // Any images with a decoded size exceeding this value will be discarded. + // This can be used to reduce the chance of an OOM during serialization and + // later during playback. + uint64_t max_decoded_image_size_bytes{std::numeric_limits<uint64_t>::max()}; + + // This flag will skip GPU accelerated content where applicable when + // capturing. This reduces hangs, capture time and may also reduce OOM + // crashes, but results in a lower fideltiy capture (i.e. the contents + // captured may not accurately reflect the content visible to the user at + // time of capture). See PaintPreviewBaseService::CaptureParams for a + // description of the effects of this flag. + bool skip_accelerated_content{false}; }; // The result of a capture of a WebContents, which may contain recordings of diff --git a/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom b/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom index 5779f87ecc3..e7f214e41b7 100644 --- a/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom +++ b/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom @@ -70,6 +70,18 @@ struct PaintPreviewCaptureParams { // The maximum allowed size of a capture that can be produced. A value of // 0 means the size is unrestricted. uint64 max_capture_size; + + // Limit on the maximum size of a decoded image that can be serialized. + // Any images with a decoded size exceeding this value will be discarded. + uint64 max_decoded_image_size_bytes; + + // This flag will skip GPU accelerated content where applicable when + // capturing. This reduces hangs, capture time and may also reduce OOM + // crashes, but results in a lower fideltiy capture (i.e. the contents + // captured may not accurately reflect the content visible to the user at + // time of capture). See PaintPreviewBaseService::CaptureParams for a + // description of the effects of this flag. + bool skip_accelerated_content; }; struct LinkData { diff --git a/chromium/components/paint_preview/common/paint_preview_tracker.cc b/chromium/components/paint_preview/common/paint_preview_tracker.cc index 4d87b57912c..280a5a24507 100644 --- a/chromium/components/paint_preview/common/paint_preview_tracker.cc +++ b/chromium/components/paint_preview/common/paint_preview_tracker.cc @@ -40,7 +40,7 @@ bool ShouldUseDenseGlyphUsage(SkTypeface* typeface) { PaintPreviewTracker::PaintPreviewTracker( const base::UnguessableToken& guid, - const base::Optional<base::UnguessableToken>& embedding_token, + const absl::optional<base::UnguessableToken>& embedding_token, bool is_main_frame) : guid_(guid), embedding_token_(embedding_token), diff --git a/chromium/components/paint_preview/common/paint_preview_tracker.h b/chromium/components/paint_preview/common/paint_preview_tracker.h index 2d3f5fcce5f..8bbe21454e7 100644 --- a/chromium/components/paint_preview/common/paint_preview_tracker.h +++ b/chromium/components/paint_preview/common/paint_preview_tracker.h @@ -29,14 +29,14 @@ class PaintPreviewTracker { public: PaintPreviewTracker( const base::UnguessableToken& guid, - const base::Optional<base::UnguessableToken>& embedding_token, + const absl::optional<base::UnguessableToken>& embedding_token, bool is_main_frame); ~PaintPreviewTracker(); // Getters ------------------------------------------------------------------ const base::UnguessableToken& Guid() const { return guid_; } - const base::Optional<base::UnguessableToken>& EmbeddingToken() const { + const absl::optional<base::UnguessableToken>& EmbeddingToken() const { return embedding_token_; } bool IsMainFrame() const { return is_main_frame_; } @@ -109,7 +109,7 @@ class PaintPreviewTracker { private: const base::UnguessableToken guid_; - const base::Optional<base::UnguessableToken> embedding_token_; + const absl::optional<base::UnguessableToken> embedding_token_; const bool is_main_frame_; // TODO(crbug.com/1155544): Change this to an SkM44. diff --git a/chromium/components/paint_preview/common/recording_map.h b/chromium/components/paint_preview/common/recording_map.h index 9deeff43b20..597de94aa60 100644 --- a/chromium/components/paint_preview/common/recording_map.h +++ b/chromium/components/paint_preview/common/recording_map.h @@ -9,11 +9,11 @@ #include "base/containers/flat_map.h" #include "base/files/file.h" -#include "base/optional.h" #include "components/paint_preview/common/capture_result.h" #include "components/paint_preview/common/proto/paint_preview.pb.h" #include "components/paint_preview/common/serialized_recording.h" #include "mojo/public/cpp/base/big_buffer.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRefCnt.h" diff --git a/chromium/components/paint_preview/common/serial_utils.cc b/chromium/components/paint_preview/common/serial_utils.cc index bee4ffe8e72..901419004da 100644 --- a/chromium/components/paint_preview/common/serial_utils.cc +++ b/chromium/components/paint_preview/common/serial_utils.cc @@ -76,40 +76,47 @@ sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) { sk_sp<SkData> SerializeImage(SkImage* image, void* ctx) { ImageSerializationContext* context = reinterpret_cast<ImageSerializationContext*>(ctx); + // Ignore texture backed content if any slipped through. This shouldn't occur + // now that ToSkPicture has a dedicated ImageProvider that forces software + // SkImage inputs, but this is a safeguard. if (context->skip_texture_backed && image->isTextureBacked()) { return SkData::MakeEmpty(); } + // If the decoded form of the image would result in it exceeding the allowable + // size then effectively delete it by providing no data. const SkImageInfo& image_info = image->imageInfo(); - // If decoding/encoding the image would result in it exceeding the allowable - // size, effectively delete it by providing no data. - if (context->max_representation_size != 0 && - image_info.computeMinByteSize() > context->max_representation_size) { + if (context->max_decoded_image_size_bytes != + std::numeric_limits<uint64_t>::max() && + image_info.computeMinByteSize() > context->max_decoded_image_size_bytes) { return SkData::MakeEmpty(); } // If there already exists encoded data use it directly. sk_sp<SkData> encoded_data = image->refEncodedData(); if (!encoded_data) { + // Use the default PNG at quality 100 as it is safe. + // TODO(crbug/1198304): Investigate supporting JPEG at quality 100 for + // opaque images. encoded_data = image->encodeToData(); } - // If encoding failed then no-op. if (!encoded_data) return SkData::MakeEmpty(); - // Ensure the encoded data fits in the restrictions if they are present. - if ((context->remaining_image_size == std::numeric_limits<uint64_t>::max() || - context->remaining_image_size >= encoded_data->size()) && - (context->max_representation_size == 0 || - encoded_data->size() < context->max_representation_size)) { - if (context->remaining_image_size != std::numeric_limits<uint64_t>::max()) - context->remaining_image_size -= encoded_data->size(); - - return encoded_data; + // Ensure the encoded data fits in the size restriction if present. + // OOM Prevention: This avoids creating/keeping large serialized images + // in-memory during serialization if the size budget is already exceeded due + // to images. + if (context->remaining_image_size != std::numeric_limits<uint64_t>::max()) { + if (context->remaining_image_size < encoded_data->size()) { + context->memory_budget_exceeded = true; + return SkData::MakeEmpty(); + } + context->remaining_image_size -= encoded_data->size(); } - return SkData::MakeEmpty(); + return encoded_data; } // Deserializes a clip rect for a subframe within the main SkPicture. These @@ -207,10 +214,15 @@ SkSerialProcs MakeSerialProcs(PictureSerializationContext* picture_ctx, // // At present this uses the native representation, but skips serializing if // loading to a bitmap for encoding might cause an OOM. - if (image_ctx->max_representation_size > 0 || - image_ctx->remaining_image_size != std::numeric_limits<uint64_t>::max()) { - procs.fImageProc = SerializeImage; - procs.fImageCtx = image_ctx; + if (image_ctx) { + image_ctx->memory_budget_exceeded = false; + if (image_ctx->max_decoded_image_size_bytes != + std::numeric_limits<uint64_t>::max() || + image_ctx->remaining_image_size != + std::numeric_limits<uint64_t>::max()) { + procs.fImageProc = SerializeImage; + procs.fImageCtx = image_ctx; + } } return procs; } diff --git a/chromium/components/paint_preview/common/serial_utils.h b/chromium/components/paint_preview/common/serial_utils.h index 01608911e19..645d86f330b 100644 --- a/chromium/components/paint_preview/common/serial_utils.h +++ b/chromium/components/paint_preview/common/serial_utils.h @@ -57,15 +57,17 @@ struct ImageSerializationContext { // max value of uint64_t. uint64_t remaining_image_size{std::numeric_limits<uint64_t>::max()}; - // The maximum size of the representation for serialization. Images - // that are larger than this when encoded or will need to be inflated to a - // bitmap larger than this are skipped to avoid OOMs. If this value is 0 image - // procs are skipped and the default behavior is used. - uint64_t max_representation_size{0}; + // The maximum size of a decoded image allowed for serialization. Images that + // are larger than this when decoded are skipped. + uint64_t max_decoded_image_size_bytes{std::numeric_limits<uint64_t>::max()}; // Skip texture backed images. Must be true if serialized off the main // thread. bool skip_texture_backed{false}; + + // Will be set to true post serialization if the `remaining_image_size` budget + // was exceeded. + bool memory_budget_exceeded{false}; }; // Maps a content ID to a clip rect. diff --git a/chromium/components/paint_preview/common/serial_utils_unittest.cc b/chromium/components/paint_preview/common/serial_utils_unittest.cc index 35410e9f253..65284a85711 100644 --- a/chromium/components/paint_preview/common/serial_utils_unittest.cc +++ b/chromium/components/paint_preview/common/serial_utils_unittest.cc @@ -165,6 +165,7 @@ TEST(PaintPreviewSerialUtils, TestImageContextLimitBudget) { sk_sp<SkData> data = pic->serialize(&serial_procs); EXPECT_NE(data, nullptr); + EXPECT_TRUE(ictx.memory_budget_exceeded); SkDeserialProcs deserial_procs; size_t deserialized_images = 0; deserial_procs.fImageCtx = &deserialized_images; @@ -202,7 +203,7 @@ TEST(PaintPreviewSerialUtils, TestImageContextLimitSize) { TypefaceUsageMap usage_map; TypefaceSerializationContext typeface_ctx(&usage_map); ImageSerializationContext ictx; - ictx.max_representation_size = 200; + ictx.max_decoded_image_size_bytes = 200; SkSerialProcs serial_procs = MakeSerialProcs(&picture_ctx, &typeface_ctx, &ictx); @@ -212,6 +213,7 @@ TEST(PaintPreviewSerialUtils, TestImageContextLimitSize) { sk_sp<SkData> data = pic->serialize(&serial_procs); EXPECT_NE(data, nullptr); + EXPECT_FALSE(ictx.memory_budget_exceeded); SkDeserialProcs deserial_procs; size_t deserialized_images = 0; deserial_procs.fImageCtx = &deserialized_images; diff --git a/chromium/components/paint_preview/common/serialized_recording.cc b/chromium/components/paint_preview/common/serialized_recording.cc index b5db42264c7..c0a7e62d84a 100644 --- a/chromium/components/paint_preview/common/serialized_recording.cc +++ b/chromium/components/paint_preview/common/serialized_recording.cc @@ -5,13 +5,13 @@ #include "components/paint_preview/common/serialized_recording.h" #include "base/notreached.h" -#include "base/optional.h" #include "base/trace_event/common/trace_event_common.h" #include "base/trace_event/trace_event.h" #include "components/paint_preview/common/file_stream.h" #include "components/paint_preview/common/paint_preview_tracker.h" #include "components/paint_preview/common/serial_utils.h" #include "mojo/public/cpp/base/big_buffer.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkStream.h" namespace paint_preview { @@ -24,13 +24,20 @@ bool SerializeSkPicture(sk_sp<const SkPicture> skp, PaintPreviewTracker* tracker, SkWStream* out_stream) { TypefaceSerializationContext typeface_context(tracker->GetTypefaceUsageMap()); + ImageSerializationContext* image_context = + tracker->GetImageSerializationContext(); auto serial_procs = MakeSerialProcs(tracker->GetPictureSerializationContext(), - &typeface_context, - tracker->GetImageSerializationContext()); + &typeface_context, image_context); skp->serialize(out_stream, &serial_procs); out_stream->flush(); - return true; + + // If the memory budget was exceeded while serializing images and it is not + // tolerated (inferred from setting a max decoded image size) then abort. + const bool tolerates_discarding = + image_context->max_decoded_image_size_bytes != + std::numeric_limits<uint64_t>::max(); + return tolerates_discarding || !image_context->memory_budget_exceeded; } } // namespace @@ -76,7 +83,7 @@ bool SerializedRecording::IsValid() const { } } -base::Optional<SkpResult> SerializedRecording::Deserialize() && { +absl::optional<SkpResult> SerializedRecording::Deserialize() && { TRACE_EVENT0("paint_preview", "SerializedRecording::Deserialize"); SkpResult result; SkDeserialProcs procs = MakeDeserialProcs(&result.ctx); @@ -120,7 +127,7 @@ sk_sp<SkPicture> SerializedRecording::DeserializeWithContext( bool RecordToFile(base::File file, sk_sp<const SkPicture> skp, PaintPreviewTracker* tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, size_t* serialized_size) { if (!file.IsValid()) return false; @@ -137,25 +144,25 @@ bool RecordToFile(base::File file, return !file_stream.DidWriteFail(); } -base::Optional<mojo_base::BigBuffer> RecordToBuffer( +absl::optional<mojo_base::BigBuffer> RecordToBuffer( sk_sp<const SkPicture> skp, PaintPreviewTracker* tracker, - base::Optional<size_t> maybe_max_capture_size, + absl::optional<size_t> maybe_max_capture_size, size_t* serialized_size) { SkDynamicMemoryWStream memory_stream; if (!SerializeSkPicture(skp, tracker, &memory_stream)) - return base::nullopt; + return absl::nullopt; size_t max_capture_size = maybe_max_capture_size.value_or(SIZE_MAX); if (max_capture_size == 0) - return base::nullopt; + return absl::nullopt; sk_sp<SkData> data = memory_stream.detachAsData(); *serialized_size = std::min(data->size(), max_capture_size); mojo_base::BigBuffer buffer( base::span<const uint8_t>(data->bytes(), *serialized_size)); if (data->size() > max_capture_size) - return base::nullopt; + return absl::nullopt; return {std::move(buffer)}; } diff --git a/chromium/components/paint_preview/common/serialized_recording.h b/chromium/components/paint_preview/common/serialized_recording.h index 337d3b19180..925994fcd0f 100644 --- a/chromium/components/paint_preview/common/serialized_recording.h +++ b/chromium/components/paint_preview/common/serialized_recording.h @@ -5,18 +5,15 @@ #ifndef COMPONENTS_PAINT_PREVIEW_COMMON_SERIALIZED_RECORDING_H_ #define COMPONENTS_PAINT_PREVIEW_COMMON_SERIALIZED_RECORDING_H_ -#include <memory> -#include <utility> - #include "base/containers/flat_map.h" #include "base/files/file.h" #include "base/gtest_prod_util.h" -#include "base/optional.h" #include "base/unguessable_token.h" #include "components/paint_preview/common/mojom/paint_preview_types.mojom-shared.h" #include "components/paint_preview/common/serial_utils.h" #include "mojo/public/cpp/base/big_buffer.h" #include "mojo/public/cpp/bindings/union_traits.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkRefCnt.h" class SkPicture; @@ -98,12 +95,22 @@ class SerializedRecording { RecordingMapFromPaintPreviewProtoSingleFrame); FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, Roundtrip); + FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithImage); + FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithLazyImage); + FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithPaintWorklet); + FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithTexture); + FRIEND_TEST_ALL_PREFIXES(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithLazyTexture); // Deserialize into an |SkPicture|. The result will not include any embedded // subframes. // // This is not safe to call in the browser process. - base::Optional<SkpResult> Deserialize() &&; + absl::optional<SkpResult> Deserialize() &&; // Deserialize into an |SkPicture|. |ctx| should contain entries for any // subframes that should be included in the output. @@ -126,7 +133,7 @@ class SerializedRecording { RecordingPersistence persistence_; base::File file_; - base::Optional<mojo_base::BigBuffer> buffer_; + absl::optional<mojo_base::BigBuffer> buffer_; }; // Serialize and write |skp| to |file|. @@ -140,7 +147,7 @@ class SerializedRecording { bool RecordToFile(base::File file, sk_sp<const SkPicture> skp, PaintPreviewTracker* tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, size_t* serialized_size); // Serialize and write |recording| to a memory buffer. @@ -151,10 +158,10 @@ bool RecordToFile(base::File file, // serialized output. // // Returns the memory buffer on success. -base::Optional<mojo_base::BigBuffer> RecordToBuffer( +absl::optional<mojo_base::BigBuffer> RecordToBuffer( sk_sp<const SkPicture> skp, PaintPreviewTracker* tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, size_t* serialized_size); } // namespace paint_preview diff --git a/chromium/components/paint_preview/common/serialized_recording_unittest.cc b/chromium/components/paint_preview/common/serialized_recording_unittest.cc index 0cce745ac9a..1ba72b714c1 100644 --- a/chromium/components/paint_preview/common/serialized_recording_unittest.cc +++ b/chromium/components/paint_preview/common/serialized_recording_unittest.cc @@ -6,7 +6,6 @@ #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" -#include "base/optional.h" #include "base/threading/thread_restrictions.h" #include "base/unguessable_token.h" #include "components/paint_preview/common/capture_result.h" @@ -15,6 +14,7 @@ #include "components/paint_preview/common/recording_map.h" #include "components/paint_preview/common/serial_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPaint.h" @@ -70,6 +70,26 @@ sk_sp<const SkPicture> PaintPictureSingleGrayPixel() { &expected_deserialization_context, {}); } +sk_sp<const SkPicture> PaintPictureLargeImage(gfx::Size bounds) { + SkBitmap bitmap; + { + bitmap.allocPixels( + SkImageInfo::MakeN32Premul(bounds.width(), bounds.height())); + SkCanvas canvas(bitmap, SkSurfaceProps{}); + canvas.drawColor(SK_ColorDKGRAY); + } + + SkRect sk_bounds = SkRect::MakeWH(bounds.width(), bounds.height()); + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(sk_bounds); + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(SK_ColorRED); + canvas->drawRect(sk_bounds, paint); + canvas->drawImage(SkImage::MakeFromBitmap(bitmap), 0, 0); + return recorder.finishRecordingAsPicture(); +} + SkBitmap CreateBitmapFromPicture(const SkPicture* pic) { SkRect cull_rect = pic->cullRect(); SkBitmap bitmap; @@ -113,18 +133,18 @@ TEST(PaintPreviewSerializedRecordingTest, RoundtripWithFileBacking) { sk_sp<const SkPicture> pic = PaintPictureSingleGrayPixel(); base::FilePath path = temp_dir.GetPath().AppendASCII("root.skp"); - PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::nullopt, + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, /*is_main_frame=*/true); size_t serialized_size = 0; ASSERT_TRUE(RecordToFile( base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE), - pic, &tracker, base::nullopt, &serialized_size)); + pic, &tracker, absl::nullopt, &serialized_size)); ASSERT_GE(serialized_size, 0u); SerializedRecording recording(path); ASSERT_TRUE(recording.IsValid()); - base::Optional<SkpResult> result = std::move(recording).Deserialize(); + absl::optional<SkpResult> result = std::move(recording).Deserialize(); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->ctx.empty()); ExpectPicturesEqual(result->skp, pic); @@ -133,11 +153,11 @@ TEST(PaintPreviewSerializedRecordingTest, RoundtripWithFileBacking) { TEST(PaintPreviewSerializedRecordingTest, RoundtripWithMemoryBufferBacking) { sk_sp<const SkPicture> pic = PaintPictureSingleGrayPixel(); - PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::nullopt, + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, /*is_main_frame=*/true); size_t serialized_size = 0; - base::Optional<mojo_base::BigBuffer> buffer = - RecordToBuffer(pic, &tracker, base::nullopt, &serialized_size); + absl::optional<mojo_base::BigBuffer> buffer = + RecordToBuffer(pic, &tracker, absl::nullopt, &serialized_size); ASSERT_GE(serialized_size, 0u); ASSERT_TRUE(buffer.has_value()); @@ -145,12 +165,47 @@ TEST(PaintPreviewSerializedRecordingTest, RoundtripWithMemoryBufferBacking) { SerializedRecording(std::move(buffer.value())); ASSERT_TRUE(recording.IsValid()); - base::Optional<SkpResult> result = std::move(recording).Deserialize(); + absl::optional<SkpResult> result = std::move(recording).Deserialize(); ASSERT_TRUE(result.has_value()); ASSERT_TRUE(result->ctx.empty()); ExpectPicturesEqual(result->skp, pic); } +TEST(PaintPreviewSerializedRecordingTest, ImageDiscardingTolerated) { + sk_sp<const SkPicture> pic = PaintPictureLargeImage(gfx::Size(200, 200)); + + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, + /*is_main_frame=*/true); + auto* image_context = tracker.GetImageSerializationContext(); + image_context->remaining_image_size = 200; + image_context->max_decoded_image_size_bytes = 300 * 300 * 4; + size_t serialized_size = 0; + absl::optional<mojo_base::BigBuffer> buffer = + RecordToBuffer(pic, &tracker, absl::nullopt, &serialized_size); + ASSERT_GE(serialized_size, 0u); + ASSERT_TRUE(buffer.has_value()); + ASSERT_TRUE(image_context->memory_budget_exceeded); + + SerializedRecording recording = + SerializedRecording(std::move(buffer.value())); + ASSERT_TRUE(recording.IsValid()); +} + +TEST(PaintPreviewSerializedRecordingTest, ImageDiscardingNotTolerated) { + sk_sp<const SkPicture> pic = PaintPictureLargeImage(gfx::Size(200, 200)); + + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, + /*is_main_frame=*/true); + auto* image_context = tracker.GetImageSerializationContext(); + image_context->remaining_image_size = 200; + size_t serialized_size = 0; + absl::optional<mojo_base::BigBuffer> buffer = + RecordToBuffer(pic, &tracker, absl::nullopt, &serialized_size); + ASSERT_FALSE(buffer.has_value()); + ASSERT_EQ(serialized_size, 0U); + ASSERT_TRUE(image_context->memory_budget_exceeded); +} + TEST(PaintPreviewSerializedRecordingTest, InvalidBacking) { SerializedRecording recording; ASSERT_FALSE(recording.IsValid()); @@ -162,7 +217,7 @@ TEST(PaintPreviewSerializedRecordingTest, RoundtripHasEmbeddedContent) { ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); base::FilePath path = temp_dir.GetPath().AppendASCII("root.skp"); - PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::nullopt, + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, /*is_main_frame=*/true); base::UnguessableToken subframe0 = base::UnguessableToken::Create(); @@ -178,13 +233,13 @@ TEST(PaintPreviewSerializedRecordingTest, RoundtripHasEmbeddedContent) { size_t serialized_size = 0; ASSERT_TRUE(RecordToFile( base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE), - pic, &tracker, base::nullopt, &serialized_size)); + pic, &tracker, absl::nullopt, &serialized_size)); ASSERT_GE(serialized_size, 0u); SerializedRecording recording(path); ASSERT_TRUE(recording.IsValid()); - base::Optional<SkpResult> result = std::move(recording).Deserialize(); + absl::optional<SkpResult> result = std::move(recording).Deserialize(); ASSERT_TRUE(result.has_value()); EXPECT_FALSE(result->ctx.empty()); @@ -199,11 +254,11 @@ TEST(PaintPreviewSerializedRecordingTest, const base::UnguessableToken root_frame_guid = base::UnguessableToken::Create(); - PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::nullopt, + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, /*is_main_frame=*/true); size_t serialized_size = 0; - base::Optional<mojo_base::BigBuffer> buffer = - RecordToBuffer(pic, &tracker, base::nullopt, &serialized_size); + absl::optional<mojo_base::BigBuffer> buffer = + RecordToBuffer(pic, &tracker, absl::nullopt, &serialized_size); ASSERT_GE(serialized_size, 0u); ASSERT_TRUE(buffer.has_value()); @@ -218,7 +273,7 @@ TEST(PaintPreviewSerializedRecordingTest, RecordingMap recording_map = std::move(pair.first); EXPECT_FALSE(recording_map.empty()); ASSERT_NE(recording_map.find(root_frame_guid), recording_map.end()); - base::Optional<SkpResult> result = + absl::optional<SkpResult> result = std::move(recording_map.at(root_frame_guid)).Deserialize(); ASSERT_TRUE(result.has_value()); @@ -237,13 +292,13 @@ TEST(PaintPreviewSerializedRecordingTest, const base::UnguessableToken root_frame_guid = base::UnguessableToken::Create(); - PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::nullopt, + PaintPreviewTracker tracker(base::UnguessableToken::Create(), absl::nullopt, /*is_main_frame=*/true); size_t serialized_size = 0; ASSERT_TRUE(RecordToFile( base::File(root_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE), - pic, &tracker, base::nullopt, &serialized_size)); + pic, &tracker, absl::nullopt, &serialized_size)); ASSERT_GE(serialized_size, 0u); PaintPreviewProto proto; @@ -257,7 +312,7 @@ TEST(PaintPreviewSerializedRecordingTest, RecordingMap recording_map = RecordingMapFromPaintPreviewProto(proto); EXPECT_FALSE(recording_map.empty()); ASSERT_NE(recording_map.find(root_frame_guid), recording_map.end()); - base::Optional<SkpResult> result = + absl::optional<SkpResult> result = std::move(recording_map.at(root_frame_guid)).Deserialize(); ASSERT_TRUE(result.has_value()); diff --git a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java index 3d372e64a2f..8b753b2e297 100644 --- a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java +++ b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java @@ -217,6 +217,9 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase { public boolean isAccessibilityEnabled() { return false; } + + @Override + public void onAccessibilityNotSupported() {} }, 0xffffffff, false); mPlayerManager.setCompressOnClose(false); }); @@ -427,6 +430,9 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase { public boolean isAccessibilityEnabled() { return false; } + + @Override + public void onAccessibilityNotSupported() {} }, 0xffffffff, false); mPlayerManager.setCompressOnClose(false); getActivity().setContentView(mPlayerManager.getView()); diff --git a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java index b7980196d3c..702e336e19b 100644 --- a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java +++ b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java @@ -30,11 +30,11 @@ public class PlayerFrameCoordinatorTest { PlayerFrameCoordinator rootCoordinator = new PlayerFrameCoordinator( RuntimeEnvironment.systemContext, Mockito.mock(PlayerCompositorDelegate.class), Mockito.mock(UnguessableToken.class), 100, 2000, 0, 0, true, null, - Mockito.mock(PlayerGestureListener.class), null, null); + Mockito.mock(PlayerGestureListener.class), null, null, null); PlayerFrameCoordinator childCoordinator = new PlayerFrameCoordinator( RuntimeEnvironment.systemContext, Mockito.mock(PlayerCompositorDelegate.class), Mockito.mock(UnguessableToken.class), 100, 200, 0, 0, true, null, - Mockito.mock(PlayerGestureListener.class), null, null); + Mockito.mock(PlayerGestureListener.class), null, null, null); rootCoordinator.addSubFrame(childCoordinator, new Rect(10, 20, 35, 40)); rootCoordinator.getMediator().setLayoutDimensions(100, 200); diff --git a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java index 200d69446a8..2b5e670c066 100644 --- a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java +++ b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java @@ -237,8 +237,8 @@ public class PlayerFrameMediatorTest { mScroller = new OverScroller(ContextUtils.getApplicationContext()); mGestureListener = new PlayerGestureListener(null, () -> mHasUserInteraction = true, null); Size contentSize = new Size(CONTENT_WIDTH, CONTENT_HEIGHT); - mMediator = new PlayerFrameMediator(mModel, mCompositorDelegate, mGestureListener, - mFrameGuid, contentSize, 0, 0); + mMediator = new PlayerFrameMediator( + mModel, mCompositorDelegate, mGestureListener, mFrameGuid, contentSize, 0, 0, null); mScaleController = new PlayerFrameScaleController(mModel.get(PlayerFrameProperties.SCALE_MATRIX), mMediator, null, mGestureListener::onScale); diff --git a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc index c9b11a35754..e827f4b6270 100644 --- a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc +++ b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc @@ -15,6 +15,7 @@ #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/trace_event.h" #include "base/unguessable_token.h" #include "components/paint_preview/browser/paint_preview_base_service.h" #include "components/paint_preview/player/android/jni_headers/PlayerCompositorDelegateImpl_jni.h" @@ -252,7 +253,7 @@ jint PlayerCompositorDelegateAndroid::RequestBitmap( ScopedJavaGlobalRef<jobject>(j_error_callback), request_id_); ++request_id_; - base::Optional<base::UnguessableToken> frame_guid; + absl::optional<base::UnguessableToken> frame_guid; if (j_frame_guid) { frame_guid = base::android::UnguessableTokenAndroid::FromJavaUnguessableToken( diff --git a/chromium/components/paint_preview/player/bitmap_request.cc b/chromium/components/paint_preview/player/bitmap_request.cc index a09c52654e6..d777fae3652 100644 --- a/chromium/components/paint_preview/player/bitmap_request.cc +++ b/chromium/components/paint_preview/player/bitmap_request.cc @@ -7,7 +7,7 @@ namespace paint_preview { BitmapRequest::BitmapRequest( - const base::Optional<base::UnguessableToken>& frame_guid, + const absl::optional<base::UnguessableToken>& frame_guid, const gfx::Rect& clip_rect, float scale_factor, BitmapRequestCallback callback) diff --git a/chromium/components/paint_preview/player/bitmap_request.h b/chromium/components/paint_preview/player/bitmap_request.h index b4de5299d3b..5b2fd37c819 100644 --- a/chromium/components/paint_preview/player/bitmap_request.h +++ b/chromium/components/paint_preview/player/bitmap_request.h @@ -6,9 +6,9 @@ #define COMPONENTS_PAINT_PREVIEW_PLAYER_BITMAP_REQUEST_H_ #include "base/callback.h" -#include "base/optional.h" #include "base/unguessable_token.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/rect.h" @@ -19,7 +19,7 @@ struct BitmapRequest { base::OnceCallback<void(mojom::PaintPreviewCompositor::BitmapStatus, const SkBitmap&)>; - BitmapRequest(const base::Optional<base::UnguessableToken>& frame_guid, + BitmapRequest(const absl::optional<base::UnguessableToken>& frame_guid, const gfx::Rect& clip_rect, float scale_factor, BitmapRequestCallback callback); @@ -28,7 +28,7 @@ struct BitmapRequest { BitmapRequest& operator=(BitmapRequest&& other) noexcept; BitmapRequest(BitmapRequest&& other) noexcept; - base::Optional<base::UnguessableToken> frame_guid; + absl::optional<base::UnguessableToken> frame_guid; gfx::Rect clip_rect; float scale_factor; BitmapRequestCallback callback; diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.cc b/chromium/components/paint_preview/player/player_compositor_delegate.cc index 4b054152653..324d06dab1e 100644 --- a/chromium/components/paint_preview/player/player_compositor_delegate.cc +++ b/chromium/components/paint_preview/player/player_compositor_delegate.cc @@ -14,7 +14,6 @@ #include "base/memory/read_only_shared_memory_region.h" #include "base/memory/weak_ptr.h" #include "base/notreached.h" -#include "base/optional.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" @@ -32,6 +31,7 @@ #include "components/paint_preview/public/paint_preview_compositor_service.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/rect.h" @@ -62,15 +62,15 @@ BuildHitTesters(const PaintPreviewProto& proto) { std::move(hit_testers)); } -base::Optional<base::ReadOnlySharedMemoryRegion> ToReadOnlySharedMemory( +absl::optional<base::ReadOnlySharedMemoryRegion> ToReadOnlySharedMemory( const paint_preview::PaintPreviewProto& proto) { auto region = base::WritableSharedMemoryRegion::Create(proto.ByteSizeLong()); if (!region.IsValid()) - return base::nullopt; + return absl::nullopt; auto mapping = region.Map(); if (!mapping.IsValid()) - return base::nullopt; + return absl::nullopt; proto.SerializeToArray(mapping.memory(), mapping.size()); return base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region)); @@ -203,7 +203,7 @@ void PlayerCompositorDelegate::InitializeInternal( } int32_t PlayerCompositorDelegate::RequestBitmap( - const base::Optional<base::UnguessableToken>& frame_guid, + const absl::optional<base::UnguessableToken>& frame_guid, const gfx::Rect& clip_rect, float scale_factor, base::OnceCallback<void(mojom::PaintPreviewCompositor::BitmapStatus, @@ -323,7 +323,7 @@ void PlayerCompositorDelegate::OnCompositorClientCreated( TRACE_ID_LOCAL(this)); if (!proto_) { paint_preview_service_->GetFileMixin()->GetCapturedPaintPreviewProto( - key, base::nullopt, + key, absl::nullopt, base::BindOnce(&PlayerCompositorDelegate::OnProtoAvailable, weak_factory_.GetWeakPtr(), expected_url)); } else { diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.h b/chromium/components/paint_preview/player/player_compositor_delegate.h index cf8bebf1814..8d4b803539f 100644 --- a/chromium/components/paint_preview/player/player_compositor_delegate.h +++ b/chromium/components/paint_preview/player/player_compositor_delegate.h @@ -10,7 +10,6 @@ #include "base/containers/flat_map.h" #include "base/memory/memory_pressure_listener.h" #include "base/memory/weak_ptr.h" -#include "base/optional.h" #include "base/unguessable_token.h" #include "components/paint_preview/browser/hit_tester.h" #include "components/paint_preview/browser/paint_preview_base_service.h" @@ -20,6 +19,7 @@ #include "components/paint_preview/public/paint_preview_compositor_service.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { class MemoryPressureMonitor; @@ -77,7 +77,7 @@ class PlayerCompositorDelegate { // Pass this ID to `CancelBitmapRequest(int32_t)` to cancel the request if it // hasn't already been sent. int32_t RequestBitmap( - const base::Optional<base::UnguessableToken>& frame_guid, + const absl::optional<base::UnguessableToken>& frame_guid, const gfx::Rect& clip_rect, float scale_factor, base::OnceCallback<void(mojom::PaintPreviewCompositor::BitmapStatus, diff --git a/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc b/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc index e8fef04f627..90a85e86802 100644 --- a/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc +++ b/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc @@ -46,7 +46,7 @@ class FakePaintPreviewCompositorClient : public PaintPreviewCompositorClient { FakePaintPreviewCompositorClient& operator=( const FakePaintPreviewCompositorClient&) = delete; - const base::Optional<base::UnguessableToken>& Token() const override { + const absl::optional<base::UnguessableToken>& Token() const override { return token_; } @@ -123,7 +123,7 @@ class FakePaintPreviewCompositorClient : public PaintPreviewCompositorClient { private: mojom::PaintPreviewCompositor::BeginCompositeStatus response_status_; - base::Optional<base::UnguessableToken> token_; + absl::optional<base::UnguessableToken> token_; base::OnceClosure disconnect_handler_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; }; @@ -642,7 +642,7 @@ TEST_F(PlayerCompositorDelegateTest, CompressOnClose) { false), base::BindOnce( [](base::FilePath* out, - const base::Optional<base::FilePath>& file_path) { + const absl::optional<base::FilePath>& file_path) { *out = file_path.value(); }, base::Unretained(&dir))); @@ -845,7 +845,7 @@ TEST_F(PlayerCompositorDelegateTest, RequestMainFrameBitmapSuccess) { base::RunLoop loop; player_compositor_delegate.RequestBitmap( - base::nullopt, gfx::Rect(10, 20, 30, 40), 1.0, + absl::nullopt, gfx::Rect(10, 20, 30, 40), 1.0, base::BindOnce( [](base::OnceClosure quit, mojom::PaintPreviewCompositor::BitmapStatus status, diff --git a/chromium/components/paint_preview/public/paint_preview_compositor_client.h b/chromium/components/paint_preview/public/paint_preview_compositor_client.h index 9f49438c501..4fc22afdd16 100644 --- a/chromium/components/paint_preview/public/paint_preview_compositor_client.h +++ b/chromium/components/paint_preview/public/paint_preview_compositor_client.h @@ -6,9 +6,9 @@ #define COMPONENTS_PAINT_PREVIEW_PUBLIC_PAINT_PREVIEW_COMPOSITOR_CLIENT_H_ #include "base/callback_forward.h" -#include "base/optional.h" #include "base/unguessable_token.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" namespace gfx { @@ -27,7 +27,7 @@ class PaintPreviewCompositorClient { // Returns the token associated with the client. Will be null if the client // isn't started. - virtual const base::Optional<base::UnguessableToken>& Token() const = 0; + virtual const absl::optional<base::UnguessableToken>& Token() const = 0; // Adds `closure` as a disconnect handler. virtual void SetDisconnectHandler(base::OnceClosure closure) = 0; diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc index b1a1e54f57a..fd928b6efc4 100644 --- a/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc +++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc @@ -17,7 +17,10 @@ #include "content/public/test/render_view_test.h" #include "content/public/test/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/platform/web_runtime_features.h" #include "third_party/blink/public/web/web_local_frame.h" +#include "third_party/blink/public/web/web_testing_support.h" #include "third_party/skia/include/core/SkPicture.h" #include "ui/native_theme/native_theme_features.h" @@ -25,6 +28,8 @@ namespace paint_preview { namespace { +constexpr char kCompositeAfterPaint[] = "CompositeAfterPaint"; + // Checks that |status| == |expected_status| and loads |response| into // |out_response| if |expected_status| == kOk. If |expected_status| != kOk // |out_response| can safely be nullptr. @@ -37,22 +42,39 @@ void OnCaptureFinished(mojom::PaintPreviewStatus expected_status, *out_response = std::move(response); } +std::string CompositeAfterPaintToString( + const ::testing::TestParamInfo<bool>& cap_enabled) { + if (cap_enabled.param) { + return "WithCompositeAfterPaint"; + } + return "NoCompositeAfterPaint"; +} + } // namespace -class PaintPreviewRecorderRenderViewTest : public content::RenderViewTest { +class PaintPreviewRecorderRenderViewTest + : public content::RenderViewTest, + public ::testing::WithParamInterface<bool> { public: - PaintPreviewRecorderRenderViewTest() {} - ~PaintPreviewRecorderRenderViewTest() override {} - - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - + PaintPreviewRecorderRenderViewTest() { + std::vector<base::Feature> enabled; // TODO(crbug/1022398): This is required to bypass a seemingly unrelated // DCHECK for |use_overlay_scrollbars_| in NativeThemeAura on ChromeOS when // painting scrollbars when first calling LoadHTML(). feature_list_.InitAndDisableFeature(features::kOverlayScrollbar); + blink::WebTestingSupport::SaveRuntimeFeatures(); + } + + ~PaintPreviewRecorderRenderViewTest() override { + // Restore blink runtime features to their original values. + blink::WebTestingSupport::ResetRuntimeFeatures(); + } + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); RenderViewTest::SetUp(); + blink::WebRuntimeFeatures::EnableFeatureFromString(kCompositeAfterPaint, + GetParam()); } content::RenderFrame* GetFrame() { return view_->GetMainRenderFrame(); } @@ -93,7 +115,7 @@ class PaintPreviewRecorderRenderViewTest : public content::RenderViewTest { base::test::ScopedFeatureList feature_list_; }; -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndClipping) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndClipping) { LoadHTML( "<!doctype html>" "<body>" @@ -158,19 +180,20 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndClipping) { 0xFFFFFFFFU); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameWithScroll) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameWithScroll) { LoadHTML( "<!doctype html>" "<body>" - " <div style='width: 600px; height: 80vh; " + " <div style='width: 600px; height: 200px; " " background-color: #ff0000'> </div>" - " <div style='width: 600px; height: 1200px; " + " <div style='width: 600px; height: 5000px; " " background-color: #00ff00'> </div>" "</body>"); // Scroll to bottom of page to ensure scroll position has no effect on // capture. ExecuteJavaScriptForTests("window.scrollTo(0,document.body.scrollHeight);"); + content::RunAllTasksUntilIdle(); auto out_response = mojom::PaintPreviewCaptureResponse::New(); content::RenderFrame* frame = GetFrame(); @@ -204,7 +227,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameWithScroll) { EXPECT_EQ(bitmap.getColor(50, pic->cullRect().height() - 100), 0xFF00FF00U); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureFragment) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureFragment) { // Use position absolute position to check that the captured link dimensions // match what is specified. LoadHTML( @@ -232,7 +255,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureFragment) { EXPECT_EQ(out_response->links[0]->rect.height(), 30); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidFile) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidFile) { LoadHTML("<body></body>"); mojom::PaintPreviewCaptureParamsPtr params = @@ -255,7 +278,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidFile) { content::RunAllTasksUntilIdle(); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidXYClip) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidXYClip) { LoadHTML("<body></body>"); mojom::PaintPreviewCaptureParamsPtr params = @@ -280,7 +303,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureInvalidXYClip) { content::RunAllTasksUntilIdle(); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndLocalFrame) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndLocalFrame) { LoadHTML( "<!doctype html>" "<body style='min-height:1000px;'>" @@ -299,7 +322,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndLocalFrame) { EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureLocalFrame) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureLocalFrame) { LoadHTML( "<!doctype html>" "<body style='min-height:1000px;'>" @@ -318,7 +341,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureLocalFrame) { EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureUnclippedLocalFrame) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureUnclippedLocalFrame) { LoadHTML( "<!doctype html>" "<body style='min-height:1000px;'>" @@ -360,7 +383,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureUnclippedLocalFrame) { EXPECT_EQ(bitmap.getColor(50, 800), 0xFFFF0000U); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureCustomClipRect) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureCustomClipRect) { LoadHTML( "<!doctype html>" "<body>" @@ -406,7 +429,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureCustomClipRect) { EXPECT_EQ(out_response->links[0]->rect.height(), 30); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureWithClamp) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureWithClamp) { LoadHTML( "<!doctype html>" "<body>" @@ -440,7 +463,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureWithClamp) { EXPECT_LT(pic->cullRect().width(), kLarge); } -TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureFullIfWidthHeightAre0) { +TEST_P(PaintPreviewRecorderRenderViewTest, TestCaptureFullIfWidthHeightAre0) { LoadHTML( "<!doctype html>" "<body>" @@ -473,7 +496,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureFullIfWidthHeightAre0) { EXPECT_GT(pic->cullRect().width(), 0U); } -TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslate) { +TEST_P(PaintPreviewRecorderRenderViewTest, CaptureWithTranslate) { // URLs should be annotated correctly when a CSS transform is applied. LoadHTML( R"( @@ -513,7 +536,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslate) { EXPECT_NEAR(out_response->links[0]->rect.height(), 20, 3); } -TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslateThenRotate) { +TEST_P(PaintPreviewRecorderRenderViewTest, CaptureWithTranslateThenRotate) { // URLs should be annotated correctly when a CSS transform is applied. LoadHTML( R"( @@ -555,7 +578,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslateThenRotate) { #endif } -TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithRotateThenTranslate) { +TEST_P(PaintPreviewRecorderRenderViewTest, CaptureWithRotateThenTranslate) { // URLs should be annotated correctly when a CSS transform is applied. LoadHTML( R"( @@ -597,7 +620,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithRotateThenTranslate) { #endif } -TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithScale) { +TEST_P(PaintPreviewRecorderRenderViewTest, CaptureWithScale) { // URLs should be annotated correctly when a CSS transform is applied. LoadHTML( R"( @@ -637,7 +660,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithScale) { EXPECT_NEAR(out_response->links[0]->rect.height(), 20, 3); } -TEST_F(PaintPreviewRecorderRenderViewTest, CaptureSaveRestore) { +TEST_P(PaintPreviewRecorderRenderViewTest, CaptureSaveRestore) { // URLs should be annotated correctly when a CSS transform is applied. LoadHTML( R"( @@ -694,4 +717,9 @@ TEST_F(PaintPreviewRecorderRenderViewTest, CaptureSaveRestore) { EXPECT_NEAR(out_response->links[1]->rect.height(), 20, 3); } +INSTANTIATE_TEST_SUITE_P(All, + PaintPreviewRecorderRenderViewTest, + testing::Values(true, false), + CompositeAfterPaintToString); + } // namespace paint_preview diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc index 75efd829b7b..e17aaa71e77 100644 --- a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc +++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc @@ -11,18 +11,19 @@ #include "base/bind.h" #include "base/bind_post_task.h" #include "base/metrics/histogram_functions.h" -#include "base/optional.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/task_runner.h" #include "base/time/time.h" #include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/trace_event.h" #include "cc/paint/paint_record.h" #include "cc/paint/paint_recorder.h" #include "components/paint_preview/common/paint_preview_tracker.h" #include "components/paint_preview/common/serialized_recording.h" #include "components/paint_preview/renderer/paint_preview_recorder_utils.h" #include "content/public/renderer/render_frame.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -58,7 +59,7 @@ void BuildAndSendResponse(std::unique_ptr<PaintPreviewTracker> tracker, FinishedRecording out, CapturePaintPreviewCallback callback) { if (out.status == mojom::PaintPreviewStatus::kOk) { - BuildResponse(tracker.get(), out.response.get(), /*log=*/true); + BuildResponse(tracker.get(), out.response.get()); } std::move(callback).Run(out.status, std::move(out.response)); } @@ -68,7 +69,7 @@ void BuildAndSendResponse(std::unique_ptr<PaintPreviewTracker> tracker, void RecordToFileOnThreadPool(sk_sp<const SkPicture> skp, base::File skp_file, std::unique_ptr<PaintPreviewTracker> tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, FinishedRecording out, CapturePaintPreviewCallback callback) { TRACE_EVENT0("paint_preview", "RecordToFileOnThreadPool"); @@ -87,7 +88,7 @@ void RecordToFileOnThreadPool(sk_sp<const SkPicture> skp, void SerializeFileRecording(sk_sp<const SkPicture> skp, base::File skp_file, std::unique_ptr<PaintPreviewTracker> tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, FinishedRecording out, CapturePaintPreviewCallback callback) { base::ThreadPool::PostTask( @@ -104,12 +105,12 @@ void SerializeFileRecording(sk_sp<const SkPicture> skp, void SerializeMemoryBufferRecording( sk_sp<const SkPicture> skp, std::unique_ptr<PaintPreviewTracker> tracker, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, FinishedRecording out, CapturePaintPreviewCallback callback) { TRACE_EVENT0("paint_preview", "SerializeMemoryBufferRecording"); size_t serialized_size = 0; - base::Optional<mojo_base::BigBuffer> buffer = + absl::optional<mojo_base::BigBuffer> buffer = RecordToBuffer(skp, tracker.get(), max_capture_size, &serialized_size); out.status = buffer.has_value() ? mojom::PaintPreviewStatus::kOk : mojom::PaintPreviewStatus::kCaptureFailed; @@ -126,7 +127,7 @@ void FinishRecordingOnUIThread(sk_sp<const cc::PaintRecord> recording, std::unique_ptr<PaintPreviewTracker> tracker, RecordingPersistence persistence, base::File skp_file, - base::Optional<size_t> max_capture_size, + absl::optional<size_t> max_capture_size, mojom::PaintPreviewCaptureResponsePtr response, CapturePaintPreviewCallback callback) { TRACE_EVENT0("paint_preview", "FinishRecordingOnUIThread"); @@ -137,9 +138,9 @@ void FinishRecordingOnUIThread(sk_sp<const cc::PaintRecord> recording, return; } - TRACE_EVENT_BEGIN0("paint_preview", "ParseGlyphsAndLinks"); - ParseGlyphsAndLinks(recording.get(), tracker.get()); - TRACE_EVENT_END0("paint_preview", "ParseGlyphsAndLinks"); + TRACE_EVENT_BEGIN0("paint_preview", "PreProcessPaintOpBuffer"); + PreProcessPaintOpBuffer(recording.get(), tracker.get()); + TRACE_EVENT_END0("paint_preview", "PreProcessPaintOpBuffer"); // This cannot be done async if the recording contains a GPU accelerated // image. @@ -297,7 +298,8 @@ void PaintPreviewRecorderImpl::CapturePaintPreviewInternal( base::TimeTicks start_time = base::TimeTicks::Now(); TRACE_EVENT_BEGIN0("paint_preview", "WebLocalFrame::CapturePaintPreview"); bool success = frame->CapturePaintPreview( - bounds, canvas, /*include_linked_destinations=*/params->capture_links); + bounds, canvas, /*include_linked_destinations=*/params->capture_links, + /*skip_accelerated_content=*/params->skip_accelerated_content); TRACE_EVENT_END0("paint_preview", "WebLocalFrame::CapturePaintPreview"); canvas->restore(); base::TimeDelta capture_time = base::TimeTicks::Now() - start_time; @@ -330,17 +332,20 @@ void PaintPreviewRecorderImpl::CapturePaintPreviewInternal( return; } - // Convert the special value |0| to |base::nullopt|. - base::Optional<size_t> max_capture_size; + // Convert the special value |0| to |absl::nullopt|. + absl::optional<size_t> max_capture_size; if (params->max_capture_size == 0) { - max_capture_size = base::nullopt; + max_capture_size = absl::nullopt; } else { max_capture_size = params->max_capture_size; auto* image_ctx = tracker->GetImageSerializationContext(); image_ctx->remaining_image_size = params->max_capture_size; - image_ctx->max_representation_size = params->max_capture_size; } + auto* image_ctx = tracker->GetImageSerializationContext(); + image_ctx->max_decoded_image_size_bytes = + params->max_decoded_image_size_bytes; + FinishRecordingOnUIThread(recorder.finishRecordingAsPicture(), bounds, std::move(tracker), params->persistence, std::move(params->file), max_capture_size, diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc index b9f9fb794ac..e4ec8a5a186 100644 --- a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc +++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc @@ -9,9 +9,8 @@ #include "base/bind.h" #include "base/trace_event/common/trace_event_common.h" #include "base/trace_event/trace_event.h" -#include "cc/paint/draw_image.h" -#include "cc/paint/image_provider.h" #include "cc/paint/paint_image.h" +#include "cc/paint/paint_image_builder.h" #include "components/paint_preview/common/file_stream.h" #include "components/paint_preview/common/paint_preview_tracker.h" #include "mojo/public/cpp/base/shared_memory_utils.h" @@ -20,8 +19,37 @@ namespace paint_preview { -void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer, - PaintPreviewTracker* tracker) { +namespace { + +// Converts a texture backed paint image in the PaintOpBuffer to one that is not +// texture backed. +cc::PaintImage MakeUnaccelerated(cc::PaintImage& paint_image) { + DCHECK(paint_image.IsTextureBacked()); + auto sk_image = paint_image.GetSwSkImage(); + if (sk_image->isLazyGenerated()) { + // Texture backed images should always be returned as SkImage_Raster type + // (bitmap). This is just a catchall in the event a lazy image is somehow + // returned in which case we should just raster it. + SkBitmap bitmap; + bitmap.allocPixels(sk_image->imageInfo(), + sk_image->imageInfo().minRowBytes()); + if (!sk_image->readPixels(bitmap.pixmap(), 0, 0)) { + return paint_image; + } + // Make immutable to skip an extra copy. + bitmap.setImmutable(); + sk_image = SkImage::MakeFromBitmap(bitmap); + } + return cc::PaintImageBuilder::WithDefault() + .set_id(cc::PaintImage::GetNextId()) + .set_image(sk_image, cc::PaintImage::GetNextContentId()) + .TakePaintImage(); +} + +} // namespace + +void PreProcessPaintOpBuffer(const cc::PaintOpBuffer* buffer, + PaintPreviewTracker* tracker) { for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) { switch (it->GetType()) { case cc::PaintOpType::DrawTextBlob: { @@ -33,7 +61,7 @@ void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer, // Recurse into nested records if they contain text blobs (equivalent to // nested SkPictures). auto* record_op = static_cast<cc::DrawRecordOp*>(*it); - ParseGlyphsAndLinks(record_op->record.get(), tracker); + PreProcessPaintOpBuffer(record_op->record.get(), tracker); break; } case cc::PaintOpType::Annotate: { @@ -92,33 +120,26 @@ void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer, tracker->Translate(translate_op->dx, translate_op->dy); break; } + case cc::PaintOpType::DrawImage: { + auto* image_op = static_cast<cc::DrawImageOp*>(*it); + if (image_op->image.IsTextureBacked()) { + image_op->image = MakeUnaccelerated(image_op->image); + } + break; + } + case cc::PaintOpType::DrawImageRect: { + auto* image_op = static_cast<cc::DrawImageRectOp*>(*it); + if (image_op->image.IsTextureBacked()) { + image_op->image = MakeUnaccelerated(image_op->image); + } + break; + } default: continue; } } } -class SkPictureImageProvider : public cc::ImageProvider { - public: - SkPictureImageProvider() = default; - ~SkPictureImageProvider() override = default; - - SkPictureImageProvider& operator=(const SkPictureImageProvider&) = delete; - SkPictureImageProvider& operator=(SkPictureImageProvider&& other); - - // Converts GPU accelerated content into software rastered content using - // GetSwSkImage(). - cc::ImageProvider::ScopedResult GetRasterContent( - const cc::DrawImage& draw_image) override { - const cc::PaintImage& paint_image = draw_image.paint_image(); - return cc::ImageProvider::ScopedResult(cc::DecodedDrawImage( - paint_image.GetSwSkImage(), /*dark_mode_color_filter=*/nullptr, - /*src_rect_offset=*/SkSize::Make(0, 0), - /*scale_adjustment=*/SkSize::Make(1.f, 1.f), - draw_image.filter_quality(), /*is_budgeted=*/true)); - } -}; - sk_sp<const SkPicture> PaintRecordToSkPicture( sk_sp<const cc::PaintRecord> recording, PaintPreviewTracker* tracker, @@ -129,10 +150,9 @@ sk_sp<const SkPicture> PaintRecordToSkPicture( base::BindRepeating(&PaintPreviewTracker::CustomDataToSkPictureCallback, base::Unretained(tracker)); - SkPictureImageProvider raster_accelerated_images; auto skp = ToSkPicture(recording, SkRect::MakeWH(bounds.width(), bounds.height()), - &raster_accelerated_images, custom_callback); + nullptr, custom_callback); if (!skp || skp->cullRect().width() == 0 || skp->cullRect().height() == 0) return nullptr; @@ -141,18 +161,11 @@ sk_sp<const SkPicture> PaintRecordToSkPicture( } void BuildResponse(PaintPreviewTracker* tracker, - mojom::PaintPreviewCaptureResponse* response, - bool log) { + mojom::PaintPreviewCaptureResponse* response) { // Ensure these always exist. DCHECK(tracker); DCHECK(response); - // paint_preview::BuildResponse has been showing in a large number of crashes - // under stack scans. In order to determine if these entries are "real" we - // should log the calls and check the log output. - if (log) - LOG(WARNING) << "paint_preview::BuildResponse() called"; - response->embedding_token = tracker->EmbeddingToken(); tracker->MoveLinks(&response->links); diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.h b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.h index adfab3e0e99..47ddf3dff2a 100644 --- a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.h +++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.h @@ -21,10 +21,13 @@ namespace paint_preview { class PaintPreviewTracker; -// Walks |buffer| to extract all the glyphs from its text blobs and links. The -// extracted data is written to to |tracker|. -void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer, - PaintPreviewTracker* tracker); +// Pre processes the PaintOpBuffer prior to conversion to SkPicture. +// 1. Walks |buffer| to extract all the glyphs from its text blobs and links. +// The extracted data is written to `tracker`. +// 2. Tracks geometry changes for frames and saves them to `tracker`. +// 3. Unaccelerates GPU accelerated PaintImages. +void PreProcessPaintOpBuffer(const cc::PaintOpBuffer* buffer, + PaintPreviewTracker* tracker); // Convert |recording| into an SkPicture, tracking embedded content. Will return // |nullptr| if the resulting picture failed or zero sized. @@ -35,8 +38,7 @@ sk_sp<const SkPicture> PaintRecordToSkPicture( // NOTE: |tracker| is effectively const here despite being passed by pointer. void BuildResponse(PaintPreviewTracker* tracker, - mojom::PaintPreviewCaptureResponse* response, - bool log = false); + mojom::PaintPreviewCaptureResponse* response); } // namespace paint_preview diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc index 19a10d21c71..532e18b3989 100644 --- a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc +++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc @@ -14,12 +14,14 @@ #include "base/memory/discardable_memory_allocator.h" #include "base/memory/read_only_shared_memory_region.h" #include "base/notreached.h" -#include "base/optional.h" #include "base/test/test_discardable_memory_allocator.h" #include "base/unguessable_token.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_flags.h" +#include "cc/paint/paint_image.h" +#include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_recorder.h" +#include "cc/paint/paint_worklet_input.h" #include "components/paint_preview/common/file_stream.h" #include "components/paint_preview/common/mojom/paint_preview_recorder.mojom-shared.h" #include "components/paint_preview/common/paint_preview_tracker.h" @@ -28,6 +30,7 @@ #include "mojo/public/cpp/base/big_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkFont.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkTextBlob.h" @@ -68,7 +71,7 @@ TEST(PaintPreviewRecorderUtilsTest, TestParseGlyphs) { PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::UnguessableToken::Create(), true); - ParseGlyphsAndLinks(record.get(), &tracker); + PreProcessPaintOpBuffer(record.get(), &tracker); auto* usage_map = tracker.GetTypefaceUsageMap(); EXPECT_TRUE(usage_map->count(typeface->uniqueID())); EXPECT_TRUE( @@ -129,7 +132,7 @@ TEST(PaintPreviewRecorderUtilsTest, TestParseLinks) { PaintPreviewTracker tracker(base::UnguessableToken::Create(), base::UnguessableToken::Create(), true); - ParseGlyphsAndLinks(record.get(), &tracker); + PreProcessPaintOpBuffer(record.get(), &tracker); std::vector<mojom::LinkDataPtr> links; tracker.MoveLinks(&links); @@ -184,7 +187,7 @@ TEST(PaintPreviewRecorderUtilsTest, TestTransformSubframeRects) { EXPECT_EQ(rect.width(), old_cull_rect.width()); EXPECT_EQ(rect.height(), old_cull_rect.height()); - ParseGlyphsAndLinks(record.get(), &tracker); + PreProcessPaintOpBuffer(record.get(), &tracker); auto* picture_ctx = tracker.GetPictureSerializationContext(); ASSERT_EQ(picture_ctx->content_id_to_transformed_clip.size(), 1U); @@ -218,26 +221,20 @@ class PaintPreviewRecorderUtilsSerializeAsSkPictureTest cc::PaintFlags flags; canvas->drawRect(SkRect::MakeWH(dimensions.width(), dimensions.height()), flags); - SkBitmap bitmap; - bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); - { - SkCanvas sk_canvas(bitmap); - sk_canvas.drawColor(SK_ColorRED); - } - canvas->drawImage(cc::PaintImage::CreateFromBitmap(bitmap), 0, 0); } void TearDown() override { base::DiscardableMemoryAllocator::SetInstance(nullptr); } - base::Optional<SerializedRecording> SerializeAsSkPicture( - base::Optional<size_t> max_capture_size, + absl::optional<SerializedRecording> SerializeAsSkPicture( + absl::optional<size_t> max_capture_size, size_t* serialized_size) { - auto skp = PaintRecordToSkPicture(recorder.finishRecordingAsPicture(), - &tracker, dimensions); + auto recording = recorder.finishRecordingAsPicture(); + PreProcessPaintOpBuffer(recording.get(), &tracker); + auto skp = PaintRecordToSkPicture(recording, &tracker, dimensions); if (!skp) - return base::nullopt; + return absl::nullopt; canvas = nullptr; @@ -245,30 +242,30 @@ class PaintPreviewRecorderUtilsSerializeAsSkPictureTest case RecordingPersistence::kFileSystem: { base::ScopedTempDir temp_dir; if (!temp_dir.CreateUniqueTempDir()) - return base::nullopt; + return absl::nullopt; base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file"); base::File write_file( file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); if (!RecordToFile(std::move(write_file), skp, &tracker, max_capture_size, serialized_size)) - return base::nullopt; + return absl::nullopt; return {SerializedRecording(file_path)}; } break; case RecordingPersistence::kMemoryBuffer: { - base::Optional<mojo_base::BigBuffer> buffer = + absl::optional<mojo_base::BigBuffer> buffer = RecordToBuffer(skp, &tracker, max_capture_size, serialized_size); if (!buffer.has_value()) - return base::nullopt; + return absl::nullopt; return {SerializedRecording(std::move(buffer.value()))}; } break; } NOTREACHED(); - return base::nullopt; + return absl::nullopt; } PaintPreviewTracker tracker; @@ -298,16 +295,36 @@ TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, Roundtrip) { ctx.insert(content_id); size_t out_size = 0; - auto recording = SerializeAsSkPicture(base::nullopt, &out_size); + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); ASSERT_TRUE(recording.has_value()); - base::Optional<SkpResult> result = std::move(recording.value()).Deserialize(); + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); ASSERT_TRUE(result.has_value()); for (auto& content_id : ctx) { EXPECT_TRUE(result->ctx.contains(content_id)); result->ctx.erase(content_id); } EXPECT_TRUE(result->ctx.empty()); +} + +TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, RoundtripWithImage) { + { + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawColor(SK_ColorRED); + cc::PaintImage paint_image = cc::PaintImage::CreateFromBitmap(bitmap); + ASSERT_FALSE(paint_image.IsLazyGenerated()); + ASSERT_FALSE(paint_image.IsPaintWorklet()); + canvas->drawImage(paint_image, 0U, 0U); + } + + size_t out_size = 0; + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); + ASSERT_TRUE(recording.has_value()); + + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); + ASSERT_TRUE(result.has_value()); SkBitmap bitmap; bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); @@ -316,6 +333,179 @@ TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, Roundtrip) { EXPECT_EQ(bitmap.getColor(10, 10), SK_ColorRED); } +class FakeTextureBacking : public cc::TextureBacking { + public: + explicit FakeTextureBacking(sk_sp<SkImage> image) : image_(image) {} + + const SkImageInfo& GetSkImageInfo() override { return image_->imageInfo(); } + gpu::Mailbox GetMailbox() const override { return mailbox_; } + sk_sp<SkImage> GetAcceleratedSkImage() override { return nullptr; } + sk_sp<SkImage> GetSkImageViaReadback() override { return image_; } + bool readPixels(const SkImageInfo& dstInfo, + void* dstPixels, + size_t dstRowBytes, + int srcX, + int srcY) override { + return false; + } + void FlushPendingSkiaOps() override {} + + private: + gpu::Mailbox mailbox_; + sk_sp<SkImage> image_; +}; + +TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithTexture) { + { + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawColor(SK_ColorRED); + cc::PaintImage paint_image = + cc::PaintImageBuilder::WithDefault() + .set_id(cc::PaintImage::GetNextId()) + .set_texture_backing( + sk_sp<FakeTextureBacking>( + new FakeTextureBacking(SkImage::MakeFromBitmap(bitmap))), + cc::PaintImage::GetNextContentId()) + .TakePaintImage(); + canvas->drawImage(paint_image, 0U, 0U); + } + + size_t out_size = 0; + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); + ASSERT_TRUE(recording.has_value()); + + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); + ASSERT_TRUE(result.has_value()); + + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawPicture(result->skp); + EXPECT_EQ(bitmap.getColor(10, 10), SK_ColorRED); +} + +TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithLazyTexture) { + { + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawColor(SK_ColorRED); + auto sk_image = SkImage::MakeFromBitmap(bitmap); + auto data = sk_image->encodeToData(); + auto lazy_sk_image = SkImage::MakeFromEncoded(data); + ASSERT_TRUE(lazy_sk_image->isLazyGenerated()); + cc::PaintImage paint_image = + cc::PaintImageBuilder::WithDefault() + .set_id(cc::PaintImage::GetNextId()) + .set_texture_backing(sk_sp<FakeTextureBacking>( + new FakeTextureBacking(lazy_sk_image)), + cc::PaintImage::GetNextContentId()) + .TakePaintImage(); + cc::PaintFlags paint; + paint.setBlendMode(SkBlendMode::kSrc); + auto rect = SkRect::MakeWH(dimensions.width(), dimensions.height()); + canvas->drawImageRect(paint_image, rect, rect, SkSamplingOptions(), &paint, + SkCanvas::kStrict_SrcRectConstraint); + } + + size_t out_size = 0; + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); + ASSERT_TRUE(recording.has_value()); + + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); + ASSERT_TRUE(result.has_value()); + + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawPicture(result->skp); + EXPECT_EQ(bitmap.getColor(10, 10), SK_ColorRED); +} + +TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithLazyImage) { + { + cc::PaintRecorder inner_recorder; + cc::PaintCanvas* inner_canvas = + inner_recorder.beginRecording(dimensions.width(), dimensions.width()); + inner_canvas->drawColor(SK_ColorRED); + cc::PaintImage paint_image = + cc::PaintImageBuilder::WithDefault() + .set_id(1) + .set_paint_record(inner_recorder.finishRecordingAsPicture(), + dimensions, cc::PaintImage::GetNextContentId()) + .TakePaintImage(); + ASSERT_TRUE(paint_image.IsLazyGenerated()); + ASSERT_FALSE(paint_image.IsPaintWorklet()); + canvas->drawImage(paint_image, 0U, 0U); + } + + size_t out_size = 0; + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); + ASSERT_TRUE(recording.has_value()); + + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); + ASSERT_TRUE(result.has_value()); + + SkBitmap bitmap; + bitmap.allocN32Pixels(dimensions.width(), dimensions.height()); + SkCanvas sk_canvas(bitmap); + sk_canvas.drawPicture(result->skp); + EXPECT_EQ(bitmap.getColor(10, 10), SK_ColorRED); +} + +class TestPaintWorkletInput : public cc::PaintWorkletInput { + public: + explicit TestPaintWorkletInput(const gfx::SizeF& size) + : container_size_(size) {} + + gfx::SizeF GetSize() const override { return container_size_; } + int WorkletId() const override { return 1U; } + const std::vector<PaintWorkletInput::PropertyKey>& GetPropertyKeys() + const override { + return property_keys_; + } + + protected: + ~TestPaintWorkletInput() override = default; + + private: + gfx::SizeF container_size_; + std::vector<PaintWorkletInput::PropertyKey> property_keys_; +}; + +TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, + RoundtripWithPaintWorklet) { + { + gfx::SizeF size(100, 50); + scoped_refptr<TestPaintWorkletInput> input = + base::MakeRefCounted<TestPaintWorkletInput>(size); + cc::PaintImage paint_image = cc::PaintImageBuilder::WithDefault() + .set_id(1) + .set_paint_worklet_input(std::move(input)) + .TakePaintImage(); + ASSERT_FALSE(paint_image.IsLazyGenerated()); + ASSERT_TRUE(paint_image.IsPaintWorklet()); + cc::PaintFlags paint; + paint.setBlendMode(SkBlendMode::kSrc); + auto rect = SkRect::MakeWH(dimensions.width(), dimensions.height()); + canvas->drawImageRect(paint_image, rect, rect, SkSamplingOptions(), &paint, + SkCanvas::kStrict_SrcRectConstraint); + } + + size_t out_size = 0; + auto recording = SerializeAsSkPicture(absl::nullopt, &out_size); + // The paint worklet needs to be skipped. Just make sure it doesn't crash. + ASSERT_TRUE(recording.has_value()); + + absl::optional<SkpResult> result = std::move(recording.value()).Deserialize(); + ASSERT_TRUE(result.has_value()); +} + TEST_P(PaintPreviewRecorderUtilsSerializeAsSkPictureTest, FailIfExceedMaxSize) { size_t out_size = 2; auto recording = SerializeAsSkPicture({1}, &out_size); |