#include "pdf/pdf_ink_module.h"
#include <algorithm>
#include <array>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path.h"
#include "base/strings/to_string.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
#include "base/types/zip.h"
#include "base/values.h"
#include "pdf/page_orientation.h"
#include "pdf/pdf_caret.h"
#include "pdf/pdf_features.h"
#include "pdf/pdf_ink_brush.h"
#include "pdf/pdf_ink_conversions.h"
#include "pdf/pdf_ink_metrics_handler.h"
#include "pdf/pdf_ink_module_client.h"
#include "pdf/pdf_ink_transform.h"
#include "pdf/pdfium/pdfium_ink_reader.h"
#include "pdf/test/input_event_util.h"
#include "pdf/test/mock_pdf_caret_client.h"
#include "pdf/test/mouse_event_builder.h"
#include "pdf/test/pdf_ink_test_helpers.h"
#include "pdf/test/test_helpers.h"
#include "pdf/ui/thumbnail.h"
#include "printing/units.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/common/input/web_touch_event.h"
#include "third_party/ink/src/ink/brush/brush.h"
#include "third_party/ink/src/ink/brush/type_matchers.h"
#include "third_party/ink/src/ink/geometry/affine_transform.h"
#include "third_party/ink/src/ink/strokes/input/stroke_input_batch.h"
#include "third_party/ink/src/ink/strokes/input/type_matchers.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/mojom/cursor_type.mojom.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d_f.h"
using testing::_;
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Field;
using testing::InSequence;
using testing::NiceMock;
using testing::Pair;
using testing::Pointwise;
using testing::Return;
using testing::SizeIs;
namespace chrome_pdf {
namespace {
constexpr gfx::PointF kLeftVerticalStrokePoint1(10.0f, 15.0f);
constexpr gfx::PointF kLeftVerticalStrokePoint2(10.0f, 35.0f);
constexpr gfx::PointF kRightVerticalStrokePoint1(40.0f, 15.0f);
constexpr gfx::PointF kRightVerticalStrokePoint2(40.0f, 35.0f);
constexpr gfx::RectF kVerticalLayout2Pages[] = {
gfx::RectF(5.0f,
5.0f,
50.0f,
60.0f),
gfx::RectF(5.0f,
70.0f,
50.0f,
60.0f),
};
constexpr gfx::PointF kTwoPageVerticalLayoutPointOutsidePages(10.0f, 0.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint1InsidePage0(10.0f, 10.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint2InsidePage0(15.0f, 15.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint3InsidePage0(20.0f, 15.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint4InsidePage0(10.0f, 20.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint1InsidePage1(10.0f, 75.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint2InsidePage1(15.0f, 80.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPoint3InsidePage1(20.0f, 80.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutHorzLinePoint0Canonical(10.0f,
10.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutHorzLinePoint1Canonical(15.0f,
10.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutVertLinePoint0Canonical(5.0f, 5.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutVertLinePoint1Canonical(5.0f,
15.0f);
constexpr gfx::PointF kTwoPageVerticalLayoutPageExitAndReentryPoints[] = {
gfx::PointF(10.0f, 5.0f), gfx::PointF(10.0f, 0.0f),
gfx::PointF(15.0f, 0.0f), gfx::PointF(15.0f, 5.0f),
gfx::PointF(15.0f, 10.0f)};
constexpr gfx::PointF kTwoPageVerticalLayoutPageExitAndReentrySegment1[] = {
gfx::PointF(5.0f, 5.0f), gfx::PointF(5.0f, 0.0f)};
constexpr gfx::PointF kTwoPageVerticalLayoutPageExitAndReentrySegment2[] = {
gfx::PointF(10.0f, 0.0f), gfx::PointF(10.0f, 5.0f),
gfx::PointF(15.0f, 10.0f)};
constexpr auto kTwoPageVerticalLayoutHorzLinePage0Inputs =
std::to_array<PdfInkInputData>({
{kTwoPageVerticalLayoutHorzLinePoint0Canonical, base::Seconds(0)},
{kTwoPageVerticalLayoutHorzLinePoint1Canonical, base::Seconds(0)},
});
constexpr auto kTwoPageVerticalLayoutVertLinePage0Inputs =
std::to_array<PdfInkInputData>({
{kTwoPageVerticalLayoutVertLinePoint0Canonical, base::Seconds(0)},
{kTwoPageVerticalLayoutVertLinePoint1Canonical, base::Seconds(0)},
});
constexpr auto kTwoPageVerticalLayoutHorzLinePage1Inputs =
std::to_array<PdfInkInputData>({
{kTwoPageVerticalLayoutHorzLinePoint0Canonical, base::Seconds(0)},
{kTwoPageVerticalLayoutHorzLinePoint1Canonical, base::Seconds(0)},
});
constexpr SkColor kYellow = SkColorSetRGB(0xFD, 0xD6, 0x63);
constexpr TestAnnotationBrushMessageParams kRedBrushParams{
SkColorSetRGB(0xF2, 0x8B, 0x82),
6.0};
MATCHER_P(InkStrokeEq, expected_brush, "") {
const auto& [actual_stroke, expected_inputs] = arg;
const auto brush_matcher = ink::BrushEq(expected_brush);
const auto input_matcher = ink::StrokeInputBatchEq(expected_inputs);
return testing::Matches(brush_matcher)(actual_stroke->GetBrush()) &&
testing::Matches(input_matcher)(actual_stroke->GetInputs());
}
MATCHER_P(InkStrokeBrushColorEq, expected_color, "") {
return chrome_pdf::GetSkColorFromInkBrush(arg.GetBrush()) == expected_color;
}
MATCHER_P(InkStrokeBrushSizeEq, expected_size, "") {
return arg.GetBrush().GetSize() == expected_size;
}
MATCHER_P(InkStrokeDrawingBrushTypeEq, expected_type, "") {
const float opacity = GetOpacityMultiplierFromBrush(arg.GetBrush());
if (expected_type == PdfInkBrush::Type::kPen) {
return opacity == 1.0f;
}
CHECK_EQ(expected_type, PdfInkBrush::Type::kHighlighter);
return opacity == 0.4f;
}
MATCHER_P(CursorBitmapImageSizeEq, dimensions, "") {
return arg.type() == ui::mojom::CursorType::kCustom &&
arg.custom_bitmap().dimensions() == dimensions;
}
MATCHER_P2(WebKeyboardEventEq, key, modifiers, "") {
if (arg.windows_key_code != key) {
*result_listener << "Expected key: " << key
<< " actual: " << arg.windows_key_code;
return false;
}
if (arg.GetModifiers() != modifiers) {
*result_listener << "Expected modifiers: " << modifiers
<< " actual: " << arg.GetModifiers();
return false;
}
return true;
}
base::Value::Dict CreateGetAnnotationBrushMessage(
const std::string& brush_type) {
auto message = base::Value::Dict()
.Set("type", "getAnnotationBrush")
.Set("messageId", "foo");
if (!brush_type.empty()) {
message.Set("brushType", brush_type);
}
return message;
}
blink::WebTouchEvent CreateTouchEvent(blink::WebInputEvent::Type type,
base::span<const gfx::PointF> points) {
CHECK_LE(points.size(), blink::WebTouchEvent::kTouchesLengthCap);
constexpr int kNoModifiers = 0;
blink::WebTouchEvent touch_event(
type, kNoModifiers, blink::WebInputEvent::GetStaticTimeStampForTests());
for (auto [touch, point] : base::zip(touch_event.touches, points)) {
touch.SetPositionInWidget(point);
}
touch_event.touches_length = points.size();
return touch_event;
}
blink::WebTouchEvent CreatePenEvent(blink::WebInputEvent::Type type,
base::span<const gfx::PointF> points) {
blink::WebTouchEvent pen_event = CreateTouchEvent(type, points);
for (size_t i = 0; i < pen_event.touches_length; ++i) {
pen_event.touches[i].pointer_type =
blink::WebPointerProperties::PointerType::kPen;
}
return pen_event;
}
class FakeClient : public PdfInkModuleClient {
public:
FakeClient() = default;
FakeClient(const FakeClient&) = delete;
FakeClient& operator=(const FakeClient&) = delete;
~FakeClient() override = default;
MOCK_METHOD(void,
DiscardStroke,
(int page_index, InkStrokeId id),
(override));
MOCK_METHOD(void,
ExtendSelectionByPoint,
(const gfx::PointF& point),
(override));
MOCK_METHOD(gfx::Transform,
GetCanonicalToPdfTransform,
(int page_index),
(override));
MOCK_METHOD(ui::Cursor, GetCursor, (), (override));
PageOrientation GetOrientation() const override { return orientation_; }
MOCK_METHOD(PdfCaret*, GetPdfCaret, (), (override));
MOCK_METHOD(SelectionRectMap, GetSelectionRectMap, (), (override));
gfx::Size GetThumbnailSize(int page_index) override {
CHECK_GE(page_index, 0);
CHECK_LT(static_cast<size_t>(page_index), page_layouts_.size());
return Thumbnail::CalculateImageSize(page_layouts_[page_index].size(),
1);
}
gfx::Vector2dF GetViewportOriginOffset() override {
return viewport_origin_offset_;
}
gfx::Rect GetPageContentsRect(int page_index) override {
CHECK_GE(page_index, 0);
CHECK_LT(static_cast<size_t>(page_index), page_layouts_.size());
return gfx::ToEnclosedRect(page_layouts_[page_index]);
}
gfx::SizeF GetPageSizeInPoints(int page_index) override {
CHECK_GE(page_index, 0);
CHECK_LT(static_cast<size_t>(page_index), page_layouts_.size());
gfx::SizeF page_size = page_layouts_[page_index].size();
page_size.Scale(printing::kUnitConversionFactorPixelsToPoints);
return page_size;
}
float GetZoom() const override { return zoom_; }
void Invalidate(const gfx::Rect& rect) override {
invalidations_.push_back(rect);
}
bool IsPageVisible(int page_index) override {
return base::Contains(visible_page_indices_, page_index);
}
MOCK_METHOD(bool,
IsSelectableTextOrLinkArea,
(const gfx::PointF& point),
(override));
MOCK_METHOD(PdfInkModuleClient::DocumentV2InkPathShapesMap,
LoadV2InkPathsFromPdf,
(),
(override));
MOCK_METHOD(void,
OnTextOrLinkAreaClick,
(const gfx::PointF& point, int click_count),
(override));
MOCK_METHOD(int, PageIndexFromPoint, (const gfx::PointF& point), (override));
MOCK_METHOD(void, PostMessage, (base::Value::Dict message), (override));
MOCK_METHOD(void,
RequestThumbnail,
(int page_index, SendThumbnailCallback callback),
(override));
MOCK_METHOD(void,
StrokeAdded,
(int page_index, InkStrokeId id, const ink::Stroke& stroke),
(override));
void StrokeFinished(bool modified) override {
if (modified) {
++modified_stroke_finished_count_;
} else {
++unmodified_stroke_finished_count_;
}
}
void StrokeStarted() override { ++stroke_started_count_; }
MOCK_METHOD(void, UpdateInkCursor, (const ui::Cursor&), (override));
MOCK_METHOD(void,
UpdateShapeActive,
(int page_index, InkModeledShapeId id, bool active),
(override));
MOCK_METHOD(void,
UpdateStrokeActive,
(int page_index, InkStrokeId id, bool active),
(override));
int VisiblePageIndexFromPoint(const gfx::PointF& point) override {
for (size_t i = 0; i < page_layouts_.size(); ++i) {
if (IsPageVisible(i) && page_layouts_[i].Contains(point)) {
return i;
}
}
return -1;
}
int stroke_started_count() const { return stroke_started_count_; }
int modified_stroke_finished_count() const {
return modified_stroke_finished_count_;
}
int unmodified_stroke_finished_count() const {
return unmodified_stroke_finished_count_;
}
const std::vector<gfx::Rect>& invalidations() const { return invalidations_; }
void set_page_layouts(base::span<const gfx::RectF> page_layouts) {
page_layouts_ = base::ToVector(page_layouts);
}
void set_page_visibility(int index, bool visible) {
if (visible) {
visible_page_indices_.insert(index);
} else {
visible_page_indices_.erase(index);
}
}
void set_orientation(PageOrientation orientation) {
orientation_ = orientation;
}
void set_viewport_origin_offset(const gfx::Vector2dF& offset) {
viewport_origin_offset_ = offset;
}
void set_zoom(float zoom) { zoom_ = zoom; }
private:
int stroke_started_count_ = 0;
int modified_stroke_finished_count_ = 0;
int unmodified_stroke_finished_count_ = 0;
std::vector<gfx::RectF> page_layouts_;
std::set<int> visible_page_indices_;
PageOrientation orientation_ = PageOrientation::kOriginal;
gfx::Vector2dF viewport_origin_offset_;
float zoom_ = 1.0f;
std::vector<gfx::Rect> invalidations_;
};
class MockPdfCaret : public PdfCaret {
public:
explicit MockPdfCaret(PdfCaretClient* client) : PdfCaret(client) {}
MockPdfCaret(const MockPdfCaret&) = delete;
MockPdfCaret& operator=(const MockPdfCaret&) = delete;
~MockPdfCaret() override = default;
MOCK_METHOD(bool,
OnKeyDown,
(const blink::WebKeyboardEvent& event),
(override));
};
class PdfInkModuleMetricsTestBase {
protected:
static constexpr char kHighlighterColorMetric[] =
"PDF.Ink2StrokeHighlighterColor";
static constexpr char kHighlighterSizeMetric[] =
"PDF.Ink2StrokeHighlighterSize";
static constexpr char kInputDeviceMetric[] = "PDF.Ink2StrokeInputDeviceType";
static constexpr char kTypeMetric[] = "PDF.Ink2StrokeBrushType";
base::HistogramTester& histograms() { return histograms_; }
private:
base::HistogramTester histograms_;
};
class PdfInkModuleTest : public testing::TestWithParam<InkTestVariation> {
public:
void SetUp() override {
feature_list_.InitAndEnableFeatureWithParameters(
chrome_pdf::features::kPdfInk2,
{{features::kPdfInk2TextAnnotations.name,
base::ToString(UseTextAnnotations())},
{features::kPdfInk2TextHighlighting.name,
base::ToString(UseTextHighlighting())}});
ink_module_ = std::make_unique<PdfInkModule>(client_);
}
protected:
bool UseTextAnnotations() const { return GetParam().use_text_annotations; }
bool UseTextHighlighting() const { return GetParam().use_text_highlighting; }
void EnableDrawAnnotationMode() {
EXPECT_TRUE(ink_module().OnMessage(
CreateSetAnnotationModeMessageForTesting(InkAnnotationMode::kDraw)));
EXPECT_TRUE(ink_module().enabled());
}
void VerifyAndClearExpectations() {
testing::Mock::VerifyAndClearExpectations(this);
}
FakeClient& client() { return client_; }
PdfInkModule& ink_module() { return *ink_module_; }
const PdfInkModule& ink_module() const { return *ink_module_; }
private:
base::test::ScopedFeatureList feature_list_;
NiceMock<FakeClient> client_;
std::unique_ptr<PdfInkModule> ink_module_;
};
}
TEST_P(PdfInkModuleTest, UnknownMessage) {
EXPECT_FALSE(
ink_module().OnMessage(base::Value::Dict().Set("type", "nonInkMessage")));
}
TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageEraser) {
EnableDrawAnnotationMode();
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "eraser",
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(
ink_module().OnMessage(CreateGetAnnotationBrushMessage("eraser")));
}
TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessagePen) {
EnableDrawAnnotationMode();
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "pen",
"size": 3.0,
"color": {
"r": 0,
"g": 0,
"b": 0,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(ink_module().OnMessage(CreateGetAnnotationBrushMessage("pen")));
}
TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageHighlighter) {
EnableDrawAnnotationMode();
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "highlighter",
"size": 8.0,
"color": {
"r": 242,
"g": 139,
"b": 130,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(
ink_module().OnMessage(CreateGetAnnotationBrushMessage("highlighter")));
}
TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageDefault) {
EnableDrawAnnotationMode();
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "pen",
"size": 3.0,
"color": {
"r": 0,
"g": 0,
"b": 0,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(ink_module().OnMessage(CreateGetAnnotationBrushMessage("")));
}
TEST_P(PdfInkModuleTest, HandleGetAnnotationBrushMessageCurrent) {
EnableDrawAnnotationMode();
EXPECT_TRUE(ink_module().OnMessage(
CreateSetAnnotationBrushMessageForTesting("eraser", nullptr)));
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "eraser",
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(ink_module().OnMessage(CreateGetAnnotationBrushMessage("")));
}
TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageEraser) {
EnableDrawAnnotationMode();
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("eraser", nullptr);
EXPECT_TRUE(ink_module().OnMessage(message));
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
EXPECT_FALSE(brush);
}
TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessagePen) {
EnableDrawAnnotationMode();
TestAnnotationBrushMessageParams message_params{kYellow,
8.0};
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
EXPECT_TRUE(ink_module().OnMessage(message));
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
const ink::Brush& ink_brush = brush->ink_brush();
EXPECT_EQ(kYellow, GetSkColorFromInkBrush(ink_brush));
EXPECT_EQ(8.0f, ink_brush.GetSize());
ASSERT_EQ(1u, ink_brush.CoatCount());
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
EXPECT_EQ(1.0f, coat.tip.corner_rounding);
EXPECT_EQ(1.0f, GetOpacityMultiplierFromBrush(ink_brush));
}
TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageHighlighter) {
EnableDrawAnnotationMode();
TestAnnotationBrushMessageParams message_params{kYellow,
4.5};
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("highlighter", &message_params);
EXPECT_TRUE(ink_module().OnMessage(message));
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
const ink::Brush& ink_brush = brush->ink_brush();
EXPECT_EQ(kYellow, GetSkColorFromInkBrush(ink_brush));
EXPECT_EQ(4.5f, ink_brush.GetSize());
ASSERT_EQ(1u, ink_brush.CoatCount());
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
EXPECT_EQ(0.0f, coat.tip.corner_rounding);
EXPECT_EQ(0.4f, GetOpacityMultiplierFromBrush(ink_brush));
}
TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageColorZero) {
EnableDrawAnnotationMode();
TestAnnotationBrushMessageParams message_params{
SkColorSetRGB(0x00, 0x00, 0x00),
4.5};
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
EXPECT_TRUE(ink_module().OnMessage(message));
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
const ink::Brush& ink_brush = brush->ink_brush();
EXPECT_EQ(SK_ColorBLACK, GetSkColorFromInkBrush(ink_brush));
EXPECT_EQ(4.5f, ink_brush.GetSize());
ASSERT_EQ(1u, ink_brush.CoatCount());
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
EXPECT_EQ(1.0f, coat.tip.corner_rounding);
EXPECT_EQ(1.0f, GetOpacityMultiplierFromBrush(ink_brush));
}
TEST_P(PdfInkModuleTest, HandleSetAnnotationModeMessage) {
EXPECT_CALL(client(), LoadV2InkPathsFromPdf())
.WillOnce(Return(PdfInkModuleClient::DocumentV2InkPathShapesMap{
{0,
PdfInkModuleClient::PageV2InkPathShapesMap{
{InkModeledShapeId(0), ink::PartitionedMesh()},
{InkModeledShapeId(1), ink::PartitionedMesh()}}},
{3,
PdfInkModuleClient::PageV2InkPathShapesMap{
{InkModeledShapeId(2), ink::PartitionedMesh()}}},
}));
const auto kShapeMapMatcher = ElementsAre(
Pair(0, ElementsAre(Field(&PdfInkModule::LoadedV2ShapeState::id,
InkModeledShapeId(0)),
Field(&PdfInkModule::LoadedV2ShapeState::id,
InkModeledShapeId(1)))),
Pair(3, ElementsAre(Field(&PdfInkModule::LoadedV2ShapeState::id,
InkModeledShapeId(2)))));
EXPECT_FALSE(ink_module().enabled());
base::Value::Dict message =
CreateSetAnnotationModeMessageForTesting(InkAnnotationMode::kOff);
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_FALSE(ink_module().enabled());
EXPECT_TRUE(ink_module().loaded_v2_shapes_.empty());
message.Set("mode", "draw");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
message.Set("mode", "off");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_FALSE(ink_module().enabled());
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
if (UseTextAnnotations()) {
message.Set("mode", "text");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
message.Set("mode", "off");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_FALSE(ink_module().enabled());
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
}
}
TEST_P(PdfInkModuleTest, MaybeSetCursorWhenTogglingAnnotationMode) {
EXPECT_FALSE(ink_module().enabled());
EXPECT_CALL(client(), UpdateInkCursor(_)).WillOnce([this]() {
EXPECT_TRUE(ink_module().enabled());
});
base::Value::Dict message =
CreateSetAnnotationModeMessageForTesting(InkAnnotationMode::kDraw);
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
message.Set("mode", "off");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_FALSE(ink_module().enabled());
VerifyAndClearExpectations();
if (UseTextAnnotations()) {
EXPECT_CALL(client(), UpdateInkCursor(_));
message.Set("mode", "text");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
message.Set("mode", "off");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_FALSE(ink_module().enabled());
VerifyAndClearExpectations();
EXPECT_CALL(client(), UpdateInkCursor(_)).Times(3);
message.Set("mode", "draw");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
message.Set("mode", "text");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
message.Set("mode", "draw");
EXPECT_TRUE(ink_module().OnMessage(message));
EXPECT_TRUE(ink_module().enabled());
}
}
TEST_P(PdfInkModuleTest, MaybeSetCursorWhenChangingBrushes) {
{
InSequence seq;
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(6, bitmap.width());
EXPECT_EQ(6, bitmap.height());
});
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(20, bitmap.width());
EXPECT_EQ(20, bitmap.height());
});
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(6, bitmap.width());
EXPECT_EQ(6, bitmap.height());
});
}
EnableDrawAnnotationMode();
TestAnnotationBrushMessageParams message_params{
SkColorSetRGB(0x00, 0xFF, 0x00),
16.0};
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
EXPECT_TRUE(ink_module().OnMessage(message));
message = CreateSetAnnotationBrushMessageForTesting("eraser", nullptr);
EXPECT_TRUE(ink_module().OnMessage(message));
}
TEST_P(PdfInkModuleTest, MaybeSetCursorWhenChangingZoom) {
{
InSequence seq;
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(6, bitmap.width());
EXPECT_EQ(6, bitmap.height());
});
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(20, bitmap.width());
EXPECT_EQ(20, bitmap.height());
});
EXPECT_CALL(client(), UpdateInkCursor(_))
.WillOnce([](const ui::Cursor& cursor) {
ASSERT_EQ(ui::mojom::CursorType::kCustom, cursor.type());
const SkBitmap& bitmap = cursor.custom_bitmap();
EXPECT_EQ(10, bitmap.width());
EXPECT_EQ(10, bitmap.height());
});
}
EnableDrawAnnotationMode();
TestAnnotationBrushMessageParams message_params{
SkColorSetRGB(0x00, 0xFF, 0x00),
16.0};
base::Value::Dict message =
CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
EXPECT_TRUE(ink_module().OnMessage(message));
client().set_zoom(0.5f);
ink_module().OnGeometryChanged();
}
class PdfInkModuleStrokeTest : public PdfInkModuleTest {
protected:
static constexpr gfx::PointF kMouseDownPoint = gfx::PointF(10.0f, 15.0f);
static constexpr gfx::PointF kMouseMovePoint = gfx::PointF(20.0f, 25.0f);
static constexpr gfx::PointF kMouseUpPoint = gfx::PointF(30.0f, 17.0f);
static constexpr gfx::PointF kMousePoints[] = {
kMouseDownPoint, kMouseMovePoint, kMouseUpPoint};
using StrokeInputPoints = std::vector<gfx::PointF>;
using PageStrokeInputPoints = std::vector<StrokeInputPoints>;
using DocumentStrokeInputPointsMap = std::map<int, PageStrokeInputPoints>;
void SetUp() override {
PdfInkModuleTest::SetUp();
EXPECT_CALL(client(), PostMessage)
.WillRepeatedly([&](const base::Value::Dict& dict) {
const std::string* type = dict.FindString("type");
ASSERT_TRUE(type);
if (*type != "updateInk2Thumbnail") {
return;
}
std::optional<int> page_number = dict.FindInt("pageNumber");
ASSERT_TRUE(page_number.has_value());
std::optional<bool> is_ink = dict.FindBool("isInk");
ASSERT_TRUE(is_ink.has_value());
auto& updated = is_ink.value() ? updated_ink_thumbnail_page_indices_
: updated_pdf_thumbnail_page_indices_;
updated.push_back(page_number.value() - 1);
});
}
void InitializeSimpleSinglePageBasicLayout() {
constexpr gfx::RectF kPage(0.0f, 0.0f, 50.0f, 60.0f);
client().set_page_layouts(base::span_from_ref(kPage));
client().set_page_visibility(0, true);
}
void InitializeScaledLandscapeSinglePageBasicLayout() {
constexpr gfx::RectF kPage(0.0f, 0.0f, 120.0f, 100.0f);
client().set_page_layouts(base::span_from_ref(kPage));
client().set_page_visibility(0, true);
}
void InitializeVerticalTwoPageLayout() {
client().set_page_layouts(kVerticalLayout2Pages);
client().set_page_visibility(0, true);
client().set_page_visibility(1, true);
}
void ApplyStrokeWithMouseAtPoints(
const gfx::PointF& mouse_down_point,
base::span<const gfx::PointF> mouse_move_points,
const gfx::PointF& mouse_up_point) {
ApplyStrokeWithMouseAtPointsMaybeHandled(
mouse_down_point, mouse_move_points, mouse_up_point,
true);
}
void ApplyStrokeWithMouseAtPointsNotHandled(
const gfx::PointF& mouse_down_point,
base::span<const gfx::PointF> mouse_move_points,
const gfx::PointF& mouse_up_point) {
ApplyStrokeWithMouseAtPointsMaybeHandled(
mouse_down_point, mouse_move_points, mouse_up_point,
false);
}
void RunStrokeCheckTest(bool annotation_mode_enabled) {
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(
annotation_mode_enabled ? InkAnnotationMode::kDraw
: InkAnnotationMode::kOff)));
EXPECT_EQ(annotation_mode_enabled, ink_module().enabled());
ApplyStrokeWithMouseAtPointsMaybeHandled(
kMouseDownPoint, base::span_from_ref(kMouseMovePoint), kMouseUpPoint,
annotation_mode_enabled);
ValidateRunStrokeCheckTest(
annotation_mode_enabled);
}
void ApplyStrokeWithMouseAtMouseDownPoint() {
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint, base::span_from_ref(kMouseDownPoint), kMouseDownPoint);
}
void ApplyStrokeWithTouchAtPoints(
base::span<const gfx::PointF> touch_start_points,
std::vector<base::span<const gfx::PointF>> all_touch_move_points,
base::span<const gfx::PointF> touch_end_points) {
ApplyStrokeWithTouchAtPointsMaybeHandled(
touch_start_points, all_touch_move_points, touch_end_points,
true);
}
void ApplyStrokeWithTouchAtPointsNotHandled(
base::span<const gfx::PointF> touch_start_points,
std::vector<base::span<const gfx::PointF>> all_touch_move_points,
base::span<const gfx::PointF> touch_end_points) {
ApplyStrokeWithTouchAtPointsMaybeHandled(
touch_start_points, all_touch_move_points, touch_end_points,
false);
}
void RunStrokeTouchCheckTest(bool annotation_mode_enabled) {
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(
annotation_mode_enabled ? InkAnnotationMode::kDraw
: InkAnnotationMode::kOff)));
EXPECT_EQ(annotation_mode_enabled, ink_module().enabled());
const std::vector<base::span<const gfx::PointF>> all_touch_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithTouchAtPointsMaybeHandled(
base::span_from_ref(kMouseDownPoint), all_touch_move_points,
base::span_from_ref(kMouseUpPoint),
annotation_mode_enabled);
ValidateRunStrokeCheckTest(
annotation_mode_enabled);
}
void RunStrokeMultiTouchCheckTest(bool annotation_mode_enabled) {
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(
annotation_mode_enabled ? InkAnnotationMode::kDraw
: InkAnnotationMode::kOff)));
EXPECT_EQ(annotation_mode_enabled, ink_module().enabled());
const std::vector<gfx::PointF> touch_start_points{kMouseDownPoint,
kMouseDownPoint};
const std::vector<gfx::PointF> touch_move_points{kMouseMovePoint,
kMouseMovePoint};
const std::vector<base::span<const gfx::PointF>> all_touch_move_points{
touch_move_points,
};
const std::vector<gfx::PointF> touch_end_points{kMouseUpPoint,
kMouseUpPoint};
ApplyStrokeWithTouchAtPointsMaybeHandled(
touch_start_points, all_touch_move_points, touch_end_points,
false);
ValidateRunStrokeCheckTest(false);
}
void ApplyStrokeWithPenAtPoints(
base::span<const gfx::PointF> pen_start_points,
std::vector<base::span<const gfx::PointF>> all_pen_move_points,
base::span<const gfx::PointF> pen_end_points) {
ApplyStrokeWithPenAtPointsMaybeHandled(pen_start_points,
all_pen_move_points, pen_end_points,
true);
}
void RunStrokePenCheckTest(bool annotation_mode_enabled) {
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(
annotation_mode_enabled ? InkAnnotationMode::kDraw
: InkAnnotationMode::kOff)));
EXPECT_EQ(annotation_mode_enabled, ink_module().enabled());
const std::vector<base::span<const gfx::PointF>> all_pen_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithPenAtPointsMaybeHandled(
base::span_from_ref(kMouseDownPoint), all_pen_move_points,
base::span_from_ref(kMouseUpPoint),
annotation_mode_enabled);
ValidateRunStrokeCheckTest(
annotation_mode_enabled);
}
void RunStrokeMissedEndEventThenMouseMoveTest() {
{
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateMoveWebMouseEventToPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
}
{
constexpr gfx::PointF kMouseMovePoint2 = gfx::PointF(21.0f, 26.0f);
blink::WebMouseEvent mouse_move_event =
CreateMoveWebMouseEventToPosition(kMouseMovePoint2);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
}
{
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kMouseUpPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
}
void SelectBrushTool(PdfInkBrush::Type type,
const TestAnnotationBrushMessageParams& params) {
EXPECT_TRUE(
ink_module().OnMessage(CreateSetAnnotationBrushMessageForTesting(
PdfInkBrush::TypeToString(type), ¶ms)));
}
void SelectEraserTool() {
EXPECT_TRUE(ink_module().OnMessage(
CreateSetAnnotationBrushMessageForTesting("eraser", nullptr)));
}
DocumentStrokeInputPointsMap StrokeInputPositions() const {
return StrokeInputPositionsImpl(false);
}
DocumentStrokeInputPointsMap VisibleStrokeInputPositions() const {
return StrokeInputPositionsImpl(true);
}
std::map<int, std::vector<raw_ref<const ink::Stroke>>> CollectVisibleStrokes()
const {
PageInkStrokeIterator strokes_iter(ink_module().strokes_);
std::map<int, std::vector<raw_ref<const ink::Stroke>>> visible_strokes;
for (auto page_stroke = strokes_iter.GetNextStrokeAndAdvance();
page_stroke.has_value();
page_stroke = strokes_iter.GetNextStrokeAndAdvance()) {
visible_strokes[page_stroke.value().page_index].push_back(
page_stroke.value().stroke);
}
return visible_strokes;
}
int GetInputOfTypeCountForPage(int page_index,
ink::StrokeInput::ToolType tool_type) const {
CHECK_GE(page_index, 0);
const auto& strokes = ink_module().strokes_;
auto it = strokes.find(page_index);
if (it == strokes.end()) {
return 0;
}
int count = 0;
for (const auto& stroke_state : it->second) {
count +=
std::ranges::count_if(stroke_state.stroke.GetInputs(),
[&tool_type](const ink::StrokeInput& input) {
return input.tool_type == tool_type;
});
}
return count;
}
void ExpectStrokesAdded(int strokes_affected) {
CHECK_GT(strokes_affected, 0);
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(strokes_affected);
}
void ExpectNoStrokeAdded() {
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
}
void ExpectUpdateStrokesActive(int strokes_affected, bool expected_active) {
CHECK_GT(strokes_affected, 0);
EXPECT_CALL(client(), UpdateStrokeActive(_, _, expected_active))
.Times(strokes_affected);
}
void ExpectNoUpdateStrokeActive() {
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
}
void ExpectStrokeCounts(int started,
int modified_finished,
int unmodified_finished) {
EXPECT_EQ(started, client().stroke_started_count());
EXPECT_EQ(modified_finished, client().modified_stroke_finished_count());
EXPECT_EQ(unmodified_finished, client().unmodified_stroke_finished_count());
}
const std::vector<int>& updated_ink_thumbnail_page_indices() const {
return updated_ink_thumbnail_page_indices_;
}
const std::vector<int>& updated_pdf_thumbnail_page_indices() const {
return updated_pdf_thumbnail_page_indices_;
}
private:
struct PageInkStroke {
int page_index;
raw_ref<const ink::Stroke> stroke;
};
class PageInkStrokeIterator {
public:
explicit PageInkStrokeIterator(
const PdfInkModule::DocumentStrokesMap& strokes)
: strokes_(strokes), pages_iterator_(strokes_->cbegin()) {
AdvanceToNextPageWithVisibleStrokes();
}
PageInkStrokeIterator(const PageInkStrokeIterator&) = delete;
PageInkStrokeIterator& operator=(const PageInkStrokeIterator&) = delete;
~PageInkStrokeIterator() = default;
std::optional<PageInkStroke> GetNextStrokeAndAdvance() {
if (pages_iterator_ == strokes_->cend()) {
return std::nullopt;
}
CHECK(page_strokes_iterator_ != pages_iterator_->second.cend());
CHECK(page_strokes_iterator_->should_draw);
const ink::Stroke& page_stroke = page_strokes_iterator_->stroke;
int page_index = pages_iterator_->first;
AdvanceForCurrentPage();
if (page_strokes_iterator_ == pages_iterator_->second.cend()) {
++pages_iterator_;
AdvanceToNextPageWithVisibleStrokes();
}
return PageInkStroke{page_index, raw_ref<const ink::Stroke>(page_stroke)};
}
private:
void AdvanceToNextPageWithVisibleStrokes() {
for (; pages_iterator_ != strokes_->cend(); ++pages_iterator_) {
for (page_strokes_iterator_ = pages_iterator_->second.cbegin();
page_strokes_iterator_ != pages_iterator_->second.cend();
++page_strokes_iterator_) {
if (page_strokes_iterator_->should_draw) {
return;
}
}
}
}
void AdvanceForCurrentPage() {
CHECK(pages_iterator_ != strokes_->cend());
do {
++page_strokes_iterator_;
if (page_strokes_iterator_ == pages_iterator_->second.cend()) {
break;
}
} while (!page_strokes_iterator_->should_draw);
}
const raw_ref<const PdfInkModule::DocumentStrokesMap> strokes_;
PdfInkModule::DocumentStrokesMap::const_iterator pages_iterator_;
PdfInkModule::PageStrokes::const_iterator page_strokes_iterator_;
};
DocumentStrokeInputPointsMap StrokeInputPositionsImpl(
bool only_visible) const {
DocumentStrokeInputPointsMap all_strokes_points;
for (const auto& [page_index, strokes] : ink_module().strokes_) {
for (const auto& stroke : strokes) {
if (only_visible && !stroke.should_draw) {
continue;
}
const ink::StrokeInputBatch& input_batch = stroke.stroke.GetInputs();
StrokeInputPoints stroke_points;
stroke_points.reserve(input_batch.Size());
for (ink::StrokeInput stroke_input : input_batch) {
stroke_points.emplace_back(stroke_input.position.x,
stroke_input.position.y);
}
all_strokes_points[page_index].push_back(std::move(stroke_points));
}
}
return all_strokes_points;
}
void ApplyStrokeWithMouseAtPointsMaybeHandled(
const gfx::PointF& mouse_down_point,
base::span<const gfx::PointF> mouse_move_points,
const gfx::PointF& mouse_up_point,
bool expect_mouse_events_handled) {
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(mouse_down_point);
EXPECT_EQ(expect_mouse_events_handled,
ink_module().HandleInputEvent(mouse_down_event));
for (const gfx::PointF& mouse_move_point : mouse_move_points) {
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(mouse_move_point);
EXPECT_EQ(expect_mouse_events_handled,
ink_module().HandleInputEvent(mouse_move_event));
}
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(mouse_up_point);
EXPECT_EQ(expect_mouse_events_handled,
ink_module().HandleInputEvent(mouse_up_event));
}
void ApplyStrokeWithTouchAtPointsMaybeHandled(
base::span<const gfx::PointF> touch_start_points,
std::vector<base::span<const gfx::PointF>> all_touch_move_points,
base::span<const gfx::PointF> touch_end_points,
bool expect_touch_events_handled) {
blink::WebTouchEvent touch_start_event = CreateTouchEvent(
blink::WebInputEvent::Type::kTouchStart, touch_start_points);
EXPECT_EQ(expect_touch_events_handled,
ink_module().HandleInputEvent(touch_start_event));
for (const auto& touch_move_points : all_touch_move_points) {
blink::WebTouchEvent touch_move_event = CreateTouchEvent(
blink::WebInputEvent::Type::kTouchMove, touch_move_points);
EXPECT_EQ(expect_touch_events_handled,
ink_module().HandleInputEvent(touch_move_event));
}
blink::WebTouchEvent touch_end_event = CreateTouchEvent(
blink::WebInputEvent::Type::kTouchEnd, touch_end_points);
EXPECT_EQ(expect_touch_events_handled,
ink_module().HandleInputEvent(touch_end_event));
}
void ApplyStrokeWithPenAtPointsMaybeHandled(
base::span<const gfx::PointF> pen_start_points,
std::vector<base::span<const gfx::PointF>> all_pen_move_points,
base::span<const gfx::PointF> pen_end_points,
bool expect_pen_events_handled) {
blink::WebTouchEvent pen_start_event = CreatePenEvent(
blink::WebInputEvent::Type::kTouchStart, pen_start_points);
EXPECT_EQ(expect_pen_events_handled,
ink_module().HandleInputEvent(pen_start_event));
for (const auto& pen_move_points : all_pen_move_points) {
blink::WebTouchEvent pen_move_event = CreatePenEvent(
blink::WebInputEvent::Type::kTouchMove, pen_move_points);
EXPECT_EQ(expect_pen_events_handled,
ink_module().HandleInputEvent(pen_move_event));
}
blink::WebTouchEvent pen_end_event =
CreatePenEvent(blink::WebInputEvent::Type::kTouchEnd, pen_end_points);
EXPECT_EQ(expect_pen_events_handled,
ink_module().HandleInputEvent(pen_end_event));
}
void ValidateRunStrokeCheckTest(bool expect_stroke_success) {
EXPECT_EQ(expect_stroke_success ? 1 : 0, client().stroke_started_count());
EXPECT_EQ(expect_stroke_success ? 1 : 0,
client().modified_stroke_finished_count());
EXPECT_EQ(0, client().unmodified_stroke_finished_count());
if (expect_stroke_success) {
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
} else {
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
}
}
std::vector<int> updated_ink_thumbnail_page_indices_;
std::vector<int> updated_pdf_thumbnail_page_indices_;
};
TEST_P(PdfInkModuleStrokeTest, NoAnnotationWithMouseIfNotEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(false);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, AnnotationWithMouseIfEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, NoAnnotationWithTouchIfNotEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeTouchCheckTest(false);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, AnnotationWithTouchIfEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeTouchCheckTest(true);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, NoAnnotationWithMultiTouchIfNotEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeMultiTouchCheckTest(false);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, NoAnnotationWithMultiTouchIfEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeMultiTouchCheckTest(true);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, NoAnnotationWithPenIfNotEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokePenCheckTest(false);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, AnnotationWithPenIfEnabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokePenCheckTest(true);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, IgnoreTouchEventsAfterPenEvent) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
const std::vector<base::span<const gfx::PointF>> all_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
ApplyStrokeWithTouchAtPointsNotHandled(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
ApplyStrokeWithTouchAtPointsNotHandled(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(6, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, AnnotationWithMouseInterruptedByPenEvents) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_move_no_left_button_event =
CreateMoveWebMouseEventToPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_no_left_button_event));
const std::vector<base::span<const gfx::PointF>> all_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
all_move_points,
base::span_from_ref(kMouseUpPoint));
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kMouseUpPoint);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_up_event));
ExpectStrokeCounts(2, 2,
0);
EXPECT_EQ(2, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(3, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, AnnotationWithPenIgnoresMouseEvents) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
blink::WebTouchEvent pen_start_event =
CreatePenEvent(blink::WebInputEvent::Type::kTouchStart,
base::span_from_ref(kMouseDownPoint));
EXPECT_TRUE(ink_module().HandleInputEvent(pen_start_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebTouchEvent pen_end_event =
CreatePenEvent(blink::WebInputEvent::Type::kTouchEnd,
base::span_from_ref(kMouseUpPoint));
EXPECT_TRUE(ink_module().HandleInputEvent(pen_end_event));
ExpectStrokeCounts(1, 1,
0);
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
EXPECT_EQ(0, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kTouch));
EXPECT_EQ(2, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kStylus));
}
TEST_P(PdfInkModuleStrokeTest, CanonicalAnnotationPoints) {
constexpr gfx::SizeF kPageSize(100.0f, 120.0f);
constexpr gfx::PointF kPageOrigin(5.0f, -15.0f);
constexpr gfx::RectF kPageLayout(kPageOrigin, kPageSize);
client().set_page_layouts(base::span_from_ref(kPageLayout));
client().set_page_visibility(0, true);
client().set_orientation(PageOrientation::kClockwise180);
client().set_zoom(2.0f);
RunStrokeCheckTest(true);
constexpr gfx::PointF kCanonicalMouseDownPosition(47.0f, 44.5f);
constexpr gfx::PointF kCanonicalMouseMovePosition(42.0f, 39.5f);
constexpr gfx::PointF kCanonicalMouseUpPosition(37.0f, 43.5f);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, PageStrokeInputPoints{{kCanonicalMouseDownPosition,
kCanonicalMouseMovePosition,
kCanonicalMouseUpPosition}})));
}
TEST_P(PdfInkModuleStrokeTest, BasicLayoutInvalidationsFromStroke) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
constexpr gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8, 13),
gfx::Size(4, 4));
constexpr gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8, 13),
gfx::Size(14, 14));
constexpr gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18, 15),
gfx::Size(14, 12));
constexpr gfx::Rect kInvalidationAreaFinishedStroke(7, 12, 27, 9);
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaFinishedStroke));
}
TEST_P(PdfInkModuleStrokeTest, TransformedLayoutInvalidationsFromStroke) {
constexpr gfx::SizeF kPageSize(100.0f, 120.0f);
constexpr gfx::PointF kPageOrigin(5.0f, -15.0f);
constexpr gfx::RectF kPageLayout(kPageOrigin, kPageSize);
client().set_page_layouts(base::span_from_ref(kPageLayout));
client().set_page_visibility(0, true);
client().set_orientation(PageOrientation::kClockwise180);
client().set_zoom(2.0f);
RunStrokeCheckTest(true);
constexpr gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8, 13),
gfx::Size(4, 4));
constexpr gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8, 13),
gfx::Size(14, 14));
constexpr gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18, 15),
gfx::Size(14, 12));
constexpr gfx::Rect kInvalidationAreaFinishedStroke(6, 11, 29, 11);
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaFinishedStroke));
}
TEST_P(PdfInkModuleStrokeTest, StrokeOutsidePage) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ApplyStrokeWithMouseAtPointsNotHandled(
kTwoPageVerticalLayoutPointOutsidePages,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_TRUE(StrokeInputPositions().empty());
}
TEST_P(PdfInkModuleStrokeTest, StrokeInsidePages) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage1,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
kTwoPageVerticalLayoutPoint3InsidePage1);
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, SizeIs(1)), Pair(1, SizeIs(1))));
}
TEST_P(PdfInkModuleStrokeTest, StrokeAcrossPages) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
kTwoPageVerticalLayoutPoint3InsidePage1);
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
}
TEST_P(PdfInkModuleStrokeTest, StrokePageExitAndReentry) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
kTwoPageVerticalLayoutPageExitAndReentryPoints,
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(
0,
ElementsAre(ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
}
TEST_P(PdfInkModuleStrokeTest, StrokePageExitAndReentryWithQuickMoves) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
constexpr gfx::PointF kQuickPageExitAndReentryPoints[] = {
kTwoPageVerticalLayoutPointOutsidePages,
kTwoPageVerticalLayoutPoint2InsidePage0};
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
kQuickPageExitAndReentryPoints,
kTwoPageVerticalLayoutPoint2InsidePage0);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAre(ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
ElementsAreArray({gfx::PointF(6.666667f, 0.0f),
gfx::PointF(10.0f, 10.0f)})))));
}
TEST_P(PdfInkModuleStrokeTest, EraseStroke) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
SelectEraserTool();
ApplyStrokeWithMouseAtMouseDownPoint();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
ApplyStrokeWithMouseAtMouseDownPoint();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(3, 2,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
EXPECT_TRUE(updated_pdf_thumbnail_page_indices().empty());
}
TEST_P(PdfInkModuleStrokeTest, EraseOnPageWithoutStrokes) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
SelectEraserTool();
ApplyStrokeWithMouseAtMouseDownPoint();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(1, 0,
1);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokeEntirelyOffPage) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
SelectEraserTool();
constexpr gfx::PointF kOffPagePoint(99.0f, 99.0f);
ApplyStrokeWithMouseAtPointsNotHandled(
kOffPagePoint, base::span_from_ref(kOffPagePoint), kOffPagePoint);
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokeErasesTwoStrokes) {
InitializeSimpleSinglePageBasicLayout();
ExpectStrokesAdded(2);
ExpectNoUpdateStrokeActive();
RunStrokeCheckTest(true);
constexpr gfx::PointF kMouseDownPoint2 = gfx::PointF(10.0f, 30.0f);
constexpr gfx::PointF kMouseUpPoint2 = gfx::PointF(30.0f, 30.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint2, base::span_from_ref(kMouseMovePoint), kMouseUpPoint2);
const auto kStroke2Matcher =
ElementsAre(kMouseDownPoint2, kMouseMovePoint, kMouseUpPoint2);
const auto kVisibleStrokesMatcher = ElementsAre(
Pair(0, ElementsAre(ElementsAreArray(kMousePoints), kStroke2Matcher)));
EXPECT_THAT(VisibleStrokeInputPositions(), kVisibleStrokesMatcher);
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
SelectEraserTool();
ApplyStrokeWithMouseAtPoints(
kMouseMovePoint, base::span_from_ref(kMouseMovePoint), kMouseMovePoint);
EXPECT_THAT(VisibleStrokeInputPositions(), kVisibleStrokesMatcher);
ExpectStrokeCounts(3, 2,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
SelectEraserTool();
VerifyAndClearExpectations();
ExpectNoStrokeAdded();
ExpectUpdateStrokesActive(2, false);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint, base::span_from_ref(kMouseMovePoint), kMouseUpPoint);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint2, base::span_from_ref(kMouseMovePoint), kMouseUpPoint2);
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(5, 4,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0, 0, 0));
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokesAcrossTwoPages) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ExpectStrokeCounts(0, 0,
0);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
ExpectStrokesAdded(2);
ExpectNoUpdateStrokeActive();
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage1,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
kTwoPageVerticalLayoutPoint3InsidePage1);
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, SizeIs(1)), Pair(1, SizeIs(1))));
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 1));
SelectEraserTool();
VerifyAndClearExpectations();
ExpectNoStrokeAdded();
ExpectUpdateStrokesActive(2, false);
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
std::vector<gfx::PointF>{kTwoPageVerticalLayoutPoint2InsidePage0,
kTwoPageVerticalLayoutPoint1InsidePage1},
kTwoPageVerticalLayoutPoint3InsidePage1);
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(3, 3,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 1, 0, 1));
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokePageExitAndReentry) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
EXPECT_TRUE(StrokeInputPositions().empty());
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
kTwoPageVerticalLayoutPageExitAndReentryPoints,
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(
0,
ElementsAre(ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
SelectEraserTool();
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
kTwoPageVerticalLayoutPageExitAndReentryPoints,
kTwoPageVerticalLayoutPoint3InsidePage0);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(
0,
ElementsAre(ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
ElementsAreArray(
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokeWithTouch) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeTouchCheckTest(true);
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
SelectEraserTool();
const std::vector<base::span<const gfx::PointF>> touch_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint),
touch_move_points,
base::span_from_ref(kMouseDownPoint));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint),
touch_move_points,
base::span_from_ref(kMouseDownPoint));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(3, 2,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
ApplyStrokeWithMouseAtMouseDownPoint();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(4, 2,
2);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
}
TEST_P(PdfInkModuleStrokeTest, EraseStrokeWithPen) {
InitializeSimpleSinglePageBasicLayout();
RunStrokePenCheckTest(true);
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
SelectEraserTool();
const std::vector<base::span<const gfx::PointF>> pen_move_points{
base::span_from_ref(kMouseMovePoint),
};
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
pen_move_points,
base::span_from_ref(kMouseDownPoint));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(2, 2,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
pen_move_points,
base::span_from_ref(kMouseDownPoint));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(3, 2,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
ApplyStrokeWithMouseAtMouseDownPoint();
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(4, 2,
2);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
}
TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseDown) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
}
TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseMoveDuringDrawing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_TRUE(ink_module().OnMessage(CreateGetAnnotationBrushMessage("pen")));
RunStrokeMissedEndEventThenMouseMoveTest();
}
TEST_P(PdfInkModuleStrokeTest, StrokeMissedEndEventThenMouseMoveDuringErasing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectEraserTool();
RunStrokeMissedEndEventThenMouseMoveTest();
}
TEST_P(PdfInkModuleStrokeTest, StrokeWithNoEndEventThenTouchStart) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kMouseMovePoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
EXPECT_TRUE(ink_module().HandleInputEvent(
CreateTouchEvent(blink::WebInputEvent::Type::kTouchStart,
base::span_from_ref(kMouseDownPoint))));
}
TEST_P(PdfInkModuleStrokeTest, ChangeBrushColorDuringDrawing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
TestAnnotationBrushMessageParams black_pen_message_params{
SkColorSetRGB(0x00, 0x00, 0x00),
3.0};
SelectBrushTool(PdfInkBrush::Type::kPen, black_pen_message_params);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kLeftVerticalStrokePoint1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
TestAnnotationBrushMessageParams red_pen_message_params{
SkColorSetRGB(0xF2, 0x8B, 0x82),
3.0};
SelectBrushTool(PdfInkBrush::Type::kPen, red_pen_message_params);
VerifyAndClearExpectations();
static constexpr int kPageIndex = 0;
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(0),
InkStrokeBrushColorEq(SK_ColorBLACK)));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
VerifyAndClearExpectations();
EXPECT_CALL(
client(),
StrokeAdded(kPageIndex, InkStrokeId(1),
InkStrokeBrushColorEq(SkColorSetRGB(0xF2, 0x8B, 0x82))));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleStrokeTest, ChangeBrushSizeDuringDrawing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(6, 6))));
TestAnnotationBrushMessageParams message_params{
SkColorSetRGB(0x00, 0x00, 0x00),
2.0};
SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kLeftVerticalStrokePoint1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
message_params.size = 6.0f;
SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
VerifyAndClearExpectations();
static constexpr int kPageIndex = 0;
{
InSequence seq;
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(0),
InkStrokeBrushSizeEq(2.0f)));
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
}
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(1),
InkStrokeBrushSizeEq(6.0f)));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleStrokeTest, ChangeToEraserDuringDrawing) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
static constexpr int kPageIndex = 0;
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(1), _));
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kRightVerticalStrokePoint1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
SelectEraserTool();
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kRightVerticalStrokePoint2);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kRightVerticalStrokePoint2);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_up_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(),
UpdateStrokeActive(kPageIndex, InkStrokeId(1), false));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleStrokeTest, ChangeToDrawingDuringErasing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
static constexpr int kPageIndex = 0;
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(0), _));
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(1), _));
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
ApplyStrokeWithMouseAtPoints(kLeftVerticalStrokePoint1,
base::span_from_ref(kLeftVerticalStrokePoint2),
kLeftVerticalStrokePoint2);
ApplyStrokeWithMouseAtPoints(kRightVerticalStrokePoint1,
base::span_from_ref(kRightVerticalStrokePoint2),
kRightVerticalStrokePoint2);
SelectEraserTool();
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(), UpdateStrokeActive(kPageIndex, InkStrokeId(0), _));
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kLeftVerticalStrokePoint1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
TestAnnotationBrushMessageParams message_params{
SkColorSetRGB(0x00, 0x00, 0x00),
8.0};
SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kRightVerticalStrokePoint2);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kRightVerticalStrokePoint1);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_up_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(2), _));
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleStrokeTest, ChangeDrawingBrushTypeDuringDrawing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(6, 6))));
TestAnnotationBrushMessageParams pen_message_params{
SkColorSetRGB(0x00, 0x00, 0x00),
2.0};
SelectBrushTool(PdfInkBrush::Type::kPen, pen_message_params);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kLeftVerticalStrokePoint1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
TestAnnotationBrushMessageParams highlighter_message_params{
SkColorSetRGB(0xDD, 0xF3, 0x00),
8.0};
SelectBrushTool(PdfInkBrush::Type::kHighlighter, highlighter_message_params);
VerifyAndClearExpectations();
static constexpr int kPageIndex = 0;
{
InSequence seq;
EXPECT_CALL(
client(),
StrokeAdded(kPageIndex, InkStrokeId(0),
InkStrokeDrawingBrushTypeEq(PdfInkBrush::Type::kPen)));
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
}
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kLeftVerticalStrokePoint2);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(1),
InkStrokeDrawingBrushTypeEq(
PdfInkBrush::Type::kHighlighter)));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleStrokeTest, EventWithPastTimeStamp) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kMouseDownPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kMouseMovePoint);
mouse_move_event.SetTimeStamp(mouse_move_event.TimeStamp() -
base::Milliseconds(10));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kMouseUpPoint);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
EXPECT_EQ(2, GetInputOfTypeCountForPage(
0, ink::StrokeInput::ToolType::kMouse));
}
class PdfInkModuleUndoRedoTest : public PdfInkModuleStrokeTest {
protected:
void PerformUndo() {
EXPECT_TRUE(
ink_module().OnMessage(CreateSetAnnotationUndoRedoMessageForTesting(
TestAnnotationUndoRedoMessageType::kUndo)));
}
void PerformRedo() {
EXPECT_TRUE(
ink_module().OnMessage(CreateSetAnnotationUndoRedoMessageForTesting(
TestAnnotationUndoRedoMessageType::kRedo)));
}
};
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoEmpty) {
InitializeSimpleSinglePageBasicLayout();
EnableDrawAnnotationMode();
EXPECT_TRUE(StrokeInputPositions().empty());
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
PerformUndo();
EXPECT_TRUE(StrokeInputPositions().empty());
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
PerformRedo();
EXPECT_TRUE(StrokeInputPositions().empty());
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoBasic) {
InitializeSimpleSinglePageBasicLayout();
ExpectStrokesAdded(1);
ExpectUpdateStrokesActive(1, false);
RunStrokeCheckTest(true);
const auto kMatcher =
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints))));
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
VerifyAndClearExpectations();
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
PerformUndo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
VerifyAndClearExpectations();
ExpectNoStrokeAdded();
ExpectUpdateStrokesActive(1, true);
PerformRedo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0, 0));
VerifyAndClearExpectations();
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
PerformRedo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0, 0));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoInvalidationsBasic) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
constexpr gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8, 13),
gfx::Size(4, 4));
constexpr gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8, 13),
gfx::Size(14, 14));
constexpr gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18, 15),
gfx::Size(14, 12));
constexpr gfx::Rect kInvalidationAreaEntireStroke(gfx::Point(7, 12),
gfx::Size(27, 9));
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke));
PerformUndo();
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke));
PerformRedo();
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoInvalidationsScaledRotated90) {
InitializeScaledLandscapeSinglePageBasicLayout();
client().set_orientation(PageOrientation::kClockwise90);
client().set_zoom(2.0f);
RunStrokeCheckTest(true);
constexpr gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8, 13),
gfx::Size(4, 4));
constexpr gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8, 13),
gfx::Size(14, 14));
constexpr gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18, 15),
gfx::Size(14, 12));
constexpr gfx::Rect kInvalidationAreaEntireStroke(gfx::Point(6, 11),
gfx::Size(29, 11));
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke));
PerformUndo();
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke));
PerformRedo();
EXPECT_THAT(
client().invalidations(),
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
kInvalidationAreaMouseUp, kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke,
kInvalidationAreaEntireStroke));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoAnnotationModeDisabled) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
const auto kMatcher =
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints))));
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
EXPECT_TRUE(ink_module().OnMessage(
CreateSetAnnotationModeMessageForTesting(InkAnnotationMode::kOff)));
EXPECT_FALSE(ink_module().enabled());
PerformUndo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0));
PerformRedo();
EXPECT_THAT(StrokeInputPositions(), kMatcher);
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0, 0));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoBetweenDraws) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
constexpr gfx::PointF kMouseDownPoint1 = gfx::PointF(11.0f, 15.0f);
constexpr gfx::PointF kMouseMovePoint1 = gfx::PointF(21.0f, 25.0f);
constexpr gfx::PointF kMouseUpPoint1 = gfx::PointF(31.0f, 17.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint1, base::span_from_ref(kMouseMovePoint1), kMouseUpPoint1);
constexpr gfx::PointF kMouseDownPoint2 = gfx::PointF(12.0f, 15.0f);
constexpr gfx::PointF kMouseMovePoint2 = gfx::PointF(22.0f, 25.0f);
constexpr gfx::PointF kMouseUpPoint2 = gfx::PointF(32.0f, 17.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint2, base::span_from_ref(kMouseMovePoint2), kMouseUpPoint2);
constexpr gfx::PointF kMouseDownPoint3 = gfx::PointF(13.0f, 15.0f);
constexpr gfx::PointF kMouseMovePoint3 = gfx::PointF(23.0f, 25.0f);
constexpr gfx::PointF kMouseUpPoint3 = gfx::PointF(33.0f, 17.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint3, base::span_from_ref(kMouseMovePoint3), kMouseUpPoint3);
const auto kInitial4StrokeMatchers = {
ElementsAre(kMouseDownPoint, kMouseMovePoint, kMouseUpPoint),
ElementsAre(kMouseDownPoint1, kMouseMovePoint1, kMouseUpPoint1),
ElementsAre(kMouseDownPoint2, kMouseMovePoint2, kMouseUpPoint2),
ElementsAre(kMouseDownPoint3, kMouseMovePoint3, kMouseUpPoint3)};
const auto kInitial4StrokeMatchersSpan = base::span(kInitial4StrokeMatchers);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitial4StrokeMatchersSpan))));
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitial4StrokeMatchersSpan))));
PerformUndo();
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitial4StrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAreArray(kInitial4StrokeMatchersSpan.first(3u)))));
PerformUndo();
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitial4StrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAreArray(kInitial4StrokeMatchersSpan.first(2u)))));
constexpr int kPageIndex = 0;
EXPECT_CALL(client(), DiscardStroke(kPageIndex, InkStrokeId(2)));
EXPECT_CALL(client(), DiscardStroke(kPageIndex, InkStrokeId(3)));
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint3, base::span_from_ref(kMouseMovePoint3), kMouseUpPoint3);
VerifyAndClearExpectations();
EXPECT_CALL(client(), DiscardStroke(_, _)).Times(0);
const auto kNext3StrokeMatchers = {
ElementsAre(kMouseDownPoint, kMouseMovePoint, kMouseUpPoint),
ElementsAre(kMouseDownPoint1, kMouseMovePoint1, kMouseUpPoint1),
ElementsAre(kMouseDownPoint3, kMouseMovePoint3, kMouseUpPoint3)};
const auto kNext3StrokeMatchersSpan = base::span(kNext3StrokeMatchers);
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAreArray(kNext3StrokeMatchersSpan.first(2u)))));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAreArray(kNext3StrokeMatchersSpan.first(1u)))));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
VerifyAndClearExpectations();
EXPECT_CALL(client(), DiscardStroke(kPageIndex, InkStrokeId(0)));
EXPECT_CALL(client(), DiscardStroke(kPageIndex, InkStrokeId(1)));
EXPECT_CALL(client(), DiscardStroke(kPageIndex, InkStrokeId(2)));
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint2, base::span_from_ref(kMouseMovePoint2), kMouseUpPoint2);
const auto kFinal1StrokeMatcher =
ElementsAre(kMouseDownPoint2, kMouseMovePoint2, kMouseUpPoint2);
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(kFinal1StrokeMatcher))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAre(kFinal1StrokeMatcher))));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoOnTwoPages) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
kTwoPageVerticalLayoutPoint3InsidePage0);
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage1,
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
kTwoPageVerticalLayoutPoint3InsidePage1);
const auto kPage0Matcher =
Pair(0, ElementsAre(ElementsAre(gfx::PointF(5.0f, 5.0f),
gfx::PointF(10.0f, 10.0f),
gfx::PointF(15.0f, 10.0f))));
const auto kPage1Matcher =
Pair(1, ElementsAre(ElementsAre(gfx::PointF(5.0f, 5.0f),
gfx::PointF(10.0f, 10.0f),
gfx::PointF(15.0f, 10.0f))));
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
EXPECT_THAT(VisibleStrokeInputPositions(), ElementsAre(kPage0Matcher));
PerformUndo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
PerformRedo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
EXPECT_THAT(VisibleStrokeInputPositions(), ElementsAre(kPage0Matcher));
PerformRedo();
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(kPage0Matcher, kPage1Matcher));
}
TEST_P(PdfInkModuleUndoRedoTest, UndoRedoEraseLoadedV2Shapes) {
constexpr int kPageIndex = 0;
constexpr InkModeledShapeId kShapeId0(0);
constexpr InkModeledShapeId kShapeId1(1);
const auto ink_points = base::ToVector(
kMousePoints,
[](const gfx::PointF& point) { return InkPointFromGfxPoint(point); });
std::optional<ink::Mesh> mesh0 =
CreateInkMeshFromPolylineForTesting(ink_points);
ASSERT_TRUE(mesh0.has_value());
auto shape0 =
ink::PartitionedMesh::FromMeshes(base::span_from_ref(mesh0.value()));
ASSERT_TRUE(shape0.ok());
constexpr ink::Point kCornerPoints[] = {
{49, 59},
{48, 59},
{48, 58},
};
std::optional<ink::Mesh> mesh1 =
CreateInkMeshFromPolylineForTesting(kCornerPoints);
ASSERT_TRUE(mesh1.has_value());
auto shape1 =
ink::PartitionedMesh::FromMeshes(base::span_from_ref(mesh1.value()));
ASSERT_TRUE(shape1.ok());
EXPECT_CALL(client(), LoadV2InkPathsFromPdf())
.WillOnce(Return(PdfInkModuleClient::DocumentV2InkPathShapesMap{
{kPageIndex, PdfInkModuleClient::PageV2InkPathShapesMap{
{kShapeId0, *shape0},
{kShapeId1, *shape1},
}}}));
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
EXPECT_CALL(client(), UpdateShapeActive(_, _, _)).Times(0);
InitializeSimpleSinglePageBasicLayout();
EnableDrawAnnotationMode();
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_TRUE(updated_pdf_thumbnail_page_indices().empty());
EXPECT_CALL(client(), RequestThumbnail)
.WillRepeatedly([&](int page_index, SendThumbnailCallback callback) {
std::move(callback).Run(
Thumbnail(gfx::SizeF(50, 25), 1));
});
SelectEraserTool();
ApplyStrokeWithMouseAtPoints(
gfx::PointF(), base::span_from_ref(gfx::PointF()), gfx::PointF());
VerifyAndClearExpectations();
EXPECT_TRUE(updated_pdf_thumbnail_page_indices().empty());
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
EXPECT_CALL(client(),
UpdateShapeActive(kPageIndex, kShapeId0, false));
EXPECT_CALL(client(), UpdateShapeActive(_, kShapeId1, _)).Times(0);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint, base::span_from_ref(kMouseMovePoint), kMouseUpPoint);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint, base::span_from_ref(kMouseMovePoint), kMouseUpPoint);
VerifyAndClearExpectations();
EXPECT_THAT(updated_pdf_thumbnail_page_indices(), ElementsAre(0));
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
EXPECT_CALL(client(),
UpdateShapeActive(kPageIndex, kShapeId0, true));
EXPECT_CALL(client(), UpdateShapeActive(_, kShapeId1, _)).Times(0);
PerformUndo();
VerifyAndClearExpectations();
EXPECT_THAT(updated_pdf_thumbnail_page_indices(), ElementsAre(0, 0));
ExpectNoStrokeAdded();
ExpectNoUpdateStrokeActive();
EXPECT_CALL(client(),
UpdateShapeActive(kPageIndex, kShapeId0, false));
EXPECT_CALL(client(), UpdateShapeActive(_, kShapeId1, _)).Times(0);
PerformRedo();
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_THAT(updated_pdf_thumbnail_page_indices(), ElementsAre(0, 0, 0));
}
TEST_P(PdfInkModuleUndoRedoTest, StrokeStrokeUndoStroke) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
constexpr gfx::PointF kMouseDownPoint2 = gfx::PointF(11.0f, 15.0f);
constexpr gfx::PointF kMouseMovePoint2 = gfx::PointF(21.0f, 25.0f);
constexpr gfx::PointF kMouseUpPoint2 = gfx::PointF(31.0f, 17.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint2, base::span_from_ref(kMouseMovePoint2), kMouseUpPoint2);
const auto kInitialStrokeMatchers = {
ElementsAre(kMouseDownPoint, kMouseMovePoint, kMouseUpPoint),
ElementsAre(kMouseDownPoint2, kMouseMovePoint2, kMouseUpPoint2)};
const auto kInitialStrokeMatchersSpan = base::span(kInitialStrokeMatchers);
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitialStrokeMatchersSpan))));
EXPECT_THAT(
VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitialStrokeMatchersSpan))));
PerformUndo();
EXPECT_THAT(
StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kInitialStrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(
0, ElementsAreArray(kInitialStrokeMatchersSpan.first(1u)))));
EXPECT_CALL(client(), DiscardStroke(0, InkStrokeId(1)));
constexpr gfx::PointF kMouseDownPoint3 = gfx::PointF(12.0f, 15.0f);
constexpr gfx::PointF kMouseMovePoint3 = gfx::PointF(22.0f, 25.0f);
constexpr gfx::PointF kMouseUpPoint3 = gfx::PointF(32.0f, 17.0f);
ApplyStrokeWithMouseAtPoints(
kMouseDownPoint3, base::span_from_ref(kMouseMovePoint3), kMouseUpPoint3);
const auto kNextStrokeMatchers = {
ElementsAre(kMouseDownPoint, kMouseMovePoint, kMouseUpPoint),
ElementsAre(kMouseDownPoint3, kMouseMovePoint3, kMouseUpPoint3)};
const auto kNextStrokeMatchersSpan = base::span(kNextStrokeMatchers);
EXPECT_THAT(StrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNextStrokeMatchersSpan))));
EXPECT_THAT(VisibleStrokeInputPositions(),
ElementsAre(Pair(0, ElementsAreArray(kNextStrokeMatchersSpan))));
}
using PdfInkModuleGetVisibleStrokesTest = PdfInkModuleStrokeTest;
TEST_P(PdfInkModuleGetVisibleStrokesTest, NoPageStrokes) {
EXPECT_TRUE(CollectVisibleStrokes().empty());
}
TEST_P(PdfInkModuleGetVisibleStrokesTest, MultiplePageStrokes) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint2InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint3InsidePage0),
kTwoPageVerticalLayoutPoint3InsidePage0);
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint1InsidePage0,
base::span_from_ref(kTwoPageVerticalLayoutPoint4InsidePage0),
kTwoPageVerticalLayoutPoint4InsidePage0);
ApplyStrokeWithMouseAtPoints(
kTwoPageVerticalLayoutPoint2InsidePage1,
base::span_from_ref(kTwoPageVerticalLayoutPoint3InsidePage1),
kTwoPageVerticalLayoutPoint3InsidePage1);
std::optional<ink::StrokeInputBatch> expected_page0_horz_line_input_batch =
CreateInkInputBatch(kTwoPageVerticalLayoutHorzLinePage0Inputs);
ASSERT_TRUE(expected_page0_horz_line_input_batch.has_value());
std::optional<ink::StrokeInputBatch> expected_page0_vert_line_input_batch =
CreateInkInputBatch(kTwoPageVerticalLayoutVertLinePage0Inputs);
ASSERT_TRUE(expected_page0_vert_line_input_batch.has_value());
std::optional<ink::StrokeInputBatch> expected_page1_horz_line_input_batch =
CreateInkInputBatch(kTwoPageVerticalLayoutHorzLinePage1Inputs);
ASSERT_TRUE(expected_page1_horz_line_input_batch.has_value());
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(
Pair(0, Pointwise(InkStrokeEq(brush->ink_brush()),
{expected_page0_horz_line_input_batch.value(),
expected_page0_vert_line_input_batch.value()})),
Pair(1, Pointwise(InkStrokeEq(brush->ink_brush()),
{expected_page1_horz_line_input_batch.value()}))));
}
class PdfInkModuleMetricsTest : public PdfInkModuleMetricsTestBase,
public PdfInkModuleUndoRedoTest {
protected:
static constexpr char kPenColorMetric[] = "PDF.Ink2StrokePenColor";
static constexpr char kPenSizeMetric[] = "PDF.Ink2StrokePenSize";
};
TEST_P(PdfInkModuleMetricsTest, StrokeUndoRedoDoesNotAffectMetrics) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1);
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 1);
histograms().ExpectUniqueSample(kPenSizeMetric,
StrokeMetricBrushSize::kMedium, 1);
histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack,
1);
PerformUndo();
PerformRedo();
histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1);
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 1);
histograms().ExpectUniqueSample(kPenSizeMetric,
StrokeMetricBrushSize::kMedium, 1);
histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack,
1);
}
TEST_P(PdfInkModuleMetricsTest, StrokeBrushColorPen) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(false);
histograms().ExpectTotalCount(kPenColorMetric, 0);
RunStrokeCheckTest(true);
histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack,
1);
TestAnnotationBrushMessageParams params = kRedBrushParams;
SelectBrushTool(PdfInkBrush::Type::kPen, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kRed1,
1);
histograms().ExpectTotalCount(kPenColorMetric, 2);
params.color = SkColorSetRGB(0x88, 0x59, 0x45);
SelectBrushTool(PdfInkBrush::Type::kPen, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kTan3,
1);
histograms().ExpectTotalCount(kPenColorMetric, 3);
histograms().ExpectTotalCount(kHighlighterColorMetric, 0);
}
TEST_P(PdfInkModuleMetricsTest, StrokeBrushColorHighlighter) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
TestAnnotationBrushMessageParams params = kRedBrushParams;
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kHighlighterColorMetric,
StrokeMetricHighlighterColor::kLightRed, 1);
histograms().ExpectTotalCount(kHighlighterColorMetric, 1);
params.color = SkColorSetRGB(0xFF, 0x63, 0x0C);
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kHighlighterColorMetric,
StrokeMetricHighlighterColor::kOrange, 1);
histograms().ExpectTotalCount(kHighlighterColorMetric, 2);
histograms().ExpectTotalCount(kPenColorMetric, 0);
}
TEST_P(PdfInkModuleMetricsTest, StrokeBrushSizePen) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(true);
histograms().ExpectUniqueSample(kPenSizeMetric,
StrokeMetricBrushSize::kMedium, 1);
TestAnnotationBrushMessageParams params = {SkColorSetRGB(0xF2, 0x8B, 0x82),
1.0};
SelectBrushTool(PdfInkBrush::Type::kPen, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kPenSizeMetric,
StrokeMetricBrushSize::kExtraThin, 1);
histograms().ExpectTotalCount(kPenSizeMetric, 2);
params.size = 8.0f;
SelectBrushTool(PdfInkBrush::Type::kPen, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kPenSizeMetric,
StrokeMetricBrushSize::kExtraThick, 1);
histograms().ExpectTotalCount(kPenSizeMetric, 3);
histograms().ExpectTotalCount(kHighlighterSizeMetric, 0);
}
TEST_P(PdfInkModuleMetricsTest, StrokeBrushSizeHighlighter) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
TestAnnotationBrushMessageParams params = {SkColorSetRGB(0xF2, 0x8B, 0x82),
8.0};
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectUniqueSample(kHighlighterSizeMetric,
StrokeMetricBrushSize::kMedium, 1);
params.size = 4.0f;
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kHighlighterSizeMetric,
StrokeMetricBrushSize::kExtraThin, 1);
histograms().ExpectTotalCount(kHighlighterSizeMetric, 2);
params.size = 16.0f;
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kHighlighterSizeMetric,
StrokeMetricBrushSize::kExtraThick, 1);
histograms().ExpectTotalCount(kPenSizeMetric, 0);
histograms().ExpectTotalCount(kHighlighterSizeMetric, 3);
}
TEST_P(PdfInkModuleMetricsTest, StrokeBrushType) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(false);
histograms().ExpectTotalCount(kTypeMetric, 0);
RunStrokeCheckTest(true);
histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1);
TestAnnotationBrushMessageParams params = kRedBrushParams;
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kTypeMetric,
StrokeMetricBrushType::kHighlighter, 1);
histograms().ExpectTotalCount(kTypeMetric, 2);
SelectEraserTool();
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser,
1);
histograms().ExpectTotalCount(kTypeMetric, 3);
ApplyStrokeWithMouseAtPoints(
kMouseUpPoint, base::span_from_ref(kMouseUpPoint), kMouseUpPoint);
histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser,
1);
histograms().ExpectTotalCount(kTypeMetric, 3);
params.size = 3.0f;
SelectBrushTool(PdfInkBrush::Type::kPen, params);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kPen, 2);
histograms().ExpectTotalCount(kTypeMetric, 4);
}
TEST_P(PdfInkModuleMetricsTest, StrokeInputDeviceMouse) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeCheckTest(false);
histograms().ExpectTotalCount(kInputDeviceMetric, 0);
RunStrokeCheckTest(true);
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 1);
SelectEraserTool();
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 2);
ApplyStrokeWithMouseAtMouseDownPoint();
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 2);
}
TEST_P(PdfInkModuleMetricsTest, StrokeInputDeviceTouch) {
InitializeSimpleSinglePageBasicLayout();
RunStrokeTouchCheckTest(false);
histograms().ExpectTotalCount(kInputDeviceMetric, 0);
RunStrokeTouchCheckTest(true);
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kTouch, 1);
SelectEraserTool();
const std::vector<base::span<const gfx::PointF>> move_point{
base::span_from_ref(kMouseDownPoint),
};
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
base::span_from_ref(kMouseDownPoint));
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kTouch, 2);
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
base::span_from_ref(kMouseDownPoint));
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kTouch, 2);
}
TEST_P(PdfInkModuleMetricsTest, StrokeInputDevicePen) {
InitializeSimpleSinglePageBasicLayout();
RunStrokePenCheckTest(false);
histograms().ExpectTotalCount(kInputDeviceMetric, 0);
RunStrokePenCheckTest(true);
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kPen, 1);
SelectEraserTool();
const std::vector<base::span<const gfx::PointF>> move_point{
base::span_from_ref(kMouseDownPoint),
};
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
base::span_from_ref(kMouseDownPoint));
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kPen, 2);
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
base::span_from_ref(kMouseDownPoint));
histograms().ExpectUniqueSample(kInputDeviceMetric,
StrokeMetricInputDeviceType::kPen, 2);
}
class PdfInkModuleTextHighlightTest : public PdfInkModuleUndoRedoTest {
public:
static constexpr TestAnnotationBrushMessageParams kOrangeBrushParams{
SkColorSetRGB(0xFF, 0x63, 0x0C),
6.0};
static constexpr gfx::Rect kHorizontalSelection{10, 15, 30, 10};
static constexpr gfx::Rect kVerticalSelection{10, 15, 6, 10};
static constexpr gfx::PointF kStartPointInsidePage0{10.0, 10.0};
static constexpr gfx::PointF kEndPointInsidePage0{15.0, 10.0};
static constexpr SkColor kOrangeColor = SkColorSetRGB(0xFF, 0x63, 0x0C);
protected:
void RunSingleSelectionWithMouseTest(
const gfx::Rect& selection_rect,
base::span<const PdfInkInputData> expected_inputs,
float expected_size) {
SetUpSingleSelectionTest(selection_rect);
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
VerifySingleSelectionTest(expected_inputs, expected_size);
}
void SetSelectionRectMap(
const std::map<int, std::vector<gfx::Rect>>& selection_map) {
ON_CALL(client(), GetCanonicalToPdfTransform(_))
.WillByDefault(Return(gfx::Transform()));
PdfInkModuleClient::SelectionRectMap pdf_selection_map;
for (const auto& [page_index, selection_rects] : selection_map) {
const gfx::Transform transform = GetEventToCanonicalTransform(
client().GetOrientation(), client().GetPageContentsRect(page_index),
client().GetZoom());
std::vector<PdfRect>& pdf_rects = pdf_selection_map[page_index];
pdf_rects.reserve(selection_rects.size());
for (const gfx::Rect& selection_rect : selection_rects) {
gfx::RectF mapped_rect = transform.MapRect(gfx::RectF(selection_rect));
pdf_rects.push_back(PdfRect(mapped_rect));
}
}
ON_CALL(client(), GetSelectionRectMap())
.WillByDefault(Return(pdf_selection_map));
}
void SetSelectionRectsOnFirstPage(
base::span<const gfx::Rect> selection_rects) {
CHECK(!selection_rects.empty());
SetSelectionRectMap({{0, base::ToVector(selection_rects)}});
}
void SetTextAreaPoints(base::span<const gfx::PointF> points) {
EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
.WillRepeatedly(Return(false));
for (const auto& point : points) {
EXPECT_CALL(client(), IsSelectableTextOrLinkArea(point))
.WillRepeatedly(Return(true));
}
}
void SetUpSingleSelectionTest(const gfx::Rect& selection_rect) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectsOnFirstPage(base::span_from_ref(selection_rect));
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
}
void VerifySingleSelectionTest(
base::span<const PdfInkInputData> expected_inputs,
float expected_size) {
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch(expected_inputs);
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush expected_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, expected_size);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(expected_brush.ink_brush()),
{expected_batch.value()}))));
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
const ink::Brush& ink_brush = brush->ink_brush();
EXPECT_EQ(kOrangeColor, GetSkColorFromInkBrush(ink_brush));
EXPECT_EQ(6.0f, ink_brush.GetSize());
EXPECT_EQ(0.4f, GetOpacityMultiplierFromBrush(ink_brush));
}
void ClickTextAtPoint(const gfx::PointF& point, int click_count) {
blink::WebMouseEvent mouse_down_event =
MouseEventBuilder()
.CreateLeftClickAtPosition(point)
.SetClickCount(click_count)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_up_event =
MouseEventBuilder()
.CreateLeftMouseUpAtPosition(point)
.SetClickCount(click_count)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
};
TEST_P(PdfInkModuleTextHighlightTest, PenDoesNotSelectText) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kPen, kRedBrushParams);
EXPECT_CALL(client(), GetSelectionRectMap()).Times(0);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(_, _)).Times(0);
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch({PdfInkInputData(kStartPointInsidePage0),
PdfInkInputData(kEndPointInsidePage0)});
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
ASSERT_TRUE(brush);
EXPECT_THAT(CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(brush->ink_brush()),
{expected_batch.value()}))));
}
TEST_P(PdfInkModuleTextHighlightTest, SingleHorizontalSelection) {
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))},
10.0);
}
TEST_P(PdfInkModuleTextHighlightTest, SingleVerticalSelection) {
RunSingleSelectionWithMouseTest(
kVerticalSelection,
{PdfInkInputData(gfx::PointF(13.0, 18.0)),
PdfInkInputData(gfx::PointF(13.0, 22.0))},
6.0);
}
TEST_P(PdfInkModuleTextHighlightTest, SingleSquareSelection) {
RunSingleSelectionWithMouseTest(
gfx::Rect(10, 15, 12, 12),
{PdfInkInputData(gfx::PointF(16.0, 21.0))},
12.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleHorizontalSelectionRotatedClockwise90) {
client().set_orientation(PageOrientation::kClockwise90);
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(20.0, 14.0)),
PdfInkInputData(gfx::PointF(20.0, 34.0))},
10.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleVerticalSelectionRotatedClockwise90) {
client().set_orientation(PageOrientation::kClockwise90);
RunSingleSelectionWithMouseTest(
kVerticalSelection,
{PdfInkInputData(gfx::PointF(18.0, 36.0)),
PdfInkInputData(gfx::PointF(22.0, 36.0))},
6.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleHorizontalSelectionRotatedClockwise180) {
client().set_orientation(PageOrientation::kClockwise180);
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(14.0, 39.0)),
PdfInkInputData(gfx::PointF(34.0, 39.0))},
10.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleVerticalSelectionRotatedClockwise180) {
client().set_orientation(PageOrientation::kClockwise180);
RunSingleSelectionWithMouseTest(
kVerticalSelection,
{PdfInkInputData(gfx::PointF(36.0, 37.0)),
PdfInkInputData(gfx::PointF(36.0, 41.0))},
6.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleHorizontalSelectionRotatedClockwise270) {
client().set_orientation(PageOrientation::kClockwise270);
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(39.0, 15.0)),
PdfInkInputData(gfx::PointF(39.0, 35.0))},
10.0);
}
TEST_P(PdfInkModuleTextHighlightTest,
SingleVerticalSelectionRotatedClockwise270) {
client().set_orientation(PageOrientation::kClockwise270);
RunSingleSelectionWithMouseTest(
kVerticalSelection,
{PdfInkInputData(gfx::PointF(37.0, 13.0)),
PdfInkInputData(gfx::PointF(41.0, 13.0))},
6.0);
}
TEST_P(PdfInkModuleTextHighlightTest, SingleSelectionZoomedIn) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
client().set_zoom(2.0f);
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(7.5, 10.0)),
PdfInkInputData(gfx::PointF(17.5, 10.0))},
5.0);
}
TEST_P(PdfInkModuleTextHighlightTest, SingleSelectionZoomedOut) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
client().set_zoom(0.5f);
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(30.0, 40.0)),
PdfInkInputData(gfx::PointF(70.0, 40.0))},
20.0);
}
TEST_P(PdfInkModuleTextHighlightTest, MultipleSelection) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectsOnFirstPage(
{kHorizontalSelection, gfx::Rect(15, 25, 10, 5)});
constexpr gfx::PointF kEndPoint2InsidePage0{25.0, 30.0};
SetTextAreaPoints({kStartPointInsidePage0, kEndPoint2InsidePage0});
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPoint2InsidePage0));
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPoint2InsidePage0},
kEndPoint2InsidePage0);
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_selection0_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))});
ASSERT_TRUE(expected_selection0_batch.has_value());
std::optional<ink::StrokeInputBatch> expected_selection1_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(17.5, 27.5)),
PdfInkInputData(gfx::PointF(22.5, 27.5))});
ASSERT_TRUE(expected_selection1_batch.has_value());
std::map<int, std::vector<raw_ref<const ink::Stroke>>> collected_strokes =
CollectVisibleStrokes();
ASSERT_EQ(1u, collected_strokes.size());
std::vector<raw_ref<const ink::Stroke>>& strokes_on_page0 =
collected_strokes[0];
ASSERT_EQ(2u, strokes_on_page0.size());
const PdfInkBrush expected_selection0_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, 10.0f);
raw_ref<const ink::Stroke> actual_selection0 = strokes_on_page0[0];
EXPECT_THAT(actual_selection0->GetBrush(),
ink::BrushEq(expected_selection0_brush.ink_brush()));
EXPECT_THAT(actual_selection0->GetInputs(),
ink::StrokeInputBatchEq(expected_selection0_batch.value()));
const PdfInkBrush expected_selection1_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, 5.0f);
raw_ref<const ink::Stroke> actual_selection1 = strokes_on_page0[1];
EXPECT_THAT(actual_selection1->GetBrush(),
ink::BrushEq(expected_selection1_brush.ink_brush()));
EXPECT_THAT(actual_selection1->GetInputs(),
ink::StrokeInputBatchEq(expected_selection1_batch.value()));
}
TEST_P(PdfInkModuleTextHighlightTest, OneClickCount) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectMap({});
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
ClickTextAtPoint(kStartPointInsidePage0, 1);
ExpectStrokeCounts(1, 0,
1);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_TRUE(CollectVisibleStrokes().empty());
}
TEST_P(PdfInkModuleTextHighlightTest, TwoClickCount) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
2));
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
blink::WebMouseEvent mouse_down_event =
MouseEventBuilder()
.CreateLeftClickAtPosition(kStartPointInsidePage0)
.SetClickCount(2)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
ExpectStrokeCounts(2, 1,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))});
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush expected_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor,
10.0f);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(expected_brush.ink_brush()),
{expected_batch.value()}))));
EXPECT_TRUE(ink_module().HandleInputEvent(
CreateLeftClickWebMouseMoveEventAtPosition(kStartPointInsidePage0)));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
ExpectStrokeCounts(2, 1,
1);
}
TEST_P(PdfInkModuleTextHighlightTest, ThreeClickCount) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
ClickTextAtPoint(kStartPointInsidePage0, 2);
SetSelectionRectsOnFirstPage(base::span_from_ref(gfx::Rect(5, 15, 45, 12)));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
3));
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
blink::WebMouseEvent mouse_down_event =
MouseEventBuilder()
.CreateLeftClickAtPosition(kStartPointInsidePage0)
.SetClickCount(3)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
ExpectStrokeCounts(3, 2,
1);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 0, 0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(11.0, 21.0)),
PdfInkInputData(gfx::PointF(44.0, 21.0))});
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush expected_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor,
12.0f);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(expected_brush.ink_brush()),
{expected_batch.value()}))));
EXPECT_TRUE(ink_module().HandleInputEvent(
CreateLeftClickWebMouseMoveEventAtPosition(kStartPointInsidePage0)));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
ExpectStrokeCounts(3, 2,
1);
}
TEST_P(PdfInkModuleTextHighlightTest, MouseUpOnNonSelection) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
VerifyAndClearExpectations();
SetSelectionRectsOnFirstPage(base::span_from_ref(gfx::Rect(10, 15, 2, 10)));
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(11.0, 16.0)),
PdfInkInputData(gfx::PointF(11.0, 24.0))});
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush expected_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor,
2.0f);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(expected_brush.ink_brush()),
{expected_batch.value()}))));
}
TEST_P(PdfInkModuleTextHighlightTest, MultiplePages) {
EnableDrawAnnotationMode();
InitializeVerticalTwoPageLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(
{kStartPointInsidePage0, kTwoPageVerticalLayoutPoint1InsidePage1});
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
constexpr gfx::Rect kHorizontalSelectionInPage1{10, 75, 15, 14};
SetSelectionRectMap(
{{0, {kHorizontalSelection}}, {1, {kHorizontalSelectionInPage1}}});
EXPECT_CALL(client(),
PageIndexFromPoint(gfx::PointF(kHorizontalSelection.origin())))
.WillRepeatedly(Return(0));
EXPECT_CALL(client(), PageIndexFromPoint(
gfx::PointF(kHorizontalSelectionInPage1.origin())))
.WillRepeatedly(Return(1));
EXPECT_CALL(client(),
ExtendSelectionByPoint(kTwoPageVerticalLayoutPoint1InsidePage1));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(
kTwoPageVerticalLayoutPoint1InsidePage1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(
kTwoPageVerticalLayoutPoint1InsidePage1);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0, 1));
std::optional<ink::StrokeInputBatch> expected_page0_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(10.0, 15.0)),
PdfInkInputData(gfx::PointF(30.0, 15.0))});
ASSERT_TRUE(expected_page0_batch.has_value());
std::optional<ink::StrokeInputBatch> expected_page1_batch =
CreateInkInputBatch({PdfInkInputData(gfx::PointF(12.0, 12.0)),
PdfInkInputData(gfx::PointF(13.0, 12.0))});
ASSERT_TRUE(expected_page1_batch.has_value());
const PdfInkBrush expected_page0_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, 10.0f);
const PdfInkBrush expected_page1_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, 14.0f);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(
Pair(0, Pointwise(InkStrokeEq(expected_page0_brush.ink_brush()),
{expected_page0_batch.value()})),
Pair(1, Pointwise(InkStrokeEq(expected_page1_brush.ink_brush()),
{expected_page1_batch.value()}))));
}
TEST_P(PdfInkModuleTextHighlightTest,
StrokeMissedEndEventThenMouseMoveDuringTextSelecting) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectsOnFirstPage(base::span_from_ref(gfx::Rect(9, 14, 5, 10)));
EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(client(), ExtendSelectionByPoint(kMouseUpPoint));
RunStrokeMissedEndEventThenMouseMoveTest();
}
TEST_P(PdfInkModuleTextHighlightTest, TouchSingleHorizontalSelection) {
SetUpSingleSelectionTest(kHorizontalSelection);
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kStartPointInsidePage0),
{base::span_from_ref(kEndPointInsidePage0)},
base::span_from_ref(kEndPointInsidePage0));
constexpr auto kExpectedInputs = std::to_array<PdfInkInputData>(
{PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))});
VerifySingleSelectionTest(kExpectedInputs, 10.0f);
}
TEST_P(PdfInkModuleTextHighlightTest, TouchOneClickCount) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectMap({});
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
blink::WebTouchEvent touch_event =
CreateTouchEvent(blink::WebInputEvent::Type::kTouchStart,
base::span_from_ref(kStartPointInsidePage0));
EXPECT_TRUE(ink_module().HandleInputEvent(touch_event));
touch_event = CreateTouchEvent(blink::WebInputEvent::Type::kTouchEnd,
base::span_from_ref(kStartPointInsidePage0));
EXPECT_TRUE(ink_module().HandleInputEvent(touch_event));
ExpectStrokeCounts(1, 0,
1);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_TRUE(CollectVisibleStrokes().empty());
}
TEST_P(PdfInkModuleTextHighlightTest, MultiTouchDoesNotSelectText) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_)).Times(0);
ApplyStrokeWithTouchAtPointsNotHandled(
{kStartPointInsidePage0, kStartPointInsidePage0},
{{kEndPointInsidePage0, kEndPointInsidePage0}},
{kEndPointInsidePage0, kEndPointInsidePage0});
}
TEST_P(PdfInkModuleTextHighlightTest, PenSingleHorizontalSelection) {
const std::vector<PdfInkInputData> expected_inputs{
PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))};
SetUpSingleSelectionTest(kHorizontalSelection);
ApplyStrokeWithPenAtPoints(base::span_from_ref(kStartPointInsidePage0),
{base::span_from_ref(kEndPointInsidePage0)},
base::span_from_ref(kEndPointInsidePage0));
VerifySingleSelectionTest(expected_inputs, 10.0f);
}
TEST_P(PdfInkModuleTextHighlightTest, PenOneClickCount) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectMap({});
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
blink::WebTouchEvent pen_event =
CreatePenEvent(blink::WebInputEvent::Type::kTouchStart,
base::span_from_ref(kStartPointInsidePage0));
EXPECT_TRUE(ink_module().HandleInputEvent(pen_event));
pen_event = CreatePenEvent(blink::WebInputEvent::Type::kTouchEnd,
base::span_from_ref(kStartPointInsidePage0));
EXPECT_TRUE(ink_module().HandleInputEvent(pen_event));
ExpectStrokeCounts(1, 0,
1);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_TRUE(CollectVisibleStrokes().empty());
}
TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMove) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
TestAnnotationBrushMessageParams params = kRedBrushParams;
SelectBrushTool(PdfInkBrush::Type::kPen, params);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
blink::WebMouseEvent mouse_move_event =
CreateMoveWebMouseEventToPosition(kEndPointInsidePage0);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
params.size = 8.0f;
SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
VerifyAndClearExpectations();
EXPECT_CALL(client(),
UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
mouse_move_event = CreateMoveWebMouseEventToPosition(kStartPointInsidePage0);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
{
InSequence seq;
EXPECT_CALL(client(), GetCursor())
.WillOnce(Return(ui::mojom::CursorType::kIBeam));
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
}
mouse_move_event = CreateMoveWebMouseEventToPosition(kEndPointInsidePage0);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
}
TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileTextSelecting) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
VerifyAndClearExpectations();
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(),
UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
blink::WebMouseEvent mouse_move_event =
CreateMoveWebMouseEventToPosition(kStartPointInsidePage0);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMoveWhileBrushDrawing) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
EXPECT_CALL(client(),
UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
VerifyAndClearExpectations();
SetTextAreaPoints(base::span_from_ref(kEndPointInsidePage0));
EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
blink::WebMouseEvent mouse_move_event =
CreateMoveWebMouseEventToPosition(kStartPointInsidePage0);
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(), UpdateInkCursor(_)).Times(0);
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
VerifyAndClearExpectations();
EXPECT_CALL(client(),
UpdateInkCursor(ui::Cursor(ui::mojom::CursorType::kIBeam)));
blink::WebMouseEvent mouse_up_event =
CreateLeftClickWebMouseUpEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
TEST_P(PdfInkModuleTextHighlightTest, IgnoreVerySmallTextSelection) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
static constexpr PdfRect kVerySmallSelectionRect(
kStartPointInsidePage0.x(), kStartPointInsidePage0.y(),
kStartPointInsidePage0.x() + 1.0f, kStartPointInsidePage0.y() + 0.001f);
PdfInkModuleClient::SelectionRectMap pdf_selection_map;
pdf_selection_map[0].push_back(kVerySmallSelectionRect);
EXPECT_CALL(client(), GetSelectionRectMap())
.WillRepeatedly(Return(pdf_selection_map));
SetTextAreaPoints({kStartPointInsidePage0, kEndPointInsidePage0});
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
ExpectStrokeCounts(1, 1,
0);
EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
EXPECT_TRUE(CollectVisibleStrokes().empty());
}
TEST_P(PdfInkModuleTextHighlightTest,
TextHighlightMissedEndEventThenMouseDown) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1))
.Times(2);
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
ExpectStrokeCounts(2, 1,
0);
}
TEST_P(PdfInkModuleTextHighlightTest,
TextHighlightWithNoEndEventThenTouchStart) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
1))
.Times(2);
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
blink::WebMouseEvent mouse_down_event =
CreateLeftClickWebMouseEventAtPosition(kStartPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
blink::WebMouseEvent mouse_move_event =
CreateLeftClickWebMouseMoveEventAtPosition(kEndPointInsidePage0);
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
EXPECT_TRUE(ink_module().HandleInputEvent(
CreateTouchEvent(blink::WebInputEvent::Type::kTouchStart,
base::span_from_ref(kStartPointInsidePage0))));
ExpectStrokeCounts(2, 1,
0);
}
class PdfInkModuleTextHighlightCaretTest
: public PdfInkModuleTextHighlightTest {
protected:
static constexpr gfx::Rect kTestSelectionRect{22, 15, 12, 12};
static constexpr PdfInkInputData kExpectedInkInputData{
.position = gfx::PointF(28.0f, 21.0f)};
static constexpr float kExpectedBrushSize = 12.0f;
PdfInkModuleTextHighlightCaretTest() : caret_(&caret_client_) {}
MockPdfCaret* caret() { return &caret_; }
void SetUp() override {
PdfInkModuleTextHighlightTest::SetUp();
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
}
void SetUpCaret() {
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
ON_CALL(client(), GetPdfCaret()).WillByDefault(Return(caret()));
caret()->SetEnabled(true);
}
bool HandleKeyboardEvent(const blink::WebKeyboardEvent& event) {
return ink_module().HandleInputEvent(event);
}
void HandleKeyboardEventAndExpectCaret(const blink::WebKeyboardEvent& event) {
EXPECT_CALL(*caret(), OnKeyDown(WebKeyboardEventEq(event.windows_key_code,
event.GetModifiers())))
.WillOnce(Return(true));
EXPECT_TRUE(HandleKeyboardEvent(event));
}
blink::WebKeyboardEvent GenerateKeyDownEvent(ui::KeyboardCode key,
int modifiers) {
blink::WebKeyboardEvent event(
blink::WebInputEvent::Type::kKeyDown, modifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = key;
return event;
}
void RunUnhandledArrowKeyTest(ui::KeyboardCode key) {
blink::WebKeyboardEvent arrow_event = GenerateKeyDownEvent(
key, blink::WebInputEvent::Modifiers::kNoModifiers);
blink::WebKeyboardEvent shift_arrow_event =
GenerateKeyDownEvent(key, blink::WebInputEvent::Modifiers::kShiftKey);
EXPECT_FALSE(HandleKeyboardEvent(arrow_event));
EXPECT_FALSE(HandleKeyboardEvent(shift_arrow_event));
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
ON_CALL(client(), GetPdfCaret()).WillByDefault(Return(nullptr));
EXPECT_FALSE(HandleKeyboardEvent(arrow_event));
EXPECT_FALSE(HandleKeyboardEvent(shift_arrow_event));
ON_CALL(client(), GetPdfCaret()).WillByDefault(Return(caret()));
EXPECT_FALSE(HandleKeyboardEvent(arrow_event));
EXPECT_FALSE(HandleKeyboardEvent(shift_arrow_event));
}
void RunHandledArrowKeyTest(ui::KeyboardCode key) {
SetUpCaret();
InSequence seq;
SetSelectionRectMap({});
blink::WebKeyboardEvent arrow_event = GenerateKeyDownEvent(
key, blink::WebInputEvent::Modifiers::kNoModifiers);
HandleKeyboardEventAndExpectCaret(arrow_event);
EXPECT_EQ(0, client().stroke_started_count());
blink::WebKeyboardEvent shift_arrow_event =
GenerateKeyDownEvent(key, blink::WebInputEvent::Modifiers::kShiftKey);
TestKeyEventTextHighlights(shift_arrow_event,
{10, 15, 12, 12});
TestKeyEventTextHighlights(shift_arrow_event,
kTestSelectionRect);
TestKeyEventEndsTextHighlight(arrow_event,
true,
base::span_from_ref(kExpectedInkInputData),
kExpectedBrushSize);
}
void TestKeyEventTextHighlights(const blink::WebKeyboardEvent& event,
const gfx::Rect& test_selection_rect) {
SetSelectionRectsOnFirstPage(base::span_from_ref(test_selection_rect));
HandleKeyboardEventAndExpectCaret(event);
ExpectStrokeCounts(1, 0,
0);
}
void TestKeyEventEndsTextHighlight(
const blink::WebKeyboardEvent& event,
bool expected_event_handled,
base::span<const PdfInkInputData> expected_inputs,
float expected_size) {
if (expected_event_handled) {
EXPECT_CALL(*caret(), OnKeyDown(WebKeyboardEventEq(event.windows_key_code,
event.GetModifiers())))
.WillOnce(Return(true));
}
EXPECT_EQ(expected_event_handled, HandleKeyboardEvent(event));
ExpectStrokeCounts(1, 1,
0);
EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
std::optional<ink::StrokeInputBatch> expected_batch =
CreateInkInputBatch(expected_inputs);
ASSERT_TRUE(expected_batch.has_value());
const PdfInkBrush expected_brush(PdfInkBrush::Type::kHighlighter,
kOrangeColor, expected_size);
EXPECT_THAT(
CollectVisibleStrokes(),
ElementsAre(Pair(0, Pointwise(InkStrokeEq(expected_brush.ink_brush()),
{expected_batch.value()}))));
}
MockPdfCaretClient caret_client_;
MockPdfCaret caret_;
};
TEST_P(PdfInkModuleTextHighlightCaretTest, UnhandledArrowKeyLeft) {
RunUnhandledArrowKeyTest(ui::KeyboardCode::VKEY_LEFT);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, UnhandledArrowKeyRight) {
RunUnhandledArrowKeyTest(ui::KeyboardCode::VKEY_RIGHT);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, UnhandledArrowKeyUp) {
RunUnhandledArrowKeyTest(ui::KeyboardCode::VKEY_UP);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, UnhandledArrowKeyDown) {
RunUnhandledArrowKeyTest(ui::KeyboardCode::VKEY_DOWN);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, HandledArrowKeyLeft) {
RunHandledArrowKeyTest(ui::KeyboardCode::VKEY_LEFT);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, HandledArrowKeyRight) {
RunHandledArrowKeyTest(ui::KeyboardCode::VKEY_RIGHT);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, HandledArrowKeyUp) {
RunHandledArrowKeyTest(ui::KeyboardCode::VKEY_UP);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, HandledArrowKeyDown) {
RunHandledArrowKeyTest(ui::KeyboardCode::VKEY_DOWN);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, ControlPEndsTextHighlight) {
SetUpCaret();
InSequence seq;
TestKeyEventTextHighlights(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_LEFT,
blink::WebInputEvent::Modifiers::kShiftKey),
kTestSelectionRect);
TestKeyEventEndsTextHighlight(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_P,
blink::WebInputEvent::Modifiers::kControlKey),
false,
base::span_from_ref(kExpectedInkInputData), kExpectedBrushSize);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, ControlSEndsTextHighlight) {
SetUpCaret();
InSequence seq;
TestKeyEventTextHighlights(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_LEFT,
blink::WebInputEvent::Modifiers::kShiftKey),
kTestSelectionRect);
TestKeyEventEndsTextHighlight(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_S,
blink::WebInputEvent::Modifiers::kControlKey),
false,
base::span_from_ref(kExpectedInkInputData), kExpectedBrushSize);
}
TEST_P(PdfInkModuleTextHighlightCaretTest, UnhandledKeyEvents) {
blink::WebKeyboardEvent a_event = GenerateKeyDownEvent(
ui::KeyboardCode::VKEY_A, blink::WebInputEvent::Modifiers::kNoModifiers);
blink::WebKeyboardEvent control_p_event = GenerateKeyDownEvent(
ui::KeyboardCode::VKEY_P, blink::WebInputEvent::Modifiers::kControlKey);
blink::WebKeyboardEvent control_s_event = GenerateKeyDownEvent(
ui::KeyboardCode::VKEY_S, blink::WebInputEvent::Modifiers::kControlKey);
EXPECT_FALSE(HandleKeyboardEvent(a_event));
EXPECT_FALSE(HandleKeyboardEvent(control_p_event));
EXPECT_FALSE(HandleKeyboardEvent(control_s_event));
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
ON_CALL(client(), GetPdfCaret()).WillByDefault(Return(nullptr));
EXPECT_FALSE(HandleKeyboardEvent(a_event));
EXPECT_FALSE(HandleKeyboardEvent(control_p_event));
EXPECT_FALSE(HandleKeyboardEvent(control_s_event));
ON_CALL(client(), GetPdfCaret()).WillByDefault(Return(caret()));
EXPECT_FALSE(HandleKeyboardEvent(a_event));
EXPECT_FALSE(HandleKeyboardEvent(control_p_event));
EXPECT_FALSE(HandleKeyboardEvent(control_s_event));
caret()->SetEnabled(true);
EXPECT_FALSE(HandleKeyboardEvent(a_event));
EXPECT_FALSE(HandleKeyboardEvent(control_p_event));
EXPECT_FALSE(HandleKeyboardEvent(control_s_event));
}
class PdfInkModuleTextHighlightMetricsTestBase
: public PdfInkModuleMetricsTestBase {
protected:
static constexpr char kTextHighlightColorMetric[] =
"PDF.Ink2TextHighlighterColor";
static constexpr char kTextHighlightInputDeviceMetric[] =
"PDF.Ink2TextHighlightInputDeviceType";
void ValidateHighlightMetricCounts(int expected_metric_count) {
histograms().ExpectTotalCount(kTextHighlightColorMetric,
expected_metric_count);
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric,
expected_metric_count);
}
};
class PdfInkModuleTextHighlightMetricsTest
: public PdfInkModuleTextHighlightMetricsTestBase,
public PdfInkModuleTextHighlightTest {
protected:
static constexpr base::TimeDelta kOneMs = base::Milliseconds(1);
static constexpr base::TimeDelta kTextSelectionClickTimeMs =
base::Milliseconds(ui::kDoubleClickTimeMs);
void MouseMoveAndUpAtPoint(const gfx::PointF& point, int click_count) {
EXPECT_TRUE(ink_module().HandleInputEvent(
CreateLeftClickWebMouseMoveEventAtPosition(point)));
blink::WebMouseEvent mouse_up_event =
MouseEventBuilder()
.CreateLeftMouseUpAtPosition(point)
.SetClickCount(click_count)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
}
};
TEST_P(PdfInkModuleTextHighlightMetricsTest,
StrokeDoesNotAffectTextHighlightMetrics) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
RunStrokeCheckTest(true);
ExpectStrokeCounts(1, 1,
0);
ValidateHighlightMetricCounts(0);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest,
TextHighlightDoesNotAffectStrokeMetrics) {
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))},
10.0);
histograms().ExpectTotalCount(kHighlighterColorMetric, 0);
histograms().ExpectTotalCount(kInputDeviceMetric, 0);
histograms().ExpectTotalCount(kHighlighterSizeMetric, 0);
histograms().ExpectTotalCount(kTypeMetric, 0);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest,
TextHighlightUndoRedoDoesNotAffectMetrics) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
RunSingleSelectionWithMouseTest(
kHorizontalSelection,
{PdfInkInputData(gfx::PointF(15.0, 20.0)),
PdfInkInputData(gfx::PointF(35.0, 20.0))},
10.0);
ExpectStrokeCounts(1, 1,
0);
ValidateHighlightMetricCounts(1);
PerformUndo();
PerformRedo();
ExpectStrokeCounts(1, 1,
0);
ValidateHighlightMetricCounts(1);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, Color) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
histograms().ExpectTotalCount(kTextHighlightColorMetric, 0);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
SetTextAreaPoints({kStartPointInsidePage0, kEndPointInsidePage0});
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
histograms().ExpectUniqueSample(kTextHighlightColorMetric,
StrokeMetricHighlighterColor::kLightRed, 1);
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
ExpectStrokeCounts(2, 2,
0);
histograms().ExpectBucketCount(kTextHighlightColorMetric,
StrokeMetricHighlighterColor::kOrange, 1);
histograms().ExpectTotalCount(kTextHighlightColorMetric, 2);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, InputDevice) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric, 0);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
SetTextAreaPoints({kStartPointInsidePage0, kEndPointInsidePage0});
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
histograms().ExpectUniqueSample(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 1);
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kStartPointInsidePage0),
{base::span_from_ref(kEndPointInsidePage0)},
base::span_from_ref(kEndPointInsidePage0));
ExpectStrokeCounts(2, 2,
0);
histograms().ExpectBucketCount(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kTouch, 1);
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric, 2);
ApplyStrokeWithPenAtPoints(base::span_from_ref(kStartPointInsidePage0),
{base::span_from_ref(kEndPointInsidePage0)},
base::span_from_ref(kEndPointInsidePage0));
ExpectStrokeCounts(3, 3,
0);
histograms().ExpectBucketCount(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kPen, 1);
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric, 3);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickDelay) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
ClickTextAtPoint(kStartPointInsidePage0, 2);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs - kOneMs);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
GetPdfTestTaskEnvironment().FastForwardBy(kOneMs);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(1);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickMove) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
blink::WebMouseEvent mouse_event =
MouseEventBuilder()
.CreateLeftClickAtPosition(kStartPointInsidePage0)
.SetClickCount(2)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_event));
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(1);
MouseMoveAndUpAtPoint(kEndPointInsidePage0, 2);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(1);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(1);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, TwoClickMoveHighlight) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
constexpr gfx::PointF kStartPoint2InsidePage0{12.0f, 10.0f};
SetTextAreaPoints({kStartPointInsidePage0, kStartPoint2InsidePage0});
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
ClickTextAtPoint(kStartPointInsidePage0, 2);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPoint2InsidePage0,
1));
EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
ApplyStrokeWithMouseAtPoints(kStartPoint2InsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(2);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(2);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, ThreeClickDelay) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
ClickTextAtPoint(kStartPointInsidePage0, 2);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs - kOneMs);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
ClickTextAtPoint(kStartPointInsidePage0, 3);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(1);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(1);
}
TEST_P(PdfInkModuleTextHighlightMetricsTest, ThreeClickMove) {
EnableDrawAnnotationMode();
InitializeSimpleSinglePageBasicLayout();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
SetTextAreaPoints(base::span_from_ref(kStartPointInsidePage0));
ClickTextAtPoint(kStartPointInsidePage0, 1);
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
ClickTextAtPoint(kStartPointInsidePage0, 2);
ExpectStrokeCounts(2, 1,
1);
ValidateHighlightMetricCounts(0);
blink::WebMouseEvent mouse_event =
MouseEventBuilder()
.CreateLeftClickAtPosition(kStartPointInsidePage0)
.SetClickCount(3)
.Build();
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_event));
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(1);
GetPdfTestTaskEnvironment().FastForwardBy(kTextSelectionClickTimeMs);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(1);
MouseMoveAndUpAtPoint(kEndPointInsidePage0, 3);
ExpectStrokeCounts(3, 2,
1);
ValidateHighlightMetricCounts(1);
}
class PdfInkModuleTextHighlightCaretMetricsTest
: public PdfInkModuleTextHighlightMetricsTestBase,
public PdfInkModuleTextHighlightCaretTest {
protected:
void ApplySampleTextHighlight() {
SetSelectionRectsOnFirstPage(base::span_from_ref(kHorizontalSelection));
InSequence seq;
HandleKeyboardEventAndExpectCaret(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_RIGHT,
blink::WebInputEvent::Modifiers::kShiftKey));
HandleKeyboardEventAndExpectCaret(
GenerateKeyDownEvent(ui::KeyboardCode::VKEY_RIGHT,
blink::WebInputEvent::Modifiers::kNoModifiers));
}
};
TEST_P(PdfInkModuleTextHighlightCaretMetricsTest,
TextHighlightUndoRedoDoesNotAffectMetrics) {
SetUpCaret();
ValidateHighlightMetricCounts(0);
RunHandledArrowKeyTest(ui::KeyboardCode::VKEY_RIGHT);
ValidateHighlightMetricCounts(1);
PerformUndo();
PerformRedo();
ExpectStrokeCounts(1, 1,
0);
ValidateHighlightMetricCounts(1);
}
TEST_P(PdfInkModuleTextHighlightCaretMetricsTest, Color) {
SetUpCaret();
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
histograms().ExpectTotalCount(kTextHighlightColorMetric, 0);
ApplySampleTextHighlight();
ExpectStrokeCounts(1, 1,
0);
histograms().ExpectUniqueSample(kTextHighlightColorMetric,
StrokeMetricHighlighterColor::kLightRed, 1);
SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
ApplySampleTextHighlight();
ExpectStrokeCounts(2, 2,
0);
histograms().ExpectBucketCount(kTextHighlightColorMetric,
StrokeMetricHighlighterColor::kOrange, 1);
histograms().ExpectTotalCount(kTextHighlightColorMetric, 2);
}
TEST_P(PdfInkModuleTextHighlightCaretMetricsTest, InputDevice) {
SetUpCaret();
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric, 0);
ApplySampleTextHighlight();
histograms().ExpectUniqueSample(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kKeyboard, 1);
SetTextAreaPoints({kStartPointInsidePage0, kEndPointInsidePage0});
ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
kEndPointInsidePage0);
histograms().ExpectBucketCount(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kMouse, 1);
histograms().ExpectBucketCount(kTextHighlightInputDeviceMetric,
StrokeMetricInputDeviceType::kKeyboard, 1);
histograms().ExpectTotalCount(kTextHighlightInputDeviceMetric, 2);
}
INSTANTIATE_TEST_SUITE_P(All,
PdfInkModuleTest,
testing::ValuesIn(GetAllInkTestVariations()));
INSTANTIATE_TEST_SUITE_P(All,
PdfInkModuleStrokeTest,
testing::ValuesIn(GetAllInkTestVariations()));
INSTANTIATE_TEST_SUITE_P(All,
PdfInkModuleUndoRedoTest,
testing::ValuesIn(GetAllInkTestVariations()));
INSTANTIATE_TEST_SUITE_P(All,
PdfInkModuleGetVisibleStrokesTest,
testing::ValuesIn(GetAllInkTestVariations()));
INSTANTIATE_TEST_SUITE_P(All,
PdfInkModuleMetricsTest,
testing::ValuesIn(GetAllInkTestVariations()));
INSTANTIATE_TEST_SUITE_P(
All,
PdfInkModuleTextHighlightTest,
testing::ValuesIn(GetInkTestVariationsWithTextHighlighting()));
INSTANTIATE_TEST_SUITE_P(
All,
PdfInkModuleTextHighlightCaretTest,
testing::ValuesIn(GetInkTestVariationsWithTextHighlighting()));
INSTANTIATE_TEST_SUITE_P(
All,
PdfInkModuleTextHighlightMetricsTest,
testing::ValuesIn(GetInkTestVariationsWithTextHighlighting()));
INSTANTIATE_TEST_SUITE_P(
All,
PdfInkModuleTextHighlightCaretMetricsTest,
testing::ValuesIn(GetInkTestVariationsWithTextHighlighting()));
}