pdfium_engine_unittest.cc 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "pdf/pdfium/pdfium_engine.h"
  5. #include <stdint.h>
  6. #include <utility>
  7. #include "base/callback.h"
  8. #include "base/hash/md5.h"
  9. #include "base/run_loop.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/test/gmock_move_support.h"
  12. #include "base/test/gtest_util.h"
  13. #include "base/test/mock_callback.h"
  14. #include "base/test/scoped_feature_list.h"
  15. #include "base/threading/thread_task_runner_handle.h"
  16. #include "base/time/time.h"
  17. #include "build/build_config.h"
  18. #include "pdf/document_attachment_info.h"
  19. #include "pdf/document_layout.h"
  20. #include "pdf/document_metadata.h"
  21. #include "pdf/pdf_features.h"
  22. #include "pdf/pdfium/pdfium_page.h"
  23. #include "pdf/pdfium/pdfium_test_base.h"
  24. #include "pdf/test/test_client.h"
  25. #include "pdf/test/test_document_loader.h"
  26. #include "pdf/ui/thumbnail.h"
  27. #include "testing/gmock/include/gmock/gmock.h"
  28. #include "testing/gtest/include/gtest/gtest.h"
  29. #include "third_party/blink/public/common/input/web_input_event.h"
  30. #include "third_party/blink/public/common/input/web_keyboard_event.h"
  31. #include "third_party/blink/public/common/input/web_mouse_event.h"
  32. #include "third_party/blink/public/common/input/web_pointer_properties.h"
  33. #include "ui/events/keycodes/keyboard_codes.h"
  34. #include "ui/gfx/geometry/point.h"
  35. #include "ui/gfx/geometry/point_f.h"
  36. #include "ui/gfx/geometry/rect.h"
  37. #include "ui/gfx/geometry/size.h"
  38. namespace chrome_pdf {
  39. namespace {
  40. using ::testing::InSequence;
  41. using ::testing::Invoke;
  42. using ::testing::IsEmpty;
  43. using ::testing::NiceMock;
  44. using ::testing::Not;
  45. using ::testing::Return;
  46. using ::testing::StrictMock;
  47. MATCHER_P2(LayoutWithSize, width, height, "") {
  48. return arg.size() == gfx::Size(width, height);
  49. }
  50. MATCHER_P(LayoutWithOptions, options, "") {
  51. return arg.options() == options;
  52. }
  53. blink::WebMouseEvent CreateRightClickWebMouseEventAtPosition(
  54. const gfx::PointF& position) {
  55. return blink::WebMouseEvent(
  56. blink::WebInputEvent::Type::kMouseDown, /*position=*/position,
  57. /*global_position=*/position, blink::WebPointerProperties::Button::kRight,
  58. /*click_count_param=*/1,
  59. blink::WebInputEvent::Modifiers::kRightButtonDown,
  60. blink::WebInputEvent::GetStaticTimeStampForTests());
  61. }
  62. class MockTestClient : public TestClient {
  63. public:
  64. MockTestClient() {
  65. ON_CALL(*this, ProposeDocumentLayout)
  66. .WillByDefault([this](const DocumentLayout& layout) {
  67. TestClient::ProposeDocumentLayout(layout);
  68. });
  69. }
  70. MOCK_METHOD(void,
  71. ProposeDocumentLayout,
  72. (const DocumentLayout& layout),
  73. (override));
  74. MOCK_METHOD(void, ScrollToPage, (int page), (override));
  75. MOCK_METHOD(void, DocumentFocusChanged, (bool), (override));
  76. MOCK_METHOD(void, SetLinkUnderCursor, (const std::string&), (override));
  77. };
  78. } // namespace
  79. class PDFiumEngineTest : public PDFiumTestBase {
  80. protected:
  81. void ExpectPageRect(const PDFiumEngine& engine,
  82. size_t page_index,
  83. const gfx::Rect& expected_rect) {
  84. const PDFiumPage& page = GetPDFiumPageForTest(engine, page_index);
  85. EXPECT_EQ(expected_rect, page.rect());
  86. }
  87. // Tries to load a PDF incrementally, returning `true` if the PDF actually was
  88. // loaded incrementally. Note that this function will return `false` if
  89. // incremental loading fails, but also if incremental loading is disabled.
  90. bool TryLoadIncrementally() {
  91. NiceMock<MockTestClient> client;
  92. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  93. &client, FILE_PATH_LITERAL("linearized.pdf"));
  94. if (!initialize_result.engine) {
  95. ADD_FAILURE();
  96. return false;
  97. }
  98. PDFiumEngine& engine = *initialize_result.engine;
  99. // Load enough for the document to become partially available.
  100. initialize_result.document_loader->SimulateLoadData(8192);
  101. bool loaded_incrementally;
  102. if (engine.GetNumberOfPages() == 0) {
  103. // This is not necessarily a test failure; it just indicates incremental
  104. // loading is not occurring.
  105. engine.PluginSizeUpdated({});
  106. loaded_incrementally = false;
  107. } else {
  108. // Note: Plugin size chosen so all pages of the document are visible. The
  109. // engine only updates availability incrementally for visible pages.
  110. EXPECT_EQ(0, CountAvailablePages(engine));
  111. engine.PluginSizeUpdated({1024, 4096});
  112. int available_pages = CountAvailablePages(engine);
  113. loaded_incrementally =
  114. 0 < available_pages && available_pages < engine.GetNumberOfPages();
  115. }
  116. // Verify that loading can finish.
  117. initialize_result.FinishLoading();
  118. EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
  119. return loaded_incrementally;
  120. }
  121. void FinishWithPluginSizeUpdated(PDFiumEngine& engine) {
  122. engine.PluginSizeUpdated({});
  123. base::RunLoop run_loop;
  124. base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
  125. run_loop.QuitClosure());
  126. run_loop.Run();
  127. }
  128. // Counts the number of available pages. Returns `int` instead of `size_t` for
  129. // consistency with `PDFiumEngine::GetNumberOfPages()`.
  130. int CountAvailablePages(const PDFiumEngine& engine) {
  131. int available_pages = 0;
  132. for (int i = 0; i < engine.GetNumberOfPages(); ++i) {
  133. if (GetPDFiumPageForTest(engine, i).available())
  134. ++available_pages;
  135. }
  136. return available_pages;
  137. }
  138. };
  139. TEST_F(PDFiumEngineTest, InitializeWithRectanglesMultiPagesPdf) {
  140. NiceMock<MockTestClient> client;
  141. // ProposeDocumentLayout() gets called twice during loading because
  142. // PDFiumEngine::ContinueLoadingDocument() calls LoadBody() (which eventually
  143. // triggers a layout proposal), and then calls FinishLoadingDocument() (since
  144. // the document is complete), which calls LoadBody() again. Coalescing these
  145. // proposals is not correct unless we address the issue covered by
  146. // PDFiumEngineTest.ProposeDocumentLayoutWithOverlap.
  147. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
  148. .Times(2);
  149. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  150. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  151. ASSERT_TRUE(engine);
  152. ASSERT_EQ(5, engine->GetNumberOfPages());
  153. ExpectPageRect(*engine, 0, {38, 3, 266, 333});
  154. ExpectPageRect(*engine, 1, {5, 350, 333, 266});
  155. ExpectPageRect(*engine, 2, {38, 630, 266, 333});
  156. ExpectPageRect(*engine, 3, {38, 977, 266, 333});
  157. ExpectPageRect(*engine, 4, {38, 1324, 266, 333});
  158. }
  159. TEST_F(PDFiumEngineTest, InitializeWithRectanglesMultiPagesPdfInTwoUpView) {
  160. NiceMock<MockTestClient> client;
  161. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  162. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  163. ASSERT_TRUE(engine);
  164. DocumentLayout::Options options;
  165. options.set_page_spread(DocumentLayout::PageSpread::kTwoUpOdd);
  166. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithOptions(options)))
  167. .WillOnce(Return());
  168. engine->SetDocumentLayout(DocumentLayout::PageSpread::kTwoUpOdd);
  169. engine->ApplyDocumentLayout(options);
  170. ASSERT_EQ(5, engine->GetNumberOfPages());
  171. ExpectPageRect(*engine, 0, {72, 3, 266, 333});
  172. ExpectPageRect(*engine, 1, {340, 3, 333, 266});
  173. ExpectPageRect(*engine, 2, {72, 346, 266, 333});
  174. ExpectPageRect(*engine, 3, {340, 346, 266, 333});
  175. ExpectPageRect(*engine, 4, {68, 689, 266, 333});
  176. }
  177. TEST_F(PDFiumEngineTest, AppendBlankPagesWithFewerPages) {
  178. NiceMock<MockTestClient> client;
  179. {
  180. InSequence normal_then_append;
  181. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
  182. .Times(2);
  183. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 1037)));
  184. }
  185. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  186. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  187. ASSERT_TRUE(engine);
  188. engine->AppendBlankPages(3);
  189. ASSERT_EQ(3, engine->GetNumberOfPages());
  190. ExpectPageRect(*engine, 0, {5, 3, 266, 333});
  191. ExpectPageRect(*engine, 1, {5, 350, 266, 333});
  192. ExpectPageRect(*engine, 2, {5, 697, 266, 333});
  193. }
  194. TEST_F(PDFiumEngineTest, AppendBlankPagesWithMorePages) {
  195. NiceMock<MockTestClient> client;
  196. {
  197. InSequence normal_then_append;
  198. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
  199. .Times(2);
  200. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 2425)));
  201. }
  202. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  203. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  204. ASSERT_TRUE(engine);
  205. engine->AppendBlankPages(7);
  206. ASSERT_EQ(7, engine->GetNumberOfPages());
  207. ExpectPageRect(*engine, 0, {5, 3, 266, 333});
  208. ExpectPageRect(*engine, 1, {5, 350, 266, 333});
  209. ExpectPageRect(*engine, 2, {5, 697, 266, 333});
  210. ExpectPageRect(*engine, 3, {5, 1044, 266, 333});
  211. ExpectPageRect(*engine, 4, {5, 1391, 266, 333});
  212. ExpectPageRect(*engine, 5, {5, 1738, 266, 333});
  213. ExpectPageRect(*engine, 6, {5, 2085, 266, 333});
  214. }
  215. TEST_F(PDFiumEngineTest, ProposeDocumentLayoutWithOverlap) {
  216. NiceMock<MockTestClient> client;
  217. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  218. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  219. ASSERT_TRUE(engine);
  220. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1463)))
  221. .WillOnce(Return());
  222. engine->RotateClockwise();
  223. EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)))
  224. .WillOnce(Return());
  225. engine->RotateCounterclockwise();
  226. }
  227. TEST_F(PDFiumEngineTest, ApplyDocumentLayoutBeforePluginSizeUpdated) {
  228. NiceMock<MockTestClient> client;
  229. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  230. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  231. ASSERT_TRUE(initialize_result.engine);
  232. initialize_result.FinishLoading();
  233. PDFiumEngine& engine = *initialize_result.engine;
  234. DocumentLayout::Options options;
  235. options.RotatePagesClockwise();
  236. EXPECT_CALL(client, ScrollToPage(-1)).Times(0);
  237. EXPECT_EQ(gfx::Size(343, 1664), engine.ApplyDocumentLayout(options));
  238. EXPECT_CALL(client, ScrollToPage(-1)).Times(1);
  239. FinishWithPluginSizeUpdated(engine);
  240. }
  241. TEST_F(PDFiumEngineTest, ApplyDocumentLayoutAvoidsInfiniteLoop) {
  242. NiceMock<MockTestClient> client;
  243. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  244. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  245. ASSERT_TRUE(engine);
  246. DocumentLayout::Options options;
  247. EXPECT_CALL(client, ScrollToPage(-1)).Times(0);
  248. EXPECT_EQ(gfx::Size(343, 1664), engine->ApplyDocumentLayout(options));
  249. options.RotatePagesClockwise();
  250. EXPECT_CALL(client, ScrollToPage(-1)).Times(1);
  251. EXPECT_EQ(gfx::Size(343, 1463), engine->ApplyDocumentLayout(options));
  252. EXPECT_EQ(gfx::Size(343, 1463), engine->ApplyDocumentLayout(options));
  253. }
  254. TEST_F(PDFiumEngineTest, GetDocumentAttachments) {
  255. NiceMock<MockTestClient> client;
  256. std::unique_ptr<PDFiumEngine> engine =
  257. InitializeEngine(&client, FILE_PATH_LITERAL("embedded_attachments.pdf"));
  258. ASSERT_TRUE(engine);
  259. const std::vector<DocumentAttachmentInfo>& attachments =
  260. engine->GetDocumentAttachmentInfoList();
  261. ASSERT_EQ(3u, attachments.size());
  262. {
  263. const DocumentAttachmentInfo& attachment = attachments[0];
  264. EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name));
  265. EXPECT_TRUE(attachment.is_readable);
  266. EXPECT_EQ(4u, attachment.size_bytes);
  267. EXPECT_EQ("D:20170712214438-07'00'",
  268. base::UTF16ToUTF8(attachment.creation_date));
  269. EXPECT_EQ("D:20160115091400", base::UTF16ToUTF8(attachment.modified_date));
  270. std::vector<uint8_t> content = engine->GetAttachmentData(0);
  271. ASSERT_EQ(attachment.size_bytes, content.size());
  272. std::string content_str(content.begin(), content.end());
  273. EXPECT_EQ("test", content_str);
  274. }
  275. {
  276. static constexpr char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18";
  277. const DocumentAttachmentInfo& attachment = attachments[1];
  278. EXPECT_EQ("attached.pdf", base::UTF16ToUTF8(attachment.name));
  279. EXPECT_TRUE(attachment.is_readable);
  280. EXPECT_EQ(5869u, attachment.size_bytes);
  281. EXPECT_EQ("D:20170712214443-07'00'",
  282. base::UTF16ToUTF8(attachment.creation_date));
  283. EXPECT_EQ("D:20170712214410", base::UTF16ToUTF8(attachment.modified_date));
  284. std::vector<uint8_t> content = engine->GetAttachmentData(1);
  285. ASSERT_EQ(attachment.size_bytes, content.size());
  286. // The whole attachment content is too long to do string comparison.
  287. // Instead, we only verify the checksum value here.
  288. base::MD5Digest hash;
  289. base::MD5Sum(content.data(), content.size(), &hash);
  290. EXPECT_EQ(kCheckSum, base::MD5DigestToBase16(hash));
  291. }
  292. {
  293. // Test attachments with no creation date or last modified date.
  294. const DocumentAttachmentInfo& attachment = attachments[2];
  295. EXPECT_EQ("附錄.txt", base::UTF16ToUTF8(attachment.name));
  296. EXPECT_TRUE(attachment.is_readable);
  297. EXPECT_EQ(5u, attachment.size_bytes);
  298. EXPECT_THAT(attachment.creation_date, IsEmpty());
  299. EXPECT_THAT(attachment.modified_date, IsEmpty());
  300. std::vector<uint8_t> content = engine->GetAttachmentData(2);
  301. ASSERT_EQ(attachment.size_bytes, content.size());
  302. std::string content_str(content.begin(), content.end());
  303. EXPECT_EQ("test\n", content_str);
  304. }
  305. }
  306. TEST_F(PDFiumEngineTest, GetInvalidDocumentAttachment) {
  307. NiceMock<MockTestClient> client;
  308. std::unique_ptr<PDFiumEngine> engine =
  309. InitializeEngine(&client, FILE_PATH_LITERAL("invalid_attachment.pdf"));
  310. ASSERT_TRUE(engine);
  311. // Test on a document with one invalid attachment, which can make
  312. // FPDFDoc_GetAttachment() fail. This particular attachment is invalid due
  313. // to its key value violating the `Limits` entry.
  314. const std::vector<DocumentAttachmentInfo>& attachments =
  315. engine->GetDocumentAttachmentInfoList();
  316. ASSERT_EQ(1u, attachments.size());
  317. const DocumentAttachmentInfo& attachment = attachments[0];
  318. EXPECT_THAT(attachment.name, IsEmpty());
  319. EXPECT_FALSE(attachment.is_readable);
  320. EXPECT_EQ(0u, attachment.size_bytes);
  321. EXPECT_THAT(attachment.creation_date, IsEmpty());
  322. EXPECT_THAT(attachment.modified_date, IsEmpty());
  323. }
  324. TEST_F(PDFiumEngineTest, GetDocumentAttachmentWithInvalidData) {
  325. NiceMock<MockTestClient> client;
  326. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  327. &client, FILE_PATH_LITERAL("embedded_attachments_invalid_data.pdf"));
  328. ASSERT_TRUE(engine);
  329. const std::vector<DocumentAttachmentInfo>& attachments =
  330. engine->GetDocumentAttachmentInfoList();
  331. ASSERT_EQ(1u, attachments.size());
  332. // Test on an attachment which FPDFAttachment_GetFile() fails to retrieve data
  333. // from.
  334. const DocumentAttachmentInfo& attachment = attachments[0];
  335. EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name));
  336. EXPECT_FALSE(attachment.is_readable);
  337. EXPECT_EQ(0u, attachment.size_bytes);
  338. EXPECT_THAT(attachment.creation_date, IsEmpty());
  339. EXPECT_THAT(attachment.modified_date, IsEmpty());
  340. }
  341. TEST_F(PDFiumEngineTest, NoDocumentAttachmentInfo) {
  342. NiceMock<MockTestClient> client;
  343. std::unique_ptr<PDFiumEngine> engine =
  344. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  345. ASSERT_TRUE(engine);
  346. EXPECT_EQ(0u, engine->GetDocumentAttachmentInfoList().size());
  347. }
  348. TEST_F(PDFiumEngineTest, GetDocumentMetadata) {
  349. NiceMock<MockTestClient> client;
  350. std::unique_ptr<PDFiumEngine> engine =
  351. InitializeEngine(&client, FILE_PATH_LITERAL("document_info.pdf"));
  352. ASSERT_TRUE(engine);
  353. const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
  354. EXPECT_EQ(PdfVersion::k1_7, doc_metadata.version);
  355. EXPECT_EQ(714u, doc_metadata.size_bytes);
  356. EXPECT_FALSE(doc_metadata.linearized);
  357. EXPECT_EQ("Sample PDF Document Info", doc_metadata.title);
  358. EXPECT_EQ("Chromium Authors", doc_metadata.author);
  359. EXPECT_EQ("Testing", doc_metadata.subject);
  360. EXPECT_EQ("testing,chromium,pdfium,document,info", doc_metadata.keywords);
  361. EXPECT_EQ("Your Preferred Text Editor", doc_metadata.creator);
  362. EXPECT_EQ("fixup_pdf_template.py", doc_metadata.producer);
  363. base::Time expected_creation_date;
  364. ASSERT_TRUE(base::Time::FromUTCString("2020-02-05 15:39:12",
  365. &expected_creation_date));
  366. EXPECT_EQ(expected_creation_date, doc_metadata.creation_date);
  367. base::Time expected_mod_date;
  368. ASSERT_TRUE(
  369. base::Time::FromUTCString("2020-02-06 09:42:34", &expected_mod_date));
  370. EXPECT_EQ(expected_mod_date, doc_metadata.mod_date);
  371. }
  372. TEST_F(PDFiumEngineTest, GetEmptyDocumentMetadata) {
  373. NiceMock<MockTestClient> client;
  374. std::unique_ptr<PDFiumEngine> engine =
  375. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  376. ASSERT_TRUE(engine);
  377. const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
  378. EXPECT_EQ(PdfVersion::k1_7, doc_metadata.version);
  379. EXPECT_EQ(786u, doc_metadata.size_bytes);
  380. EXPECT_FALSE(doc_metadata.linearized);
  381. EXPECT_THAT(doc_metadata.title, IsEmpty());
  382. EXPECT_THAT(doc_metadata.author, IsEmpty());
  383. EXPECT_THAT(doc_metadata.subject, IsEmpty());
  384. EXPECT_THAT(doc_metadata.keywords, IsEmpty());
  385. EXPECT_THAT(doc_metadata.creator, IsEmpty());
  386. EXPECT_THAT(doc_metadata.producer, IsEmpty());
  387. EXPECT_TRUE(doc_metadata.creation_date.is_null());
  388. EXPECT_TRUE(doc_metadata.mod_date.is_null());
  389. }
  390. TEST_F(PDFiumEngineTest, GetLinearizedDocumentMetadata) {
  391. NiceMock<MockTestClient> client;
  392. std::unique_ptr<PDFiumEngine> engine =
  393. InitializeEngine(&client, FILE_PATH_LITERAL("linearized.pdf"));
  394. ASSERT_TRUE(engine);
  395. EXPECT_TRUE(engine->GetDocumentMetadata().linearized);
  396. }
  397. TEST_F(PDFiumEngineTest, GetBadPdfVersion) {
  398. NiceMock<MockTestClient> client;
  399. std::unique_ptr<PDFiumEngine> engine =
  400. InitializeEngine(&client, FILE_PATH_LITERAL("bad_version.pdf"));
  401. ASSERT_TRUE(engine);
  402. const DocumentMetadata& doc_metadata = engine->GetDocumentMetadata();
  403. EXPECT_EQ(PdfVersion::kUnknown, doc_metadata.version);
  404. }
  405. TEST_F(PDFiumEngineTest, GetNamedDestination) {
  406. NiceMock<MockTestClient> client;
  407. std::unique_ptr<PDFiumEngine> engine =
  408. InitializeEngine(&client, FILE_PATH_LITERAL("named_destinations.pdf"));
  409. ASSERT_TRUE(engine);
  410. ASSERT_EQ(2, engine->GetNumberOfPages());
  411. // A destination with a valid page object
  412. absl::optional<PDFEngine::NamedDestination> valid_page_obj =
  413. engine->GetNamedDestination("ValidPageObj");
  414. ASSERT_TRUE(valid_page_obj.has_value());
  415. EXPECT_EQ(0u, valid_page_obj->page);
  416. EXPECT_EQ("XYZ", valid_page_obj->view);
  417. ASSERT_EQ(3u, valid_page_obj->num_params);
  418. EXPECT_EQ(1.2f, valid_page_obj->params[2]);
  419. // A destination with an invalid page object
  420. absl::optional<PDFEngine::NamedDestination> invalid_page_obj =
  421. engine->GetNamedDestination("InvalidPageObj");
  422. ASSERT_FALSE(invalid_page_obj.has_value());
  423. // A destination with a valid page number
  424. absl::optional<PDFEngine::NamedDestination> valid_page_number =
  425. engine->GetNamedDestination("ValidPageNumber");
  426. ASSERT_TRUE(valid_page_number.has_value());
  427. EXPECT_EQ(1u, valid_page_number->page);
  428. // A destination with an out-of-range page number
  429. absl::optional<PDFEngine::NamedDestination> invalid_page_number =
  430. engine->GetNamedDestination("OutOfRangePageNumber");
  431. EXPECT_FALSE(invalid_page_number.has_value());
  432. }
  433. TEST_F(PDFiumEngineTest, PluginSizeUpdatedBeforeLoad) {
  434. NiceMock<MockTestClient> client;
  435. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  436. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  437. ASSERT_TRUE(initialize_result.engine);
  438. PDFiumEngine& engine = *initialize_result.engine;
  439. engine.PluginSizeUpdated({});
  440. initialize_result.FinishLoading();
  441. EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
  442. }
  443. TEST_F(PDFiumEngineTest, PluginSizeUpdatedDuringLoad) {
  444. NiceMock<MockTestClient> client;
  445. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  446. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  447. ASSERT_TRUE(initialize_result.engine);
  448. PDFiumEngine& engine = *initialize_result.engine;
  449. EXPECT_TRUE(initialize_result.document_loader->SimulateLoadData(1024));
  450. engine.PluginSizeUpdated({});
  451. initialize_result.FinishLoading();
  452. EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
  453. }
  454. TEST_F(PDFiumEngineTest, PluginSizeUpdatedAfterLoad) {
  455. NiceMock<MockTestClient> client;
  456. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  457. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  458. ASSERT_TRUE(initialize_result.engine);
  459. PDFiumEngine& engine = *initialize_result.engine;
  460. initialize_result.FinishLoading();
  461. FinishWithPluginSizeUpdated(engine);
  462. EXPECT_EQ(engine.GetNumberOfPages(), CountAvailablePages(engine));
  463. }
  464. TEST_F(PDFiumEngineTest, OnLeftMouseDownBeforePluginSizeUpdated) {
  465. NiceMock<MockTestClient> client;
  466. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  467. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  468. ASSERT_TRUE(initialize_result.engine);
  469. initialize_result.FinishLoading();
  470. PDFiumEngine& engine = *initialize_result.engine;
  471. EXPECT_TRUE(engine.HandleInputEvent(blink::WebMouseEvent(
  472. blink::WebInputEvent::Type::kMouseDown, {0, 0}, {100, 200},
  473. blink::WebPointerProperties::Button::kLeft, /*click_count_param=*/1,
  474. blink::WebInputEvent::Modifiers::kLeftButtonDown,
  475. blink::WebInputEvent::GetStaticTimeStampForTests())));
  476. }
  477. TEST_F(PDFiumEngineTest, OnLeftMouseDownAfterPluginSizeUpdated) {
  478. NiceMock<MockTestClient> client;
  479. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  480. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  481. ASSERT_TRUE(initialize_result.engine);
  482. initialize_result.FinishLoading();
  483. PDFiumEngine& engine = *initialize_result.engine;
  484. engine.PluginSizeUpdated({300, 400});
  485. EXPECT_TRUE(engine.HandleInputEvent(blink::WebMouseEvent(
  486. blink::WebInputEvent::Type::kMouseDown, {0, 0}, {100, 200},
  487. blink::WebPointerProperties::Button::kLeft, /*click_count_param=*/1,
  488. blink::WebInputEvent::Modifiers::kLeftButtonDown,
  489. blink::WebInputEvent::GetStaticTimeStampForTests())));
  490. }
  491. TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureDefault) {
  492. EXPECT_FALSE(TryLoadIncrementally());
  493. }
  494. TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureEnabled) {
  495. base::test::ScopedFeatureList scoped_feature_list;
  496. scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
  497. EXPECT_TRUE(TryLoadIncrementally());
  498. }
  499. TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureDisabled) {
  500. base::test::ScopedFeatureList scoped_feature_list;
  501. scoped_feature_list.InitAndDisableFeature(features::kPdfIncrementalLoading);
  502. EXPECT_FALSE(TryLoadIncrementally());
  503. }
  504. TEST_F(PDFiumEngineTest, RequestThumbnail) {
  505. TestClient client;
  506. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  507. &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
  508. ASSERT_TRUE(engine);
  509. const int num_pages = engine->GetNumberOfPages();
  510. ASSERT_EQ(5, num_pages);
  511. ASSERT_EQ(num_pages, CountAvailablePages(*engine));
  512. // Each page should immediately return a thumbnail.
  513. for (int i = 0; i < num_pages; ++i) {
  514. base::MockCallback<SendThumbnailCallback> send_callback;
  515. EXPECT_CALL(send_callback, Run);
  516. engine->RequestThumbnail(/*page_index=*/i, /*device_pixel_ratio=*/1,
  517. send_callback.Get());
  518. }
  519. }
  520. TEST_F(PDFiumEngineTest, RequestThumbnailLinearized) {
  521. base::test::ScopedFeatureList scoped_feature_list;
  522. scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
  523. NiceMock<MockTestClient> client;
  524. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  525. &client, FILE_PATH_LITERAL("linearized.pdf"));
  526. ASSERT_TRUE(initialize_result.engine);
  527. PDFiumEngine& engine = *initialize_result.engine;
  528. // Load only some pages.
  529. initialize_result.document_loader->SimulateLoadData(8192);
  530. // Note: Plugin size chosen so all pages of the document are visible. The
  531. // engine only updates availability incrementally for visible pages.
  532. engine.PluginSizeUpdated({1024, 4096});
  533. const int num_pages = engine.GetNumberOfPages();
  534. ASSERT_EQ(3, num_pages);
  535. const int available_pages = CountAvailablePages(engine);
  536. ASSERT_LT(0, available_pages);
  537. ASSERT_GT(num_pages, available_pages);
  538. // Initialize callbacks for first and last pages.
  539. base::MockCallback<SendThumbnailCallback> first_loaded;
  540. base::MockCallback<SendThumbnailCallback> last_loaded;
  541. // When the document is partially loaded, `SendThumbnailCallback` is only run
  542. // for the loaded page even though `RequestThumbnail()` gets called for both
  543. // pages.
  544. EXPECT_CALL(first_loaded, Run);
  545. engine.RequestThumbnail(/*page_index=*/0, /*device_pixel_ratio=*/1,
  546. first_loaded.Get());
  547. engine.RequestThumbnail(/*page_index=*/num_pages - 1,
  548. /*device_pixel_ratio=*/1, last_loaded.Get());
  549. // Finish loading the document. `SendThumbnailCallback` should be run for the
  550. // last page.
  551. EXPECT_CALL(last_loaded, Run);
  552. initialize_result.FinishLoading();
  553. }
  554. TEST_F(PDFiumEngineTest, HandleInputEventKeyDown) {
  555. NiceMock<MockTestClient> client;
  556. std::unique_ptr<PDFiumEngine> engine =
  557. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  558. ASSERT_TRUE(engine);
  559. EXPECT_CALL(client, DocumentFocusChanged(true));
  560. blink::WebKeyboardEvent key_down_event(
  561. blink::WebInputEvent::Type::kKeyDown, blink::WebInputEvent::kNoModifiers,
  562. blink::WebInputEvent::GetStaticTimeStampForTests());
  563. key_down_event.windows_key_code = ui::VKEY_TAB;
  564. EXPECT_TRUE(engine->HandleInputEvent(key_down_event));
  565. }
  566. TEST_F(PDFiumEngineTest, HandleInputEventRawKeyDown) {
  567. NiceMock<MockTestClient> client;
  568. std::unique_ptr<PDFiumEngine> engine =
  569. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  570. ASSERT_TRUE(engine);
  571. EXPECT_CALL(client, DocumentFocusChanged(true));
  572. blink::WebKeyboardEvent raw_key_down_event(
  573. blink::WebInputEvent::Type::kRawKeyDown,
  574. blink::WebInputEvent::kNoModifiers,
  575. blink::WebInputEvent::GetStaticTimeStampForTests());
  576. raw_key_down_event.windows_key_code = ui::VKEY_TAB;
  577. EXPECT_TRUE(engine->HandleInputEvent(raw_key_down_event));
  578. }
  579. namespace {
  580. #if BUILDFLAG(IS_WIN)
  581. constexpr char kSelectTextExpectedText[] =
  582. "Hello, world!\r\nGoodbye, world!\r\nHello, world!\r\nGoodbye, world!";
  583. #else
  584. constexpr char kSelectTextExpectedText[] =
  585. "Hello, world!\nGoodbye, world!\nHello, world!\nGoodbye, world!";
  586. #endif
  587. } // namespace
  588. TEST_F(PDFiumEngineTest, SelectText) {
  589. NiceMock<MockTestClient> client;
  590. std::unique_ptr<PDFiumEngine> engine =
  591. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  592. ASSERT_TRUE(engine);
  593. EXPECT_TRUE(engine->HasPermission(DocumentPermission::kCopy));
  594. EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
  595. engine->SelectAll();
  596. EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText());
  597. }
  598. TEST_F(PDFiumEngineTest, SelectTextWithCopyRestriction) {
  599. NiceMock<MockTestClient> client;
  600. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  601. &client, FILE_PATH_LITERAL("hello_world2_with_copy_restriction.pdf"));
  602. ASSERT_TRUE(engine);
  603. EXPECT_FALSE(engine->HasPermission(DocumentPermission::kCopy));
  604. // The copy restriction should not affect the text selection hehavior.
  605. EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
  606. engine->SelectAll();
  607. EXPECT_EQ(kSelectTextExpectedText, engine->GetSelectedText());
  608. }
  609. TEST_F(PDFiumEngineTest, SelectCroppedText) {
  610. NiceMock<MockTestClient> client;
  611. std::unique_ptr<PDFiumEngine> engine =
  612. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world_cropped.pdf"));
  613. ASSERT_TRUE(engine);
  614. EXPECT_THAT(engine->GetSelectedText(), IsEmpty());
  615. engine->SelectAll();
  616. #if BUILDFLAG(IS_WIN)
  617. constexpr char kExpectedText[] = "world!\r\n";
  618. #else
  619. constexpr char kExpectedText[] = "world!\n";
  620. #endif
  621. EXPECT_EQ(kExpectedText, engine->GetSelectedText());
  622. }
  623. using PDFiumEngineDeathTest = PDFiumEngineTest;
  624. TEST_F(PDFiumEngineDeathTest, RequestThumbnailRedundant) {
  625. ::testing::FLAGS_gtest_death_test_style = "threadsafe";
  626. base::test::ScopedFeatureList scoped_feature_list;
  627. scoped_feature_list.InitAndEnableFeature(features::kPdfIncrementalLoading);
  628. NiceMock<MockTestClient> client;
  629. InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
  630. &client, FILE_PATH_LITERAL("linearized.pdf"));
  631. ASSERT_TRUE(initialize_result.engine);
  632. PDFiumEngine& engine = *initialize_result.engine;
  633. // Load only some pages.
  634. initialize_result.document_loader->SimulateLoadData(8192);
  635. // Twice request a thumbnail for the second page, which is not loaded. The
  636. // second call should crash.
  637. base::MockCallback<SendThumbnailCallback> mock_callback;
  638. engine.RequestThumbnail(/*page_index=*/1, /*device_pixel_ratio=*/1,
  639. mock_callback.Get());
  640. EXPECT_DCHECK_DEATH(engine.RequestThumbnail(
  641. /*page_index=*/1, /*device_pixel_ratio=*/1, mock_callback.Get()));
  642. }
  643. class PDFiumEngineTabbingTest : public PDFiumTestBase {
  644. public:
  645. PDFiumEngineTabbingTest() = default;
  646. ~PDFiumEngineTabbingTest() override = default;
  647. PDFiumEngineTabbingTest(const PDFiumEngineTabbingTest&) = delete;
  648. PDFiumEngineTabbingTest& operator=(const PDFiumEngineTabbingTest&) = delete;
  649. bool HandleTabEvent(PDFiumEngine* engine, int modifiers) {
  650. return engine->HandleTabEvent(modifiers);
  651. }
  652. PDFiumEngine::FocusElementType GetFocusedElementType(PDFiumEngine* engine) {
  653. return engine->focus_element_type_;
  654. }
  655. int GetLastFocusedPage(PDFiumEngine* engine) {
  656. return engine->last_focused_page_;
  657. }
  658. PDFiumEngine::FocusElementType GetLastFocusedElementType(
  659. PDFiumEngine* engine) {
  660. return engine->last_focused_element_type_;
  661. }
  662. int GetLastFocusedAnnotationIndex(PDFiumEngine* engine) {
  663. return engine->last_focused_annot_index_;
  664. }
  665. PDFEngine::FocusFieldType FormFocusFieldType(PDFiumEngine* engine) {
  666. return engine->focus_field_type_;
  667. }
  668. size_t GetSelectionSize(PDFiumEngine* engine) {
  669. return engine->selection_.size();
  670. }
  671. void ScrollFocusedAnnotationIntoView(PDFiumEngine* engine) {
  672. engine->ScrollFocusedAnnotationIntoView();
  673. }
  674. };
  675. TEST_F(PDFiumEngineTabbingTest, LinkUnderCursor) {
  676. /*
  677. * Document structure
  678. * Document
  679. * ++ Page 1
  680. * ++++ Widget annotation
  681. * ++++ Widget annotation
  682. * ++++ Highlight annotation
  683. * ++++ Link annotation
  684. */
  685. // Enable feature flag.
  686. base::test::ScopedFeatureList scoped_feature_list;
  687. scoped_feature_list.InitAndEnableFeature(
  688. chrome_pdf::features::kTabAcrossPDFAnnotations);
  689. NiceMock<MockTestClient> client;
  690. std::unique_ptr<PDFiumEngine> engine =
  691. InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
  692. ASSERT_TRUE(engine);
  693. // Tab to right before the first non-link annotation.
  694. EXPECT_CALL(client, DocumentFocusChanged(true));
  695. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  696. // Tab through non-link annotations and validate link under cursor.
  697. {
  698. InSequence sequence;
  699. EXPECT_CALL(client, SetLinkUnderCursor(""));
  700. EXPECT_CALL(client, DocumentFocusChanged(false));
  701. EXPECT_CALL(client, SetLinkUnderCursor("")).Times(2);
  702. }
  703. for (int i = 0; i < 3; i++)
  704. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  705. // Tab to Link annotation.
  706. EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
  707. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  708. // Tab to previous annotation.
  709. EXPECT_CALL(client, SetLinkUnderCursor(""));
  710. ASSERT_TRUE(
  711. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  712. }
  713. // Test case for crbug.com/1088296
  714. TEST_F(PDFiumEngineTabbingTest, LinkUnderCursorAfterTabAndRightClick) {
  715. NiceMock<MockTestClient> client;
  716. std::unique_ptr<PDFiumEngine> engine =
  717. InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
  718. ASSERT_TRUE(engine);
  719. // Ensure the plugin has a pre-determined size, to enable the hit tests below.
  720. engine->PluginSizeUpdated({612, 792});
  721. // Tab to right before the first non-link annotation.
  722. EXPECT_CALL(client, DocumentFocusChanged(true));
  723. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  724. // Tab through non-link annotations and validate link under cursor.
  725. {
  726. InSequence sequence;
  727. EXPECT_CALL(client, SetLinkUnderCursor(""));
  728. EXPECT_CALL(client, DocumentFocusChanged(false));
  729. }
  730. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  731. EXPECT_CALL(client, SetLinkUnderCursor(""));
  732. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  733. EXPECT_CALL(client, SetLinkUnderCursor(""));
  734. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  735. // Tab to Link annotation.
  736. EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
  737. ASSERT_TRUE(HandleTabEvent(engine.get(), /*modifiers=*/0));
  738. // Right click somewhere far away should reset the link.
  739. constexpr gfx::PointF kOffScreenPosition(0, 0);
  740. EXPECT_CALL(client, SetLinkUnderCursor(""));
  741. EXPECT_FALSE(engine->HandleInputEvent(
  742. CreateRightClickWebMouseEventAtPosition(kOffScreenPosition)));
  743. // Right click on the link should set it again.
  744. constexpr gfx::PointF kLinkPosition(170, 595);
  745. EXPECT_CALL(client, SetLinkUnderCursor("https://www.google.com/"));
  746. EXPECT_FALSE(engine->HandleInputEvent(
  747. CreateRightClickWebMouseEventAtPosition(kLinkPosition)));
  748. }
  749. TEST_F(PDFiumEngineTabbingTest, TabbingSupportedAnnots) {
  750. /*
  751. * Document structure
  752. * Document
  753. * ++ Page 1
  754. * ++++ Widget annotation
  755. * ++++ Widget annotation
  756. * ++++ Highlight annotation
  757. * ++++ Link annotation
  758. */
  759. // Enable feature flag.
  760. base::test::ScopedFeatureList scoped_feature_list;
  761. scoped_feature_list.InitAndEnableFeature(
  762. chrome_pdf::features::kTabAcrossPDFAnnotations);
  763. TestClient client;
  764. std::unique_ptr<PDFiumEngine> engine =
  765. InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
  766. ASSERT_TRUE(engine);
  767. ASSERT_EQ(1, engine->GetNumberOfPages());
  768. ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
  769. GetFocusedElementType(engine.get()));
  770. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  771. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  772. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  773. GetFocusedElementType(engine.get()));
  774. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  775. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  776. GetFocusedElementType(engine.get()));
  777. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  778. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  779. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  780. GetFocusedElementType(engine.get()));
  781. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  782. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  783. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  784. GetFocusedElementType(engine.get()));
  785. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  786. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  787. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  788. GetFocusedElementType(engine.get()));
  789. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  790. ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
  791. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  792. GetFocusedElementType(engine.get()));
  793. }
  794. TEST_F(PDFiumEngineTabbingTest, TabbingForward) {
  795. /*
  796. * Document structure
  797. * Document
  798. * ++ Page 1
  799. * ++++ Annotation
  800. * ++++ Annotation
  801. * ++ Page 2
  802. * ++++ Annotation
  803. */
  804. NiceMock<MockTestClient> client;
  805. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  806. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  807. ASSERT_TRUE(engine);
  808. ASSERT_EQ(2, engine->GetNumberOfPages());
  809. static constexpr bool kExpectedFocusState[] = {true, false};
  810. {
  811. InSequence sequence;
  812. for (auto focused : kExpectedFocusState)
  813. EXPECT_CALL(client, DocumentFocusChanged(focused));
  814. }
  815. ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
  816. GetFocusedElementType(engine.get()));
  817. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  818. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  819. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  820. GetFocusedElementType(engine.get()));
  821. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  822. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  823. GetFocusedElementType(engine.get()));
  824. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  825. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  826. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  827. GetFocusedElementType(engine.get()));
  828. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  829. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  830. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  831. GetFocusedElementType(engine.get()));
  832. EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
  833. ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
  834. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  835. GetFocusedElementType(engine.get()));
  836. }
  837. TEST_F(PDFiumEngineTabbingTest, TabbingBackward) {
  838. /*
  839. * Document structure
  840. * Document
  841. * ++ Page 1
  842. * ++++ Annotation
  843. * ++++ Annotation
  844. * ++ Page 2
  845. * ++++ Annotation
  846. */
  847. NiceMock<MockTestClient> client;
  848. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  849. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  850. ASSERT_TRUE(engine);
  851. ASSERT_EQ(2, engine->GetNumberOfPages());
  852. static constexpr bool kExpectedFocusState[] = {true, false};
  853. {
  854. InSequence sequence;
  855. for (auto focused : kExpectedFocusState)
  856. EXPECT_CALL(client, DocumentFocusChanged(focused));
  857. }
  858. ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
  859. GetFocusedElementType(engine.get()));
  860. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  861. ASSERT_TRUE(
  862. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  863. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  864. GetFocusedElementType(engine.get()));
  865. EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
  866. ASSERT_TRUE(
  867. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  868. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  869. GetFocusedElementType(engine.get()));
  870. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  871. ASSERT_TRUE(
  872. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  873. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  874. GetFocusedElementType(engine.get()));
  875. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  876. ASSERT_TRUE(
  877. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  878. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  879. GetFocusedElementType(engine.get()));
  880. ASSERT_FALSE(
  881. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  882. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  883. GetFocusedElementType(engine.get()));
  884. }
  885. TEST_F(PDFiumEngineTabbingTest, TabbingWithModifiers) {
  886. /*
  887. * Document structure
  888. * Document
  889. * ++ Page 1
  890. * ++++ Annotation
  891. * ++++ Annotation
  892. * ++ Page 2
  893. * ++++ Annotation
  894. */
  895. TestClient client;
  896. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  897. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  898. ASSERT_TRUE(engine);
  899. ASSERT_EQ(2, engine->GetNumberOfPages());
  900. ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
  901. GetFocusedElementType(engine.get()));
  902. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  903. // Tabbing with ctrl modifier.
  904. ASSERT_FALSE(HandleTabEvent(engine.get(),
  905. blink::WebInputEvent::Modifiers::kControlKey));
  906. // Tabbing with alt modifier.
  907. ASSERT_FALSE(
  908. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
  909. // Tab to bring document into focus.
  910. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  911. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  912. GetFocusedElementType(engine.get()));
  913. // Tabbing with ctrl modifier.
  914. ASSERT_FALSE(HandleTabEvent(engine.get(),
  915. blink::WebInputEvent::Modifiers::kControlKey));
  916. // Tabbing with alt modifier.
  917. ASSERT_FALSE(
  918. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
  919. // Tab to bring first page into focus.
  920. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  921. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  922. GetFocusedElementType(engine.get()));
  923. // Tabbing with ctrl modifier.
  924. ASSERT_FALSE(HandleTabEvent(engine.get(),
  925. blink::WebInputEvent::Modifiers::kControlKey));
  926. // Tabbing with alt modifier.
  927. ASSERT_FALSE(
  928. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kAltKey));
  929. }
  930. TEST_F(PDFiumEngineTabbingTest, NoFocusableElementTabbing) {
  931. /*
  932. * Document structure
  933. * Document
  934. * ++ Page 1
  935. * ++ Page 2
  936. */
  937. NiceMock<MockTestClient> client;
  938. std::unique_ptr<PDFiumEngine> engine =
  939. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  940. ASSERT_TRUE(engine);
  941. ASSERT_EQ(2, engine->GetNumberOfPages());
  942. static constexpr bool kExpectedFocusState[] = {true, false, true, false};
  943. {
  944. InSequence sequence;
  945. for (auto focused : kExpectedFocusState)
  946. EXPECT_CALL(client, DocumentFocusChanged(focused));
  947. }
  948. ASSERT_EQ(PDFiumEngine::FocusElementType::kNone,
  949. GetFocusedElementType(engine.get()));
  950. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  951. // Tabbing forward.
  952. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  953. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  954. GetFocusedElementType(engine.get()));
  955. ASSERT_FALSE(HandleTabEvent(engine.get(), 0));
  956. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  957. GetFocusedElementType(engine.get()));
  958. // Tabbing backward.
  959. ASSERT_TRUE(
  960. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  961. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  962. GetFocusedElementType(engine.get()));
  963. ASSERT_FALSE(
  964. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  965. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  966. GetFocusedElementType(engine.get()));
  967. }
  968. TEST_F(PDFiumEngineTabbingTest, RestoringDocumentFocus) {
  969. /*
  970. * Document structure
  971. * Document
  972. * ++ Page 1
  973. * ++++ Annotation
  974. * ++++ Annotation
  975. * ++ Page 2
  976. * ++++ Annotation
  977. */
  978. NiceMock<MockTestClient> client;
  979. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  980. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  981. ASSERT_TRUE(engine);
  982. ASSERT_EQ(2, engine->GetNumberOfPages());
  983. static constexpr bool kExpectedFocusState[] = {true, false, true};
  984. {
  985. InSequence sequence;
  986. for (auto focused : kExpectedFocusState)
  987. EXPECT_CALL(client, DocumentFocusChanged(focused));
  988. }
  989. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  990. GetFocusedElementType(engine.get()));
  991. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  992. // Tabbing to bring the document into focus.
  993. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  994. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  995. GetFocusedElementType(engine.get()));
  996. engine->UpdateFocus(/*has_focus=*/false);
  997. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  998. GetFocusedElementType(engine.get()));
  999. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  1000. GetLastFocusedElementType(engine.get()));
  1001. EXPECT_EQ(-1, GetLastFocusedAnnotationIndex(engine.get()));
  1002. engine->UpdateFocus(/*has_focus=*/true);
  1003. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  1004. GetFocusedElementType(engine.get()));
  1005. }
  1006. TEST_F(PDFiumEngineTabbingTest, RestoringAnnotFocus) {
  1007. /*
  1008. * Document structure
  1009. * Document
  1010. * ++ Page 1
  1011. * ++++ Annotation
  1012. * ++++ Annotation
  1013. * ++ Page 2
  1014. * ++++ Annotation
  1015. */
  1016. NiceMock<MockTestClient> client;
  1017. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  1018. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  1019. ASSERT_TRUE(engine);
  1020. ASSERT_EQ(2, engine->GetNumberOfPages());
  1021. static constexpr bool kExpectedFocusState[] = {true, false};
  1022. {
  1023. InSequence sequence;
  1024. for (auto focused : kExpectedFocusState)
  1025. EXPECT_CALL(client, DocumentFocusChanged(focused));
  1026. }
  1027. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  1028. GetFocusedElementType(engine.get()));
  1029. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  1030. // Tabbing to bring last annotation of page 0 into focus.
  1031. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1032. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1033. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1034. engine->UpdateFocus(/*has_focus=*/false);
  1035. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1036. GetLastFocusedElementType(engine.get()));
  1037. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1038. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1039. GetFocusedElementType(engine.get()));
  1040. EXPECT_EQ(0, GetLastFocusedAnnotationIndex(engine.get()));
  1041. engine->UpdateFocus(/*has_focus=*/true);
  1042. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1043. GetFocusedElementType(engine.get()));
  1044. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1045. // Tabbing now should bring the second page's annotation to focus.
  1046. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1047. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1048. GetFocusedElementType(engine.get()));
  1049. EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
  1050. }
  1051. TEST_F(PDFiumEngineTabbingTest, VerifyFormFieldStatesOnTabbing) {
  1052. /*
  1053. * Document structure
  1054. * Document
  1055. * ++ Page 1
  1056. * ++++ Annotation (Text Field)
  1057. * ++++ Annotation (Radio Button)
  1058. */
  1059. TestClient client;
  1060. std::unique_ptr<PDFiumEngine> engine =
  1061. InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
  1062. ASSERT_TRUE(engine);
  1063. ASSERT_EQ(1, engine->GetNumberOfPages());
  1064. // Bring focus to the document.
  1065. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1066. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  1067. GetFocusedElementType(engine.get()));
  1068. EXPECT_EQ(PDFEngine::FocusFieldType::kNoFocus,
  1069. FormFocusFieldType(engine.get()));
  1070. EXPECT_FALSE(engine->CanEditText());
  1071. // Bring focus to the text field on the page.
  1072. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1073. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1074. GetFocusedElementType(engine.get()));
  1075. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1076. EXPECT_EQ(PDFEngine::FocusFieldType::kText, FormFocusFieldType(engine.get()));
  1077. EXPECT_TRUE(engine->CanEditText());
  1078. // Bring focus to the button on the page.
  1079. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1080. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1081. GetFocusedElementType(engine.get()));
  1082. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1083. EXPECT_EQ(PDFEngine::FocusFieldType::kNonText,
  1084. FormFocusFieldType(engine.get()));
  1085. EXPECT_FALSE(engine->CanEditText());
  1086. }
  1087. TEST_F(PDFiumEngineTabbingTest, ClearSelectionOnFocusInFormTextArea) {
  1088. TestClient client;
  1089. std::unique_ptr<PDFiumEngine> engine =
  1090. InitializeEngine(&client, FILE_PATH_LITERAL("form_text_fields.pdf"));
  1091. ASSERT_TRUE(engine);
  1092. ASSERT_EQ(1, engine->GetNumberOfPages());
  1093. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  1094. GetFocusedElementType(engine.get()));
  1095. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  1096. // Select all text.
  1097. engine->SelectAll();
  1098. EXPECT_EQ(1u, GetSelectionSize(engine.get()));
  1099. // Tab to bring focus to a form text area annotation.
  1100. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1101. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1102. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1103. GetFocusedElementType(engine.get()));
  1104. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1105. EXPECT_EQ(0u, GetSelectionSize(engine.get()));
  1106. }
  1107. TEST_F(PDFiumEngineTabbingTest, RetainSelectionOnFocusNotInFormTextArea) {
  1108. TestClient client;
  1109. std::unique_ptr<PDFiumEngine> engine =
  1110. InitializeEngine(&client, FILE_PATH_LITERAL("annots.pdf"));
  1111. ASSERT_TRUE(engine);
  1112. ASSERT_EQ(1, engine->GetNumberOfPages());
  1113. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  1114. GetFocusedElementType(engine.get()));
  1115. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  1116. // Select all text.
  1117. engine->SelectAll();
  1118. EXPECT_EQ(1u, GetSelectionSize(engine.get()));
  1119. // Tab to bring focus to a non form text area annotation (Button).
  1120. ASSERT_TRUE(
  1121. HandleTabEvent(engine.get(), blink::WebInputEvent::Modifiers::kShiftKey));
  1122. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1123. GetFocusedElementType(engine.get()));
  1124. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1125. EXPECT_EQ(1u, GetSelectionSize(engine.get()));
  1126. }
  1127. class ScrollingTestClient : public TestClient {
  1128. public:
  1129. ScrollingTestClient() = default;
  1130. ~ScrollingTestClient() override = default;
  1131. ScrollingTestClient(const ScrollingTestClient&) = delete;
  1132. ScrollingTestClient& operator=(const ScrollingTestClient&) = delete;
  1133. // Mock PDFEngine::Client methods.
  1134. MOCK_METHOD(void, ScrollToX, (int), (override));
  1135. MOCK_METHOD(void, ScrollToY, (int), (override));
  1136. };
  1137. TEST_F(PDFiumEngineTabbingTest, MaintainViewportWhenFocusIsUpdated) {
  1138. StrictMock<ScrollingTestClient> client;
  1139. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  1140. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  1141. ASSERT_TRUE(engine);
  1142. ASSERT_EQ(2, engine->GetNumberOfPages());
  1143. engine->PluginSizeUpdated(gfx::Size(60, 40));
  1144. {
  1145. InSequence sequence;
  1146. static constexpr gfx::Point kScrollValue = {510, 478};
  1147. EXPECT_CALL(client, ScrollToY(kScrollValue.y()))
  1148. .WillOnce(Invoke(
  1149. [&engine]() { engine->ScrolledToYPosition(kScrollValue.y()); }));
  1150. EXPECT_CALL(client, ScrollToX(kScrollValue.x()))
  1151. .WillOnce(Invoke(
  1152. [&engine]() { engine->ScrolledToXPosition(kScrollValue.x()); }));
  1153. }
  1154. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  1155. GetFocusedElementType(engine.get()));
  1156. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  1157. // Tabbing to bring the document into focus.
  1158. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1159. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  1160. GetFocusedElementType(engine.get()));
  1161. // Tab to an annotation.
  1162. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1163. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1164. GetFocusedElementType(engine.get()));
  1165. // Scroll focused annotation out of viewport.
  1166. static constexpr gfx::Point kScrollPosition = {242, 746};
  1167. engine->ScrolledToXPosition(kScrollPosition.x());
  1168. engine->ScrolledToYPosition(kScrollPosition.y());
  1169. engine->UpdateFocus(/*has_focus=*/false);
  1170. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1171. GetLastFocusedElementType(engine.get()));
  1172. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1173. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1174. GetFocusedElementType(engine.get()));
  1175. EXPECT_EQ(1, GetLastFocusedAnnotationIndex(engine.get()));
  1176. // Restore focus, we shouldn't have any calls to scroll viewport.
  1177. engine->UpdateFocus(/*has_focus=*/true);
  1178. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1179. GetFocusedElementType(engine.get()));
  1180. EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
  1181. }
  1182. TEST_F(PDFiumEngineTabbingTest, ScrollFocusedAnnotationIntoView) {
  1183. StrictMock<ScrollingTestClient> client;
  1184. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  1185. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  1186. ASSERT_TRUE(engine);
  1187. ASSERT_EQ(2, engine->GetNumberOfPages());
  1188. engine->PluginSizeUpdated(gfx::Size(60, 40));
  1189. {
  1190. InSequence sequence;
  1191. static constexpr gfx::Point kScrollValues[] = {{510, 478}, {510, 478}};
  1192. for (const auto& scroll_value : kScrollValues) {
  1193. EXPECT_CALL(client, ScrollToY(scroll_value.y()))
  1194. .WillOnce(Invoke([&engine, &scroll_value]() {
  1195. engine->ScrolledToYPosition(scroll_value.y());
  1196. }));
  1197. EXPECT_CALL(client, ScrollToX(scroll_value.x()))
  1198. .WillOnce(Invoke([&engine, &scroll_value]() {
  1199. engine->ScrolledToXPosition(scroll_value.x());
  1200. }));
  1201. }
  1202. }
  1203. EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
  1204. GetFocusedElementType(engine.get()));
  1205. EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
  1206. // Tabbing to bring the document into focus.
  1207. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1208. EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
  1209. GetFocusedElementType(engine.get()));
  1210. // Tab to an annotation.
  1211. ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
  1212. EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
  1213. GetFocusedElementType(engine.get()));
  1214. // Scroll focused annotation out of viewport.
  1215. static constexpr gfx::Point kScrollPosition = {242, 746};
  1216. engine->ScrolledToXPosition(kScrollPosition.x());
  1217. engine->ScrolledToYPosition(kScrollPosition.y());
  1218. // Scroll the focused annotation into view.
  1219. ScrollFocusedAnnotationIntoView(engine.get());
  1220. }
  1221. class ReadOnlyTestClient : public TestClient {
  1222. public:
  1223. ReadOnlyTestClient() = default;
  1224. ~ReadOnlyTestClient() override = default;
  1225. ReadOnlyTestClient(const ReadOnlyTestClient&) = delete;
  1226. ReadOnlyTestClient& operator=(const ReadOnlyTestClient&) = delete;
  1227. // Mock PDFEngine::Client methods.
  1228. MOCK_METHOD(void,
  1229. FormFieldFocusChange,
  1230. (PDFEngine::FocusFieldType),
  1231. (override));
  1232. MOCK_METHOD(void, SetSelectedText, (const std::string&), (override));
  1233. };
  1234. using PDFiumEngineReadOnlyTest = PDFiumTestBase;
  1235. TEST_F(PDFiumEngineReadOnlyTest, KillFormFocus) {
  1236. NiceMock<ReadOnlyTestClient> client;
  1237. std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
  1238. &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
  1239. ASSERT_TRUE(engine);
  1240. // Setting read-only mode should kill form focus.
  1241. EXPECT_FALSE(engine->IsReadOnly());
  1242. EXPECT_CALL(client,
  1243. FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
  1244. engine->SetReadOnly(true);
  1245. // Attempting to focus during read-only mode should once more trigger a
  1246. // killing of form focus.
  1247. EXPECT_TRUE(engine->IsReadOnly());
  1248. EXPECT_CALL(client,
  1249. FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
  1250. engine->UpdateFocus(true);
  1251. }
  1252. TEST_F(PDFiumEngineReadOnlyTest, UnselectText) {
  1253. NiceMock<ReadOnlyTestClient> client;
  1254. std::unique_ptr<PDFiumEngine> engine =
  1255. InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf"));
  1256. ASSERT_TRUE(engine);
  1257. // Update the plugin size so that all the text is visible by
  1258. // `SelectionChangeInvalidator`.
  1259. engine->PluginSizeUpdated({500, 500});
  1260. // Select text before going into read-only mode.
  1261. EXPECT_FALSE(engine->IsReadOnly());
  1262. EXPECT_CALL(client, SetSelectedText(Not(IsEmpty())));
  1263. engine->SelectAll();
  1264. // Setting read-only mode should unselect the text.
  1265. EXPECT_CALL(client, SetSelectedText(IsEmpty()));
  1266. engine->SetReadOnly(true);
  1267. }
  1268. } // namespace chrome_pdf