0

[PDF Ink Signatures] Add metric for PDFs loaded with V2 Ink annotations

Add a metric to track the number of PDFs loaded with V2 Ink annotations.
In order to do so, add a method in PDFiumEngine to check if a PDF has
any V2 Ink paths, without having to store and transform the paths.

This metric only tracks PDFs loaded for users with Ink2 enabled.

Bug: 380433757
Change-Id: I412ab1cb4a9b9a763e0f704527e234145b12e306
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6181044
Reviewed-by: Alan Screen <awscreen@chromium.org>
Commit-Queue: Andy Phan <andyphan@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1411985}
This commit is contained in:
Andy Phan
2025-01-27 16:42:25 -08:00
committed by Chromium LUCI CQ
parent 17cbd339ff
commit 54565c8a1d
11 changed files with 123 additions and 0 deletions

@ -158,4 +158,8 @@ void ReportEraseStroke(float size, ink::StrokeInput::ToolType tool_type) {
ReportStrokeInputDeviceType(tool_type);
}
void RecordPdfLoadedWithV2InkAnnotations(bool has_annotations) {
base::UmaHistogramBoolean("PDF.LoadedWithV2InkAnnotations", has_annotations);
}
} // namespace chrome_pdf

@ -105,6 +105,8 @@ void ReportDrawStroke(PdfInkBrush::Type type,
void ReportEraseStroke(float size, ink::StrokeInput::ToolType tool_type);
void RecordPdfLoadedWithV2InkAnnotations(bool has_annotations);
} // namespace chrome_pdf
#endif // PDF_PDF_INK_METRICS_HANDLER_H_

@ -124,6 +124,7 @@
#if BUILDFLAG(ENABLE_PDF_INK2)
#include "base/memory/raw_ref.h"
#include "pdf/pdf_ink_ids.h"
#include "pdf/pdf_ink_metrics_handler.h"
#include "pdf/pdf_ink_module.h"
#include "pdf/pdf_ink_module_client.h"
#include "third_party/skia/include/core/SkCanvas.h"
@ -2472,6 +2473,14 @@ void PdfViewWebPlugin::RecordDocumentMetrics() {
return;
metrics_handler_->RecordDocumentMetrics(engine_->GetDocumentMetadata());
#if BUILDFLAG(ENABLE_PDF_INK2)
// `metrics_handler_` is only initialized when not in Print Preview, so the
// V2 ink annotations load metric will not count Print Preview loads.
if (ink_module_) {
RecordPdfLoadedWithV2InkAnnotations(engine_->ContainsV2InkPath());
}
#endif // BUILDFLAG(ENABLE_PDF_INK2)
}
void PdfViewWebPlugin::SendAttachments() {

@ -19,6 +19,7 @@
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
@ -115,6 +116,11 @@ using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SizeIs;
#if BUILDFLAG(ENABLE_PDF_INK2)
constexpr char kPdfLoadedWithV2InkAnnotationsMetric[] =
"PDF.LoadedWithV2InkAnnotations";
#endif // BUILDFLAG(ENABLE_PDF_INK2)
// `kCanvasSize` needs to be big enough to hold plugin's snapshots during
// testing.
constexpr gfx::Size kCanvasSize(100, 100);
@ -3040,6 +3046,51 @@ TEST_F(PdfViewWebPluginInk2SaveTest, AnnotationInEditMode) {
pdf_receiver_.FlushForTesting();
}
using PdfViewWebPluginInkMetricTest = PdfViewWebPluginInkTest;
TEST_F(PdfViewWebPluginInkMetricTest, LoadedWithoutV2InkAnnotations) {
base::HistogramTester histograms;
EXPECT_CALL(*engine_ptr_, ContainsV2InkPath()).WillOnce(Return(false));
plugin_->DocumentLoadComplete();
histograms.ExpectUniqueSample(kPdfLoadedWithV2InkAnnotationsMetric, false, 1);
}
TEST_F(PdfViewWebPluginInkMetricTest, LoadedWithV2InkAnnotations) {
base::HistogramTester histograms;
EXPECT_CALL(*engine_ptr_, ContainsV2InkPath()).WillOnce(Return(true));
plugin_->DocumentLoadComplete();
histograms.ExpectUniqueSample(kPdfLoadedWithV2InkAnnotationsMetric, true, 1);
}
class PdfViewWebPluginPrintPreviewInkMetricTest
: public PdfViewWebPluginPrintPreviewTest {
private:
base::test::ScopedFeatureList feature_list_{features::kPdfInk2};
};
TEST_F(PdfViewWebPluginPrintPreviewInkMetricTest,
LoadedWithV2InkAnnotationsDoesNotCountPrintPreview) {
base::HistogramTester histograms;
OnMessageWithEngineUpdate(ParseMessage(R"({
"type": "resetPrintPreviewMode",
"url": "chrome-untrusted://print/0/0/print.pdf",
"grayscale": false,
"pageCount": 1,
})"));
EXPECT_CALL(*engine_ptr_, ContainsV2InkPath()).Times(0);
plugin_->DocumentLoadComplete();
// The V2 ink annotations PDF load metric should not increment for Print
// Preview.
histograms.ExpectTotalCount(kPdfLoadedWithV2InkAnnotationsMetric, 0);
}
#endif // BUILDFLAG(ENABLE_PDF_INK2)
} // namespace chrome_pdf

@ -4459,6 +4459,15 @@ void PDFiumEngine::DiscardStroke(int page_index, InkStrokeId id) {
ink_stroke_objects_map_.erase(it);
}
bool PDFiumEngine::ContainsV2InkPath() const {
for (const auto& page : pages_) {
if (PageContainsV2InkPath(page->GetPage())) {
return true;
}
}
return false;
}
std::map<InkModeledShapeId, ink::PartitionedMesh>
PDFiumEngine::LoadV2InkPathsForPage(int page_index) {
CHECK(PageIndexInBounds(page_index));

@ -396,6 +396,10 @@ class PDFiumEngine : public DocumentLoader::Client, public IFSDK_PAUSE {
// `ApplyStroke()`. Virtual to support testing.
virtual void DiscardStroke(int page_index, InkStrokeId id);
// Returns whether any of the pages contains a "V2" path created by Ink.
// Virtual to support testing.
virtual bool ContainsV2InkPath() const;
// Loads "V2" Ink paths from a page in the PDF identified by `page_index`. The
// `page_index` must be in bounds.
//

@ -2046,6 +2046,20 @@ TEST_P(PDFiumEngineInkTest, CannotSelectTextInAnnotationMode) {
EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
}
TEST_P(PDFiumEngineInkTest, ContainsV2InkPath) {
NiceMock<MockTestClient> client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("blank.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
EXPECT_FALSE(engine->ContainsV2InkPath());
engine = InitializeEngine(&client, FILE_PATH_LITERAL("ink_v2.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
EXPECT_TRUE(engine->ContainsV2InkPath());
}
TEST_P(PDFiumEngineInkTest, LoadV2InkPathsForPage) {
NiceMock<MockTestClient> client;
std::unique_ptr<PDFiumEngine> engine =

@ -150,6 +150,20 @@ std::optional<ink::PartitionedMesh> ReadV2InkModeledShapeFromPath(
} // namespace
bool PageContainsV2InkPath(FPDF_PAGE page) {
if (!page) {
return false;
}
const int page_object_count = FPDFPage_CountObjects(page);
for (int i = 0; i < page_object_count; ++i) {
if (IsV2InkPath(FPDFPage_GetObject(page, i))) {
return true;
}
}
return false;
}
std::vector<ReadV2InkPathResult> ReadV2InkPathsFromPageAsModeledShapes(
FPDF_PAGE page) {
std::vector<ReadV2InkPathResult> results;

@ -24,6 +24,10 @@ struct ReadV2InkPathResult {
ink::PartitionedMesh shape;
};
// Returns whether the given `page` contains a "V2" path created by Ink.
// Returns false if `page` is null.
bool PageContainsV2InkPath(FPDF_PAGE page);
// For the given `page`, iterate through all page objects and import "V2" paths
// created by Ink as ink::PartitionedMeshs. For each shape, also return its
// associated page object. The shapes do not have outlines and are only suitable

@ -109,6 +109,8 @@ class TestPDFiumEngine : public PDFiumEngine {
MOCK_METHOD(void, DiscardStroke, (int, InkStrokeId), (override));
MOCK_METHOD(bool, ContainsV2InkPath, (), (const override));
MOCK_METHOD((std::map<InkModeledShapeId, ink::PartitionedMesh>),
LoadV2InkPathsForPage,
(int),

@ -146,6 +146,16 @@ chromium-metrics-reviews@google.com.
</token>
</histogram>
<histogram name="PDF.LoadedWithV2InkAnnotations" enum="Boolean"
expires_after="2025-12-01">
<owner>andyphan@chromium.org</owner>
<owner>thestig@chromium.org</owner>
<summary>
Tracks the number of PDF documents loaded with existing V2 ink annotations
in the PDF viewer.
</summary>
</histogram>
<histogram name="PDF.LoadStatus2" enum="ChromePDFViewerLoadStatus"
expires_after="2025-06-08">
<owner>kmoon@chromium.org</owner>