| // Copyright 2021 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/pdf_view_plugin_base.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/contains.h" |
| #include "base/containers/flat_set.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/test/icu_test_util.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "pdf/accessibility_structs.h" |
| #include "pdf/buildflags.h" |
| #include "pdf/content_restriction.h" |
| #include "pdf/document_attachment_info.h" |
| #include "pdf/document_metadata.h" |
| #include "pdf/pdf_engine.h" |
| #include "pdf/pdfium/pdfium_engine.h" |
| #include "pdf/ppapi_migration/callback.h" |
| #include "pdf/ppapi_migration/graphics.h" |
| #include "pdf/ppapi_migration/url_loader.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace chrome_pdf { |
| |
| namespace { |
| |
| // Keep it in-sync with the `kFinalFallbackName` returned by |
| // net::GetSuggestedFilename(). |
| constexpr char kDefaultDownloadFileName[] = "download"; |
| |
| // Dummy data to save. |
| constexpr uint8_t kSaveData[] = {'1', '2', '3'}; |
| |
| // Page number. |
| constexpr uint32_t kPageNumber = 13u; |
| |
| class TestPDFiumEngine : public PDFiumEngine { |
| public: |
| explicit TestPDFiumEngine(PDFEngine::Client* client) |
| : PDFiumEngine(client, PDFiumFormFiller::ScriptOption::kNoJavaScript) {} |
| |
| TestPDFiumEngine(const TestPDFiumEngine&) = delete; |
| |
| TestPDFiumEngine& operator=(const TestPDFiumEngine&) = delete; |
| |
| ~TestPDFiumEngine() override = default; |
| |
| bool HasPermission(DocumentPermission permission) const override { |
| return base::Contains(permissions_, permission); |
| } |
| |
| const std::vector<DocumentAttachmentInfo>& GetDocumentAttachmentInfoList() |
| const override { |
| return doc_attachment_info_list_; |
| } |
| |
| const DocumentMetadata& GetDocumentMetadata() const override { |
| return metadata_; |
| } |
| |
| int GetNumberOfPages() const override { |
| return static_cast<int>(kPageNumber); |
| } |
| |
| base::Value GetBookmarks() override { |
| // Return an empty bookmark list. |
| return base::Value(base::Value::Type::LIST); |
| } |
| |
| uint32_t GetLoadedByteSize() override { return sizeof(kSaveData); } |
| |
| bool ReadLoadedBytes(uint32_t length, void* buffer) override { |
| DCHECK_LE(length, GetLoadedByteSize()); |
| memcpy(buffer, kSaveData, length); |
| return true; |
| } |
| |
| std::vector<uint8_t> GetSaveData() override { |
| return std::vector<uint8_t>(std::begin(kSaveData), std::end(kSaveData)); |
| } |
| |
| void SetPermissions(const std::vector<DocumentPermission>& permissions) { |
| permissions_.clear(); |
| |
| for (auto& permission : permissions) |
| permissions_.insert(permission); |
| } |
| |
| protected: |
| std::vector<DocumentAttachmentInfo>& doc_attachment_info_list() { |
| return doc_attachment_info_list_; |
| } |
| |
| DocumentMetadata& metadata() { return metadata_; } |
| |
| private: |
| std::vector<DocumentAttachmentInfo> doc_attachment_info_list_; |
| |
| DocumentMetadata metadata_; |
| |
| base::flat_set<DocumentPermission> permissions_; |
| }; |
| |
| class TestPDFiumEngineWithDocInfo : public TestPDFiumEngine { |
| public: |
| explicit TestPDFiumEngineWithDocInfo(PDFEngine::Client* client) |
| : TestPDFiumEngine(client) {} |
| |
| base::Value GetBookmarks() override { |
| // Create `bookmark1` which navigates to an in-doc position. This bookmark |
| // will be in the top-level bookmark list. |
| base::Value bookmark1(base::Value::Type::DICTIONARY); |
| bookmark1.SetStringKey("title", "Bookmark 1"); |
| bookmark1.SetIntKey("page", 2); |
| bookmark1.SetIntKey("x", 10); |
| bookmark1.SetIntKey("y", 20); |
| bookmark1.SetDoubleKey("zoom", 2.0); |
| |
| // Create `bookmark2` which navigates to a web page. This bookmark will be a |
| // child of `bookmark1`. |
| base::Value bookmark2(base::Value::Type::DICTIONARY); |
| bookmark2.SetStringKey("title", "Bookmark 2"); |
| bookmark2.SetStringKey("uri", "test.com"); |
| |
| base::Value children_of_bookmark1(base::Value::Type::LIST); |
| children_of_bookmark1.Append(std::move(bookmark2)); |
| bookmark1.SetKey("children", std::move(children_of_bookmark1)); |
| |
| // Create the top-level bookmark list. |
| base::Value bookmarks(base::Value::Type::LIST); |
| bookmarks.Append(std::move(bookmark1)); |
| return bookmarks; |
| } |
| |
| absl::optional<gfx::Size> GetUniformPageSizePoints() override { |
| return gfx::Size(1000, 1200); |
| } |
| |
| // Initialize attachments, metadata for testing. |
| void InitializeDocument() { |
| InitializeDocumentAttachments(); |
| InitializeDocumentMetadata(); |
| } |
| |
| private: |
| void InitializeDocumentAttachments() { |
| doc_attachment_info_list().resize(3); |
| |
| // A regular attachment. |
| doc_attachment_info_list()[0].name = u"attachment1.txt"; |
| doc_attachment_info_list()[0].creation_date = u"D:20170712214438-07'00'"; |
| doc_attachment_info_list()[0].modified_date = u"D:20160115091400"; |
| doc_attachment_info_list()[0].is_readable = true; |
| doc_attachment_info_list()[0].size_bytes = 13u; |
| |
| // An unreadable attachment. |
| doc_attachment_info_list()[1].name = u"attachment2.pdf"; |
| doc_attachment_info_list()[1].is_readable = false; |
| |
| // A readable attachment that exceeds download size limit. |
| doc_attachment_info_list()[2].name = u"attachment3.mov"; |
| doc_attachment_info_list()[2].is_readable = true; |
| doc_attachment_info_list()[2].size_bytes = |
| PdfViewPluginBase::kMaximumSavedFileSize + 1; |
| } |
| |
| void InitializeDocumentMetadata() { |
| metadata().version = PdfVersion::k1_7; |
| metadata().size_bytes = 13u; |
| metadata().page_count = 13u; |
| metadata().linearized = true; |
| metadata().has_attachments = true; |
| metadata().tagged = true; |
| metadata().form_type = FormType::kAcroForm; |
| metadata().title = "Title"; |
| metadata().author = "Author"; |
| metadata().subject = "Subject"; |
| metadata().keywords = "Keywords"; |
| metadata().creator = "Creator"; |
| metadata().producer = "Producer"; |
| ASSERT_TRUE(base::Time::FromUTCString("2021-05-04 11:12:13", |
| &metadata().creation_date)); |
| ASSERT_TRUE( |
| base::Time::FromUTCString("2021-06-04 15:16:17", &metadata().mod_date)); |
| } |
| }; |
| |
| // This test approach relies on PdfViewPluginBase continuing to exist. |
| // PdfViewPluginBase and PdfViewWebPlugin are going to merge once |
| // OutOfProcessInstance is deprecated. |
| class FakePdfViewPluginBase : public PdfViewPluginBase { |
| public: |
| // Public for testing. |
| using PdfViewPluginBase::accessibility_state; |
| using PdfViewPluginBase::document_load_state; |
| using PdfViewPluginBase::edit_mode; |
| using PdfViewPluginBase::engine; |
| using PdfViewPluginBase::full_frame; |
| using PdfViewPluginBase::HandleMessage; |
| using PdfViewPluginBase::InitializeEngine; |
| using PdfViewPluginBase::set_document_load_state; |
| using PdfViewPluginBase::set_full_frame; |
| |
| MOCK_METHOD(bool, Confirm, (const std::string&), (override)); |
| |
| MOCK_METHOD(std::string, |
| Prompt, |
| (const std::string&, const std::string&), |
| (override)); |
| |
| MOCK_METHOD(std::vector<PDFEngine::Client::SearchStringResult>, |
| SearchString, |
| (const char16_t*, const char16_t*, bool), |
| (override)); |
| |
| MOCK_METHOD(bool, IsPrintPreview, (), (override)); |
| |
| MOCK_METHOD(void, SetSelectedText, (const std::string&), (override)); |
| |
| MOCK_METHOD(void, SetLinkUnderCursor, (const std::string&), (override)); |
| |
| MOCK_METHOD(bool, IsValidLink, (const std::string&), (override)); |
| |
| MOCK_METHOD(std::unique_ptr<Graphics>, |
| CreatePaintGraphics, |
| (const gfx::Size&), |
| (override)); |
| |
| MOCK_METHOD(bool, BindPaintGraphics, (Graphics&), (override)); |
| |
| MOCK_METHOD(void, |
| ScheduleTaskOnMainThread, |
| (const base::Location&, ResultCallback, int32_t, base::TimeDelta), |
| (override)); |
| |
| MOCK_METHOD(base::WeakPtr<PdfViewPluginBase>, GetWeakPtr, (), (override)); |
| |
| MOCK_METHOD(std::unique_ptr<UrlLoader>, |
| CreateUrlLoaderInternal, |
| (), |
| (override)); |
| |
| MOCK_METHOD(void, DidOpen, (std::unique_ptr<UrlLoader>, int32_t), (override)); |
| |
| void SendMessage(base::Value message) override { |
| sent_messages_.push_back(std::move(message)); |
| } |
| |
| MOCK_METHOD(void, SaveAs, (), (override)); |
| |
| MOCK_METHOD(void, InitImageData, (const gfx::Size&), (override)); |
| |
| MOCK_METHOD(void, SetFormFieldInFocus, (bool in_focus), (override)); |
| |
| MOCK_METHOD(void, |
| SetAccessibilityDocInfo, |
| (const AccessibilityDocInfo&), |
| (override)); |
| |
| MOCK_METHOD(void, |
| SetAccessibilityPageInfo, |
| (AccessibilityPageInfo, |
| std::vector<AccessibilityTextRunInfo>, |
| std::vector<AccessibilityCharInfo>, |
| AccessibilityPageObjects), |
| (override)); |
| |
| MOCK_METHOD(void, |
| SetAccessibilityViewportInfo, |
| (const AccessibilityViewportInfo&), |
| (override)); |
| |
| MOCK_METHOD(void, SetContentRestrictions, (int), (override)); |
| |
| MOCK_METHOD(void, SetPluginCanSave, (bool), (override)); |
| |
| MOCK_METHOD(void, PluginDidStartLoading, (), (override)); |
| |
| MOCK_METHOD(void, PluginDidStopLoading, (), (override)); |
| |
| MOCK_METHOD(void, InvokePrintDialog, (), (override)); |
| |
| MOCK_METHOD(void, |
| NotifySelectionChanged, |
| (const gfx::PointF&, int, const gfx::PointF&, int), |
| (override)); |
| |
| MOCK_METHOD(void, NotifyUnsupportedFeature, (), (override)); |
| |
| MOCK_METHOD(void, UserMetricsRecordAction, (const std::string&), (override)); |
| |
| const std::vector<base::Value>& sent_messages() const { |
| return sent_messages_; |
| } |
| |
| private: |
| std::vector<base::Value> sent_messages_; |
| }; |
| |
| base::Value CreateExpectedFormTextFieldFocusChangeResponse() { |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "formFocusChange"); |
| message.SetBoolKey("focused", false); |
| return message; |
| } |
| |
| base::Value CreateExpectedAttachmentsResponse() { |
| base::Value attachments(base::Value::Type::LIST); |
| { |
| base::Value attachment(base::Value::Type::DICTIONARY); |
| attachment.SetStringKey("name", "attachment1.txt"); |
| attachment.SetIntKey("size", 13); |
| attachment.SetBoolKey("readable", true); |
| attachments.Append(std::move(attachment)); |
| } |
| { |
| base::Value attachment(base::Value::Type::DICTIONARY); |
| attachment.SetStringKey("name", "attachment2.pdf"); |
| attachment.SetIntKey("size", 0); |
| attachment.SetBoolKey("readable", false); |
| attachments.Append(std::move(attachment)); |
| } |
| { |
| base::Value attachment(base::Value::Type::DICTIONARY); |
| attachment.SetStringKey("name", "attachment3.mov"); |
| attachment.SetIntKey("size", -1); |
| attachment.SetBoolKey("readable", true); |
| attachments.Append(std::move(attachment)); |
| } |
| |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "attachments"); |
| message.SetKey("attachmentsData", std::move(attachments)); |
| return message; |
| } |
| |
| base::Value CreateExpectedBookmarksResponse(base::Value bookmarks) { |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "bookmarks"); |
| message.SetKey("bookmarksData", std::move(bookmarks)); |
| return message; |
| } |
| |
| base::Value CreateExpectedMetadataResponse() { |
| base::Value metadata(base::Value::Type::DICTIONARY); |
| metadata.SetStringKey("version", "1.7"); |
| metadata.SetStringKey("fileSize", "13 B"); |
| metadata.SetBoolKey("linearized", true); |
| |
| metadata.SetStringKey("title", "Title"); |
| metadata.SetStringKey("author", "Author"); |
| metadata.SetStringKey("subject", "Subject"); |
| metadata.SetStringKey("keywords", "Keywords"); |
| metadata.SetStringKey("creator", "Creator"); |
| metadata.SetStringKey("producer", "Producer"); |
| metadata.SetStringKey("creationDate", "5/4/21, 4:12:13 AM"); |
| metadata.SetStringKey("modDate", "6/4/21, 8:16:17 AM"); |
| metadata.SetStringKey("pageSize", "13.89 × 16.67 in (portrait)"); |
| metadata.SetBoolKey("canSerializeDocument", true); |
| |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "metadata"); |
| message.SetKey("metadataData", std::move(metadata)); |
| return message; |
| } |
| |
| base::Value CreateExpectedNoMetadataResponse() { |
| base::Value metadata(base::Value::Type::DICTIONARY); |
| metadata.SetStringKey("fileSize", "0 B"); |
| metadata.SetBoolKey("linearized", false); |
| metadata.SetStringKey("pageSize", "Varies"); |
| metadata.SetBoolKey("canSerializeDocument", true); |
| |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "metadata"); |
| message.SetKey("metadataData", std::move(metadata)); |
| return message; |
| } |
| |
| base::Value CreateExpectedLoadingProgressResponse() { |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "loadProgress"); |
| message.SetDoubleKey("progress", 100); |
| return message; |
| } |
| |
| base::Value CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType type, |
| const std::string& token) { |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "save"); |
| message.SetIntKey("saveRequestType", static_cast<int>(type)); |
| message.SetStringKey("token", token); |
| return message; |
| } |
| |
| base::Value CreateExpectedSaveToBufferResponse(const std::string& token) { |
| base::Value expected_response(base::Value::Type::DICTIONARY); |
| expected_response.SetStringKey("type", "saveData"); |
| expected_response.SetStringKey("token", token); |
| expected_response.SetStringKey("fileName", kDefaultDownloadFileName); |
| expected_response.SetKey("dataToSave", |
| base::Value(base::make_span(kSaveData))); |
| return expected_response; |
| } |
| |
| base::Value CreateExpectedSaveToFileResponse(const std::string& token) { |
| base::Value expected_response(base::Value::Type::DICTIONARY); |
| expected_response.SetStringKey("type", "consumeSaveToken"); |
| expected_response.SetStringKey("token", token); |
| return expected_response; |
| } |
| |
| } // namespace |
| |
| class PdfViewPluginBaseTest : public testing::Test { |
| protected: |
| FakePdfViewPluginBase fake_plugin_; |
| }; |
| |
| class PdfViewPluginBaseWithEngineTest : public PdfViewPluginBaseTest { |
| public: |
| void SetUp() override { |
| std::unique_ptr<TestPDFiumEngine> engine = |
| std::make_unique<TestPDFiumEngine>(&fake_plugin_); |
| fake_plugin_.InitializeEngine(std::move(engine)); |
| } |
| }; |
| |
| class PdfViewPluginBaseWithScopedLocaleTest |
| : public PdfViewPluginBaseWithEngineTest { |
| protected: |
| base::test::ScopedRestoreICUDefaultLocale scoped_locale_{"en_US"}; |
| base::test::ScopedRestoreDefaultTimezone la_time_{"America/Los_Angeles"}; |
| }; |
| |
| class PdfViewPluginBaseWithDocInfoTest |
| : public PdfViewPluginBaseWithScopedLocaleTest { |
| public: |
| void SetUp() override { |
| std::unique_ptr<TestPDFiumEngineWithDocInfo> engine = |
| std::make_unique<TestPDFiumEngineWithDocInfo>(&fake_plugin_); |
| fake_plugin_.InitializeEngine(std::move(engine)); |
| |
| // Initialize some arbitrary document information for the engine. |
| static_cast<TestPDFiumEngineWithDocInfo*>(fake_plugin_.engine()) |
| ->InitializeDocument(); |
| } |
| }; |
| |
| using PdfViewPluginBaseWithoutDocInfoTest = |
| PdfViewPluginBaseWithScopedLocaleTest; |
| |
| TEST_F(PdfViewPluginBaseTest, CreateUrlLoaderInFullFrame) { |
| fake_plugin_.set_full_frame(true); |
| ASSERT_TRUE(fake_plugin_.full_frame()); |
| |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| EXPECT_CALL(fake_plugin_, SetContentRestrictions(kContentRestrictionSave | |
| kContentRestrictionPrint)); |
| EXPECT_CALL(fake_plugin_, PluginDidStartLoading()); |
| EXPECT_CALL(fake_plugin_, CreateUrlLoaderInternal()); |
| fake_plugin_.CreateUrlLoader(); |
| EXPECT_TRUE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, CreateUrlLoaderWithoutFullFrame) { |
| ASSERT_FALSE(fake_plugin_.full_frame()); |
| |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| EXPECT_CALL(fake_plugin_, SetContentRestrictions(kContentRestrictionSave | |
| kContentRestrictionPrint)) |
| .Times(0); |
| EXPECT_CALL(fake_plugin_, PluginDidStartLoading()).Times(0); |
| EXPECT_CALL(fake_plugin_, CreateUrlLoaderInternal()); |
| fake_plugin_.CreateUrlLoader(); |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithDocInfoTest, |
| DocumentLoadCompleteInFullFramePdfViewerWithAccessibilityEnabled) { |
| // Notify the render frame about document loading. |
| fake_plugin_.set_full_frame(true); |
| ASSERT_TRUE(fake_plugin_.full_frame()); |
| fake_plugin_.CreateUrlLoader(); |
| |
| ASSERT_FALSE(fake_plugin_.IsPrintPreview()); |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| |
| // Change the accessibility state to pending so that accessibility can be |
| // loaded later. |
| fake_plugin_.EnableAccessibility(); |
| EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kPending, |
| fake_plugin_.accessibility_state()); |
| |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess")); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, PluginDidStopLoading()); |
| EXPECT_CALL(fake_plugin_, |
| SetContentRestrictions(fake_plugin_.GetContentRestrictions())); |
| EXPECT_CALL(fake_plugin_, |
| SetAccessibilityDocInfo(fake_plugin_.GetAccessibilityDocInfo())); |
| |
| fake_plugin_.DocumentLoadComplete(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete, |
| fake_plugin_.document_load_state()); |
| EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kLoaded, |
| fake_plugin_.accessibility_state()); |
| |
| // Check all the sent messages. |
| ASSERT_EQ(5u, fake_plugin_.sent_messages().size()); |
| EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(), |
| fake_plugin_.sent_messages()[0]); |
| EXPECT_EQ(CreateExpectedAttachmentsResponse(), |
| fake_plugin_.sent_messages()[1]); |
| EXPECT_EQ( |
| CreateExpectedBookmarksResponse(fake_plugin_.engine()->GetBookmarks()), |
| fake_plugin_.sent_messages()[2]); |
| EXPECT_EQ(CreateExpectedMetadataResponse(), fake_plugin_.sent_messages()[3]); |
| EXPECT_EQ(CreateExpectedLoadingProgressResponse(), |
| fake_plugin_.sent_messages()[4]); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithDocInfoTest, |
| DocumentLoadCompleteInFullFramePdfViewerWithAccessibilityDisabled) { |
| // Notify the render frame about document loading. |
| fake_plugin_.set_full_frame(true); |
| ASSERT_TRUE(fake_plugin_.full_frame()); |
| fake_plugin_.CreateUrlLoader(); |
| |
| ASSERT_FALSE(fake_plugin_.IsPrintPreview()); |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| ASSERT_EQ(PdfViewPluginBase::AccessibilityState::kOff, |
| fake_plugin_.accessibility_state()); |
| |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess")); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, PluginDidStopLoading()); |
| EXPECT_CALL(fake_plugin_, |
| SetContentRestrictions(fake_plugin_.GetContentRestrictions())); |
| EXPECT_CALL(fake_plugin_, |
| SetAccessibilityDocInfo(fake_plugin_.GetAccessibilityDocInfo())) |
| .Times(0); |
| |
| fake_plugin_.DocumentLoadComplete(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete, |
| fake_plugin_.document_load_state()); |
| EXPECT_EQ(PdfViewPluginBase::AccessibilityState::kOff, |
| fake_plugin_.accessibility_state()); |
| |
| // Check all the sent messages. |
| ASSERT_EQ(5u, fake_plugin_.sent_messages().size()); |
| EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(), |
| fake_plugin_.sent_messages()[0]); |
| EXPECT_EQ(CreateExpectedAttachmentsResponse(), |
| fake_plugin_.sent_messages()[1]); |
| EXPECT_EQ( |
| CreateExpectedBookmarksResponse(fake_plugin_.engine()->GetBookmarks()), |
| fake_plugin_.sent_messages()[2]); |
| EXPECT_EQ(CreateExpectedMetadataResponse(), fake_plugin_.sent_messages()[3]); |
| EXPECT_EQ(CreateExpectedLoadingProgressResponse(), |
| fake_plugin_.sent_messages()[4]); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithDocInfoTest, |
| DocumentLoadCompleteInNonFullFramePdfViewer) { |
| ASSERT_FALSE(fake_plugin_.full_frame()); |
| fake_plugin_.CreateUrlLoader(); |
| |
| ASSERT_FALSE(fake_plugin_.IsPrintPreview()); |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess")); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, PluginDidStopLoading()).Times(0); |
| EXPECT_CALL(fake_plugin_, |
| SetContentRestrictions(fake_plugin_.GetContentRestrictions())) |
| .Times(0); |
| |
| fake_plugin_.DocumentLoadComplete(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete, |
| fake_plugin_.document_load_state()); |
| |
| // Check all the sent messages. |
| ASSERT_EQ(5u, fake_plugin_.sent_messages().size()); |
| EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(), |
| fake_plugin_.sent_messages()[0]); |
| EXPECT_EQ(CreateExpectedAttachmentsResponse(), |
| fake_plugin_.sent_messages()[1]); |
| EXPECT_EQ( |
| CreateExpectedBookmarksResponse(fake_plugin_.engine()->GetBookmarks()), |
| fake_plugin_.sent_messages()[2]); |
| EXPECT_EQ(CreateExpectedMetadataResponse(), fake_plugin_.sent_messages()[3]); |
| EXPECT_EQ(CreateExpectedLoadingProgressResponse(), |
| fake_plugin_.sent_messages()[4]); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithoutDocInfoTest, DocumentLoadCompletePostMessages) { |
| fake_plugin_.CreateUrlLoader(); |
| |
| ASSERT_FALSE(fake_plugin_.IsPrintPreview()); |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadSuccess")); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| |
| fake_plugin_.DocumentLoadComplete(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kComplete, |
| fake_plugin_.document_load_state()); |
| |
| // Check the sent messages when the document doesn't have any metadata, |
| // attachments or bookmarks. |
| ASSERT_EQ(3u, fake_plugin_.sent_messages().size()); |
| EXPECT_EQ(CreateExpectedFormTextFieldFocusChangeResponse(), |
| fake_plugin_.sent_messages()[0]); |
| EXPECT_EQ(CreateExpectedNoMetadataResponse(), |
| fake_plugin_.sent_messages()[1]); |
| EXPECT_EQ(CreateExpectedLoadingProgressResponse(), |
| fake_plugin_.sent_messages()[2]); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, DocumentLoadFailedWithNotifiedRenderFrame) { |
| // Notify the render frame about document loading. |
| fake_plugin_.set_full_frame(true); |
| ASSERT_TRUE(fake_plugin_.full_frame()); |
| fake_plugin_.CreateUrlLoader(); |
| |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| EXPECT_TRUE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadFailure")); |
| EXPECT_CALL(fake_plugin_, PluginDidStopLoading()); |
| |
| fake_plugin_.DocumentLoadFailed(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kFailed, |
| fake_plugin_.document_load_state()); |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, DocumentLoadFailedWithoutNotifiedRenderFrame) { |
| // The render frame has never been notified about document loading before. |
| ASSERT_FALSE(fake_plugin_.full_frame()); |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| |
| ASSERT_EQ(PdfViewPluginBase::DocumentLoadState::kLoading, |
| fake_plugin_.document_load_state()); |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction("PDF.LoadFailure")); |
| EXPECT_CALL(fake_plugin_, PluginDidStopLoading()).Times(0); |
| |
| fake_plugin_.DocumentLoadFailed(); |
| EXPECT_EQ(PdfViewPluginBase::DocumentLoadState::kFailed, |
| fake_plugin_.document_load_state()); |
| EXPECT_FALSE(fake_plugin_.GetDidCallStartLoadingForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, DocumentHasUnsupportedFeatureInFullFrame) { |
| fake_plugin_.set_full_frame(true); |
| ASSERT_TRUE(fake_plugin_.full_frame()); |
| |
| // Arbitrary feature names and their matching metric names. |
| static constexpr char kFeature1[] = "feature1"; |
| static constexpr char kMetric1[] = "PDF_Unsupported_feature1"; |
| static constexpr char kFeature2[] = "feature2"; |
| static constexpr char kMetric2[] = "PDF_Unsupported_feature2"; |
| |
| // Find unsupported `kFeature1` for the first time. |
| EXPECT_FALSE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric1)); |
| EXPECT_FALSE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| EXPECT_CALL(fake_plugin_, NotifyUnsupportedFeature()); |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction(kMetric1)); |
| |
| fake_plugin_.DocumentHasUnsupportedFeature(kFeature1); |
| EXPECT_TRUE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric1)); |
| EXPECT_TRUE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| |
| // Find unsupported `kFeature2` for the first time. |
| EXPECT_FALSE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric2)); |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction(kMetric2)); |
| |
| fake_plugin_.DocumentHasUnsupportedFeature(kFeature2); |
| EXPECT_TRUE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric2)); |
| EXPECT_TRUE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| |
| // Find unsupported `kFeature1` for the second time. |
| fake_plugin_.DocumentHasUnsupportedFeature(kFeature1); |
| EXPECT_TRUE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric1)); |
| EXPECT_TRUE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, DocumentHasUnsupportedFeatureWithoutFullFrame) { |
| ASSERT_FALSE(fake_plugin_.full_frame()); |
| |
| // An arbitrary feature name and its matching metric name. |
| static constexpr char kFeature[] = "feature"; |
| static constexpr char kMetric[] = "PDF_Unsupported_feature"; |
| |
| EXPECT_FALSE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric)); |
| EXPECT_FALSE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| |
| // NotifyUnsupportedFeature() should never be called if the viewer doesn't |
| // occupy the whole frame, but the metrics should still be recorded. |
| EXPECT_CALL(fake_plugin_, NotifyUnsupportedFeature()).Times(0); |
| EXPECT_CALL(fake_plugin_, UserMetricsRecordAction(kMetric)); |
| |
| fake_plugin_.DocumentHasUnsupportedFeature(kFeature); |
| EXPECT_TRUE(fake_plugin_.UnsupportedFeatureIsReportedForTesting(kMetric)); |
| EXPECT_FALSE( |
| fake_plugin_.GetNotifiedBrowserAboutUnsupportedFeatureForTesting()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, EnteredEditMode) { |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(true)); |
| fake_plugin_.EnteredEditMode(); |
| |
| base::Value expected_response(base::Value::Type::DICTIONARY); |
| expected_response.SetStringKey("type", "setIsEditing"); |
| |
| EXPECT_TRUE(fake_plugin_.edit_mode()); |
| ASSERT_EQ(1u, fake_plugin_.sent_messages().size()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages()[0]); |
| } |
| |
| using PdfViewPluginBaseSaveTest = PdfViewPluginBaseWithEngineTest; |
| |
| #if BUILDFLAG(ENABLE_INK) |
| TEST_F(PdfViewPluginBaseSaveTest, SaveAnnotationInNonEditMode) { |
| ASSERT_FALSE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveAnnotInNonEditModeToken[] = |
| "save-annot-in-non-edit-mode-token"; |
| base::Value message = |
| CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType::kAnnotation, |
| kSaveAnnotInNonEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToBufferResponse(kSaveAnnotInNonEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(true)); |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| |
| TEST_F(PdfViewPluginBaseSaveTest, SaveAnnotationInEditMode) { |
| fake_plugin_.EnteredEditMode(); |
| ASSERT_TRUE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveAnnotInEditModeToken[] = |
| "save-annot-in-edit-mode-token"; |
| base::Value message = |
| CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType::kAnnotation, |
| kSaveAnnotInEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToBufferResponse(kSaveAnnotInEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(true)); |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| #endif // BUILDFLAG(ENABLE_INK) |
| |
| TEST_F(PdfViewPluginBaseSaveTest, SaveOriginalInNonEditMode) { |
| ASSERT_FALSE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveOriginalInNonEditModeToken[] = |
| "save-original-in-non-edit-mode-token"; |
| base::Value message = |
| CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType::kOriginal, |
| kSaveOriginalInNonEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToFileResponse(kSaveOriginalInNonEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SaveAs()); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(false)).Times(2); |
| |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| |
| TEST_F(PdfViewPluginBaseSaveTest, SaveOriginalInEditMode) { |
| fake_plugin_.EnteredEditMode(); |
| ASSERT_TRUE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveOriginalInEditModeToken[] = |
| "save-original-in-edit-mode-token"; |
| base::Value message = |
| CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType::kOriginal, |
| kSaveOriginalInEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToFileResponse(kSaveOriginalInEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SaveAs()); |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(false)); |
| EXPECT_CALL(fake_plugin_, SetPluginCanSave(true)); |
| |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| |
| #if BUILDFLAG(ENABLE_INK) |
| TEST_F(PdfViewPluginBaseSaveTest, SaveEditedInNonEditMode) { |
| ASSERT_FALSE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveEditedInNonEditModeToken[] = |
| "save-edited-in-non-edit-mode"; |
| base::Value message = |
| CreateSaveRequestMessage(PdfViewPluginBase::SaveRequestType::kEdited, |
| kSaveEditedInNonEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToBufferResponse(kSaveEditedInNonEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| #endif // BUILDFLAG(ENABLE_INK) |
| |
| TEST_F(PdfViewPluginBaseSaveTest, SaveEditedInEditMode) { |
| fake_plugin_.EnteredEditMode(); |
| ASSERT_TRUE(fake_plugin_.edit_mode()); |
| |
| static constexpr char kSaveEditedInEditModeToken[] = |
| "save-edited-in-edit-mode-token"; |
| base::Value message = CreateSaveRequestMessage( |
| PdfViewPluginBase::SaveRequestType::kEdited, kSaveEditedInEditModeToken); |
| base::Value expected_response = |
| CreateExpectedSaveToBufferResponse(kSaveEditedInEditModeToken); |
| |
| EXPECT_CALL(fake_plugin_, SetFormFieldInFocus(false)); |
| fake_plugin_.HandleMessage(message); |
| ASSERT_FALSE(fake_plugin_.sent_messages().empty()); |
| EXPECT_EQ(expected_response, fake_plugin_.sent_messages().back()); |
| } |
| |
| TEST_F(PdfViewPluginBaseTest, HandleSetBackgroundColorMessage) { |
| const SkColor kNewBackgroundColor = SK_ColorGREEN; |
| ASSERT_NE(kNewBackgroundColor, fake_plugin_.GetBackgroundColor()); |
| |
| base::Value message(base::Value::Type::DICTIONARY); |
| message.SetStringKey("type", "setBackgroundColor"); |
| message.SetDoubleKey("color", kNewBackgroundColor); |
| |
| fake_plugin_.HandleMessage(message); |
| EXPECT_EQ(kNewBackgroundColor, fake_plugin_.GetBackgroundColor()); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithEngineTest, GetContentRestrictions) { |
| auto* engine = static_cast<TestPDFiumEngine*>(fake_plugin_.engine()); |
| static constexpr int kContentRestrictionCutPaste = |
| kContentRestrictionCut | kContentRestrictionPaste; |
| |
| // Test engine without any permissions. |
| engine->SetPermissions({}); |
| |
| int content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste | kContentRestrictionCopy | |
| kContentRestrictionPrint, |
| content_restrictions); |
| |
| // Test engine with only copy permission. |
| engine->SetPermissions({DocumentPermission::kCopy}); |
| |
| content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste | kContentRestrictionPrint, |
| content_restrictions); |
| |
| // Test engine with only print low quality permission. |
| engine->SetPermissions({DocumentPermission::kPrintLowQuality}); |
| |
| content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste | kContentRestrictionCopy, |
| content_restrictions); |
| |
| // Test engine with both copy and print low quality permissions. |
| engine->SetPermissions( |
| {DocumentPermission::kCopy, DocumentPermission::kPrintLowQuality}); |
| |
| content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste, content_restrictions); |
| |
| // Test engine with print high and low quality permissions. |
| engine->SetPermissions({DocumentPermission::kPrintHighQuality, |
| DocumentPermission::kPrintLowQuality}); |
| |
| content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste | kContentRestrictionCopy, |
| content_restrictions); |
| |
| // Test engine with copy, print high and low quality permissions. |
| engine->SetPermissions({DocumentPermission::kCopy, |
| DocumentPermission::kPrintHighQuality, |
| DocumentPermission::kPrintLowQuality}); |
| |
| content_restrictions = fake_plugin_.GetContentRestrictions(); |
| EXPECT_EQ(kContentRestrictionCutPaste, content_restrictions); |
| } |
| |
| TEST_F(PdfViewPluginBaseWithEngineTest, GetAccessibilityDocInfo) { |
| auto* engine = static_cast<TestPDFiumEngine*>(fake_plugin_.engine()); |
| |
| // Test engine without any permissions. |
| engine->SetPermissions({}); |
| |
| AccessibilityDocInfo doc_info = fake_plugin_.GetAccessibilityDocInfo(); |
| EXPECT_EQ(kPageNumber, doc_info.page_count); |
| EXPECT_FALSE(doc_info.text_accessible); |
| EXPECT_FALSE(doc_info.text_copyable); |
| |
| // Test engine with only copy permission. |
| engine->SetPermissions({DocumentPermission::kCopy}); |
| |
| doc_info = fake_plugin_.GetAccessibilityDocInfo(); |
| EXPECT_EQ(kPageNumber, doc_info.page_count); |
| EXPECT_FALSE(doc_info.text_accessible); |
| EXPECT_TRUE(doc_info.text_copyable); |
| |
| // Test engine with only copy accessible permission. |
| engine->SetPermissions({DocumentPermission::kCopyAccessible}); |
| |
| doc_info = fake_plugin_.GetAccessibilityDocInfo(); |
| EXPECT_EQ(kPageNumber, doc_info.page_count); |
| EXPECT_TRUE(doc_info.text_accessible); |
| EXPECT_FALSE(doc_info.text_copyable); |
| |
| // Test engine with both copy and copy accessible permission. |
| engine->SetPermissions( |
| {DocumentPermission::kCopy, DocumentPermission::kCopyAccessible}); |
| |
| doc_info = fake_plugin_.GetAccessibilityDocInfo(); |
| EXPECT_EQ(kPageNumber, doc_info.page_count); |
| EXPECT_TRUE(doc_info.text_accessible); |
| EXPECT_TRUE(doc_info.text_copyable); |
| } |
| |
| } // namespace chrome_pdf |