blob: 00e015658a339a39d114669a40db0c2bdbdd7117 [file] [log] [blame]
// 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