Commit 29b91f26 authored by Kamila Hasanbega's avatar Kamila Hasanbega Committed by Commit Bot

Revert "[Paint Preview] Compositing Service"

This reverts commit ba5c7d6f.

Reason for revert: It breaks several tests 
https://ci.chromium.org/p/chromium/builders/ci/Win7%20Tests%20%28dbg%29%281%29
https://ci.chromium.org/p/chromium/builders/ci/Linux%20Tests%20%28dbg%29%281%29


Original change's description:
> [Paint Preview] Compositing Service
> 
> Adds a paint preview compositor service for compositing collections of
> SkPictures representing frames into bitmaps. This is very similar
> in principle to the PDF compositor service, but produces tileable
> bitmaps rather than a PDF.
> 
> A client to this service should start the compositing collection
> portion of the service in a utility process. The client can then
> create and delete dedicated compositor instances for each group of
> SkPictures. Realistically only one compositor will be actively
> communicating at a time. However, multiple compositors can be
> "warm" (data is deserialized and in memory for fast switching).
> 
> Bug: 1011430
> Change-Id: I21cb594cd94982f8d4fc5800d9ae4d74b5d9973b
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1842273
> Reviewed-by: Jochen Eisinger <jochen@chromium.org>
> Reviewed-by: Scott Violet <sky@chromium.org>
> Reviewed-by: Colin Blundell <blundell@chromium.org>
> Reviewed-by: Ken Buchanan <kenrb@chromium.org>
> Reviewed-by: Ian Vollick <vollick@chromium.org>
> Reviewed-by: Mehran Mahmoudi <mahmoudi@chromium.org>
> Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#709946}

TBR=vollick@chromium.org,sky@chromium.org,kenrb@chromium.org,blundell@chromium.org,jochen@chromium.org,blundell@google.com,mahmoudi@chromium.org,ckitagawa@chromium.org

Change-Id: Idbeb4bb3989d803f1efcde86f1066b791731b08b
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1011430
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1886610Reviewed-by: default avatarKamila Hasanbega <hkamila@chromium.org>
Commit-Queue: Kamila Hasanbega <hkamila@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710251}
parent cc6dca01
......@@ -260,7 +260,6 @@ test("components_unittests") {
"//components/security_interstitials/content:unit_tests",
"//components/security_state/content:unit_tests",
"//components/services/heap_profiling:unit_tests",
"//components/services/paint_preview_compositor:unit_tests",
"//components/services/quarantine:unit_tests",
"//components/spellcheck/browser:unit_tests",
"//components/spellcheck/renderer:unit_tests",
......
......@@ -12,27 +12,13 @@ namespace paint_preview {
namespace {
// Supported by MSVC, g++, and clang++. Ensures no gaps in packing.
#pragma pack(push, 1)
struct SerializedRectData {
uint32_t content_id;
int64_t x;
int64_t y;
int64_t width;
int64_t height;
};
#pragma pack(pop)
// Serializes a SkPicture representing a subframe as a custom data placeholder.
sk_sp<SkData> SerializeSubframe(SkPicture* picture, void* ctx) {
const PictureSerializationContext* context =
reinterpret_cast<PictureSerializationContext*>(ctx);
SerializedRectData rect_data = {
picture->uniqueID(), picture->cullRect().x(), picture->cullRect().y(),
picture->cullRect().width(), picture->cullRect().height()};
if (context->count(picture->uniqueID()))
return SkData::MakeWithCopy(&rect_data, sizeof(rect_data));
uint32_t content_id = picture->uniqueID();
if (context->count(content_id))
return SkData::MakeWithCopy(&content_id, sizeof(content_id));
// Defers picture serialization behavior to Skia.
return nullptr;
}
......@@ -58,23 +44,23 @@ sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
return subset_data;
}
// Deserializies a clip rect for a subframe within the main SkPicture. These
// represent subframes and require special decoding as they are custom data
// rather than a valid SkPicture.
// Deserializies a SkPicture within the main SkPicture. These represent
// subframes and require special decoding as they are custom data rather than a
// valid SkPicture.
// Precondition: the version of the SkPicture should be checked prior to
// invocation to ensure deserialization will succeed.
sk_sp<SkPicture> DeserializeSubframe(const void* data,
size_t length,
void* ctx) {
SerializedRectData rect_data;
if (length < sizeof(rect_data))
uint32_t content_id;
if (length < sizeof(content_id))
return MakeEmptyPicture();
memcpy(&rect_data, data, sizeof(rect_data));
memcpy(&content_id, data, sizeof(content_id));
auto* context = reinterpret_cast<DeserializationContext*>(ctx);
context->insert(
{rect_data.content_id,
gfx::Rect(rect_data.x, rect_data.y, rect_data.width, rect_data.height)});
auto it = context->find(content_id);
if (it == context->end() || !it->second)
return MakeEmptyPicture();
return it->second;
}
} // namespace
......
......@@ -15,7 +15,6 @@
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSerialProcs.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/geometry/rect.h"
namespace paint_preview {
......@@ -34,8 +33,8 @@ struct TypefaceSerializationContext {
base::flat_set<SkFontID> finished; // Should be empty on first use.
};
// Maps a content ID to a clip rect.
using DeserializationContext = base::flat_map<uint32_t, gfx::Rect>;
// Maps a content ID to a SkPicture.
using DeserializationContext = base::flat_map<uint32_t, sk_sp<SkPicture>>;
// Creates a no-op SkPicture.
sk_sp<SkPicture> MakeEmptyPicture();
......
......@@ -28,6 +28,9 @@ TEST(PaintPreviewSerialUtils, TestPictureProcs) {
EXPECT_TRUE(
picture_ctx.insert(std::make_pair(content_id, kFrameGuid)).second);
DeserializationContext deserial_ctx;
EXPECT_TRUE(deserial_ctx.insert(std::make_pair(content_id, pic)).second);
TypefaceUsageMap usage_map;
TypefaceSerializationContext typeface_ctx(&usage_map);
......@@ -35,22 +38,16 @@ TEST(PaintPreviewSerialUtils, TestPictureProcs) {
EXPECT_EQ(serial_procs.fPictureCtx, &picture_ctx);
EXPECT_EQ(serial_procs.fTypefaceCtx, &typeface_ctx);
DeserializationContext deserial_ctx;
SkDeserialProcs deserial_procs = MakeDeserialProcs(&deserial_ctx);
EXPECT_EQ(deserial_procs.fPictureCtx, &deserial_ctx);
// Check that serializing then deserialize the picture works produces a
// correct clip rect.
// Check that serializing then deserialize the picture works.
sk_sp<SkData> serial_pic_data =
serial_procs.fPictureProc(pic.get(), serial_procs.fPictureCtx);
sk_sp<SkPicture> deserial_pic = deserial_procs.fPictureProc(
serial_pic_data->data(), serial_pic_data->size(),
deserial_procs.fPictureCtx);
EXPECT_TRUE(deserial_ctx.count(content_id));
EXPECT_EQ(deserial_ctx[content_id].x(), pic->cullRect().x());
EXPECT_EQ(deserial_ctx[content_id].y(), pic->cullRect().y());
EXPECT_EQ(deserial_ctx[content_id].width(), pic->cullRect().width());
EXPECT_EQ(deserial_ctx[content_id].height(), pic->cullRect().height());
EXPECT_EQ(deserial_pic->uniqueID(), content_id);
}
TEST(PaintPreviewSerialUtils, TestSerialPictureNotInMap) {
......@@ -68,6 +65,29 @@ TEST(PaintPreviewSerialUtils, TestSerialPictureNotInMap) {
nullptr);
}
TEST(PaintPreviewSerialUtils, TestDeserialPictureNotInMap) {
uint32_t empty_content_id = 5;
DeserializationContext deserial_ctx;
EXPECT_TRUE(
deserial_ctx.insert(std::make_pair(empty_content_id, nullptr)).second);
SkDeserialProcs deserial_procs = MakeDeserialProcs(&deserial_ctx);
EXPECT_EQ(deserial_procs.fPictureCtx, &deserial_ctx);
sk_sp<SkPicture> deserial_pic =
deserial_procs.fPictureProc(nullptr, 0U, deserial_procs.fPictureCtx);
EXPECT_NE(deserial_pic, nullptr); // Produce empty pic rather than nullptr.
uint32_t missing_content_id = 5;
deserial_pic = deserial_procs.fPictureProc(&missing_content_id,
sizeof(missing_content_id),
deserial_procs.fPictureCtx);
EXPECT_NE(deserial_pic, nullptr); // Produce empty pic rather than nullptr.
deserial_pic = deserial_procs.fPictureProc(
&empty_content_id, sizeof(empty_content_id), deserial_procs.fPictureCtx);
EXPECT_NE(deserial_pic, nullptr); // Produce empty pic rather than nullptr.
}
TEST(PaintPreviewSerialUtils, TestSerialTypeface) {
PictureSerializationContext picture_ctx;
......
# 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.
import("//testing/test.gni")
assert(!is_ios, "Paint Previews are not supported on iOS.")
static_library("paint_preview_compositor") {
sources = [
"paint_preview_compositor_collection_impl.cc",
"paint_preview_compositor_collection_impl.h",
"paint_preview_compositor_impl.cc",
"paint_preview_compositor_impl.h",
"paint_preview_frame.cc",
"paint_preview_frame.h",
]
deps = [
"//base",
"//components/discardable_memory/client",
"//components/paint_preview/common",
"//components/paint_preview/common/proto",
"//mojo/public/cpp/bindings",
"//skia",
"//ui/gfx/geometry",
"//url",
]
if (is_win) {
deps += [ "//content/public/child" ]
}
public_deps = [
"//components/services/paint_preview_compositor/public/mojom",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"paint_preview_compositor_collection_impl_unittest.cc",
"paint_preview_compositor_impl_unittest.cc",
]
deps = [
":paint_preview_compositor",
"//base",
"//base/test:test_support",
"//components/paint_preview/common",
"//components/paint_preview/common/proto",
"//skia",
"//testing/gmock",
"//testing/gtest",
]
}
test("paint_preview_compositor_unit_tests") {
deps = [
":unit_tests",
"//base",
"//base/test:test_support",
"//components/test:run_all_unittests",
]
}
include_rules = [
"+components/discardable_memory/client",
"+components/paint_preview",
"+content/public/child", # Windows direct write proxy access.
"+mojo/public/cpp",
"+third_party/skia/include/core",
"+ui/gfx/geometry",
]
file://components/paint_preview/OWNERS
# COMPONENT: Internals>FreezeDriedTabs
Paint Preview Compositor is a service for compositing SkPictures and metadata
representing the painted contents of a webpage (collection of RenderFrames) into
bitmaps. These bitmaps can be consumed by a UI to replay a static version of a
webpage without a renderer (effectively a large screenshot represented by
paint-ops).
// 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/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h"
#include <utility>
#include "base/memory/discardable_memory.h"
#include "base/memory/discardable_memory_allocator.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#if defined(OS_WIN)
#include "content/public/child/dwrite_font_proxy_init_win.h"
#endif
namespace paint_preview {
PaintPreviewCompositorCollectionImpl::PaintPreviewCompositorCollectionImpl(
mojo::PendingReceiver<mojom::PaintPreviewCompositorCollection> receiver,
bool initialize_environment,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: io_task_runner_(std ::move(io_task_runner)) {
if (receiver)
receiver_.Bind(std::move(receiver));
if (!initialize_environment)
return;
#if defined(OS_WIN)
// Initialize direct write font proxy so skia can use it.
content::InitializeDWriteFontProxy();
#endif
// TODO(crbug/1013585): PDF compositor initializes Blink to leverage some
// codecs for images. This is a huge overhead and shouldn't be necessary for
// us. However, this may break some formats (WEBP?) so we may need to force
// encoding to PNG or we could provide our own codec implementations.
// Sanity check that fonts are working.
DCHECK(SkFontMgr::RefDefault()->countFamilies());
}
PaintPreviewCompositorCollectionImpl::~PaintPreviewCompositorCollectionImpl() {
#if defined(OS_WIN)
content::UninitializeDWriteFontProxy();
#endif
}
void PaintPreviewCompositorCollectionImpl::SetDiscardableSharedMemoryManager(
mojo::PendingRemote<
discardable_memory::mojom::DiscardableSharedMemoryManager> manager) {
mojo::PendingRemote<discardable_memory::mojom::DiscardableSharedMemoryManager>
manager_remote(std::move(manager));
discardable_shared_memory_manager_ = std::make_unique<
discardable_memory::ClientDiscardableSharedMemoryManager>(
std::move(manager_remote), io_task_runner_);
base::DiscardableMemoryAllocator::SetInstance(
discardable_shared_memory_manager_.get());
}
void PaintPreviewCompositorCollectionImpl::CreateCompositor(
mojo::PendingReceiver<mojom::PaintPreviewCompositor> receiver,
PaintPreviewCompositorCollectionImpl::CreateCompositorCallback callback) {
base::UnguessableToken token = base::UnguessableToken::Create();
compositors_.insert(
{token,
std::make_unique<PaintPreviewCompositorImpl>(
std::move(receiver),
base::BindOnce(&PaintPreviewCompositorCollectionImpl::OnDisconnect,
base::Unretained(this), token))});
std::move(callback).Run(token);
}
void PaintPreviewCompositorCollectionImpl::ListCompositors(
ListCompositorsCallback callback) {
std::vector<base::UnguessableToken> ids;
ids.reserve(compositors_.size());
for (const auto& compositor : compositors_)
ids.push_back(compositor.first);
std::move(callback).Run(std::move(ids));
}
void PaintPreviewCompositorCollectionImpl::OnDisconnect(
const base::UnguessableToken& id) {
compositors_.erase(id);
}
} // namespace paint_preview
// 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.
#ifndef COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_COLLECTION_IMPL_H_
#define COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_COLLECTION_IMPL_H_
#include <memory>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/unguessable_token.h"
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include "components/services/paint_preview_compositor/paint_preview_compositor_impl.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
namespace discardable_memory {
class ClientDiscardableSharedMemoryManager;
}
namespace paint_preview {
class PaintPreviewCompositorCollectionImpl
: public mojom::PaintPreviewCompositorCollection {
public:
// Create a new PaintPreviewCompositorCollectionImpl bound to |receiver| (can
// be nullptr for tests). Will attempt to initialize required font access if
// |initialize_environment| is true. |io_task_runner| is used by the
// discardable memory manager client for operations on shared memory.
PaintPreviewCompositorCollectionImpl(
mojo::PendingReceiver<mojom::PaintPreviewCompositorCollection> receiver,
bool initialize_environment,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
~PaintPreviewCompositorCollectionImpl() override;
// PaintPreviewCompositorCollection implementation.
void SetDiscardableSharedMemoryManager(
mojo::PendingRemote<
discardable_memory::mojom::DiscardableSharedMemoryManager> manager)
override;
void CreateCompositor(
mojo::PendingReceiver<mojom::PaintPreviewCompositor> compositor,
CreateCompositorCallback callback) override;
void ListCompositors(ListCompositorsCallback callback) override;
private:
// Invoked by a |compositor| when it is disconnected from its remote. Used to
// delete the corresponding instance from |compositors_|.
void OnDisconnect(const base::UnguessableToken& id);
mojo::Receiver<mojom::PaintPreviewCompositorCollection> receiver_{this};
const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
std::unique_ptr<discardable_memory::ClientDiscardableSharedMemoryManager>
discardable_shared_memory_manager_;
base::flat_map<base::UnguessableToken,
std::unique_ptr<PaintPreviewCompositorImpl>>
compositors_;
DISALLOW_COPY_AND_ASSIGN(PaintPreviewCompositorCollectionImpl);
};
} // namespace paint_preview
#endif // COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_COLLECTION_IMPL_H_
// 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/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h"
#include "base/bind.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace paint_preview {
namespace {
base::OnceCallback<void(const std::vector<base::UnguessableToken>&)>
ExpectedIdsCallbackFactory(
const std::vector<base::UnguessableToken>& expected_ids) {
return base::BindOnce(
[](const std::vector<base::UnguessableToken>& expected_compositor_ids,
const std::vector<base::UnguessableToken>& compositor_ids) {
EXPECT_THAT(compositor_ids, testing::UnorderedElementsAreArray(
expected_compositor_ids.begin(),
expected_compositor_ids.end()));
},
expected_ids);
}
} // namespace
TEST(PaintPreviewCompositorCollectionTest, TestAddCompositor) {
base::test::TaskEnvironment task_environment;
PaintPreviewCompositorCollectionImpl collection(mojo::NullReceiver(), false,
nullptr);
base::UnguessableToken token_1, token_2;
ASSERT_TRUE(token_1.is_empty());
ASSERT_TRUE(token_2.is_empty());
auto create_cb_1 = base::BindOnce(
[](base::UnguessableToken* out_token,
const base::UnguessableToken& token) { *out_token = token; },
base::Unretained(&token_1));
auto create_cb_2 = base::BindOnce(
[](base::UnguessableToken* out_token,
const base::UnguessableToken& token) { *out_token = token; },
base::Unretained(&token_2));
{
mojo::Remote<mojom::PaintPreviewCompositor> compositor_1;
collection.CreateCompositor(compositor_1.BindNewPipeAndPassReceiver(),
std::move(create_cb_1));
EXPECT_FALSE(token_1.is_empty());
EXPECT_TRUE(compositor_1.is_bound());
EXPECT_TRUE(compositor_1.is_connected());
collection.ListCompositors(ExpectedIdsCallbackFactory({token_1}));
{
mojo::Remote<mojom::PaintPreviewCompositor> compositor_2;
collection.CreateCompositor(compositor_2.BindNewPipeAndPassReceiver(),
std::move(create_cb_2));
EXPECT_FALSE(token_2.is_empty());
EXPECT_TRUE(compositor_2.is_bound());
EXPECT_TRUE(compositor_2.is_connected());
collection.ListCompositors(
ExpectedIdsCallbackFactory({token_1, token_2}));
}
task_environment.RunUntilIdle();
collection.ListCompositors(ExpectedIdsCallbackFactory({token_1}));
}
task_environment.RunUntilIdle();
auto expect_empty =
base::BindOnce([](const std::vector<base::UnguessableToken>& ids) {
EXPECT_TRUE(ids.empty());
});
collection.ListCompositors(std::move(expect_empty));
}
TEST(PaintPreviewCompositorCollectionTest,
TestCompositorRemoteOutlivesCollection) {
base::test::TaskEnvironment task_environment;
base::UnguessableToken token;
auto create_cb = base::BindOnce(
[](base::UnguessableToken* out_token,
const base::UnguessableToken& token) { *out_token = token; },
base::Unretained(&token));
ASSERT_TRUE(token.is_empty());
mojo::Remote<mojom::PaintPreviewCompositor> compositor;
{
PaintPreviewCompositorCollectionImpl collection(mojo::NullReceiver(), false,
nullptr);
collection.CreateCompositor(compositor.BindNewPipeAndPassReceiver(),
std::move(create_cb));
EXPECT_FALSE(token.is_empty());
EXPECT_TRUE(compositor.is_bound());
EXPECT_TRUE(compositor.is_connected());
}
task_environment.RunUntilIdle();
EXPECT_TRUE(compositor.is_bound());
EXPECT_FALSE(compositor.is_connected());
// Ensure this doesn't crash even if the collection is out of scope (thus all
// the receivers are deleted).
compositor->SetRootFrameUrl(GURL("https://www.foo.com"));
}
} // namespace paint_preview
// 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/services/paint_preview_compositor/paint_preview_compositor_impl.h"
#include <utility>
#include "components/paint_preview/common/file_stream.h"
#include "components/paint_preview/common/proto/paint_preview.pb.h"
#include "components/paint_preview/common/serial_utils.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkMatrix.h"
namespace paint_preview {
PaintPreviewCompositorImpl::PaintPreviewCompositorImpl(
mojo::PendingReceiver<mojom::PaintPreviewCompositor> receiver,
base::OnceClosure disconnect_handler) {
if (receiver) {
receiver_.Bind(std::move(receiver));
receiver_.set_disconnect_handler(std::move(disconnect_handler));
}
}
PaintPreviewCompositorImpl::~PaintPreviewCompositorImpl() {
receiver_.reset();
}
void PaintPreviewCompositorImpl::BeginComposite(
mojom::PaintPreviewBeginCompositeRequestPtr request,
BeginCompositeCallback callback) {
auto response = mojom::PaintPreviewBeginCompositeResponse::New();
auto mapping = request->proto.Map();
if (!mapping.IsValid()) {
std::move(callback).Run(
mojom::PaintPreviewCompositor::Status::kDeserializingFailure,
std::move(response));
return;
}
PaintPreviewProto paint_preview;
bool ok = paint_preview.ParseFromArray(mapping.memory(), mapping.size());
if (!ok) {
std::move(callback).Run(
mojom::PaintPreviewCompositor::Status::kDeserializingFailure,
std::move(response));
return;
}
if (!AddFrame(paint_preview.root_frame(), &request->file_map, &response)) {
std::move(callback).Run(
mojom::PaintPreviewCompositor::Status::kCompositingFailure,
std::move(response));
return;
}
response->root_frame_guid = paint_preview.root_frame().id();
for (const auto& subframe_proto : paint_preview.subframes())
AddFrame(subframe_proto, &request->file_map, &response);
std::move(callback).Run(mojom::PaintPreviewCompositor::Status::kSuccess,
std::move(response));
}
void PaintPreviewCompositorImpl::BitmapForFrame(
uint64_t frame_guid,
const gfx::Rect& clip_rect,
float scale_factor,
BitmapForFrameCallback callback) {
SkBitmap bitmap;
auto frame_it = frames_.find(frame_guid);
if (frame_it == frames_.end()) {
std::move(callback).Run(
mojom::PaintPreviewCompositor::Status::kCompositingFailure, bitmap);
return;
}
auto skp = frame_it->second.skp;
bitmap.allocPixels(
SkImageInfo::MakeN32Premul(clip_rect.width(), clip_rect.height()));
SkCanvas canvas(bitmap);
SkMatrix matrix;
matrix.setScaleTranslate(scale_factor, scale_factor, -clip_rect.x(),
-clip_rect.y());
canvas.drawPicture(skp, &matrix, nullptr);
std::move(callback).Run(mojom::PaintPreviewCompositor::Status::kSuccess,
bitmap);
}
void PaintPreviewCompositorImpl::SetRootFrameUrl(const GURL& url) {
url_ = url;
}
PaintPreviewFrame PaintPreviewCompositorImpl::DeserializeFrame(
const PaintPreviewFrameProto& frame_proto,
base::File file_handle) {
PaintPreviewFrame frame;
FileRStream rstream(std::move(file_handle));
DeserializationContext ctx;
SkDeserialProcs procs = MakeDeserialProcs(&ctx);
frame.skp = SkPicture::MakeFromStream(&rstream, &procs);
for (const auto& id_pair : frame_proto.content_id_proxy_id_map()) {
// It is possible that subframes recorded in this map were not captured
// (e.g. renderer crash, closed, etc.). Missing subframes are allowable
// since having just the main frame is sufficient to create a preview.
auto rect_it = ctx.find(id_pair.first);
if (rect_it == ctx.end())
continue;
mojom::SubframeClipRect rect;
rect.frame_guid = id_pair.second;
rect.clip_rect = rect_it->second;
frame.subframe_clip_rects.push_back(rect);
}
return frame;
}
bool PaintPreviewCompositorImpl::AddFrame(
const PaintPreviewFrameProto& frame_proto,
FileMap* file_map,
mojom::PaintPreviewBeginCompositeResponsePtr* response) {
uint64_t id = frame_proto.id();
auto file_it = file_map->find(id);
if (file_it == file_map->end() || !file_it->second.IsValid())
return false;
PaintPreviewFrame frame =
DeserializeFrame(frame_proto, std::move(file_it->second));
file_map->erase(file_it);
auto frame_data = mojom::FrameData::New();
SkRect sk_rect = frame.skp->cullRect();
frame_data->scroll_extents = gfx::Size(sk_rect.width(), sk_rect.height());
frame_data->subframes.reserve(frame.subframe_clip_rects.size());
for (const auto& subframe_clip_rect : frame.subframe_clip_rects)
frame_data->subframes.push_back(subframe_clip_rect.Clone());
(*response)->frames.insert({id, std::move(frame_data)});
frames_.insert({id, std::move(frame)});
return true;
}
} // namespace paint_preview
// 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.
#ifndef COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_IMPL_H_
#define COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_IMPL_H_
#include <stdint.h>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "components/paint_preview/common/proto/paint_preview.pb.h"
#include "components/services/paint_preview_compositor/paint_preview_frame.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
namespace paint_preview {
class PaintPreviewCompositorImpl : public mojom::PaintPreviewCompositor {
public:
using FileMap = base::flat_map<uint64_t, base::File>;
// Creates a new PaintPreviewCompositorImpl that receives mojo requests over
// |receiver|. |receiver| should be created by the remote and
// |disconnect_handler| is invoked when the remote closes the connection
// invalidating |receiver|.
//
// For testing |receiver| can be a NullReceiver (i.e. a 'local' instance not
// connected to a remote) and |disconnect_handler| should be a no-op.
explicit PaintPreviewCompositorImpl(
mojo::PendingReceiver<mojom::PaintPreviewCompositor> receiver,
base::OnceClosure disconnect_handler);
~PaintPreviewCompositorImpl() override;
// PaintPreviewCompositor implementation.
void BeginComposite(mojom::PaintPreviewBeginCompositeRequestPtr request,
BeginCompositeCallback callback) override;
void BitmapForFrame(uint64_t frame_guid,
const gfx::Rect& clip_rect,
float scale_factor,
BitmapForFrameCallback callback) override;
void SetRootFrameUrl(const GURL& url) override;
private:
// Deserializes the contents of |file_handle| and associates it with the
// metadata in |frame_proto|.
PaintPreviewFrame DeserializeFrame(const PaintPreviewFrameProto& frame_proto,
base::File file_handle);
// Adds |frame_proto| to |frames_| and copies required data into |response|.
// Consumes the corresponding file in |file_map|. Returns true on success.
bool AddFrame(const PaintPreviewFrameProto& frame_proto,
FileMap* file_map,
mojom::PaintPreviewBeginCompositeResponsePtr* response);
mojo::Receiver<mojom::PaintPreviewCompositor> receiver_{this};
GURL url_;
// A mapping from frame GUID to its associated data.
base::flat_map<int64_t, PaintPreviewFrame> frames_;
DISALLOW_COPY_AND_ASSIGN(PaintPreviewCompositorImpl);
};
} // namespace paint_preview
#endif // COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_COMPOSITOR_IMPL_H_
// 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/services/paint_preview_compositor/paint_preview_frame.h"
namespace paint_preview {
PaintPreviewFrame::PaintPreviewFrame() = default;
PaintPreviewFrame::~PaintPreviewFrame() = default;
PaintPreviewFrame::PaintPreviewFrame(PaintPreviewFrame&& other) = default;
PaintPreviewFrame& PaintPreviewFrame::operator=(PaintPreviewFrame&& other) =
default;
} // namespace paint_preview
// 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.
#ifndef COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_FRAME_H_
#define COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_FRAME_H_
#include <vector>
#include "base/macros.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkRefCnt.h"
namespace paint_preview {
// A deserialized in-memory representation of a PaintPreviewFrame and its
// associated subframe clip rects.
struct PaintPreviewFrame {
public:
PaintPreviewFrame();
~PaintPreviewFrame();
PaintPreviewFrame(PaintPreviewFrame&& other);
PaintPreviewFrame& operator=(PaintPreviewFrame&& other);
sk_sp<SkPicture> skp;
std::vector<mojom::SubframeClipRect> subframe_clip_rects;
private:
DISALLOW_COPY_AND_ASSIGN(PaintPreviewFrame);
};
} // namespace paint_preview
#endif // COMPONENTS_SERVICES_PAINT_PREVIEW_COMPOSITOR_PAINT_PREVIEW_FRAME_H_
# 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
"paint_preview_compositor.mojom",
]
public_deps = [
"//components/discardable_memory/public/mojom",
"//mojo/public/mojom/base",
"//skia/public/mojom",
"//ui/gfx/geometry/mojom",
"//url/mojom:url_mojom_gurl",
]
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
per-file *.typemap=set noparent
per-file *.typemap=file://ipc/SECURITY_OWNERS
per-file *_mojom_traits*.*=set noparent
per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
// 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.
module paint_preview.mojom;
import "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom";
import "mojo/public/mojom/base/file.mojom";
import "mojo/public/mojom/base/shared_memory.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "skia/public/mojom/bitmap.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "url/mojom/url.mojom";
// A request sent to the PaintPreviewCompositor to request deserialization.
struct PaintPreviewBeginCompositeRequest {
// A serialized PaintPreviewProto.
mojo_base.mojom.ReadOnlySharedMemoryRegion proto;
// A mapping between frame GUIDs and file handles.
map<uint64, mojo_base.mojom.File> file_map;
};
// A struct representing the clip rect of a subframe within its parent.
struct SubframeClipRect {
uint64 frame_guid;
gfx.mojom.Rect clip_rect;
};
// A struct representing the scroll extents and subframes of a parent frame.
struct FrameData {
// The dimensions of the frame.
gfx.mojom.Size scroll_extents;
// This is not a map because a parent can, in theory, embed the same subframe
// multiple times.
array<SubframeClipRect> subframes;
};
// A response sent as a result of a begin composite request. It provides the
// ID of the root frame (used for the primary UI) and the map can be used for
// looking up the data associated with each frame.
struct PaintPreviewBeginCompositeResponse {
uint64 root_frame_guid;
map<uint64, FrameData> frames;
};
// A compositor that converts a single paint preview into bitmaps.
interface PaintPreviewCompositor {
// The status of the action requested of the compositor.
enum Status {
// The request succeeded.
kSuccess = 0,
// Indicates there was an issue deserializing the proto or root frame.
// Problematic subframes are omitted since compositing can occur without
// them.
kDeserializingFailure = 1,
// Indicates there was a failure to composite a bitmap for the requested
// frame. Either; 1. it failed to deserialize or 2. there was an issue with
// the parameters provided.
kCompositingFailure = 2,
};
// Starts the compositing process for |request|. On success returns
// |response| containing metadata required for UI. |status| will be non-zero
// on failure.
BeginComposite(PaintPreviewBeginCompositeRequest request) =>
(Status status, PaintPreviewBeginCompositeResponse? response);
// Requests a bitmap associated with |frame_guid| the dimensions and
// location of the bitmap will match |clip_rect| at scale |scale_factor|.
// Returns |bitmap| on success and will indicate failure via a non-zero
// |status|.
BitmapForFrame(uint64 frame_guid, gfx.mojom.Rect clip_rect,
float scale_factor) => (Status status, skia.mojom.Bitmap? bitmap);
// Sets the root frame of the compositor. Used for tracing and diagnostics.
SetRootFrameUrl(url.mojom.Url url);
};
// Holds a collection of PaintPreviewCompositor instances running in the same
// process.
interface PaintPreviewCompositorCollection {
// Provides an interface for managing discardable shared memory regions. Must
// be called before calling any methods on managed PaintPreviewCompositors.
SetDiscardableSharedMemoryManager(
pending_remote<discardable_memory.mojom.DiscardableSharedMemoryManager>
manager);
// Adds a PaintPreviewCompositor to the utility process. Returns an ID for
// the compositor.
CreateCompositor(pending_receiver<PaintPreviewCompositor> compositor)
=> (mojo_base.mojom.UnguessableToken compositor_id);
// Returns a list of active compositor IDs.
ListCompositors() => (array<mojo_base.mojom.UnguessableToken> compositor_ids);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment