| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/paint_preview/player/player_compositor_delegate.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/files/file_path.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/utf_string_conversions.h" |
| #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/compositor_utils.h" |
| #include "components/paint_preview/browser/paint_preview_base_service.h" |
| #include "components/paint_preview/common/proto/paint_preview.pb.h" |
| #include "components/paint_preview/common/recording_map.h" |
| #include "components/paint_preview/common/serialized_recording.h" |
| #include "components/paint_preview/public/paint_preview_compositor_client.h" |
| #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/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace paint_preview { |
| |
| namespace { |
| |
| std::pair<base::UnguessableToken, std::unique_ptr<HitTester>> BuildHitTester( |
| const PaintPreviewFrameProto& proto) { |
| std::pair<base::UnguessableToken, std::unique_ptr<HitTester>> out( |
| base::UnguessableToken::Deserialize(proto.embedding_token_high(), |
| proto.embedding_token_low()), |
| std::make_unique<HitTester>()); |
| out.second->Build(proto); |
| return out; |
| } |
| |
| base::flat_map<base::UnguessableToken, std::unique_ptr<HitTester>> |
| BuildHitTesters(const PaintPreviewProto& proto) { |
| std::vector<std::pair<base::UnguessableToken, std::unique_ptr<HitTester>>> |
| hit_testers; |
| hit_testers.reserve(proto.subframes_size() + 1); |
| hit_testers.push_back(BuildHitTester(proto.root_frame())); |
| for (const auto& frame_proto : proto.subframes()) |
| hit_testers.push_back(BuildHitTester(frame_proto)); |
| |
| return base::flat_map<base::UnguessableToken, std::unique_ptr<HitTester>>( |
| std::move(hit_testers)); |
| } |
| |
| base::Optional<base::ReadOnlySharedMemoryRegion> ToReadOnlySharedMemory( |
| const paint_preview::PaintPreviewProto& proto) { |
| auto region = base::WritableSharedMemoryRegion::Create(proto.ByteSizeLong()); |
| if (!region.IsValid()) |
| return base::nullopt; |
| |
| auto mapping = region.Map(); |
| if (!mapping.IsValid()) |
| return base::nullopt; |
| |
| proto.SerializeToArray(mapping.memory(), mapping.size()); |
| return base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region)); |
| } |
| |
| paint_preview::mojom::PaintPreviewBeginCompositeRequestPtr |
| PrepareCompositeRequest(const paint_preview::PaintPreviewProto& proto) { |
| paint_preview::mojom::PaintPreviewBeginCompositeRequestPtr |
| begin_composite_request = |
| paint_preview::mojom::PaintPreviewBeginCompositeRequest::New(); |
| begin_composite_request->recording_map = |
| RecordingMapFromPaintPreviewProto(proto); |
| if (begin_composite_request->recording_map.empty()) |
| return nullptr; |
| |
| auto read_only_proto = ToReadOnlySharedMemory(proto); |
| if (!read_only_proto) { |
| DVLOG(1) << "Failed to read proto to read-only shared memory."; |
| return nullptr; |
| } |
| begin_composite_request->proto = std::move(read_only_proto.value()); |
| return begin_composite_request; |
| } |
| |
| } // namespace |
| |
| PlayerCompositorDelegate::PlayerCompositorDelegate( |
| PaintPreviewBaseService* paint_preview_service, |
| const GURL& expected_url, |
| const DirectoryKey& key, |
| base::OnceClosure compositor_error, |
| bool skip_service_launch) |
| : compositor_error_(std::move(compositor_error)), |
| paint_preview_service_(paint_preview_service), |
| key_(key), |
| compress_on_close_(true) { |
| if (skip_service_launch) { |
| paint_preview_service_->GetCapturedPaintPreviewProto( |
| key, base::BindOnce(&PlayerCompositorDelegate::OnProtoAvailable, |
| weak_factory_.GetWeakPtr(), expected_url)); |
| return; |
| } |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("paint_preview", |
| "PlayerCompositorDelegate CreateCompositor", |
| TRACE_ID_LOCAL(this)); |
| paint_preview_compositor_service_ = StartCompositorService( |
| base::BindOnce(&PlayerCompositorDelegate::OnCompositorServiceDisconnected, |
| weak_factory_.GetWeakPtr())); |
| |
| paint_preview_compositor_client_ = |
| paint_preview_compositor_service_->CreateCompositor( |
| base::BindOnce(&PlayerCompositorDelegate::OnCompositorClientCreated, |
| weak_factory_.GetWeakPtr(), expected_url, key)); |
| paint_preview_compositor_client_->SetDisconnectHandler( |
| base::BindOnce(&PlayerCompositorDelegate::OnCompositorClientDisconnected, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| PlayerCompositorDelegate::~PlayerCompositorDelegate() { |
| if (compress_on_close_) { |
| paint_preview_service_->GetTaskRunner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(base::IgnoreResult(&FileManager::CompressDirectory), |
| paint_preview_service_->GetFileManager(), key_)); |
| } |
| } |
| |
| void PlayerCompositorDelegate::OnCompositorServiceDisconnected() { |
| LOG(ERROR) << "Compositor service disconnected."; |
| if (compositor_error_) |
| std::move(compositor_error_).Run(); |
| } |
| |
| void PlayerCompositorDelegate::OnCompositorClientCreated( |
| const GURL& expected_url, |
| const DirectoryKey& key) { |
| TRACE_EVENT_NESTABLE_ASYNC_END0("paint_preview", |
| "PlayerCompositorDelegate CreateCompositor", |
| TRACE_ID_LOCAL(this)); |
| paint_preview_service_->GetCapturedPaintPreviewProto( |
| key, base::BindOnce(&PlayerCompositorDelegate::OnProtoAvailable, |
| weak_factory_.GetWeakPtr(), expected_url)); |
| } |
| |
| void PlayerCompositorDelegate::OnProtoAvailable( |
| const GURL& expected_url, |
| std::unique_ptr<PaintPreviewProto> proto) { |
| if (!proto || !proto->IsInitialized()) { |
| // TODO(crbug.com/1021590): Handle initialization errors. |
| OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus:: |
| kCompositingFailure, |
| nullptr); |
| return; |
| } |
| |
| auto proto_url = GURL(proto->metadata().url()); |
| if (expected_url != proto_url) { |
| OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus:: |
| kDeserializingFailure, |
| nullptr); |
| return; |
| } |
| |
| hit_testers_ = BuildHitTesters(*proto); |
| |
| if (!paint_preview_compositor_client_) { |
| OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus:: |
| kCompositingFailure, |
| nullptr); |
| return; |
| } |
| |
| paint_preview_compositor_client_->SetRootFrameUrl(proto_url); |
| |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, |
| base::BindOnce(&PrepareCompositeRequest, *proto), |
| base::BindOnce(&PlayerCompositorDelegate::SendCompositeRequest, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void PlayerCompositorDelegate::SendCompositeRequest( |
| mojom::PaintPreviewBeginCompositeRequestPtr begin_composite_request) { |
| // TODO(crbug.com/1021590): Handle initialization errors. |
| if (!begin_composite_request) { |
| OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus:: |
| kCompositingFailure, |
| nullptr); |
| return; |
| } |
| |
| paint_preview_compositor_client_->BeginSeparatedFrameComposite( |
| std::move(begin_composite_request), |
| base::BindOnce(&PlayerCompositorDelegate::OnCompositorReady, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void PlayerCompositorDelegate::OnCompositorClientDisconnected() { |
| LOG(ERROR) << "Compositor client disconnected."; |
| if (compositor_error_) |
| std::move(compositor_error_).Run(); |
| } |
| |
| void PlayerCompositorDelegate::RequestBitmap( |
| const base::UnguessableToken& frame_guid, |
| const gfx::Rect& clip_rect, |
| float scale_factor, |
| base::OnceCallback<void(mojom::PaintPreviewCompositor::BitmapStatus, |
| const SkBitmap&)> callback) { |
| if (!paint_preview_compositor_client_) { |
| std::move(callback).Run( |
| mojom::PaintPreviewCompositor::BitmapStatus::kMissingFrame, SkBitmap()); |
| return; |
| } |
| |
| paint_preview_compositor_client_->BitmapForSeparatedFrame( |
| frame_guid, clip_rect, scale_factor, std::move(callback)); |
| } |
| |
| std::vector<const GURL*> PlayerCompositorDelegate::OnClick( |
| const base::UnguessableToken& frame_guid, |
| const gfx::Rect& rect) { |
| std::vector<const GURL*> urls; |
| auto it = hit_testers_.find(frame_guid); |
| if (it != hit_testers_.end()) |
| it->second->HitTest(rect, &urls); |
| return urls; |
| } |
| |
| } // namespace paint_preview |