123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "pdf/pdfium/pdfium_engine.h"
- #include <stdint.h>
- #include <utility>
- #include "base/callback.h"
- #include "base/hash/md5.h"
- #include "base/run_loop.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/test/gmock_move_support.h"
- #include "base/test/gtest_util.h"
- #include "base/test/mock_callback.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "build/build_config.h"
- #include "pdf/document_attachment_info.h"
- #include "pdf/document_layout.h"
- #include "pdf/document_metadata.h"
- #include "pdf/pdf_features.h"
- #include "pdf/pdfium/pdfium_page.h"
- #include "pdf/pdfium/pdfium_test_base.h"
- #include "pdf/test/test_client.h"
- #include "pdf/test/test_document_loader.h"
- #include "pdf/ui/thumbnail.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/blink/public/common/input/web_input_event.h"
- #include "third_party/blink/public/common/input/web_keyboard_event.h"
- #include "third_party/blink/public/common/input/web_mouse_event.h"
- #include "third_party/blink/public/common/input/web_pointer_properties.h"
- #include "ui/events/keycodes/keyboard_codes.h"
- #include "ui/gfx/geometry/point.h"
- #include "ui/gfx/geometry/point_f.h"
- #include "ui/gfx/geometry/rect.h"
- #include "ui/gfx/geometry/size.h"
- namespace chrome_pdf {
- namespace {
- using ::testing::InSequence;
- using ::testing::Invoke;
- using ::testing::IsEmpty;
- using ::testing::NiceMock;
- using ::testing::Not;
- using ::testing::Return;
- using ::testing::StrictMock;
- MATCHER_P2(LayoutWithSize, width, height, "") {
- return arg.size() == gfx::Size(width, height);
- }
- MATCHER_P(LayoutWithOptions, options, "") {
- return arg.options() == options;
- }
- blink::WebMouseEvent CreateRightClickWebMouseEventAtPosition(
- const gfx::PointF& position) {
- return blink::WebMouseEvent(
- blink::WebInputEvent::Type::kMouseDown, /*position=*/position,
- /*global_position=*/position, blink::WebPointerProperties::Button::kRight,
- /*click_count_param=*/1,
- blink::WebInputEvent::Modifiers::kRightButtonDown,
- blink::WebInputEvent::GetStaticTimeStampForTests());
- }
- class MockTestClient : public TestClient {
- public:
- MockTestClient() {
- ON_CALL(*this, ProposeDocumentLayout)
- .WillByDefault([this](const DocumentLayout& layout) {
- TestClient::ProposeDocumentLayout(layout);
- });
- }
- MOCK_METHOD(void,
- ProposeDocumentLayout,
- (const DocumentLayout& layout),
- (override));
- MOCK_METHOD(void, ScrollToPage, (int page), (override));
- MOCK_METHOD(void, DocumentFocusChanged, (bool), (override));
- MOCK_METHOD(void, SetLinkUnderCursor, (const std::string&), (override));
- };
- } // namespace
- class PDFiumEngineTest : public PDFiumTestBase {
- protected:
- void ExpectPageRect(const PDFiumEngine& engine,
- size_t page_index,
- const gfx::Rect& expected_rect) {
- const PDFiumPage& page = GetPDFiumPageForTest(engine, page_index);
- EXPECT_EQ(expected_rect, page.rect());
- }
- // Tries to load a PDF incrementally, returning `true` if the PDF actually was
- // loaded incrementally. Note that this function will return `false` if
- // incremental loading fails, but also if incremental loading is disabled.
- bool TryLoadIncrementally() {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("linearized.pdf"));
- if (!initialize_result.engine) {
- ADD_FAILURE();
- return false;
- }
- PDFiumEngine& engine = *initialize_result.engine;
- // Load enough for the document to become partially available.
- initialize_result.document_loader->SimulateLoadData(8192);
- bool loaded_incrementally;
- if (engine.GetNumberOfPages() == 0) {
- // This is not necessarily a test failure; it just indicates incremental
- // loading is not occurring.
- engine.PluginSizeUpdated({});
- loaded_incrementally = false;
- } else {
- // Note: Plugin size chosen so all pages of the document are visible. The
- // engine only updates availability incrementally for visible pages.
- EXPECT_EQ(0, CountAvailablePages(engine));
- engine.PluginSizeUpdated({1024, 4096});
- int available_pages = CountAvailablePages(engine);
- loaded_incrementally =
- 0 < available_pages && available_pages < engine.GetNumberOfPages();
- }
- // Verify that loading can finish.
- initialize_result.FinishLoading();
- EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
- return loaded_incrementally;
- }
- void FinishWithPluginSizeUpdated(PDFiumEngine& engine) {
- engine.PluginSizeUpdated({});
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
- }
- // Counts the number of available pages. Returns `int` instead of `size_t` for
- // consistency with `PDFiumEngine::GetNumberOfPages()`.
- int CountAvailablePages(const PDFiumEngine& engine) {
- int available_pages = 0;
- for (int i = 0; i < engine.GetNumberOfPages(); ++i) {
- if (GetPDFiumPageForTest(engine, i).available())
- ++available_pages;
- }
- return available_pages;
- }
- };
- TEST_F(PDFiumEngineTest, InitializeWithRectanglesMultiPagesPdf) {
- NiceMock<MockTestClient> client;
- // ProposeDocumentLayout() gets called twice during loading because
- // PDFiumEngine::ContinueLoadingDocument() calls LoadBody() (which eventually
- // triggers a layout proposal), and then calls FinishLoadingDocument() (since
- // the document is complete), which calls LoadBody() again. Coalescing these
- // proposals is not correct unless we address the issue covered by
- // PDFiumEngineTest.ProposeDocumentLayoutWithOverlap.
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
- .Times(2);
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(5, engine->GetNumberOfPages());
- ExpectPageRect(*engine, 0, {38, 3, 266, 333});
- ExpectPageRect(*engine, 1, {5, 350, 333, 266});
- ExpectPageRect(*engine, 2, {38, 630, 266, 333});
- ExpectPageRect(*engine, 3, {38, 977, 266, 333});
- ExpectPageRect(*engine, 4, {38, 1324, 266, 333});
- }
- TEST_F(PDFiumEngineTest, InitializeWithRectanglesMultiPagesPdfInTwoUpView) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- DocumentLayout::Options options;
- options.set_page_spread(DocumentLayout::PageSpread::kTwoUpOdd);
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithOptions(options)))
- .WillOnce(Return());
- engine->SetDocumentLayout(DocumentLayout::PageSpread::kTwoUpOdd);
- engine->ApplyDocumentLayout(options);
- ASSERT_EQ(5, engine->GetNumberOfPages());
- ExpectPageRect(*engine, 0, {72, 3, 266, 333});
- ExpectPageRect(*engine, 1, {340, 3, 333, 266});
- ExpectPageRect(*engine, 2, {72, 346, 266, 333});
- ExpectPageRect(*engine, 3, {340, 346, 266, 333});
- ExpectPageRect(*engine, 4, {68, 689, 266, 333});
- }
- TEST_F(PDFiumEngineTest, AppendBlankPagesWithFewerPages) {
- NiceMock<MockTestClient> client;
- {
- InSequence normal_then_append;
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
- .Times(2);
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 1037)));
- }
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- engine->AppendBlankPages(3);
- ASSERT_EQ(3, engine->GetNumberOfPages());
- ExpectPageRect(*engine, 0, {5, 3, 266, 333});
- ExpectPageRect(*engine, 1, {5, 350, 266, 333});
- ExpectPageRect(*engine, 2, {5, 697, 266, 333});
- }
- TEST_F(PDFiumEngineTest, AppendBlankPagesWithMorePages) {
- NiceMock<MockTestClient> client;
- {
- InSequence normal_then_append;
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
- .Times(2);
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 2425)));
- }
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- engine->AppendBlankPages(7);
- ASSERT_EQ(7, engine->GetNumberOfPages());
- ExpectPageRect(*engine, 0, {5, 3, 266, 333});
- ExpectPageRect(*engine, 1, {5, 350, 266, 333});
- ExpectPageRect(*engine, 2, {5, 697, 266, 333});
- ExpectPageRect(*engine, 3, {5, 1044, 266, 333});
- ExpectPageRect(*engine, 4, {5, 1391, 266, 333});
- ExpectPageRect(*engine, 5, {5, 1738, 266, 333});
- ExpectPageRect(*engine, 6, {5, 2085, 266, 333});
- }
- TEST_F(PDFiumEngineTest, ProposeDocumentLayoutWithOverlap) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1463)))
- .WillOnce(Return());
- engine->RotateClockwise();
- EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
- .WillOnce(Return());
- engine->RotateCounterclockwise();
- }
- TEST_F(PDFiumEngineTest, ApplyDocumentLayoutBeforePluginSizeUpdated) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- initialize_result.FinishLoading();
- PDFiumEngine& engine = *initialize_result.engine;
- DocumentLayout::Options options;
- options.RotatePagesClockwise();
- EXPECT_CALL(client, ScrollToPage(-1)).Times(0);
- EXPECT_EQ(gfx::Size(343, 1664), engine.ApplyDocumentLayout(options));
- EXPECT_CALL(client, ScrollToPage(-1)).Times(1);
- FinishWithPluginSizeUpdated(engine);
- }
- TEST_F(PDFiumEngineTest, ApplyDocumentLayoutAvoidsInfiniteLoop) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- DocumentLayout::Options options;
- EXPECT_CALL(client, ScrollToPage(-1)).Times(0);
- EXPECT_EQ(gfx::Size(343, 1664), engine->ApplyDocumentLayout(options));
- options.RotatePagesClockwise();
- EXPECT_CALL(client, ScrollToPage(-1)).Times(1);
- EXPECT_EQ(gfx::Size(343, 1463), engine->ApplyDocumentLayout(options));
- EXPECT_EQ(gfx::Size(343, 1463), engine->ApplyDocumentLayout(options));
- }
- TEST_F(PDFiumEngineTest, GetDocumentAttachments) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("embedded_attachments.pdf"));
- ASSERT_TRUE(engine);
- const std::vector<DocumentAttachmentInfo>& attachments =
- engine->GetDocumentAttachmentInfoList();
- ASSERT_EQ(3u, attachments.size());
- {
- const DocumentAttachmentInfo& attachment = attachments[0];
- EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name));
- EXPECT_TRUE(attachment.is_readable);
- EXPECT_EQ(4u, attachment.size_bytes);
- EXPECT_EQ("D:20170712214438-07'00'",
- base::UTF16ToUTF8(attachment.creation_date));
- EXPECT_EQ("D:20160115091400", base::UTF16ToUTF8(attachment.modified_date));
- std::vector<uint8_t> content = engine->GetAttachmentData(0);
- ASSERT_EQ(attachment.size_bytes, content.size());
- std::string content_str(content.begin(), content.end());
- EXPECT_EQ("test", content_str);
- }
- {
- static constexpr char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
- const DocumentAttachmentInfo& attachment = attachments[1];
- EXPECT_EQ("attached.pdf", base::UTF16ToUTF8(attachment.name));
- EXPECT_TRUE(attachment.is_readable);
- EXPECT_EQ(5869u, attachment.size_bytes);
- EXPECT_EQ("D:20170712214443-07'00'",
- base::UTF16ToUTF8(attachment.creation_date));
- EXPECT_EQ("D:20170712214410", base::UTF16ToUTF8(attachment.modified_date));
- std::vector<uint8_t> content = engine->GetAttachmentData(1);
- ASSERT_EQ(attachment.size_bytes, content.size());
- // The whole attachment content is too long to do string comparison.
- // Instead, we only verify the checksum value here.
- base::MD5Digest hash;
- base::MD5Sum(content.data(), content.size(), &hash);
- EXPECT_EQ(kCheckSum, base::MD5DigestToBase16(hash));
- }
- {
- // Test attachments with no creation date or last modified date.
- const DocumentAttachmentInfo& attachment = attachments[2];
- EXPECT_EQ("附錄.txt", base::UTF16ToUTF8(attachment.name));
- EXPECT_TRUE(attachment.is_readable);
- EXPECT_EQ(5u, attachment.size_bytes);
- EXPECT_THAT(attachment.creation_date, IsEmpty());
- EXPECT_THAT(attachment.modified_date, IsEmpty());
- std::vector<uint8_t> content = engine->GetAttachmentData(2);
- ASSERT_EQ(attachment.size_bytes, content.size());
- std::string content_str(content.begin(), content.end());
- EXPECT_EQ("test\n", content_str);
- }
- }
- TEST_F(PDFiumEngineTest, GetInvalidDocumentAttachment) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("invalid_attachment.pdf"));
- ASSERT_TRUE(engine);
- // Test on a document with one invalid attachment, which can make
- // FPDFDoc_GetAttachment() fail. This particular attachment is invalid due
- // to its key value violating the `Limits` entry.
- const std::vector<DocumentAttachmentInfo>& attachments =
- engine->GetDocumentAttachmentInfoList();
- ASSERT_EQ(1u, attachments.size());
- const DocumentAttachmentInfo& attachment = attachments[0];
- EXPECT_THAT(attachment.name, IsEmpty());
- EXPECT_FALSE(attachment.is_readable);
- EXPECT_EQ(0u, attachment.size_bytes);
- EXPECT_THAT(attachment.creation_date, IsEmpty());
- EXPECT_THAT(attachment.modified_date, IsEmpty());
- }
- TEST_F(PDFiumEngineTest, GetDocumentAttachmentWithInvalidData) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("embedded_attachments_invalid_data.pdf"));
- ASSERT_TRUE(engine);
- const std::vector<DocumentAttachmentInfo>& attachments =
- engine->GetDocumentAttachmentInfoList();
- ASSERT_EQ(1u, attachments.size());
- // Test on an attachment which FPDFAttachment_GetFile() fails to retrieve data
- // from.
- const DocumentAttachmentInfo& attachment = attachments[0];
- EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name));
- EXPECT_FALSE(attachment.is_readable);
- EXPECT_EQ(0u, attachment.size_bytes);
- EXPECT_THAT(attachment.creation_date, IsEmpty());
- EXPECT_THAT(attachment.modified_date, IsEmpty());
- }
- TEST_F(PDFiumEngineTest, NoDocumentAttachmentInfo) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_EQ(0u, engine->GetDocumentAttachmentInfoList().size());
- }
- TEST_F(PDFiumEngineTest, GetDocumentMetadata) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("document_info.pdf"));
- ASSERT_TRUE(engine);
- const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
- EXPECT_EQ(PdfVersion::k1_7, doc_metadata.version);
- EXPECT_EQ(714u, doc_metadata.size_bytes);
- EXPECT_FALSE(doc_metadata.linearized);
- EXPECT_EQ("Sample PDF Document Info", doc_metadata.title);
- EXPECT_EQ("Chromium Authors", doc_metadata.author);
- EXPECT_EQ("Testing", doc_metadata.subject);
- EXPECT_EQ("testing,chromium,pdfium,document,info", doc_metadata.keywords);
- EXPECT_EQ("Your Preferred Text Editor", doc_metadata.creator);
- EXPECT_EQ("fixup_pdf_template.py", doc_metadata.producer);
- base::Time expected_creation_date;
- ASSERT_TRUE(base::Time::FromUTCString("2020-02-05 15:39:12",
- &expected_creation_date));
- EXPECT_EQ(expected_creation_date, doc_metadata.creation_date);
- base::Time expected_mod_date;
- ASSERT_TRUE(
- base::Time::FromUTCString("2020-02-06 09:42:34", &expected_mod_date));
- EXPECT_EQ(expected_mod_date, doc_metadata.mod_date);
- }
- TEST_F(PDFiumEngineTest, GetEmptyDocumentMetadata) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
- EXPECT_EQ(PdfVersion::k1_7, doc_metadata.version);
- EXPECT_EQ(786u, doc_metadata.size_bytes);
- EXPECT_FALSE(doc_metadata.linearized);
- EXPECT_THAT(doc_metadata.title, IsEmpty());
- EXPECT_THAT(doc_metadata.author, IsEmpty());
- EXPECT_THAT(doc_metadata.subject, IsEmpty());
- EXPECT_THAT(doc_metadata.keywords, IsEmpty());
- EXPECT_THAT(doc_metadata.creator, IsEmpty());
- EXPECT_THAT(doc_metadata.producer, IsEmpty());
- EXPECT_TRUE(doc_metadata.creation_date.is_null());
- EXPECT_TRUE(doc_metadata.mod_date.is_null());
- }
- TEST_F(PDFiumEngineTest, GetLinearizedDocumentMetadata) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("linearized.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_TRUE(engine->GetDocumentMetadata().linearized);
- }
- TEST_F(PDFiumEngineTest, GetBadPdfVersion) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("bad_version.pdf"));
- ASSERT_TRUE(engine);
- const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
- EXPECT_EQ(PdfVersion::kUnknown, doc_metadata.version);
- }
- TEST_F(PDFiumEngineTest, GetNamedDestination) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("named_destinations.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- // A destination with a valid page object
- absl::optional<PDFEngine::NamedDestination> valid_page_obj =
- engine->GetNamedDestination("ValidPageObj");
- ASSERT_TRUE(valid_page_obj.has_value());
- EXPECT_EQ(0u, valid_page_obj->page);
- EXPECT_EQ("XYZ", valid_page_obj->view);
- ASSERT_EQ(3u, valid_page_obj->num_params);
- EXPECT_EQ(1.2f, valid_page_obj->params[2]);
- // A destination with an invalid page object
- absl::optional<PDFEngine::NamedDestination> invalid_page_obj =
- engine->GetNamedDestination("InvalidPageObj");
- ASSERT_FALSE(invalid_page_obj.has_value());
- // A destination with a valid page number
- absl::optional<PDFEngine::NamedDestination> valid_page_number =
- engine->GetNamedDestination("ValidPageNumber");
- ASSERT_TRUE(valid_page_number.has_value());
- EXPECT_EQ(1u, valid_page_number->page);
- // A destination with an out-of-range page number
- absl::optional<PDFEngine::NamedDestination> invalid_page_number =
- engine->GetNamedDestination("OutOfRangePageNumber");
- EXPECT_FALSE(invalid_page_number.has_value());
- }
- TEST_F(PDFiumEngineTest, PluginSizeUpdatedBeforeLoad) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- PDFiumEngine& engine = *initialize_result.engine;
- engine.PluginSizeUpdated({});
- initialize_result.FinishLoading();
- EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
- }
- TEST_F(PDFiumEngineTest, PluginSizeUpdatedDuringLoad) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- PDFiumEngine& engine = *initialize_result.engine;
- EXPECT_TRUE(initialize_result.document_loader->SimulateLoadData(1024));
- engine.PluginSizeUpdated({});
- initialize_result.FinishLoading();
- EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
- }
- TEST_F(PDFiumEngineTest, PluginSizeUpdatedAfterLoad) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- PDFiumEngine& engine = *initialize_result.engine;
- initialize_result.FinishLoading();
- FinishWithPluginSizeUpdated(engine);
- EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
- }
- TEST_F(PDFiumEngineTest, OnLeftMouseDownBeforePluginSizeUpdated) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- initialize_result.FinishLoading();
- PDFiumEngine& engine = *initialize_result.engine;
- EXPECT_TRUE(engine.HandleInputEvent(blink::WebMouseEvent(
- blink::WebInputEvent::Type::kMouseDown, {0, 0}, {100, 200},
- blink::WebPointerProperties::Button::kLeft, /*click_count_param=*/1,
- blink::WebInputEvent::Modifiers::kLeftButtonDown,
- blink::WebInputEvent::GetStaticTimeStampForTests())));
- }
- TEST_F(PDFiumEngineTest, OnLeftMouseDownAfterPluginSizeUpdated) {
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- initialize_result.FinishLoading();
- PDFiumEngine& engine = *initialize_result.engine;
- engine.PluginSizeUpdated({300, 400});
- EXPECT_TRUE(engine.HandleInputEvent(blink::WebMouseEvent(
- blink::WebInputEvent::Type::kMouseDown, {0, 0}, {100, 200},
- blink::WebPointerProperties::Button::kLeft, /*click_count_param=*/1,
- blink::WebInputEvent::Modifiers::kLeftButtonDown,
- blink::WebInputEvent::GetStaticTimeStampForTests())));
- }
- TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureDefault) {
- EXPECT_FALSE(TryLoadIncrementally());
- }
- TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureEnabled) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
- EXPECT_TRUE(TryLoadIncrementally());
- }
- TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureDisabled) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(features::kPdfIncrementalLoading);
- EXPECT_FALSE(TryLoadIncrementally());
- }
- TEST_F(PDFiumEngineTest, RequestThumbnail) {
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
- ASSERT_TRUE(engine);
- const int num_pages = engine->GetNumberOfPages();
- ASSERT_EQ(5, num_pages);
- ASSERT_EQ(num_pages, CountAvailablePages(*engine));
- // Each page should immediately return a thumbnail.
- for (int i = 0; i < num_pages; ++i) {
- base::MockCallback<SendThumbnailCallback> send_callback;
- EXPECT_CALL(send_callback, Run);
- engine->RequestThumbnail(/*page_index=*/i, /*device_pixel_ratio=*/1,
- send_callback.Get());
- }
- }
- TEST_F(PDFiumEngineTest, RequestThumbnailLinearized) {
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("linearized.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- PDFiumEngine& engine = *initialize_result.engine;
- // Load only some pages.
- initialize_result.document_loader->SimulateLoadData(8192);
- // Note: Plugin size chosen so all pages of the document are visible. The
- // engine only updates availability incrementally for visible pages.
- engine.PluginSizeUpdated({1024, 4096});
- const int num_pages = engine.GetNumberOfPages();
- ASSERT_EQ(3, num_pages);
- const int available_pages = CountAvailablePages(engine);
- ASSERT_LT(0, available_pages);
- ASSERT_GT(num_pages, available_pages);
- // Initialize callbacks for first and last pages.
- base::MockCallback<SendThumbnailCallback> first_loaded;
- base::MockCallback<SendThumbnailCallback> last_loaded;
- // When the document is partially loaded, `SendThumbnailCallback` is only run
- // for the loaded page even though `RequestThumbnail()` gets called for both
- // pages.
- EXPECT_CALL(first_loaded, Run);
- engine.RequestThumbnail(/*page_index=*/0, /*device_pixel_ratio=*/1,
- first_loaded.Get());
- engine.RequestThumbnail(/*page_index=*/num_pages - 1,
- /*device_pixel_ratio=*/1, last_loaded.Get());
- // Finish loading the document. `SendThumbnailCallback` should be run for the
- // last page.
- EXPECT_CALL(last_loaded, Run);
- initialize_result.FinishLoading();
- }
- TEST_F(PDFiumEngineTest, HandleInputEventKeyDown) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_CALL(client, DocumentFocusChanged(true));
- blink::WebKeyboardEvent key_down_event(
- blink::WebInputEvent::Type::kKeyDown, blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::GetStaticTimeStampForTests());
- key_down_event.windows_key_code = ui::VKEY_TAB;
- EXPECT_TRUE(engine->HandleInputEvent(key_down_event));
- }
- TEST_F(PDFiumEngineTest, HandleInputEventRawKeyDown) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_CALL(client, DocumentFocusChanged(true));
- blink::WebKeyboardEvent raw_key_down_event(
- blink::WebInputEvent::Type::kRawKeyDown,
- blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::GetStaticTimeStampForTests());
- raw_key_down_event.windows_key_code = ui::VKEY_TAB;
- EXPECT_TRUE(engine->HandleInputEvent(raw_key_down_event));
- }
- namespace {
- #if BUILDFLAG(IS_WIN)
- constexpr char kSelectTextExpectedText[] =
- "Hello, world!\r\nGoodbye, world!\r\nHello, world!\r\nGoodbye, world!";
- #else
- constexpr char kSelectTextExpectedText[] =
- "Hello, world!\nGoodbye, world!\nHello, world!\nGoodbye, world!";
- #endif
- } // namespace
- TEST_F(PDFiumEngineTest, SelectText) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_TRUE(engine->HasPermission(DocumentPermission::kCopy));
- EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
- engine->SelectAll();
- EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText());
- }
- TEST_F(PDFiumEngineTest, SelectTextWithCopyRestriction) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("hello_world2_with_copy_restriction.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_FALSE(engine->HasPermission(DocumentPermission::kCopy));
- // The copy restriction should not affect the text selection hehavior.
- EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
- engine->SelectAll();
- EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText());
- }
- TEST_F(PDFiumEngineTest, SelectCroppedText) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world_cropped.pdf"));
- ASSERT_TRUE(engine);
- EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
- engine->SelectAll();
- #if BUILDFLAG(IS_WIN)
- constexpr char kExpectedText[] = "world!\r\n";
- #else
- constexpr char kExpectedText[] = "world!\n";
- #endif
- EXPECT_EQ(kExpectedText, engine->GetSelectedText());
- }
- using PDFiumEngineDeathTest = PDFiumEngineTest;
- TEST_F(PDFiumEngineDeathTest, RequestThumbnailRedundant) {
- ::testing::FLAGS_gtest_death_test_style = "threadsafe";
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
- NiceMock<MockTestClient> client;
- InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
- &client, FILE_PATH_LITERAL("linearized.pdf"));
- ASSERT_TRUE(initialize_result.engine);
- PDFiumEngine& engine = *initialize_result.engine;
- // Load only some pages.
- initialize_result.document_loader->SimulateLoadData(8192);
- // Twice request a thumbnail for the second page, which is not loaded. The
- // second call should crash.
- base::MockCallback<SendThumbnailCallback> mock_callback;
- engine.RequestThumbnail(/*page_index=*/1, /*device_pixel_ratio=*/1,
- mock_callback.Get());
- EXPECT_DCHECK_DEATH(engine.RequestThumbnail(
- /*page_index=*/1, /*device_pixel_ratio=*/1, mock_callback.Get()));
- }
- class PDFiumEngineTabbingTest : public PDFiumTestBase {
- public:
- PDFiumEngineTabbingTest() = default;
- ~PDFiumEngineTabbingTest() override = default;
- PDFiumEngineTabbingTest(const PDFiumEngineTabbingTest&) = delete;
- PDFiumEngineTabbingTest& operator=(const PDFiumEngineTabbingTest&) = delete;
- bool HandleTabEvent(PDFiumEngine* engine, int modifiers) {
- return engine->HandleTabEvent(modifiers);
- }
- PDFiumEngine::FocusElementType GetFocusedElementType(PDFiumEngine* engine) {
- return engine->focus_element_type_;
- }
- int GetLastFocusedPage(PDFiumEngine* engine) {
- return engine->last_focused_page_;
- }
- PDFiumEngine::FocusElementType GetLastFocusedElementType(
- PDFiumEngine* engine) {
- return engine->last_focused_element_type_;
- }
- int GetLastFocusedAnnotationIndex(PDFiumEngine* engine) {
- return engine->last_focused_annot_index_;
- }
- PDFEngine::FocusFieldType FormFocusFieldType(PDFiumEngine* engine) {
- return engine->focus_field_type_;
- }
- size_t GetSelectionSize(PDFiumEngine* engine) {
- return engine->selection_.size();
- }
- void ScrollFocusedAnnotationIntoView(PDFiumEngine* engine) {
- engine->ScrollFocusedAnnotationIntoView();
- }
- };
- TEST_F(PDFiumEngineTabbingTest, LinkUnderCursor) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Widget annotation
- * ++++ Widget annotation
- * ++++ Highlight annotation
- * ++++ Link annotation
- */
- // Enable feature flag.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- chrome_pdf::features::kTabAcrossPDFAnnotations);
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
- ASSERT_TRUE(engine);
- // Tab to right before the first non-link annotation.
- EXPECT_CALL(client, DocumentFocusChanged(true));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Tab through non-link annotations and validate link under cursor.
- {
- InSequence sequence;
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- EXPECT_CALL(client, DocumentFocusChanged(false));
- EXPECT_CALL(client, SetLinkUnderCursor("")).Times(2);
- }
- for (int i = 0; i < 3; i++)
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Tab to Link annotation.
- EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Tab to previous annotation.
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- }
- // Test case for crbug.com/1088296
- TEST_F(PDFiumEngineTabbingTest, LinkUnderCursorAfterTabAndRightClick) {
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
- ASSERT_TRUE(engine);
- // Ensure the plugin has a pre-determined size, to enable the hit tests below.
- engine->PluginSizeUpdated({612, 792});
- // Tab to right before the first non-link annotation.
- EXPECT_CALL(client, DocumentFocusChanged(true));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Tab through non-link annotations and validate link under cursor.
- {
- InSequence sequence;
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- EXPECT_CALL(client, DocumentFocusChanged(false));
- }
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Tab to Link annotation.
- EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
- ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
- // Right click somewhere far away should reset the link.
- constexpr gfx::PointF kOffScreenPosition(0, 0);
- EXPECT_CALL(client, SetLinkUnderCursor(""));
- EXPECT_FALSE(engine->HandleInputEvent(
- CreateRightClickWebMouseEventAtPosition(kOffScreenPosition)));
- // Right click on the link should set it again.
- constexpr gfx::PointF kLinkPosition(170, 595);
- EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
- EXPECT_FALSE(engine->HandleInputEvent(
- CreateRightClickWebMouseEventAtPosition(kLinkPosition)));
- }
- TEST_F(PDFiumEngineTabbingTest, TabbingSupportedAnnots) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Widget annotation
- * ++++ Widget annotation
- * ++++ Highlight annotation
- * ++++ Link annotation
- */
- // Enable feature flag.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
- chrome_pdf::features::kTabAcrossPDFAnnotations);
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(1, engine->GetNumberOfPages());
- ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, TabbingForward) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation
- * ++++ Annotation
- * ++ Page 2
- * ++++ Annotation
- */
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- static constexpr bool kExpectedFocusState[] = {true, false};
- {
- InSequence sequence;
- for (auto focused : kExpectedFocusState)
- EXPECT_CALL(client, DocumentFocusChanged(focused));
- }
- ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
- ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, TabbingBackward) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation
- * ++++ Annotation
- * ++ Page 2
- * ++++ Annotation
- */
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- static constexpr bool kExpectedFocusState[] = {true, false};
- {
- InSequence sequence;
- for (auto focused : kExpectedFocusState)
- EXPECT_CALL(client, DocumentFocusChanged(focused));
- }
- ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- ASSERT_FALSE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, TabbingWithModifiers) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation
- * ++++ Annotation
- * ++ Page 2
- * ++++ Annotation
- */
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing with ctrl modifier.
- ASSERT_FALSE(HandleTabEvent(engine.get(),
- blink::WebInputEvent::Modifiers::kControlKey));
- // Tabbing with alt modifier.
- ASSERT_FALSE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
- // Tab to bring document into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- // Tabbing with ctrl modifier.
- ASSERT_FALSE(HandleTabEvent(engine.get(),
- blink::WebInputEvent::Modifiers::kControlKey));
- // Tabbing with alt modifier.
- ASSERT_FALSE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
- // Tab to bring first page into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- // Tabbing with ctrl modifier.
- ASSERT_FALSE(HandleTabEvent(engine.get(),
- blink::WebInputEvent::Modifiers::kControlKey));
- // Tabbing with alt modifier.
- ASSERT_FALSE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
- }
- TEST_F(PDFiumEngineTabbingTest, NoFocusableElementTabbing) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++ Page 2
- */
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- static constexpr bool kExpectedFocusState[] = {true, false, true, false};
- {
- InSequence sequence;
- for (auto focused : kExpectedFocusState)
- EXPECT_CALL(client, DocumentFocusChanged(focused));
- }
- ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing forward.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- // Tabbing backward.
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- ASSERT_FALSE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, RestoringDocumentFocus) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation
- * ++++ Annotation
- * ++ Page 2
- * ++++ Annotation
- */
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- static constexpr bool kExpectedFocusState[] = {true, false, true};
- {
- InSequence sequence;
- for (auto focused : kExpectedFocusState)
- EXPECT_CALL(client, DocumentFocusChanged(focused));
- }
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing to bring the document into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- engine->UpdateFocus(/*has_focus=*/false);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetLastFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedAnnotationIndex(engine.get()));
- engine->UpdateFocus(/*has_focus=*/true);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, RestoringAnnotFocus) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation
- * ++++ Annotation
- * ++ Page 2
- * ++++ Annotation
- */
- NiceMock<MockTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- static constexpr bool kExpectedFocusState[] = {true, false};
- {
- InSequence sequence;
- for (auto focused : kExpectedFocusState)
- EXPECT_CALL(client, DocumentFocusChanged(focused));
- }
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing to bring last annotation of page 0 into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- engine->UpdateFocus(/*has_focus=*/false);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetLastFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedAnnotationIndex(engine.get()));
- engine->UpdateFocus(/*has_focus=*/true);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- // Tabbing now should bring the second page's annotation to focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, VerifyFormFieldStatesOnTabbing) {
- /*
- * Document structure
- * Document
- * ++ Page 1
- * ++++ Annotation (Text Field)
- * ++++ Annotation (Radio Button)
- */
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(1, engine->GetNumberOfPages());
- // Bring focus to the document.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(PDFEngine::FocusFieldType::kNoFocus,
- FormFocusFieldType(engine.get()));
- EXPECT_FALSE(engine->CanEditText());
- // Bring focus to the text field on the page.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(PDFEngine::FocusFieldType::kText, FormFocusFieldType(engine.get()));
- EXPECT_TRUE(engine->CanEditText());
- // Bring focus to the button on the page.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(PDFEngine::FocusFieldType::kNonText,
- FormFocusFieldType(engine.get()));
- EXPECT_FALSE(engine->CanEditText());
- }
- TEST_F(PDFiumEngineTabbingTest, ClearSelectionOnFocusInFormTextArea) {
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("form_text_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(1, engine->GetNumberOfPages());
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Select all text.
- engine->SelectAll();
- EXPECT_EQ(1u, GetSelectionSize(engine.get()));
- // Tab to bring focus to a form text area annotation.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(0u, GetSelectionSize(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, RetainSelectionOnFocusNotInFormTextArea) {
- TestClient client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(1, engine->GetNumberOfPages());
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Select all text.
- engine->SelectAll();
- EXPECT_EQ(1u, GetSelectionSize(engine.get()));
- // Tab to bring focus to a non form text area annotation (Button).
- ASSERT_TRUE(
- HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(1u, GetSelectionSize(engine.get()));
- }
- class ScrollingTestClient : public TestClient {
- public:
- ScrollingTestClient() = default;
- ~ScrollingTestClient() override = default;
- ScrollingTestClient(const ScrollingTestClient&) = delete;
- ScrollingTestClient& operator=(const ScrollingTestClient&) = delete;
- // Mock PDFEngine::Client methods.
- MOCK_METHOD(void, ScrollToX, (int), (override));
- MOCK_METHOD(void, ScrollToY, (int), (override));
- };
- TEST_F(PDFiumEngineTabbingTest, MaintainViewportWhenFocusIsUpdated) {
- StrictMock<ScrollingTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- engine->PluginSizeUpdated(gfx::Size(60, 40));
- {
- InSequence sequence;
- static constexpr gfx::Point kScrollValue = {510, 478};
- EXPECT_CALL(client, ScrollToY(kScrollValue.y()))
- .WillOnce(Invoke(
- [&engine]() { engine->ScrolledToYPosition(kScrollValue.y()); }));
- EXPECT_CALL(client, ScrollToX(kScrollValue.x()))
- .WillOnce(Invoke(
- [&engine]() { engine->ScrolledToXPosition(kScrollValue.x()); }));
- }
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing to bring the document into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- // Tab to an annotation.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- // Scroll focused annotation out of viewport.
- static constexpr gfx::Point kScrollPosition = {242, 746};
- engine->ScrolledToXPosition(kScrollPosition.x());
- engine->ScrolledToYPosition(kScrollPosition.y());
- engine->UpdateFocus(/*has_focus=*/false);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetLastFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(1, GetLastFocusedAnnotationIndex(engine.get()));
- // Restore focus, we shouldn't have any calls to scroll viewport.
- engine->UpdateFocus(/*has_focus=*/true);
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
- }
- TEST_F(PDFiumEngineTabbingTest, ScrollFocusedAnnotationIntoView) {
- StrictMock<ScrollingTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- ASSERT_EQ(2, engine->GetNumberOfPages());
- engine->PluginSizeUpdated(gfx::Size(60, 40));
- {
- InSequence sequence;
- static constexpr gfx::Point kScrollValues[] = {{510, 478}, {510, 478}};
- for (const auto& scroll_value : kScrollValues) {
- EXPECT_CALL(client, ScrollToY(scroll_value.y()))
- .WillOnce(Invoke([&engine, &scroll_value]() {
- engine->ScrolledToYPosition(scroll_value.y());
- }));
- EXPECT_CALL(client, ScrollToX(scroll_value.x()))
- .WillOnce(Invoke([&engine, &scroll_value]() {
- engine->ScrolledToXPosition(scroll_value.x());
- }));
- }
- }
- EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
- GetFocusedElementType(engine.get()));
- EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
- // Tabbing to bring the document into focus.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
- GetFocusedElementType(engine.get()));
- // Tab to an annotation.
- ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
- EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
- GetFocusedElementType(engine.get()));
- // Scroll focused annotation out of viewport.
- static constexpr gfx::Point kScrollPosition = {242, 746};
- engine->ScrolledToXPosition(kScrollPosition.x());
- engine->ScrolledToYPosition(kScrollPosition.y());
- // Scroll the focused annotation into view.
- ScrollFocusedAnnotationIntoView(engine.get());
- }
- class ReadOnlyTestClient : public TestClient {
- public:
- ReadOnlyTestClient() = default;
- ~ReadOnlyTestClient() override = default;
- ReadOnlyTestClient(const ReadOnlyTestClient&) = delete;
- ReadOnlyTestClient& operator=(const ReadOnlyTestClient&) = delete;
- // Mock PDFEngine::Client methods.
- MOCK_METHOD(void,
- FormFieldFocusChange,
- (PDFEngine::FocusFieldType),
- (override));
- MOCK_METHOD(void, SetSelectedText, (const std::string&), (override));
- };
- using PDFiumEngineReadOnlyTest = PDFiumTestBase;
- TEST_F(PDFiumEngineReadOnlyTest, KillFormFocus) {
- NiceMock<ReadOnlyTestClient> client;
- std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
- &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
- ASSERT_TRUE(engine);
- // Setting read-only mode should kill form focus.
- EXPECT_FALSE(engine->IsReadOnly());
- EXPECT_CALL(client,
- FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
- engine->SetReadOnly(true);
- // Attempting to focus during read-only mode should once more trigger a
- // killing of form focus.
- EXPECT_TRUE(engine->IsReadOnly());
- EXPECT_CALL(client,
- FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
- engine->UpdateFocus(true);
- }
- TEST_F(PDFiumEngineReadOnlyTest, UnselectText) {
- NiceMock<ReadOnlyTestClient> client;
- std::unique_ptr<PDFiumEngine> engine =
- InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
- ASSERT_TRUE(engine);
- // Update the plugin size so that all the text is visible by
- // `SelectionChangeInvalidator`.
- engine->PluginSizeUpdated({500, 500});
- // Select text before going into read-only mode.
- EXPECT_FALSE(engine->IsReadOnly());
- EXPECT_CALL(client, SetSelectedText(Not(IsEmpty())));
- engine->SelectAll();
- // Setting read-only mode should unselect the text.
- EXPECT_CALL(client, SetSelectedText(IsEmpty()));
- engine->SetReadOnly(true);
- }
- } // namespace chrome_pdf
|