
Add CreateMouseMoveWithLeftButtonEventAtPoint() to simplify a commonly set of MouseEventBuilder calls. Change-Id: I173631a97e614d745c646822bc88ab6ec826bb77 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6195921 Reviewed-by: Alan Screen <awscreen@chromium.org> Commit-Queue: Lei Zhang <thestig@chromium.org> Cr-Commit-Position: refs/heads/main@{#1411044}
2980 lines
120 KiB
C++
2980 lines
120 KiB
C++
// Copyright 2024 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "pdf/pdf_ink_module.h"
|
|
|
|
#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/test/bind.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "base/test/values_test_util.h"
|
|
#include "base/values.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/mouse_event_builder.h"
|
|
#include "pdf/test/pdf_ink_test_helpers.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.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/type_matchers.h"
|
|
#include "third_party/skia/include/core/SkBitmap.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 {
|
|
|
|
// Some commonly used points with InitializeSimpleSinglePageBasicLayout().
|
|
constexpr gfx::PointF kLeftVerticalStrokePoint1(10.0f, 15.0f);
|
|
constexpr gfx::PointF kLeftVerticalStrokePoint2(10.0f, 35.0f);
|
|
constexpr gfx::PointF kMiddleVerticalStrokePoint1(25.0f, 15.0f);
|
|
constexpr gfx::PointF kMiddleVerticalStrokePoint2(25.0f, 35.0f);
|
|
constexpr gfx::PointF kRightVerticalStrokePoint1(40.0f, 15.0f);
|
|
constexpr gfx::PointF kRightVerticalStrokePoint2(40.0f, 35.0f);
|
|
|
|
// Constants to support a layout of 2 pages, arranged vertically with a small
|
|
// gap between them.
|
|
constexpr gfx::RectF kVerticalLayout2Pages[] = {
|
|
gfx::RectF(/*x=*/5.0f,
|
|
/*y=*/5.0f,
|
|
/*width=*/50.0f,
|
|
/*height=*/60.0f),
|
|
gfx::RectF(/*x=*/5.0f,
|
|
/*y=*/70.0f,
|
|
/*width=*/50.0f,
|
|
/*height=*/60.0f),
|
|
};
|
|
|
|
// Some commonly used points in relation to `kVerticalLayout2Pages`.
|
|
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);
|
|
|
|
// Canonical points after stroking horizontal & vertical lines with some
|
|
// commonly used points.
|
|
// Horizontal line uses: kTwoPageVerticalLayoutPoint2InsidePage0 to
|
|
// kTwoPageVerticalLayoutPoint3InsidePage0
|
|
// or: kTwoPageVerticalLayoutPoint2InsidePage1 to
|
|
// kTwoPageVerticalLayoutPoint3InsidePage1
|
|
// Vertical line uses: kTwoPageVerticalLayoutPoint1InsidePage0 to
|
|
// kTwoPageVerticalLayoutPoint4InsidePage0
|
|
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);
|
|
|
|
// The inputs for a stroke that starts in first page, leaves the bounds of that
|
|
// page, but then moves back into the page results in one stroke with two
|
|
// segments.
|
|
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)};
|
|
// The two segments created by the inputs above.
|
|
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)};
|
|
|
|
// The stroke inputs for vertical and horizontal lines in the pages. The
|
|
// `.time` fields intentionally get a common value, to match the behavior of
|
|
// `MouseEventBuilder`.
|
|
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)},
|
|
});
|
|
|
|
// Matcher for ink::Stroke objects against their expected brush and inputs.
|
|
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 for ink::Stroke objects against an expected brush color.
|
|
MATCHER_P(InkStrokeBrushColorEq, expected_color, "") {
|
|
return chrome_pdf::GetSkColorFromInkBrush(arg.GetBrush()) == expected_color;
|
|
}
|
|
|
|
// Matcher for ink::Stroke objects against an expected brush size.
|
|
MATCHER_P(InkStrokeBrushSizeEq, expected_size, "") {
|
|
return arg.GetBrush().GetSize() == expected_size;
|
|
}
|
|
|
|
// Matcher for ink::Stroke objects against an expected drawing brush type.
|
|
// A pen is opaque while a highlighter has transparency, so a drawing
|
|
// brush type can be deduced from the ink::Stroke's brush coat.
|
|
MATCHER_P(InkStrokeDrawingBrushTypeEq, expected_type, "") {
|
|
const ink::Brush& ink_brush = arg.GetBrush();
|
|
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
|
|
float opacity = coat.tips[0].opacity_multiplier;
|
|
if (expected_type == PdfInkBrush::Type::kPen) {
|
|
return opacity == 1.0f;
|
|
}
|
|
|
|
CHECK(expected_type == PdfInkBrush::Type::kHighlighter);
|
|
return opacity == 0.4f;
|
|
}
|
|
|
|
// Matcher for bitmap against expected dimensions.
|
|
MATCHER_P(BitmapImageSizeEq, dimensions, "") {
|
|
return arg.dimensions() == dimensions;
|
|
}
|
|
|
|
std::map<int, std::vector<raw_ref<const ink::Stroke>>> CollectVisibleStrokes(
|
|
PdfInkModule::PageInkStrokeIterator strokes_iter) {
|
|
std::map<int, std::vector<raw_ref<const ink::Stroke>>> visible_stroke_shapes;
|
|
for (auto page_stroke = strokes_iter.GetNextStrokeAndAdvance();
|
|
page_stroke.has_value();
|
|
page_stroke = strokes_iter.GetNextStrokeAndAdvance()) {
|
|
visible_stroke_shapes[page_stroke.value().page_index].push_back(
|
|
page_stroke.value().stroke);
|
|
}
|
|
|
|
return visible_stroke_shapes;
|
|
}
|
|
|
|
blink::WebMouseEvent CreateMouseMoveWithLeftButtonEventAtPoint(
|
|
const gfx::PointF& point) {
|
|
return MouseEventBuilder()
|
|
.SetType(blink::WebInputEvent::Type::kMouseMove)
|
|
.SetPosition(point)
|
|
.SetButton(blink::WebPointerProperties::Button::kLeft)
|
|
.Build();
|
|
}
|
|
|
|
base::Value::Dict CreateGetAnnotationBrushMessageForTesting(
|
|
const std::string& brush_type) {
|
|
base::Value::Dict message;
|
|
message.Set("type", "getAnnotationBrush");
|
|
message.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 (size_t i = 0; i < points.size(); ++i) {
|
|
touch_event.touches[i].SetPositionInWidget(points[i]);
|
|
}
|
|
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;
|
|
|
|
// PdfInkModuleClient:
|
|
PageOrientation GetOrientation() const override { return orientation_; }
|
|
|
|
gfx::Vector2dF GetViewportOriginOffset() override {
|
|
return viewport_origin_offset_;
|
|
}
|
|
|
|
gfx::Rect GetPageContentsRect(int index) override {
|
|
CHECK_GE(index, 0);
|
|
CHECK_LT(static_cast<size_t>(index), page_layouts_.size());
|
|
return gfx::ToEnclosedRect(page_layouts_[index]);
|
|
}
|
|
|
|
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(PdfInkModuleClient::DocumentV2InkPathShapesMap,
|
|
LoadV2InkPathsFromPdf,
|
|
(),
|
|
(override));
|
|
|
|
MOCK_METHOD(void, PostMessage, (base::Value::Dict message), (override));
|
|
|
|
MOCK_METHOD(void,
|
|
StrokeAdded,
|
|
(int page_index, InkStrokeId id, const ink::Stroke& stroke),
|
|
(override));
|
|
|
|
void StrokeFinished() override { ++stroke_finished_count_; }
|
|
|
|
MOCK_METHOD(void, UpdateInkCursorImage, (SkBitmap bitmap), (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));
|
|
|
|
MOCK_METHOD(void,
|
|
DiscardStroke,
|
|
(int page_index, InkStrokeId id),
|
|
(override));
|
|
|
|
void UpdateThumbnail(int page_index) override {
|
|
updated_thumbnail_page_indices_.push_back(page_index);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Point is not over a visible page in the viewer plane.
|
|
return -1;
|
|
}
|
|
|
|
int stroke_finished_count() const { return stroke_finished_count_; }
|
|
|
|
const std::vector<int>& updated_thumbnail_page_indices() const {
|
|
return updated_thumbnail_page_indices_;
|
|
}
|
|
|
|
const std::vector<gfx::Rect>& invalidations() const { return invalidations_; }
|
|
|
|
// Provide the sequence of pages and the coordinates and dimensions for how
|
|
// they are laid out in a viewer plane. It is upon the caller to ensure the
|
|
// positioning makes sense (e.g., pages do not overlap).
|
|
void set_page_layouts(base::span<const gfx::RectF> page_layouts) {
|
|
page_layouts_ = base::ToVector(page_layouts);
|
|
}
|
|
|
|
// Marks pages as visible or not. The caller is responsible for making sure
|
|
// the values makes sense.
|
|
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_finished_count_ = 0;
|
|
std::vector<int> updated_thumbnail_page_indices_;
|
|
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 PdfInkModuleTest : public testing::Test {
|
|
protected:
|
|
void EnableAnnotationMode() {
|
|
EXPECT_TRUE(
|
|
ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(true)));
|
|
}
|
|
|
|
FakeClient& client() { return client_; }
|
|
PdfInkModule& ink_module() { return ink_module_; }
|
|
const PdfInkModule& ink_module() const { return ink_module_; }
|
|
|
|
private:
|
|
base::test::ScopedFeatureList feature_list_{features::kPdfInk2};
|
|
|
|
NiceMock<FakeClient> client_;
|
|
PdfInkModule ink_module_{client_};
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(PdfInkModuleTest, UnknownMessage) {
|
|
base::Value::Dict message;
|
|
message.Set("type", "nonInkMessage");
|
|
EXPECT_FALSE(ink_module().OnMessage(message));
|
|
}
|
|
|
|
// Verify that a get eraser message gets the eraser parameters.
|
|
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageEraser) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
EXPECT_CALL(client(), PostMessage)
|
|
.WillOnce([](const base::Value::Dict& dict) {
|
|
auto expected = base::test::ParseJsonDict(R"({
|
|
"type": "getAnnotationBrushReply",
|
|
"messageId": "foo",
|
|
"data": {
|
|
"type": "eraser",
|
|
"size": 3.0,
|
|
},
|
|
})");
|
|
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
|
|
});
|
|
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateGetAnnotationBrushMessageForTesting("eraser")));
|
|
}
|
|
|
|
// Verify that a get pen message gets the pen brush parameters.
|
|
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessagePen) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
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(CreateGetAnnotationBrushMessageForTesting("pen")));
|
|
}
|
|
|
|
// Verify that a get highlighter message gets the highlighter brush parameters.
|
|
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageHighlighter) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
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(
|
|
CreateGetAnnotationBrushMessageForTesting("highlighter")));
|
|
}
|
|
|
|
// Verify that a get brush message without a parameter gets the default brush
|
|
// parameters.
|
|
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageDefault) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
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(CreateGetAnnotationBrushMessageForTesting("")));
|
|
}
|
|
|
|
// Verify that a get brush message without a parameter gets the current brush
|
|
// parameters if the brush has changed from the default brush.
|
|
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageCurrent) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
// Set the brush to eraser.
|
|
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationBrushMessageForTesting(
|
|
"eraser", /*size=*/4.5, nullptr)));
|
|
|
|
EXPECT_CALL(client(), PostMessage)
|
|
.WillOnce([](const base::Value::Dict& dict) {
|
|
auto expected = base::test::ParseJsonDict(R"({
|
|
"type": "getAnnotationBrushReply",
|
|
"messageId": "foo",
|
|
"data": {
|
|
"type": "eraser",
|
|
"size": 4.5,
|
|
},
|
|
})");
|
|
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
|
|
});
|
|
|
|
EXPECT_TRUE(
|
|
ink_module().OnMessage(CreateGetAnnotationBrushMessageForTesting("")));
|
|
}
|
|
|
|
// Verify that a set eraser message sets the annotation brush to an eraser.
|
|
TEST_F(PdfInkModuleTest, HandleSetAnnotationBrushMessageEraser) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"eraser", /*size=*/2.5, nullptr);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
|
|
const PdfInkBrush* brush = ink_module().GetPdfInkBrushForTesting();
|
|
EXPECT_FALSE(brush);
|
|
std::optional<float> eraser = ink_module().GetEraserSizeForTesting();
|
|
EXPECT_THAT(eraser, testing::Optional(2.5f));
|
|
}
|
|
|
|
// Verify that a set pen message sets the annotation brush to a pen, with the
|
|
// given params.
|
|
TEST_F(PdfInkModuleTest, HandleSetAnnotationBrushMessagePen) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/10,
|
|
/*color_g=*/255,
|
|
/*color_b=*/50};
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"pen", /*size=*/8.0, &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(SkColorSetRGB(10, 255, 50), GetSkColorFromInkBrush(ink_brush));
|
|
EXPECT_EQ(8.0f, ink_brush.GetSize());
|
|
ASSERT_EQ(1u, ink_brush.CoatCount());
|
|
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
|
|
ASSERT_EQ(1u, coat.tips.size());
|
|
EXPECT_EQ(1.0f, coat.tips[0].corner_rounding);
|
|
EXPECT_EQ(1.0f, coat.tips[0].opacity_multiplier);
|
|
}
|
|
|
|
// Verify that a set highlighter message sets the annotation brush to a
|
|
// highlighter, with the given params.
|
|
TEST_F(PdfInkModuleTest, HandleSetAnnotationBrushMessageHighlighter) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/240,
|
|
/*color_g=*/133,
|
|
/*color_b=*/0};
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"highlighter", /*size=*/4.5, &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(SkColorSetRGB(240, 133, 0), GetSkColorFromInkBrush(ink_brush));
|
|
EXPECT_EQ(4.5f, ink_brush.GetSize());
|
|
ASSERT_EQ(1u, ink_brush.CoatCount());
|
|
const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
|
|
ASSERT_EQ(1u, coat.tips.size());
|
|
EXPECT_EQ(0.0f, coat.tips[0].corner_rounding);
|
|
EXPECT_EQ(0.4f, coat.tips[0].opacity_multiplier);
|
|
}
|
|
|
|
// Verify that brushes with zero color values can be set as the annotation
|
|
// brush.
|
|
TEST_F(PdfInkModuleTest, HandleSetAnnotationBrushMessageColorZero) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/0, /*color_g=*/0,
|
|
/*color_b=*/0};
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"pen", /*size=*/4.5, &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];
|
|
ASSERT_EQ(1u, coat.tips.size());
|
|
EXPECT_EQ(1.0f, coat.tips[0].corner_rounding);
|
|
EXPECT_EQ(1.0f, coat.tips[0].opacity_multiplier);
|
|
}
|
|
|
|
TEST_F(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(/*enable=*/false);
|
|
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
EXPECT_FALSE(ink_module().enabled());
|
|
EXPECT_TRUE(ink_module().loaded_v2_shapes_.empty());
|
|
|
|
message.Set("enable", true);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
|
|
|
|
message.Set("enable", false);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
EXPECT_FALSE(ink_module().enabled());
|
|
EXPECT_THAT(ink_module().loaded_v2_shapes_, kShapeMapMatcher);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleTest, MaybeSetCursorWhenTogglingAnnotationMode) {
|
|
EXPECT_FALSE(ink_module().enabled());
|
|
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce(
|
|
[this](SkBitmap bitmap) { EXPECT_TRUE(ink_module().enabled()); });
|
|
|
|
base::Value::Dict message =
|
|
CreateSetAnnotationModeMessageForTesting(/*enable=*/true);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
message.Set("enable", false);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
EXPECT_FALSE(ink_module().enabled());
|
|
}
|
|
|
|
TEST_F(PdfInkModuleTest, MaybeSetCursorWhenChangingBrushes) {
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(6, bitmap.width());
|
|
EXPECT_EQ(6, bitmap.height());
|
|
});
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(20, bitmap.width());
|
|
EXPECT_EQ(20, bitmap.height());
|
|
});
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(10, bitmap.width());
|
|
EXPECT_EQ(10, bitmap.height());
|
|
});
|
|
}
|
|
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
|
|
/*color_g=*/255,
|
|
/*color_b=*/0};
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"pen", /*size=*/16.0, &message_params);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
|
|
message = CreateSetAnnotationBrushMessageForTesting("eraser", /*size=*/8.0,
|
|
nullptr);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleTest, MaybeSetCursorWhenChangingZoom) {
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(6, bitmap.width());
|
|
EXPECT_EQ(6, bitmap.height());
|
|
});
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(20, bitmap.width());
|
|
EXPECT_EQ(20, bitmap.height());
|
|
});
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(_))
|
|
.WillOnce([](SkBitmap bitmap) {
|
|
EXPECT_EQ(10, bitmap.width());
|
|
EXPECT_EQ(10, bitmap.height());
|
|
});
|
|
}
|
|
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
|
|
/*color_g=*/255,
|
|
/*color_b=*/0};
|
|
base::Value::Dict message = CreateSetAnnotationBrushMessageForTesting(
|
|
"pen", /*size=*/16.0, &message_params);
|
|
EXPECT_TRUE(ink_module().OnMessage(message));
|
|
|
|
client().set_zoom(0.5f);
|
|
ink_module().OnGeometryChanged();
|
|
}
|
|
|
|
TEST_F(PdfInkModuleTest, ContentFocusedPostMessage) {
|
|
EnableAnnotationMode();
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder().CreateLeftClickAtPosition(gfx::PointF()).Build();
|
|
|
|
EXPECT_CALL(client(), PostMessage)
|
|
.WillOnce([](const base::Value::Dict& dict) {
|
|
auto expected = base::test::ParseJsonDict(R"({
|
|
"type": "contentFocused",
|
|
})");
|
|
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
|
|
});
|
|
|
|
ink_module().HandleInputEvent(mouse_down_event);
|
|
}
|
|
|
|
class PdfInkModuleStrokeTest : public PdfInkModuleTest {
|
|
protected:
|
|
// Mouse locations used for `RunStrokeCheckTest()`.
|
|
// Touch events may use the same coordinates.
|
|
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};
|
|
|
|
void InitializeSimpleSinglePageBasicLayout() {
|
|
// Single page layout that matches visible area.
|
|
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() {
|
|
// Single page layout that matches visible area.
|
|
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() {
|
|
// Page 2 is below page 1. Not side-by-side.
|
|
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,
|
|
/*expect_mouse_events_handled=*/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,
|
|
/*expect_mouse_events_handled=*/false);
|
|
}
|
|
|
|
void RunStrokeCheckTest(bool annotation_mode_enabled) {
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(annotation_mode_enabled)));
|
|
EXPECT_EQ(annotation_mode_enabled, ink_module().enabled());
|
|
|
|
ApplyStrokeWithMouseAtPointsMaybeHandled(
|
|
kMouseDownPoint, base::span_from_ref(kMouseMovePoint), kMouseUpPoint,
|
|
/*expect_mouse_events_handled=*/annotation_mode_enabled);
|
|
|
|
ValidateRunStrokeCheckTest(
|
|
/*expect_stroke_success=*/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,
|
|
/*expect_touch_events_handled=*/true);
|
|
}
|
|
|
|
// TODO(crbug.com/377733396): Consider refactoring to combine with
|
|
// RunStrokeCheckTest().
|
|
void RunStrokeTouchCheckTest(bool annotation_mode_enabled) {
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(annotation_mode_enabled)));
|
|
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),
|
|
/*expect_touch_events_handled=*/annotation_mode_enabled);
|
|
|
|
ValidateRunStrokeCheckTest(
|
|
/*expect_stroke_success=*/annotation_mode_enabled);
|
|
}
|
|
|
|
// TODO(crbug.com/377733396): Consider refactoring to combine with
|
|
// RunStrokeCheckTest().
|
|
//
|
|
// Note that currently multi-touch is not handled, so the test expectations
|
|
// are different from the ones in RunStrokeTouchCheckTest().
|
|
void RunStrokeMultiTouchCheckTest(bool annotation_mode_enabled) {
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(annotation_mode_enabled)));
|
|
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,
|
|
/*expect_touch_events_handled=*/false);
|
|
|
|
ValidateRunStrokeCheckTest(/*expect_stroke_success=*/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,
|
|
/*expect_pen_events_handled=*/true);
|
|
}
|
|
|
|
// TODO(crbug.com/377733396): Consider refactoring to combine with
|
|
// RunStrokeCheckTest().
|
|
void RunStrokePenCheckTest(bool annotation_mode_enabled) {
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(annotation_mode_enabled)));
|
|
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),
|
|
/*expect_pen_events_handled=*/annotation_mode_enabled);
|
|
|
|
ValidateRunStrokeCheckTest(
|
|
/*expect_stroke_success=*/annotation_mode_enabled);
|
|
}
|
|
|
|
void RunStrokeMissedEndEventCheckTest() {
|
|
{
|
|
// Start a drawing or erase action.
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kMouseDownPoint)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// Simulate scenario where another view has taken the focus and consumed
|
|
// the mouse up event, such that subsequent mouse moves don't show the
|
|
// left mouse button being pressed. This should be handled, as it treats
|
|
// it as a signal to terminate the prior stroke.
|
|
blink::WebMouseEvent mouse_move_event =
|
|
MouseEventBuilder()
|
|
.SetType(blink::WebInputEvent::Type::kMouseMove)
|
|
.SetPosition(kMouseMovePoint)
|
|
.SetButton(blink::WebPointerProperties::Button::kNoButton)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
|
|
}
|
|
|
|
{
|
|
// Another mouse move event without the button down is no longer handled
|
|
// since there is no longer any active drawing or erasing.
|
|
constexpr gfx::PointF kMouseMovePoint2 = gfx::PointF(21.0f, 26.0f);
|
|
blink::WebMouseEvent mouse_move_event =
|
|
MouseEventBuilder()
|
|
.SetType(blink::WebInputEvent::Type::kMouseMove)
|
|
.SetPosition(kMouseMovePoint2)
|
|
.SetButton(blink::WebPointerProperties::Button::kNoButton)
|
|
.Build();
|
|
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
|
|
}
|
|
|
|
{
|
|
// Start another stroke with a new mouse down event, which is handled.
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kMouseDownPoint)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kMouseUpPoint)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
}
|
|
}
|
|
|
|
void SelectBrushTool(PdfInkBrush::Type type,
|
|
float size,
|
|
const TestAnnotationBrushMessageParams& params) {
|
|
EXPECT_TRUE(
|
|
ink_module().OnMessage(CreateSetAnnotationBrushMessageForTesting(
|
|
PdfInkBrush::TypeToString(type), size, ¶ms)));
|
|
}
|
|
|
|
void SelectEraserToolOfSize(float size) {
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationBrushMessageForTesting("eraser", size, nullptr)));
|
|
}
|
|
|
|
PdfInkModule::DocumentStrokeInputPointsMap StrokeInputPositions() const {
|
|
return ink_module().GetStrokesInputPositionsForTesting();
|
|
}
|
|
PdfInkModule::DocumentStrokeInputPointsMap VisibleStrokeInputPositions()
|
|
const {
|
|
return ink_module().GetVisibleStrokesInputPositionsForTesting();
|
|
}
|
|
|
|
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 VerifyAndClearExpectations() {
|
|
testing::Mock::VerifyAndClearExpectations(this);
|
|
}
|
|
|
|
private:
|
|
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 =
|
|
MouseEventBuilder().CreateLeftClickAtPosition(mouse_down_point).Build();
|
|
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 =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(mouse_move_point);
|
|
EXPECT_EQ(expect_mouse_events_handled,
|
|
ink_module().HandleInputEvent(mouse_move_event));
|
|
}
|
|
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder().CreateLeftMouseUpAtPosition(mouse_up_point).Build();
|
|
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_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
if (expect_stroke_success) {
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
} else {
|
|
EXPECT_TRUE(updated_thumbnail_page_indices.empty());
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, NoAnnotationWithMouseIfNotEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/false);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, AnnotationWithMouseIfEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
EXPECT_EQ(3, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, NoAnnotationWithTouchIfNotEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/false);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, AnnotationWithTouchIfEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/true);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(3, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, NoAnnotationWithMultiTouchIfNotEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeMultiTouchCheckTest(/*annotation_mode_enabled=*/false);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, NoAnnotationWithMultiTouchIfEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeMultiTouchCheckTest(/*annotation_mode_enabled=*/true);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, NoAnnotationWithPenIfNotEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokePenCheckTest(/*annotation_mode_enabled=*/false);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, AnnotationWithPenIfEnabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokePenCheckTest(/*annotation_mode_enabled=*/true);
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kMouse));
|
|
EXPECT_EQ(0, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kTouch));
|
|
EXPECT_EQ(3, ink_module().GetInputOfTypeCountForPageForTesting(
|
|
/*page_index=*/0, ink::StrokeInput::ToolType::kStylus));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, CanonicalAnnotationPoints) {
|
|
// Setup to support examining the page stroke points for a layout that is
|
|
// more complicated than what is provide by
|
|
// `InitializeSimpleSinglePageBasicLayout()`. Include viewport offset,
|
|
// scroll, rotation, and zoom.
|
|
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(/*annotation_mode_enabled=*/true);
|
|
|
|
// There should be 3 points collected, for the mouse down, move, and up
|
|
// events. Verify that the collected points match a canonical position for
|
|
// the PdfInkModuleClient setup.
|
|
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, PdfInkModule::PageStrokeInputPoints{
|
|
{kCanonicalMouseDownPosition,
|
|
kCanonicalMouseMovePosition,
|
|
kCanonicalMouseUpPosition}})));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, BasicLayoutInvalidationsFromStroke) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// The default brush param size is 3.0.
|
|
const gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(4.0f, 4.0f));
|
|
const gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(14.0f, 14.0f));
|
|
const gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18.0f, 15.0f),
|
|
gfx::Size(14.0f, 12.0f));
|
|
const gfx::Rect kInvalidationAreaFinishedStroke(8.0f, 13.0f, 25.0f, 7.0f);
|
|
EXPECT_THAT(
|
|
client().invalidations(),
|
|
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
|
|
kInvalidationAreaMouseUp, kInvalidationAreaFinishedStroke));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, TransformedLayoutInvalidationsFromStroke) {
|
|
// Setup to support examining the invalidation areas from page stroke points
|
|
// for a layout that is more complicated than what is provide by
|
|
// `InitializeSimpleSinglePageBasicLayout()`. Include viewport offset,
|
|
// scroll, rotation, and zoom.
|
|
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(/*annotation_mode_enabled=*/true);
|
|
|
|
// The default brush param size is 3.0.
|
|
const gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(4.0f, 4.0f));
|
|
const gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(14.0f, 14.0f));
|
|
const gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18.0f, 15.0f),
|
|
gfx::Size(14.0f, 12.0f));
|
|
const gfx::Rect kInvalidationAreaFinishedStroke(7.0f, 12.0f, 27.0f, 9.0f);
|
|
EXPECT_THAT(
|
|
client().invalidations(),
|
|
ElementsAre(kInvalidationAreaMouseDown, kInvalidationAreaMouseMove,
|
|
kInvalidationAreaMouseUp, kInvalidationAreaFinishedStroke));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, StrokeOutsidePage) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
// A stroke that starts outside of any page does not generate a stroke, even
|
|
// if it crosses into a page.
|
|
ApplyStrokeWithMouseAtPointsNotHandled(
|
|
kTwoPageVerticalLayoutPointOutsidePages,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, StrokeInsidePages) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
// A stroke in the first page generates a stroke only for that page.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
|
|
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
|
|
|
|
// A stroke in the second page generates a stroke only for that page.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage1,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
|
|
kTwoPageVerticalLayoutPoint3InsidePage1);
|
|
|
|
EXPECT_THAT(StrokeInputPositions(),
|
|
ElementsAre(Pair(0, SizeIs(1)), Pair(1, SizeIs(1))));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, StrokeAcrossPages) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
// A stroke that starts in first page and ends in the second page only
|
|
// generates one stroke in the first page.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
|
|
kTwoPageVerticalLayoutPoint3InsidePage1);
|
|
|
|
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, StrokePageExitAndReentry) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
kTwoPageVerticalLayoutPageExitAndReentryPoints,
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
|
|
EXPECT_THAT(
|
|
StrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0,
|
|
ElementsAre(ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
|
|
ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, StrokePageExitAndReentryWithQuickMoves) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
// When the mouse cursor moves quickly, PdfInkModule gets fewer input events.
|
|
// Simulate that here with fewer movement inputs compared to
|
|
// `kTwoPageVerticalLayoutPageExitAndReentryPoints`.
|
|
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_F(PdfInkModuleStrokeTest, EraseStroke) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Check that there are now some visible strokes.
|
|
EXPECT_THAT(
|
|
VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Stroke with the eraser tool.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
// Now there are no visible strokes left.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Erasing counts as another stroke action.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke again. The stroke that have already been erased should stay erased.
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
// Still no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Nothing got erased, so the count stays at 2.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseOnPageWithoutStrokes) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Verify there are no visible strokes to start with.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
|
|
// Stroke with the eraser tool when there are no strokes on the page.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
// Verify there are still no visible strokes and StrokeFinished() never got
|
|
// called.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
EXPECT_EQ(0, client().stroke_finished_count());
|
|
EXPECT_TRUE(client().updated_thumbnail_page_indices().empty());
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokeEntirelyOffPage) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Check that there are now some visible strokes.
|
|
EXPECT_THAT(
|
|
VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Stroke with the eraser tool outside of the page.
|
|
SelectEraserToolOfSize(3.0f);
|
|
constexpr gfx::PointF kOffPagePoint(99.0f, 99.0f);
|
|
ApplyStrokeWithMouseAtPointsNotHandled(
|
|
kOffPagePoint, base::span_from_ref(kOffPagePoint), kOffPagePoint);
|
|
|
|
// Check that the visible strokes remain, and StrokeFinished() did not get
|
|
// called again.
|
|
EXPECT_THAT(
|
|
VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokeErasesTwoStrokes) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
ExpectStrokesAdded(/*strokes_affected=*/2);
|
|
ExpectNoUpdateStrokeActive();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Draw a second stroke.
|
|
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);
|
|
|
|
// Check that there are now some visible strokes.
|
|
const auto kStroke2Matcher =
|
|
ElementsAre(kMouseDownPoint2, kMouseMovePoint, kMouseUpPoint2);
|
|
const auto kVisibleStrokesMatcher = ElementsAre(
|
|
Pair(0, ElementsAre(ElementsAreArray(kMousePoints), kStroke2Matcher)));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kVisibleStrokesMatcher);
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke with the eraser tool at `kMouseMovePoint`, where it should
|
|
// intersect with both strokes, but does not because InkStrokeModeler modeled
|
|
// the "V" shaped input into an input with a much gentler line slope.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kMouseMovePoint, base::span_from_ref(kMouseMovePoint), kMouseMovePoint);
|
|
|
|
// Check that the visible strokes are still there since the eraser tool missed
|
|
// the strokes.
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kVisibleStrokesMatcher);
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke with the eraser tool again at `kMousePoints`, but now with a much
|
|
// bigger eraser size. This will actually intersect with both strokes.
|
|
SelectEraserToolOfSize(8.0f);
|
|
VerifyAndClearExpectations();
|
|
ExpectNoStrokeAdded();
|
|
ExpectUpdateStrokesActive(/*strokes_affected=*/2, /*expected_active=*/false);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kMouseMovePoint, base::span_from_ref(kMouseMovePoint), kMouseMovePoint);
|
|
|
|
// Check that there are now no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
EXPECT_EQ(3, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokesAcrossTwoPages) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
EXPECT_EQ(0, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_TRUE(updated_thumbnail_page_indices.empty());
|
|
|
|
ExpectStrokesAdded(/*strokes_affected=*/2);
|
|
ExpectNoUpdateStrokeActive();
|
|
|
|
// A stroke in the first page generates a stroke only for that page.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
EXPECT_THAT(StrokeInputPositions(), ElementsAre(Pair(0, SizeIs(1))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// A stroke in the second page generates a stroke only for that page.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage1,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
|
|
kTwoPageVerticalLayoutPoint3InsidePage1);
|
|
EXPECT_THAT(StrokeInputPositions(),
|
|
ElementsAre(Pair(0, SizeIs(1)), Pair(1, SizeIs(1))));
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 1));
|
|
|
|
// Erasing across the two pages should erase everything.
|
|
SelectEraserToolOfSize(3.0f);
|
|
VerifyAndClearExpectations();
|
|
ExpectNoStrokeAdded();
|
|
ExpectUpdateStrokesActive(/*strokes_affected=*/2, /*expected_active=*/false);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
std::vector<gfx::PointF>{kTwoPageVerticalLayoutPoint2InsidePage0,
|
|
kTwoPageVerticalLayoutPoint1InsidePage1},
|
|
kTwoPageVerticalLayoutPoint3InsidePage1);
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
EXPECT_EQ(3, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 1, 0, 1));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokePageExitAndReentry) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Start out without any strokes.
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
|
|
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
kTwoPageVerticalLayoutPageExitAndReentryPoints,
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
|
|
EXPECT_THAT(
|
|
StrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0,
|
|
ElementsAre(ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
|
|
ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Select the eraser tool and call ApplyStrokeWithMouseAtPoints() again with
|
|
// the same arguments.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtPoints(kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
kTwoPageVerticalLayoutPageExitAndReentryPoints,
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
|
|
// The strokes are all still there, but none of them are visible.
|
|
EXPECT_THAT(
|
|
StrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0,
|
|
ElementsAre(ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment1),
|
|
ElementsAreArray(
|
|
kTwoPageVerticalLayoutPageExitAndReentrySegment2)))));
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Erasing counts as another stroke action.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokeWithTouch) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Check that there are now some visible strokes.
|
|
EXPECT_THAT(
|
|
VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Stroke with the eraser tool.
|
|
SelectEraserToolOfSize(3.0f);
|
|
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));
|
|
|
|
// Now there are no visible strokes left.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Erasing counts as another stroke action.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke again. The stroke that have already been erased should stay erased.
|
|
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint),
|
|
touch_move_points,
|
|
base::span_from_ref(kMouseDownPoint));
|
|
|
|
// Still no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Nothing got erased, so the count stays at 2.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke again with the mouse gets the same results.
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
// Still no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Nothing got erased, so the count stays at 2.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, EraseStrokeWithPen) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokePenCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Check that there are now some visible strokes.
|
|
EXPECT_THAT(
|
|
VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints)))));
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Stroke with the eraser tool.
|
|
SelectEraserToolOfSize(3.0f);
|
|
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));
|
|
|
|
// Now there are no visible strokes left.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Erasing counts as another stroke action.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke again. The stroke that have already been erased should stay erased.
|
|
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint),
|
|
pen_move_points,
|
|
base::span_from_ref(kMouseDownPoint));
|
|
|
|
// Still no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Nothing got erased, so the count stays at 2.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Stroke again with the mouse gets the same results.
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
// Still no visible strokes.
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Nothing got erased, so the count stays at 2.
|
|
EXPECT_EQ(2, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, RunStrokeMissedEndEventDuringDrawing) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(/*enable=*/true)));
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
// No need to distinguish between pen or highlighter here.
|
|
EXPECT_TRUE(
|
|
ink_module().OnMessage(CreateGetAnnotationBrushMessageForTesting("pen")));
|
|
|
|
RunStrokeMissedEndEventCheckTest();
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, RunStrokeMissedEndEventDuringErasing) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
EXPECT_TRUE(ink_module().OnMessage(
|
|
CreateSetAnnotationModeMessageForTesting(/*enable=*/true)));
|
|
EXPECT_TRUE(ink_module().enabled());
|
|
|
|
SelectEraserToolOfSize(3.0f);
|
|
|
|
RunStrokeMissedEndEventCheckTest();
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, ChangeBrushColorDuringDrawing) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Start drawing a stroke with a black pen. The stroke will not finish
|
|
// until the mouse-up event.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
TestAnnotationBrushMessageParams black_pen_message_params{/*color_r=*/0,
|
|
/*color_g=*/0,
|
|
/*color_b=*/0};
|
|
static constexpr float kPenSize = 3.0f;
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, kPenSize, black_pen_message_params);
|
|
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kLeftVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change the pen color. This has no
|
|
// immediate effect on the in-progress stroke.
|
|
TestAnnotationBrushMessageParams red_pen_message_params{/*color_r=*/242,
|
|
/*color_g=*/139,
|
|
/*color_b=*/130};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, kPenSize, red_pen_message_params);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue with mouse movement and then mouse up at a new location. Notice
|
|
// that the events are handled and the new stroke is added.
|
|
static constexpr int kPageIndex = 0;
|
|
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(0),
|
|
InkStrokeBrushColorEq(SK_ColorBLACK)));
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(kLeftVerticalStrokePoint2);
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kLeftVerticalStrokePoint2)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do another stroke. Notice that the changed pen color is in effect for
|
|
// the new stroke that is added.
|
|
EXPECT_CALL(client(),
|
|
StrokeAdded(kPageIndex, InkStrokeId(1),
|
|
InkStrokeBrushColorEq(SkColorSetRGB(242, 139, 130))));
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, ChangeBrushSizeDuringDrawing) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Start drawing a stroke with a black pen. The stroke will not finish
|
|
// until the mouse-up event. The cursor image will be updated only when
|
|
// there is not a stroke in progress.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(BitmapImageSizeEq(SkISize(6, 6))));
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
|
|
/*color_g=*/0,
|
|
/*color_b=*/0};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 2.0f, message_params);
|
|
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kLeftVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change the pen size. This has no
|
|
// immediate effect on the in-progress stroke.
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 6.0f, message_params);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue with mouse movement and then mouse up at a new location. Notice
|
|
// that the events are handled and the new stroke is added. The cursor image
|
|
// also gets updated once the stroke has finished.
|
|
static constexpr int kPageIndex = 0;
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(0),
|
|
InkStrokeBrushSizeEq(2.0f)));
|
|
EXPECT_CALL(client(),
|
|
UpdateInkCursorImage(BitmapImageSizeEq(SkISize(8, 8))));
|
|
}
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(kLeftVerticalStrokePoint2);
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kLeftVerticalStrokePoint2)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do another stroke. Notice that the changed pen color has now taken
|
|
// effect for the new stroke that is added.
|
|
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_F(PdfInkModuleStrokeTest, ChangeSizeDuringErasing) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Initialize to have three strokes, so there is something to erase.
|
|
static constexpr int kPageIndex = 0;
|
|
EXPECT_CALL(client(), StrokeAdded(kPageIndex, _, _)).Times(3);
|
|
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
|
|
|
|
ApplyStrokeWithMouseAtPoints(kLeftVerticalStrokePoint1,
|
|
base::span_from_ref(kLeftVerticalStrokePoint2),
|
|
kLeftVerticalStrokePoint2);
|
|
|
|
ApplyStrokeWithMouseAtPoints(kMiddleVerticalStrokePoint1,
|
|
base::span_from_ref(kMiddleVerticalStrokePoint2),
|
|
kMiddleVerticalStrokePoint2);
|
|
|
|
ApplyStrokeWithMouseAtPoints(kRightVerticalStrokePoint1,
|
|
base::span_from_ref(kRightVerticalStrokePoint2),
|
|
kRightVerticalStrokePoint2);
|
|
|
|
// Set up for erasing.
|
|
SelectEraserToolOfSize(2.0f);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Apply erase strokes at positions nearby the second and third strokes.
|
|
// They are not close enough for an eraser with a thin size to erase them.
|
|
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
|
|
|
|
static constexpr gfx::PointF kNearbyPointAboveMiddleVerticalStroke(25.0f,
|
|
10.0f);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kNearbyPointAboveMiddleVerticalStroke,
|
|
base::span_from_ref(kNearbyPointAboveMiddleVerticalStroke),
|
|
kNearbyPointAboveMiddleVerticalStroke);
|
|
|
|
static constexpr gfx::PointF kNearbyPointAboveRightVerticalStroke(40.0f,
|
|
10.0f);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kNearbyPointAboveRightVerticalStroke,
|
|
base::span_from_ref(kNearbyPointAboveRightVerticalStroke),
|
|
kNearbyPointAboveRightVerticalStroke);
|
|
|
|
VerifyAndClearExpectations();
|
|
|
|
// Start erasing from where the first stroke was added.
|
|
EXPECT_CALL(client(),
|
|
UpdateStrokeActive(kPageIndex, InkStrokeId(0), /*active=*/false));
|
|
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kLeftVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change the eraser size.
|
|
SelectEraserToolOfSize(6.0f);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue the stroke, moving to the nearby-point above the second stroke.
|
|
// Since the eraser has immediately updated to the thick eraser size, it is
|
|
// now close enough that the stroke gets erased.
|
|
//
|
|
// Eraser stroke movement is like below, from the mouse down position D
|
|
// moving through position M before finishing at mouse up position U:
|
|
//
|
|
// M............U
|
|
// .
|
|
// .
|
|
// left D | middle
|
|
// stroke | | stroke
|
|
// | |
|
|
//
|
|
// TODO(crbug.com/381908888): The in-progress stroke is affected by the
|
|
// size change. Update the expectation to show the in-progress stroke is
|
|
// unchanged once the brush management in PdfInkModule protects against
|
|
// such changes.
|
|
EXPECT_CALL(client(),
|
|
UpdateStrokeActive(kPageIndex, InkStrokeId(1), /*active=*/false));
|
|
|
|
static constexpr gfx::PointF kNearbyPointAboveLeftVerticalStroke(10.0f,
|
|
10.0f);
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(
|
|
kNearbyPointAboveLeftVerticalStroke);
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
|
|
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kNearbyPointAboveMiddleVerticalStroke)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do another eraser stroke at the nearby-point above the third stroke.
|
|
// This point is close enough to be deleted with the thick eraser size
|
|
// that is now in effect.
|
|
EXPECT_CALL(client(),
|
|
UpdateStrokeActive(kPageIndex, InkStrokeId(2), /*active=*/false));
|
|
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kNearbyPointAboveRightVerticalStroke,
|
|
base::span_from_ref(kNearbyPointAboveRightVerticalStroke),
|
|
kNearbyPointAboveRightVerticalStroke);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, ChangeToEraserDuringDrawing) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Draw an initial stroke.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Start drawing another stroke.
|
|
static constexpr int kPageIndex = 0;
|
|
EXPECT_CALL(client(), StrokeAdded(kPageIndex, InkStrokeId(1), _));
|
|
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kRightVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change to the eraser tool. This
|
|
// causes the in-progress stroke to finish even before the mouse-up event.
|
|
SelectEraserToolOfSize(2.0f);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue with mouse movement and then mouse up at a new location. Notice
|
|
// that the events are not handled and there is no further effect for adding
|
|
// or erasing strokes, since the prior stroke was already finished.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(kRightVerticalStrokePoint2);
|
|
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kRightVerticalStrokePoint2)
|
|
.Build();
|
|
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_up_event));
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do a simple stroke in the same place where the last stroke was added.
|
|
// Notice that the changed tool type has taken effect and the recently added
|
|
// stroke is erased.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(),
|
|
UpdateStrokeActive(kPageIndex, InkStrokeId(1), /*active=*/false));
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleStrokeTest, ChangeToDrawingDuringErasing) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Initialize to have two strokes, so there is something to erase.
|
|
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);
|
|
|
|
// Set up for erasing.
|
|
SelectEraserToolOfSize(2.0f);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Start erasing from where the first stroke was added.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(), UpdateStrokeActive(kPageIndex, InkStrokeId(0), _));
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kLeftVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change the input tool type to a
|
|
// pen. Note that this causes the in-progress erase stroke to finish even
|
|
// before the mouse-up event.
|
|
TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
|
|
/*color_g=*/0,
|
|
/*color_b=*/0};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 8.0f, message_params);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue with mouse movement and then mouse up at a new location. Notice
|
|
// that the events are not handled and there is no further effect for adding
|
|
// or erasing strokes, since the prior stroke was already finished.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(), UpdateStrokeActive(_, _, _)).Times(0);
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(kRightVerticalStrokePoint2);
|
|
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_move_event));
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kRightVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_FALSE(ink_module().HandleInputEvent(mouse_up_event));
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do another stroke. Notice that the changed tool type has taken effect
|
|
// and a new stroke is added.
|
|
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_F(PdfInkModuleStrokeTest, ChangeDrawingBrushTypeDuringDrawing) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Start drawing a stroke with a black pen. The stroke will not finish
|
|
// until the mouse-up event. The cursor image will be updated only if a
|
|
// stroke is not in progress.
|
|
EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
|
|
EXPECT_CALL(client(), UpdateInkCursorImage(BitmapImageSizeEq(SkISize(6, 6))));
|
|
TestAnnotationBrushMessageParams pen_message_params{/*color_r=*/0,
|
|
/*color_g=*/0,
|
|
/*color_b=*/0};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 2.0f, pen_message_params);
|
|
|
|
blink::WebMouseEvent mouse_down_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftClickAtPosition(kLeftVerticalStrokePoint1)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
|
|
|
|
// While the stroke is still in progress, change the input tool type to a
|
|
// highlighter. The entire stroke changes to this new type.
|
|
TestAnnotationBrushMessageParams highlighter_message_params{/*color_r=*/221,
|
|
/*color_g=*/243,
|
|
/*color_b=*/0};
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 8.0f,
|
|
highlighter_message_params);
|
|
VerifyAndClearExpectations();
|
|
|
|
// Continue with mouse movement and then mouse up at a new location. Notice
|
|
// that the events are handled and the new stroke is added. The cursor gets
|
|
// updated to reflect the tool change to highlighter only after the stroke
|
|
// is completed.
|
|
static constexpr int kPageIndex = 0;
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(
|
|
client(),
|
|
StrokeAdded(kPageIndex, InkStrokeId(0),
|
|
InkStrokeDrawingBrushTypeEq(PdfInkBrush::Type::kPen)));
|
|
EXPECT_CALL(client(),
|
|
UpdateInkCursorImage(BitmapImageSizeEq(SkISize(10, 10))));
|
|
}
|
|
blink::WebMouseEvent mouse_move_event =
|
|
CreateMouseMoveWithLeftButtonEventAtPoint(kLeftVerticalStrokePoint2);
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_move_event));
|
|
blink::WebMouseEvent mouse_up_event =
|
|
MouseEventBuilder()
|
|
.CreateLeftMouseUpAtPosition(kLeftVerticalStrokePoint2)
|
|
.Build();
|
|
EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
|
|
VerifyAndClearExpectations();
|
|
|
|
// Do another stroke. Notice that the changed input tool type has taken
|
|
// effect for the new stroke that is added.
|
|
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));
|
|
}
|
|
|
|
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_F(PdfInkModuleUndoRedoTest, UndoRedoEmpty) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
EnableAnnotationMode();
|
|
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
|
|
// Spurious undo message is a no-op.
|
|
PerformUndo();
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
|
|
// Spurious redo message is a no-op.
|
|
PerformRedo();
|
|
EXPECT_TRUE(StrokeInputPositions().empty());
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
}
|
|
|
|
TEST_F(PdfInkModuleUndoRedoTest, UndoRedoBasic) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
ExpectStrokesAdded(/*strokes_affected=*/1);
|
|
ExpectUpdateStrokesActive(/*strokes_affected=*/1, /*expect_active=*/false);
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
const auto kMatcher =
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints))));
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
|
|
// RunStrokeCheckTest() performed the only stroke.
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
PerformUndo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
// Undo/redo here and below do not trigger StrokeFinished().
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
// Spurious undo message is a no-op.
|
|
VerifyAndClearExpectations();
|
|
ExpectNoStrokeAdded();
|
|
ExpectNoUpdateStrokeActive();
|
|
PerformUndo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
VerifyAndClearExpectations();
|
|
ExpectNoStrokeAdded();
|
|
ExpectUpdateStrokesActive(/*strokes_affected=*/1, /*expect_active=*/true);
|
|
PerformRedo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0, 0));
|
|
|
|
// Spurious redo message is a no-op.
|
|
VerifyAndClearExpectations();
|
|
ExpectNoStrokeAdded();
|
|
ExpectNoUpdateStrokeActive();
|
|
PerformRedo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleUndoRedoTest, UndoRedoInvalidationsBasic) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// The default brush param size is 3.0. Invalidation areas are in screen
|
|
// coordinates.
|
|
const gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(4.0f, 4.0f));
|
|
const gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(14.0f, 14.0f));
|
|
const gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18.0f, 15.0f),
|
|
gfx::Size(14.0f, 12.0f));
|
|
// This size is smaller than the area of the merged invalidation constants
|
|
// above because InkStrokeModeler modeled the "V" shaped input into an input
|
|
// with a much gentler line slope.
|
|
const gfx::Rect kInvalidationAreaEntireStroke(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(25.0f, 7.0f));
|
|
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_F(PdfInkModuleUndoRedoTest, UndoRedoInvalidationsScaledRotated90) {
|
|
InitializeScaledLandscapeSinglePageBasicLayout();
|
|
client().set_orientation(PageOrientation::kClockwise90);
|
|
client().set_zoom(2.0f);
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// The default brush param size is 3.0. Invalidation areas are in screen
|
|
// coordinates.
|
|
const gfx::Rect kInvalidationAreaMouseDown(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(4.0f, 4.0f));
|
|
const gfx::Rect kInvalidationAreaMouseMove(gfx::Point(8.0f, 13.0f),
|
|
gfx::Size(14.0f, 14.0f));
|
|
const gfx::Rect kInvalidationAreaMouseUp(gfx::Point(18.0f, 15.0f),
|
|
gfx::Size(14.0f, 12.0f));
|
|
// This size is smaller than the area of the merged invalidation constants
|
|
// above because InkStrokeModeler modeled the "V" shaped input into an input
|
|
// with a much gentler line slope.
|
|
const gfx::Rect kInvalidationAreaEntireStroke(gfx::Point(7.0f, 12.0f),
|
|
gfx::Size(27.0f, 9.0f));
|
|
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_F(PdfInkModuleUndoRedoTest, UndoRedoAnnotationModeDisabled) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
const auto kMatcher =
|
|
ElementsAre(Pair(0, ElementsAre(ElementsAreArray(kMousePoints))));
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
|
|
// RunStrokeCheckTest() performed the only stroke.
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
const std::vector<int>& updated_thumbnail_page_indices =
|
|
client().updated_thumbnail_page_indices();
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0));
|
|
|
|
// Disable annotation mode. Undo/redo should still work.
|
|
EXPECT_TRUE(
|
|
ink_module().OnMessage(CreateSetAnnotationModeMessageForTesting(false)));
|
|
EXPECT_EQ(false, ink_module().enabled());
|
|
|
|
PerformUndo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_TRUE(VisibleStrokeInputPositions().empty());
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0));
|
|
|
|
PerformRedo();
|
|
EXPECT_THAT(StrokeInputPositions(), kMatcher);
|
|
EXPECT_THAT(VisibleStrokeInputPositions(), kMatcher);
|
|
EXPECT_EQ(1, client().stroke_finished_count());
|
|
EXPECT_THAT(updated_thumbnail_page_indices, ElementsAre(0, 0, 0));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleUndoRedoTest, UndoRedoBetweenDraws) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/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);
|
|
|
|
// After drawing 4 strokes above, there should be 4 strokes that are all
|
|
// visible.
|
|
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))));
|
|
|
|
// Undo makes 3 strokes visible.
|
|
PerformUndo();
|
|
EXPECT_THAT(
|
|
StrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAreArray(kInitial4StrokeMatchersSpan))));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0, ElementsAreArray(kInitial4StrokeMatchersSpan.first(3u)))));
|
|
|
|
// Undo again makes 2 strokes visible.
|
|
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);
|
|
|
|
// The 2 strokes that were undone have been discarded, and the newly drawn
|
|
// stroke takes their place.
|
|
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))));
|
|
|
|
// Undo makes 2 strokes visible.
|
|
PerformUndo();
|
|
EXPECT_THAT(StrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0, ElementsAreArray(kNext3StrokeMatchersSpan.first(2u)))));
|
|
|
|
// Undo again makes 1 strokes visible.
|
|
PerformUndo();
|
|
EXPECT_THAT(StrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAreArray(kNext3StrokeMatchersSpan))));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0, ElementsAreArray(kNext3StrokeMatchersSpan.first(1u)))));
|
|
|
|
// Undo again makes no strokes visible.
|
|
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);
|
|
|
|
// All strokes were undone, so they all got discarded. The newly drawn stroke
|
|
// is the only one remaining.
|
|
const auto kFinal1StrokeMatcher =
|
|
ElementsAre(kMouseDownPoint2, kMouseMovePoint2, kMouseUpPoint2);
|
|
EXPECT_THAT(StrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(kFinal1StrokeMatcher))));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAre(kFinal1StrokeMatcher))));
|
|
}
|
|
|
|
TEST_F(PdfInkModuleUndoRedoTest, UndoRedoOnTwoPages) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage0,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage0),
|
|
kTwoPageVerticalLayoutPoint3InsidePage0);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kTwoPageVerticalLayoutPoint1InsidePage1,
|
|
base::span_from_ref(kTwoPageVerticalLayoutPoint2InsidePage1),
|
|
kTwoPageVerticalLayoutPoint3InsidePage1);
|
|
|
|
// Canonical coordinates.
|
|
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_F(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();
|
|
EnableAnnotationMode();
|
|
ASSERT_TRUE(ink_module().enabled());
|
|
|
|
// Stroke with the eraser tool in the corner opposite from `kCornerPoints`,
|
|
// which does nothing.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtPoints(
|
|
gfx::PointF(), base::span_from_ref(gfx::PointF()), gfx::PointF());
|
|
VerifyAndClearExpectations();
|
|
|
|
// Stroke twice where `shape0` is, and that should deactivate only that shape
|
|
// and only once.
|
|
ExpectNoStrokeAdded();
|
|
ExpectNoUpdateStrokeActive();
|
|
EXPECT_CALL(client(),
|
|
UpdateShapeActive(kPageIndex, kShapeId0, /*active=*/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();
|
|
|
|
// Undo should reactivate `shape0`.
|
|
ExpectNoStrokeAdded();
|
|
ExpectNoUpdateStrokeActive();
|
|
EXPECT_CALL(client(),
|
|
UpdateShapeActive(kPageIndex, kShapeId0, /*active=*/true));
|
|
EXPECT_CALL(client(), UpdateShapeActive(_, kShapeId1, _)).Times(0);
|
|
PerformUndo();
|
|
VerifyAndClearExpectations();
|
|
|
|
// Redo should deactivate `shape0`.
|
|
ExpectNoStrokeAdded();
|
|
ExpectNoUpdateStrokeActive();
|
|
EXPECT_CALL(client(),
|
|
UpdateShapeActive(kPageIndex, kShapeId0, /*active=*/false));
|
|
EXPECT_CALL(client(), UpdateShapeActive(_, kShapeId1, _)).Times(0);
|
|
PerformRedo();
|
|
}
|
|
|
|
// Regression test for crbug.com/378724153.
|
|
TEST_F(PdfInkModuleUndoRedoTest, StrokeStrokeUndoStroke) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
|
|
// Draw stroke 1.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
// Draw stroke 2.
|
|
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);
|
|
|
|
// Strokes 1 and 2 should be visible.
|
|
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))));
|
|
|
|
// Undo makes 1 stroke visible.
|
|
PerformUndo();
|
|
EXPECT_THAT(
|
|
StrokeInputPositions(),
|
|
ElementsAre(Pair(0, ElementsAreArray(kInitialStrokeMatchersSpan))));
|
|
EXPECT_THAT(VisibleStrokeInputPositions(),
|
|
ElementsAre(Pair(
|
|
0, ElementsAreArray(kInitialStrokeMatchersSpan.first(1u)))));
|
|
|
|
// Stroke IDs are 0-indexed, so stroke 2 has a stroke ID of 1.
|
|
EXPECT_CALL(client(), DiscardStroke(/*page_index=*/0, InkStrokeId(1)));
|
|
|
|
// Draw stroke 3. Stroke 2 was undone and should be discarded.
|
|
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);
|
|
|
|
// Strokes 1 and 3 should be visible.
|
|
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_F(PdfInkModuleGetVisibleStrokesTest, NoPageStrokes) {
|
|
std::map<int, std::vector<raw_ref<const ink::Stroke>>>
|
|
collected_stroke_shapes =
|
|
CollectVisibleStrokes(ink_module().GetVisibleStrokesIterator());
|
|
ASSERT_EQ(collected_stroke_shapes.size(), 0u);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleGetVisibleStrokesTest, MultiplePageStrokes) {
|
|
EnableAnnotationMode();
|
|
InitializeVerticalTwoPageLayout();
|
|
|
|
// Multiple strokes on one page, plus a stroke on another page.
|
|
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);
|
|
|
|
std::map<int, std::vector<raw_ref<const ink::Stroke>>> collected_strokes =
|
|
CollectVisibleStrokes(ink_module().GetVisibleStrokesIterator());
|
|
EXPECT_THAT(
|
|
collected_strokes,
|
|
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 PdfInkModuleUndoRedoTest {
|
|
protected:
|
|
static constexpr char kPenColorMetric[] = "PDF.Ink2StrokePenColor";
|
|
static constexpr char kHighlighterColorMetric[] =
|
|
"PDF.Ink2StrokeHighlighterColor";
|
|
static constexpr char kInputDeviceMetric[] = "PDF.Ink2StrokeInputDeviceType";
|
|
static constexpr char kPenSizeMetric[] = "PDF.Ink2StrokePenSize";
|
|
static constexpr char kHighlighterSizeMetric[] =
|
|
"PDF.Ink2StrokeHighlighterSize";
|
|
static constexpr char kEraserSizeMetric[] = "PDF.Ink2StrokeEraserSize";
|
|
static constexpr char kTypeMetric[] = "PDF.Ink2StrokeBrushType";
|
|
};
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeUndoRedoDoesNotAffectMetrics) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
// Draw a pen stroke.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/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);
|
|
|
|
// Undo and redo.
|
|
PerformUndo();
|
|
PerformRedo();
|
|
|
|
// The metrics should stay the same.
|
|
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_F(PdfInkModuleMetricsTest, StrokeBrushColorPen) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/false);
|
|
|
|
histograms.ExpectTotalCount(kPenColorMetric, 0);
|
|
|
|
// Draw a stroke with the default black color.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack,
|
|
1);
|
|
|
|
// Draw a stroke with "Red 1" color.
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
|
|
/*color_b=*/0x82};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kRed1, 1);
|
|
histograms.ExpectTotalCount(kPenColorMetric, 2);
|
|
|
|
// Draw a stroke with "Tan 3" color.
|
|
params.color_r = 0x88;
|
|
params.color_g = 0x59;
|
|
params.color_b = 0x45;
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kTan3, 1);
|
|
histograms.ExpectTotalCount(kPenColorMetric, 3);
|
|
histograms.ExpectTotalCount(kHighlighterColorMetric, 0);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeBrushColorHighlighter) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
// Draw a stroke with "Light Red" color.
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
|
|
/*color_b=*/0x82};
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 6.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kHighlighterColorMetric,
|
|
StrokeMetricHighlighterColor::kLightRed, 1);
|
|
histograms.ExpectTotalCount(kHighlighterColorMetric, 1);
|
|
|
|
// Draw a stroke with "Orange" color.
|
|
params.color_r = 0xFF;
|
|
params.color_g = 0x63;
|
|
params.color_b = 0x0C;
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 6.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kHighlighterColorMetric,
|
|
StrokeMetricHighlighterColor::kOrange, 1);
|
|
histograms.ExpectTotalCount(kHighlighterColorMetric, 2);
|
|
histograms.ExpectTotalCount(kPenColorMetric, 0);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeBrushSizePen) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
// Draw a stroke.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kPenSizeMetric, StrokeMetricBrushSize::kMedium,
|
|
1);
|
|
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
|
|
/*color_b=*/130};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 1.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kPenSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThin, 1);
|
|
histograms.ExpectTotalCount(kPenSizeMetric, 2);
|
|
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 8.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kPenSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThick, 1);
|
|
histograms.ExpectTotalCount(kPenSizeMetric, 3);
|
|
histograms.ExpectTotalCount(kHighlighterSizeMetric, 0);
|
|
histograms.ExpectTotalCount(kEraserSizeMetric, 0);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeBrushSizeHighlighter) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
// Draw a stroke with medium size.
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
|
|
/*color_b=*/130};
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 8.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectUniqueSample(kHighlighterSizeMetric,
|
|
StrokeMetricBrushSize::kMedium, 1);
|
|
|
|
// Draw a stroke with extra thin size.
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 4.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kHighlighterSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThin, 1);
|
|
histograms.ExpectTotalCount(kHighlighterSizeMetric, 2);
|
|
|
|
// Draw a stroke with extra thick size.
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 16.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kHighlighterSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThick, 1);
|
|
histograms.ExpectTotalCount(kPenSizeMetric, 0);
|
|
histograms.ExpectTotalCount(kHighlighterSizeMetric, 3);
|
|
histograms.ExpectTotalCount(kEraserSizeMetric, 0);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeBrushSizeEraser) {
|
|
EnableAnnotationMode();
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
// Draw a pen stroke. Draw an eraser stroke that erases it with medium size.
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
|
|
/*color_b=*/130};
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectUniqueSample(kEraserSizeMetric,
|
|
StrokeMetricBrushSize::kMedium, 1);
|
|
|
|
// Draw a pen stroke. Draw an eraser stroke that erases it with extra thin
|
|
// size.
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
SelectEraserToolOfSize(1.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kEraserSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThin, 1);
|
|
histograms.ExpectTotalCount(kEraserSizeMetric, 2);
|
|
|
|
// Draw a pen stroke. Draw an eraser stroke that erases it with extra thick
|
|
// size.
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
SelectEraserToolOfSize(8.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kEraserSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThick, 1);
|
|
|
|
// There should be no visible strokes on the page. Draw an eraser stroke that
|
|
// does not erase any other strokes. The metric should stay the same.
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kEraserSizeMetric,
|
|
StrokeMetricBrushSize::kExtraThick, 1);
|
|
|
|
histograms.ExpectTotalCount(kPenSizeMetric, 3);
|
|
histograms.ExpectTotalCount(kHighlighterSizeMetric, 0);
|
|
histograms.ExpectTotalCount(kEraserSizeMetric, 3);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeBrushType) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/false);
|
|
|
|
histograms.ExpectTotalCount(kTypeMetric, 0);
|
|
|
|
// Draw a pen stroke.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1);
|
|
|
|
// Draw a highlighter stroke.
|
|
TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
|
|
/*color_b=*/130};
|
|
SelectBrushTool(PdfInkBrush::Type::kHighlighter, 6.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kHighlighter,
|
|
1);
|
|
histograms.ExpectTotalCount(kTypeMetric, 2);
|
|
|
|
// Draw an eraser stroke.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, 1);
|
|
histograms.ExpectTotalCount(kTypeMetric, 3);
|
|
|
|
// Draw an eraser stroke at a different point that does not erase any other
|
|
// strokes. The metric should stay the same.
|
|
ApplyStrokeWithMouseAtPoints(
|
|
kMouseUpPoint, base::span_from_ref(kMouseUpPoint), kMouseUpPoint);
|
|
|
|
histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, 1);
|
|
histograms.ExpectTotalCount(kTypeMetric, 3);
|
|
|
|
// Draw another pen stroke.
|
|
SelectBrushTool(PdfInkBrush::Type::kPen, 3.0f, params);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kPen, 2);
|
|
histograms.ExpectTotalCount(kTypeMetric, 4);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeInputDeviceMouse) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/false);
|
|
|
|
histograms.ExpectTotalCount(kInputDeviceMetric, 0);
|
|
|
|
// Draw a stroke with a mouse.
|
|
RunStrokeCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kMouse, 1);
|
|
|
|
// Draw an eraser stroke with a mouse that erases the first stroke.
|
|
SelectEraserToolOfSize(3.0f);
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kMouse, 2);
|
|
|
|
// Draw another eraser stroke with a mouse that erases nothing.
|
|
ApplyStrokeWithMouseAtMouseDownPoint();
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kMouse, 2);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeInputDeviceTouch) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/false);
|
|
|
|
histograms.ExpectTotalCount(kInputDeviceMetric, 0);
|
|
|
|
// Draw a stroke with touch.
|
|
RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kTouch, 1);
|
|
|
|
// Draw an eraser stroke with touch that erases the first stroke.
|
|
SelectEraserToolOfSize(3.0f);
|
|
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);
|
|
|
|
// Draw another eraser stroke with touch that erases nothing.
|
|
ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
|
|
base::span_from_ref(kMouseDownPoint));
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kTouch, 2);
|
|
}
|
|
|
|
TEST_F(PdfInkModuleMetricsTest, StrokeInputDevicePen) {
|
|
InitializeSimpleSinglePageBasicLayout();
|
|
base::HistogramTester histograms;
|
|
|
|
RunStrokePenCheckTest(/*annotation_mode_enabled=*/false);
|
|
|
|
histograms.ExpectTotalCount(kInputDeviceMetric, 0);
|
|
|
|
// Draw a stroke with a pen.
|
|
RunStrokePenCheckTest(/*annotation_mode_enabled=*/true);
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kPen, 1);
|
|
|
|
// Draw an eraser stroke with a pen that erases the first stroke.
|
|
SelectEraserToolOfSize(3.0f);
|
|
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);
|
|
|
|
// Draw another eraser stroke with a pen that erases nothing.
|
|
ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint), move_point,
|
|
base::span_from_ref(kMouseDownPoint));
|
|
|
|
histograms.ExpectUniqueSample(kInputDeviceMetric,
|
|
StrokeMetricInputDeviceType::kPen, 2);
|
|
}
|
|
|
|
} // namespace chrome_pdf
|