blob: 51b783d17c2c8be3ae33b7e1f7beaac6feaa03e4 [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/renderer/paint_preview_recorder_utils.h"
#include <string>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/unguessable_token.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_recorder.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.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"
#include "third_party/skia/include/core/SkTypeface.h"
namespace paint_preview {
namespace {
constexpr int32_t kRoutingId = 1;
} // namespace
TEST(PaintPreviewServiceUtilsTest, TestParseGlyphs) {
auto typeface = SkTypeface::MakeDefault();
SkFont font(typeface);
std::string unichars_1 = "abc";
std::string unichars_2 = "efg";
auto blob_1 = SkTextBlob::MakeFromString(unichars_1.c_str(), font);
auto blob_2 = SkTextBlob::MakeFromString(unichars_2.c_str(), font);
cc::PaintFlags flags;
cc::PaintRecorder outer_recorder;
cc::PaintCanvas* outer_canvas = outer_recorder.beginRecording(100, 100);
outer_canvas->drawTextBlob(blob_1, 10, 10, flags);
cc::PaintRecorder inner_recorder;
cc::PaintCanvas* inner_canvas = inner_recorder.beginRecording(50, 50);
inner_canvas->drawTextBlob(blob_2, 15, 20, flags);
outer_canvas->drawPicture(inner_recorder.finishRecordingAsPicture());
auto record = outer_recorder.finishRecordingAsPicture();
PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
true);
ParseGlyphs(record.get(), &tracker);
auto* usage_map = tracker.GetTypefaceUsageMap();
EXPECT_TRUE(usage_map->count(typeface->uniqueID()));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('a')));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('b')));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('c')));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('e')));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('f')));
EXPECT_TRUE(
(*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('g')));
}
TEST(PaintPreviewServiceUtilsTest, TestSerializeAsSkPicture) {
PaintPreviewTracker tracker(base::UnguessableToken::Create(), kRoutingId,
true);
gfx::Rect dimensions(100, 100);
cc::PaintRecorder recorder;
cc::PaintCanvas* canvas =
recorder.beginRecording(dimensions.width(), dimensions.width());
cc::PaintFlags flags;
canvas->drawRect(SkRect::MakeWH(dimensions.width(), dimensions.height()),
flags);
base::flat_set<uint32_t> ctx;
uint32_t content_id =
tracker.CreateContentForRemoteFrame(gfx::Rect(10, 10), kRoutingId + 1);
canvas->recordCustomData(content_id);
ctx.insert(content_id);
content_id =
tracker.CreateContentForRemoteFrame(gfx::Rect(20, 20), kRoutingId + 2);
canvas->recordCustomData(content_id);
ctx.insert(content_id);
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
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);
auto record = recorder.finishRecordingAsPicture();
EXPECT_TRUE(SerializeAsSkPicture(record, &tracker, dimensions,
std::move(write_file)));
base::File read_file(file_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ);
FileRStream rstream(std::move(read_file));
SkDeserialProcs procs;
procs.fPictureProc = [](const void* data, size_t length, void* ctx) {
uint32_t content_id;
if (length < sizeof(content_id))
return MakeEmptyPicture();
memcpy(&content_id, data, sizeof(content_id));
auto* context = reinterpret_cast<base::flat_set<uint32_t>*>(ctx);
EXPECT_TRUE(context->count(content_id));
context->erase(content_id);
return MakeEmptyPicture();
};
procs.fPictureCtx = &ctx;
SkPicture::MakeFromStream(&rstream, &procs);
EXPECT_TRUE(ctx.empty());
}
TEST(PaintPreviewServiceUtilsTest, TestBuildAndSerializeProto) {
auto token = base::UnguessableToken::Create();
PaintPreviewTracker tracker(token, kRoutingId, true);
tracker.AnnotateLink(GURL("www.google.com"), gfx::Rect(1, 2, 3, 4));
tracker.AnnotateLink(GURL("www.chromium.org"), gfx::Rect(10, 20, 10, 20));
tracker.CreateContentForRemoteFrame(gfx::Rect(1, 1, 1, 1), kRoutingId + 1);
tracker.CreateContentForRemoteFrame(gfx::Rect(1, 2, 4, 8), kRoutingId + 2);
auto response = mojom::PaintPreviewCaptureResponse::New();
BuildResponse(&tracker, response.get());
EXPECT_EQ(static_cast<int>(response->id), kRoutingId);
EXPECT_EQ(response->links.size(), 2U);
EXPECT_EQ(response->links.size(), tracker.GetLinks().size());
for (size_t i = 0; i < response->links.size(); ++i) {
EXPECT_THAT(response->links[i]->url, tracker.GetLinks()[i].url);
EXPECT_THAT(response->links[i]->rect, tracker.GetLinks()[i].rect);
}
auto* content_map = tracker.GetPictureSerializationContext();
for (const auto& id_pair : response->content_id_proxy_id_map) {
auto it = content_map->find(id_pair.first);
EXPECT_NE(it, content_map->end());
EXPECT_EQ(id_pair.first, it->first);
EXPECT_EQ(id_pair.second, it->second);
}
}
} // namespace paint_preview