1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "pdf/pdf_view_web_plugin.h"
15 #include "base/containers/span.h"
16 #include "base/location.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string_piece.h"
21 #include "base/test/bind.h"
22 #include "base/test/values_test_util.h"
23 #include "base/time/time.h"
24 #include "base/values.h"
25 #include "cc/paint/paint_canvas.h"
26 #include "cc/test/pixel_comparator.h"
27 #include "cc/test/pixel_test_utils.h"
28 #include "mojo/public/cpp/bindings/associated_receiver.h"
29 #include "mojo/public/cpp/bindings/associated_remote.h"
30 #include "net/cookies/site_for_cookies.h"
31 #include "pdf/accessibility_structs.h"
32 #include "pdf/buildflags.h"
33 #include "pdf/content_restriction.h"
34 #include "pdf/document_layout.h"
35 #include "pdf/mojom/pdf.mojom.h"
36 #include "pdf/paint_ready_rect.h"
37 #include "pdf/pdf_accessibility_data_handler.h"
38 #include "pdf/test/mock_web_associated_url_loader.h"
39 #include "pdf/test/test_helpers.h"
40 #include "pdf/test/test_pdfium_engine.h"
41 #include "printing/metafile_skia.h"
42 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
43 #include "testing/gmock/include/gmock/gmock.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
46 #include "third_party/blink/public/common/input/web_input_event.h"
47 #include "third_party/blink/public/common/input/web_keyboard_event.h"
48 #include "third_party/blink/public/common/input/web_mouse_event.h"
49 #include "third_party/blink/public/common/loader/http_body_element_type.h"
50 #include "third_party/blink/public/platform/web_data.h"
51 #include "third_party/blink/public/platform/web_http_body.h"
52 #include "third_party/blink/public/platform/web_http_header_visitor.h"
53 #include "third_party/blink/public/platform/web_input_event_result.h"
54 #include "third_party/blink/public/platform/web_string.h"
55 #include "third_party/blink/public/platform/web_text_input_type.h"
56 #include "third_party/blink/public/platform/web_url.h"
57 #include "third_party/blink/public/platform/web_url_request.h"
58 #include "third_party/blink/public/platform/web_url_response.h"
59 #include "third_party/blink/public/web/web_associated_url_loader.h"
60 #include "third_party/blink/public/web/web_associated_url_loader_client.h"
61 #include "third_party/blink/public/web/web_plugin_container.h"
62 #include "third_party/blink/public/web/web_plugin_params.h"
63 #include "third_party/blink/public/web/web_print_params.h"
64 #include "third_party/skia/include/core/SkBitmap.h"
65 #include "third_party/skia/include/core/SkCanvas.h"
66 #include "third_party/skia/include/core/SkColor.h"
67 #include "third_party/skia/include/core/SkRefCnt.h"
68 #include "third_party/skia/include/core/SkSurface.h"
69 #include "ui/base/cursor/cursor.h"
70 #include "ui/events/blink/blink_event_util.h"
71 #include "ui/events/keycodes/dom/dom_code.h"
72 #include "ui/events/keycodes/dom/dom_key.h"
73 #include "ui/gfx/canvas.h"
74 #include "ui/gfx/geometry/point.h"
75 #include "ui/gfx/geometry/point_f.h"
76 #include "ui/gfx/geometry/rect.h"
77 #include "ui/gfx/geometry/size.h"
78 #include "ui/gfx/geometry/skia_conversions.h"
79 #include "ui/gfx/geometry/vector2d_f.h"
80 #include "ui/gfx/range/range.h"
81 #include "ui/latency/latency_info.h"
84 namespace chrome_pdf {
88 using ::testing::AnyNumber;
89 using ::testing::ElementsAre;
90 using ::testing::ElementsAreArray;
92 using ::testing::InSequence;
93 using ::testing::Invoke;
94 using ::testing::IsEmpty;
95 using ::testing::IsFalse;
96 using ::testing::IsTrue;
97 using ::testing::MockFunction;
98 using ::testing::NiceMock;
99 using ::testing::Pointwise;
100 using ::testing::Return;
101 using ::testing::SaveArg;
102 using ::testing::SizeIs;
104 // `kCanvasSize` needs to be big enough to hold plugin's snapshots during
106 constexpr gfx::Size kCanvasSize(100, 100);
108 // Note: Make sure `kDefaultColor` is different from `kPaintColor` and the
109 // plugin's background color. This will help identify bitmap changes after
111 constexpr SkColor kDefaultColor = SK_ColorGREEN;
113 constexpr SkColor kPaintColor = SK_ColorRED;
116 // The plugin container's device scale.
119 // The window area in CSS pixels.
120 gfx::Rect window_rect;
122 // The target painting area on the canvas in CSS pixels.
123 gfx::Rect paint_rect;
125 // The expected clipped area to be filled with paint color. The clipped area
126 // should be the intersection of `paint_rect` and `window_rect`.
127 gfx::Rect expected_clipped_rect;
130 MATCHER(SearchStringResultEq, "") {
131 PDFEngine::Client::SearchStringResult l = std::get<0>(arg);
132 PDFEngine::Client::SearchStringResult r = std::get<1>(arg);
133 return l.start_index == r.start_index && l.length == r.length;
136 MATCHER_P(IsExpectedImeKeyEvent, expected_text, "") {
137 if (arg.GetType() != blink::WebInputEvent::Type::kChar)
140 const auto& event = static_cast<const blink::WebKeyboardEvent&>(arg);
141 return event.GetModifiers() == blink::WebInputEvent::kNoModifiers &&
142 event.windows_key_code == expected_text[0] &&
143 event.native_key_code == expected_text[0] &&
144 event.dom_code == static_cast<int>(ui::DomCode::NONE) &&
145 event.dom_key == ui::DomKey::NONE && !event.is_system_key &&
146 !event.is_browser_shortcut && event.text == expected_text &&
147 event.unmodified_text == expected_text;
150 base::Value::Dict ParseMessage(base::StringPiece json) {
151 return std::move(base::test::ParseJson(json).GetDict());
154 // Generates the expected `SkBitmap` with `paint_color` filled in the expected
155 // clipped area and `kDefaultColor` as the background color.
156 SkBitmap GenerateExpectedBitmapForPaint(const gfx::Rect& expected_clipped_rect,
157 SkColor paint_color) {
158 sk_sp<SkSurface> expected_surface =
159 CreateSkiaSurfaceForTesting(kCanvasSize, kDefaultColor);
160 expected_surface->getCanvas()->clipIRect(
161 gfx::RectToSkIRect(expected_clipped_rect));
162 expected_surface->getCanvas()->clear(paint_color);
164 SkBitmap expected_bitmap;
165 expected_surface->makeImageSnapshot()->asLegacyBitmap(&expected_bitmap);
166 return expected_bitmap;
169 class MockHeaderVisitor : public blink::WebHTTPHeaderVisitor {
173 (const blink::WebString&, const blink::WebString&),
177 class MockPdfAccessibilityDataHandler : public PdfAccessibilityDataHandler {
179 // PdfAccessibilityDataHandler:
181 SetAccessibilityViewportInfo,
182 (AccessibilityViewportInfo),
185 SetAccessibilityDocInfo,
186 (AccessibilityDocInfo),
189 SetAccessibilityPageInfo,
190 (AccessibilityPageInfo,
191 std::vector<AccessibilityTextRunInfo>,
192 std::vector<AccessibilityCharInfo>,
193 AccessibilityPageObjects),
197 class FakePdfViewWebPluginClient : public PdfViewWebPlugin::Client {
199 FakePdfViewWebPluginClient() {
200 ON_CALL(*this, CreateAssociatedURLLoader).WillByDefault([]() {
201 auto associated_loader =
202 std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
203 ON_CALL(*associated_loader, LoadAsynchronously)
204 .WillByDefault([](const blink::WebURLRequest& /*request*/,
205 blink::WebAssociatedURLLoaderClient* client) {
206 // TODO(crbug.com/1322928): Must trigger callback to free
208 client->DidReceiveResponse(blink::WebURLResponse());
209 client->DidFinishLoading();
211 return associated_loader;
213 ON_CALL(*this, GetEmbedderOriginString)
215 Return("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/"));
216 ON_CALL(*this, HasFrame).WillByDefault(Return(true));
219 // PdfViewWebPlugin::Client:
220 MOCK_METHOD(std::unique_ptr<base::Value>,
222 (v8::Local<v8::Value>, v8::Local<v8::Context>),
225 MOCK_METHOD(base::WeakPtr<Client>, GetWeakPtr, (), (override));
227 MOCK_METHOD(std::unique_ptr<PDFiumEngine>,
229 (PDFEngine::Client*, PDFiumFormFiller::ScriptOption),
234 (blink::WebPluginContainer*),
236 MOCK_METHOD(blink::WebPluginContainer*, PluginContainer, (), (override));
238 MOCK_METHOD(net::SiteForCookies, SiteForCookies, (), (const override));
240 MOCK_METHOD(blink::WebURL,
242 (const blink::WebString&),
245 MOCK_METHOD(void, PostMessage, (base::Value::Dict), (override));
247 MOCK_METHOD(void, Invalidate, (), (override));
250 RequestTouchEventType,
251 (blink::WebPluginContainer::TouchEventRequestType),
254 MOCK_METHOD(void, ReportFindInPageMatchCount, (int, int, bool), (override));
256 MOCK_METHOD(void, ReportFindInPageSelection, (int, int, bool), (override));
259 ReportFindInPageTickmarks,
260 (const std::vector<gfx::Rect>&),
263 MOCK_METHOD(float, DeviceScaleFactor, (), (override));
265 MOCK_METHOD(gfx::PointF, GetScrollPosition, (), (override));
267 MOCK_METHOD(void, UsePluginAsFindHandler, (), (override));
270 SetReferrerForRequest,
271 (blink::WebURLRequest&, const blink::WebURL&),
274 MOCK_METHOD(void, Alert, (const blink::WebString&), (override));
276 MOCK_METHOD(bool, Confirm, (const blink::WebString&), (override));
278 MOCK_METHOD(blink::WebString,
280 (const blink::WebString&, const blink::WebString&),
284 TextSelectionChanged,
285 (const blink::WebString&, uint32_t, const gfx::Range&),
288 MOCK_METHOD(std::unique_ptr<blink::WebAssociatedURLLoader>,
289 CreateAssociatedURLLoader,
290 (const blink::WebAssociatedURLLoaderOptions&),
293 MOCK_METHOD(void, UpdateTextInputState, (), (override));
295 MOCK_METHOD(void, UpdateSelectionBounds, (), (override));
297 MOCK_METHOD(std::string, GetEmbedderOriginString, (), (override));
299 MOCK_METHOD(bool, HasFrame, (), (const override));
301 MOCK_METHOD(void, DidStartLoading, (), (override));
302 MOCK_METHOD(void, DidStopLoading, (), (override));
304 MOCK_METHOD(void, RecordComputedAction, (const std::string&), (override));
306 MOCK_METHOD(std::unique_ptr<PdfAccessibilityDataHandler>,
307 CreateAccessibilityDataHandler,
308 (PdfAccessibilityActionHandler*),
312 class FakePdfService : public pdf::mojom::PdfService {
316 (mojo::PendingRemote<pdf::mojom::PdfListener>),
318 MOCK_METHOD(void, UpdateContentRestrictions, (int32_t), (override));
319 MOCK_METHOD(void, HasUnsupportedFeature, (), (override));
322 (const GURL&, network::mojom::ReferrerPolicy),
326 (const gfx::PointF&, int32_t, const gfx::PointF&, int32_t),
328 MOCK_METHOD(void, SetPluginCanSave, (bool), (override));
333 class PdfViewWebPluginWithoutInitializeTest : public testing::Test {
335 // Custom deleter for `plugin_`. PdfViewWebPlugin must be destroyed by
336 // PdfViewWebPlugin::Destroy() instead of its destructor.
337 struct PluginDeleter {
338 void operator()(PdfViewWebPlugin* ptr) { ptr->Destroy(); }
341 static void AddToPluginParams(base::StringPiece name,
342 base::StringPiece value,
343 blink::WebPluginParams& params) {
344 params.attribute_names.push_back(
345 blink::WebString::FromUTF8(name.data(), name.size()));
346 params.attribute_values.push_back(
347 blink::WebString::FromUTF8(value.data(), value.size()));
350 void SetUpPlugin(base::StringPiece document_url,
351 const blink::WebPluginParams& params) {
352 auto client = std::make_unique<NiceMock<FakePdfViewWebPluginClient>>();
353 client_ptr_ = client.get();
355 ON_CALL(*client_ptr_, CompleteURL)
356 .WillByDefault([parsed_document_url = GURL(document_url)](
357 const blink::WebString& partial_url) {
358 return parsed_document_url.Resolve(partial_url.Utf8());
360 ON_CALL(*client_ptr_, CreateEngine)
361 .WillByDefault([this](
362 PDFEngine::Client* client,
363 PDFiumFormFiller::ScriptOption /*script_option*/) {
364 auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
365 engine_ptr_ = engine.get();
368 ON_CALL(*client_ptr_, CreateAccessibilityDataHandler)
369 .WillByDefault([this]() {
371 std::make_unique<NiceMock<MockPdfAccessibilityDataHandler>>();
372 accessibility_data_handler_ptr_ = handler.get();
378 std::unique_ptr<PdfViewWebPlugin, PluginDeleter>(new PdfViewWebPlugin(
380 mojo::AssociatedRemote<pdf::mojom::PdfService>(
381 pdf_receiver_.BindNewEndpointAndPassDedicatedRemote()),
385 void SetUpPluginWithUrl(const std::string& url) {
386 blink::WebPluginParams params;
387 AddToPluginParams("src", url, params);
388 SetUpPluginParams(params);
390 SetUpPlugin(url, params);
393 // Allows derived classes to customize plugin parameters within
394 // `SetUpPluginWithUrl()`.
395 virtual void SetUpPluginParams(blink::WebPluginParams& params) {}
397 // Allows derived classes to customize `client_ptr_` within `SetUpPlugin()`.
398 virtual void SetUpClient() {}
400 void TearDown() override { plugin_.reset(); }
402 void ExpectUpdateTextInputState(
403 blink::WebTextInputType expected_text_input_type) {
404 EXPECT_CALL(*client_ptr_, UpdateTextInputState)
405 .WillOnce([this, expected_text_input_type]() {
406 EXPECT_EQ(expected_text_input_type,
407 plugin_->GetPluginTextInputType());
411 NiceMock<FakePdfService> pdf_service_;
412 mojo::AssociatedReceiver<pdf::mojom::PdfService> pdf_receiver_{&pdf_service_};
414 raw_ptr<FakePdfViewWebPluginClient> client_ptr_;
415 std::unique_ptr<PdfViewWebPlugin, PluginDeleter> plugin_;
416 raw_ptr<TestPDFiumEngine> engine_ptr_;
417 raw_ptr<MockPdfAccessibilityDataHandler> accessibility_data_handler_ptr_;
420 class PdfViewWebPluginTest : public PdfViewWebPluginWithoutInitializeTest {
422 static constexpr char kPdfUrl[] = "http://localhost/example.pdf";
424 void SetUp() override {
425 SetUpPluginWithUrl(kPdfUrl);
427 EXPECT_TRUE(plugin_->InitializeForTesting());
430 void SetDocumentDimensions(const gfx::Size& dimensions) {
431 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
432 .WillRepeatedly(Return(dimensions));
433 SendViewportMessage(/*zoom=*/1.0);
436 void SendViewportMessage(double zoom) {
437 base::Value::Dict message = ParseMessage(R"({
439 "userInitiated": false,
443 "defaultPageOrientation": 0,
444 "twoUpViewEnabled": false,
450 message.Set("zoom", zoom);
451 plugin_->OnMessage(message);
454 void UpdatePluginGeometry(float device_scale, const gfx::Rect& window_rect) {
455 UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
457 // Waits for main thread callback scheduled by `PaintManager`.
458 base::RunLoop run_loop;
459 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
460 run_loop.QuitClosure());
464 void UpdatePluginGeometryWithoutWaiting(float device_scale,
465 const gfx::Rect& window_rect) {
466 // The plugin container's device scale must be set before calling
468 EXPECT_CALL(*client_ptr_, DeviceScaleFactor)
469 .WillRepeatedly(Return(device_scale));
470 plugin_->UpdateGeometry(window_rect, window_rect, window_rect,
471 /*is_visible=*/true);
474 void TestUpdateGeometrySetsPluginRect(float device_scale,
475 const gfx::Rect& window_rect,
476 float expected_device_scale,
477 const gfx::Rect& expected_plugin_rect) {
478 UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
479 EXPECT_EQ(expected_device_scale, plugin_->GetDeviceScaleForTesting())
480 << "Device scale comparison failure at device scale of "
482 EXPECT_EQ(expected_plugin_rect, plugin_->GetPluginRectForTesting())
483 << "Plugin rect comparison failure at device scale of " << device_scale
484 << ", window rect of " << window_rect.ToString();
487 void TestPaintEmptySnapshots(float device_scale,
488 const gfx::Rect& window_rect,
489 const gfx::Rect& paint_rect,
490 const gfx::Rect& expected_clipped_rect) {
491 UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
492 canvas_.DrawColor(kDefaultColor);
494 plugin_->Paint(canvas_.sk_canvas(), paint_rect);
496 // Expect the clipped area on canvas to be filled with plugin's background
498 SkBitmap expected_bitmap = GenerateExpectedBitmapForPaint(
499 expected_clipped_rect, plugin_->GetBackgroundColor());
501 cc::MatchesBitmap(canvas_.GetBitmap(), expected_bitmap,
502 cc::ExactPixelComparator(/*discard_alpha=*/false)))
503 << "Failure at device scale of " << device_scale << ", window rect of "
504 << window_rect.ToString();
507 void TestPaintSnapshots(float device_scale,
508 const gfx::Rect& window_rect,
509 const gfx::Rect& paint_rect,
510 const gfx::Rect& expected_clipped_rect) {
511 UpdatePluginGeometry(device_scale, window_rect);
512 canvas_.DrawColor(kDefaultColor);
514 // Paint the plugin with `kPaintColor`.
515 plugin_->UpdateSnapshot(CreateSkiaImageForTesting(
516 plugin_->GetPluginRectForTesting().size(), kPaintColor));
517 plugin_->Paint(canvas_.sk_canvas(), paint_rect);
519 // Expect the clipped area on canvas to be filled with `kPaintColor`.
520 SkBitmap expected_bitmap =
521 GenerateExpectedBitmapForPaint(expected_clipped_rect, kPaintColor);
523 cc::MatchesBitmap(canvas_.GetBitmap(), expected_bitmap,
524 cc::ExactPixelComparator(/*discard_alpha=*/false)))
525 << "Failure at device scale of " << device_scale << ", window rect of "
526 << window_rect.ToString();
529 // Provides the cc::PaintCanvas for painting.
530 gfx::Canvas canvas_{kCanvasSize, /*image_scale=*/1.0f, /*is_opaque=*/true};
533 class PdfViewWebPluginFullFrameTest : public PdfViewWebPluginTest {
535 void SetUpPluginParams(blink::WebPluginParams& params) override {
536 AddToPluginParams("full-frame", "full-frame", params);
540 TEST_F(PdfViewWebPluginWithoutInitializeTest, Initialize) {
541 SetUpPluginWithUrl("http://localhost/example.pdf");
543 EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader)
544 .WillOnce([](const blink::WebAssociatedURLLoaderOptions& options) {
545 EXPECT_TRUE(options.grant_universal_access);
547 auto associated_loader =
548 std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
549 EXPECT_CALL(*associated_loader, LoadAsynchronously)
550 .WillOnce([](const blink::WebURLRequest& request,
551 blink::WebAssociatedURLLoaderClient* client) {
552 EXPECT_EQ("http://localhost/example.pdf",
553 request.Url().GetString().Utf8());
554 EXPECT_EQ("GET", request.HttpMethod().Utf8());
555 EXPECT_TRUE(request.HttpBody().IsNull());
557 NiceMock<MockHeaderVisitor> header_visitor;
558 EXPECT_CALL(header_visitor, VisitHeader).Times(0);
559 request.VisitHttpHeaderFields(&header_visitor);
561 EXPECT_FALSE(client->WillFollowRedirect(blink::WebURL(),
562 blink::WebURLResponse()));
563 client->DidReceiveResponse(blink::WebURLResponse());
564 client->DidFinishLoading();
566 return associated_loader;
568 EXPECT_CALL(*client_ptr_, SetReferrerForRequest).Times(0);
570 EXPECT_TRUE(plugin_->InitializeForTesting());
573 TEST_F(PdfViewWebPluginWithoutInitializeTest, InitializeWithEmptyUrl) {
574 SetUpPluginWithUrl("");
576 EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
578 EXPECT_FALSE(plugin_->InitializeForTesting());
581 TEST_F(PdfViewWebPluginWithoutInitializeTest, InitializeForPrintPreview) {
582 SetUpPluginWithUrl("about:blank");
584 EXPECT_CALL(*client_ptr_, GetEmbedderOriginString)
585 .WillRepeatedly(Return("chrome://print/"));
586 EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
588 EXPECT_TRUE(plugin_->InitializeForTesting());
591 TEST_F(PdfViewWebPluginTest, CreateUrlLoader) {
592 EXPECT_CALL(*client_ptr_, DidStartLoading).Times(0);
593 EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
594 plugin_->CreateUrlLoader();
596 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kLoading,
597 plugin_->document_load_state_for_testing());
598 pdf_receiver_.FlushForTesting();
601 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoader) {
602 EXPECT_CALL(*client_ptr_, DidStartLoading);
603 EXPECT_CALL(pdf_service_,
604 UpdateContentRestrictions(kContentRestrictionSave |
605 kContentRestrictionPrint));
606 plugin_->CreateUrlLoader();
608 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kLoading,
609 plugin_->document_load_state_for_testing());
610 pdf_receiver_.FlushForTesting();
613 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoaderMultipleTimes) {
614 plugin_->CreateUrlLoader();
616 EXPECT_CALL(*client_ptr_, DidStartLoading).Times(0);
617 plugin_->CreateUrlLoader();
620 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoaderAfterDocumentLoadFailed) {
621 plugin_->CreateUrlLoader();
622 plugin_->DocumentLoadFailed();
624 EXPECT_CALL(*client_ptr_, DidStartLoading);
625 plugin_->CreateUrlLoader();
628 TEST_F(PdfViewWebPluginTest, DocumentLoadComplete) {
629 plugin_->CreateUrlLoader();
631 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
632 EXPECT_CALL(*client_ptr_, PostMessage);
633 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
634 "type": "formFocusChange",
637 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
638 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
639 "type": "printPreviewLoaded",
642 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
644 EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
645 EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
646 plugin_->DocumentLoadComplete();
648 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
649 plugin_->document_load_state_for_testing());
650 pdf_receiver_.FlushForTesting();
653 TEST_F(PdfViewWebPluginFullFrameTest, DocumentLoadComplete) {
654 // Must flush IPCs after `CreateUrlLoader()` in full-frame mode, otherwise
655 // there's an unexpected `UpdateContentRestrictions()` call (see the
656 // `PdfViewWebPluginFullFrameTest.CreateUrlLoader` test).
657 plugin_->CreateUrlLoader();
658 pdf_receiver_.FlushForTesting();
660 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
661 EXPECT_CALL(*client_ptr_, PostMessage);
662 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
663 "type": "formFocusChange",
666 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
667 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
668 "type": "printPreviewLoaded",
671 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
673 EXPECT_CALL(*client_ptr_, DidStopLoading);
674 EXPECT_CALL(pdf_service_, UpdateContentRestrictions(kContentRestrictionPrint |
675 kContentRestrictionPaste |
676 kContentRestrictionCut |
677 kContentRestrictionCopy));
678 plugin_->DocumentLoadComplete();
680 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
681 plugin_->document_load_state_for_testing());
682 pdf_receiver_.FlushForTesting();
685 TEST_F(PdfViewWebPluginTest, DocumentLoadFailed) {
686 plugin_->CreateUrlLoader();
688 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadFailure"));
689 EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
690 plugin_->DocumentLoadFailed();
692 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kFailed,
693 plugin_->document_load_state_for_testing());
696 TEST_F(PdfViewWebPluginFullFrameTest, DocumentLoadFailed) {
697 plugin_->CreateUrlLoader();
699 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadFailure"));
700 EXPECT_CALL(*client_ptr_, DidStopLoading);
701 plugin_->DocumentLoadFailed();
703 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kFailed,
704 plugin_->document_load_state_for_testing());
707 TEST_F(PdfViewWebPluginTest, DocumentHasUnsupportedFeature) {
708 EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
709 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature1"));
710 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature2"));
712 // `HasUnsupportedFeature()` is not called if the viewer is not full-frame.
713 EXPECT_CALL(pdf_service_, HasUnsupportedFeature).Times(0);
715 plugin_->DocumentHasUnsupportedFeature("feature1");
716 plugin_->DocumentHasUnsupportedFeature("feature2");
718 pdf_receiver_.FlushForTesting();
721 TEST_F(PdfViewWebPluginTest, DocumentHasUnsupportedFeatureWithRepeatedFeature) {
722 // Metrics should only be recorded once per feature.
723 EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
724 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature"));
726 // `HasUnsupportedFeature()` is not called if the viewer is not full-frame.
727 EXPECT_CALL(pdf_service_, HasUnsupportedFeature).Times(0);
729 plugin_->DocumentHasUnsupportedFeature("feature");
730 plugin_->DocumentHasUnsupportedFeature("feature");
732 pdf_receiver_.FlushForTesting();
735 TEST_F(PdfViewWebPluginFullFrameTest, DocumentHasUnsupportedFeature) {
736 EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
737 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature1"));
738 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature2"));
740 // `HasUnsupportedFeature()` is called once for all features.
741 EXPECT_CALL(pdf_service_, HasUnsupportedFeature);
743 plugin_->DocumentHasUnsupportedFeature("feature1");
744 plugin_->DocumentHasUnsupportedFeature("feature2");
746 pdf_receiver_.FlushForTesting();
749 TEST_F(PdfViewWebPluginFullFrameTest,
750 DocumentHasUnsupportedFeatureWithRepeatedFeature) {
751 // Metrics should only be recorded once per feature.
752 EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
753 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature"));
755 // `HasUnsupportedFeature()` is called once for all features.
756 EXPECT_CALL(pdf_service_, HasUnsupportedFeature);
758 plugin_->DocumentHasUnsupportedFeature("feature");
759 plugin_->DocumentHasUnsupportedFeature("feature");
761 pdf_receiver_.FlushForTesting();
764 TEST_F(PdfViewWebPluginTest, DocumentLoadProgress) {
765 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
766 "type": "loadProgress",
769 plugin_->DocumentLoadProgress(10, 200);
772 TEST_F(PdfViewWebPluginTest, DocumentLoadProgressIgnoreSmall) {
773 plugin_->DocumentLoadProgress(2, 100);
775 EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
776 plugin_->DocumentLoadProgress(3, 100);
779 TEST_F(PdfViewWebPluginTest, DocumentLoadProgressMultipleSmall) {
780 plugin_->DocumentLoadProgress(2, 100);
782 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
783 "type": "loadProgress",
786 plugin_->DocumentLoadProgress(3, 100);
787 plugin_->DocumentLoadProgress(4, 100);
790 TEST_F(PdfViewWebPluginTest, EnableAccessibilityBeforeDocumentLoadComplete) {
791 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
793 plugin_->EnableAccessibility();
795 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
796 plugin_->CreateUrlLoader();
797 plugin_->DocumentLoadComplete();
800 TEST_F(PdfViewWebPluginTest,
801 EnableAccessibilityBeforeDocumentLoadCompleteRepeated) {
802 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
804 plugin_->EnableAccessibility();
805 plugin_->EnableAccessibility();
807 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
808 plugin_->CreateUrlLoader();
809 plugin_->DocumentLoadComplete();
812 TEST_F(PdfViewWebPluginTest, EnableAccessibilityAfterDocumentLoadComplete) {
813 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
815 plugin_->CreateUrlLoader();
816 plugin_->DocumentLoadComplete();
818 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
819 plugin_->EnableAccessibility();
822 TEST_F(PdfViewWebPluginTest,
823 EnableAccessibilityAfterDocumentLoadCompleteRepeated) {
824 plugin_->CreateUrlLoader();
825 plugin_->DocumentLoadComplete();
826 plugin_->EnableAccessibility();
828 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
830 plugin_->EnableAccessibility();
833 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithNoPermissions) {
834 EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
835 kContentRestrictionPaste | kContentRestrictionPrint,
836 plugin_->GetContentRestrictionsForTesting());
837 EXPECT_FALSE(plugin_->CanCopy());
840 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithCopyAllowed) {
841 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
842 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
843 .WillRepeatedly(Return(true));
845 EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste |
846 kContentRestrictionPrint,
847 plugin_->GetContentRestrictionsForTesting());
848 EXPECT_TRUE(plugin_->CanCopy());
851 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithPrintLowQualityAllowed) {
852 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
853 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
854 .WillRepeatedly(Return(true));
856 EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
857 kContentRestrictionPaste,
858 plugin_->GetContentRestrictionsForTesting());
861 TEST_F(PdfViewWebPluginTest,
862 GetContentRestrictionsWithCopyAndPrintLowQualityAllowed) {
863 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
864 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
865 .WillRepeatedly(Return(true));
866 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
867 .WillRepeatedly(Return(true));
869 EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste,
870 plugin_->GetContentRestrictionsForTesting());
873 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithPrintAllowed) {
874 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
875 EXPECT_CALL(*engine_ptr_,
876 HasPermission(DocumentPermission::kPrintHighQuality))
877 .WillRepeatedly(Return(true));
878 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
879 .WillRepeatedly(Return(true));
881 EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
882 kContentRestrictionPaste,
883 plugin_->GetContentRestrictionsForTesting());
886 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithCopyAndPrintAllowed) {
887 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
888 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
889 .WillRepeatedly(Return(true));
890 EXPECT_CALL(*engine_ptr_,
891 HasPermission(DocumentPermission::kPrintHighQuality))
892 .WillRepeatedly(Return(true));
893 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
894 .WillRepeatedly(Return(true));
896 EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste,
897 plugin_->GetContentRestrictionsForTesting());
900 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithNoPermissions) {
901 AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
903 EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
904 EXPECT_FALSE(doc_info.text_accessible);
905 EXPECT_FALSE(doc_info.text_copyable);
908 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithCopyAllowed) {
909 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
910 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
911 .WillRepeatedly(Return(true));
913 AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
915 EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
916 EXPECT_FALSE(doc_info.text_accessible);
917 EXPECT_TRUE(doc_info.text_copyable);
920 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithCopyAccessibleAllowed) {
921 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
922 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopyAccessible))
923 .WillRepeatedly(Return(true));
925 AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
927 EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
928 EXPECT_TRUE(doc_info.text_accessible);
929 EXPECT_FALSE(doc_info.text_copyable);
932 TEST_F(PdfViewWebPluginTest,
933 GetAccessibilityDocInfoWithCopyAndCopyAccessibleAllowed) {
934 EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
935 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
936 .WillRepeatedly(Return(true));
937 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopyAccessible))
938 .WillRepeatedly(Return(true));
940 AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
942 EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
943 EXPECT_TRUE(doc_info.text_accessible);
944 EXPECT_TRUE(doc_info.text_copyable);
947 TEST_F(PdfViewWebPluginTest, UpdateGeometrySetsPluginRect) {
948 EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
949 TestUpdateGeometrySetsPluginRect(
950 /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(4, 4, 12, 12),
951 /*expected_device_scale=*/2.0f,
952 /*expected_plugin_rect=*/gfx::Rect(4, 4, 12, 12));
955 TEST_F(PdfViewWebPluginTest,
956 UpdateGeometrySetsPluginRectOnVariousDeviceScales) {
957 struct UpdateGeometryParams {
958 // The plugin container's device scale.
961 // The window rect in CSS pixels.
962 gfx::Rect window_rect;
964 // The expected plugin device scale.
965 float expected_device_scale;
967 // The expected plugin rect in device pixels.
968 gfx::Rect expected_plugin_rect;
971 static constexpr UpdateGeometryParams kUpdateGeometryParams[] = {
972 {1.0f, gfx::Rect(3, 4, 5, 6), 1.0f, gfx::Rect(3, 4, 5, 6)},
973 {2.0f, gfx::Rect(3, 4, 5, 6), 2.0f, gfx::Rect(3, 4, 5, 6)},
976 for (const auto& params : kUpdateGeometryParams) {
977 TestUpdateGeometrySetsPluginRect(params.device_scale, params.window_rect,
978 params.expected_device_scale,
979 params.expected_plugin_rect);
983 TEST_F(PdfViewWebPluginTest, UpdateGeometrySetsPluginRectWithEmptyWindow) {
984 EXPECT_CALL(*engine_ptr_, ZoomUpdated).Times(0);
985 TestUpdateGeometrySetsPluginRect(
986 /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(2, 2, 0, 0),
987 /*expected_device_scale=*/1.0f, /*expected_plugin_rect=*/gfx::Rect());
990 TEST_F(PdfViewWebPluginTest, UpdateGeometryScroll) {
991 SetDocumentDimensions({100, 200});
993 EXPECT_CALL(*client_ptr_, GetScrollPosition)
994 .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
995 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(4));
996 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(6));
997 UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1000 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollStopped) {
1001 SetDocumentDimensions({100, 200});
1003 plugin_->OnMessage(ParseMessage(R"({
1004 "type": "stopScrolling",
1007 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1008 .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
1009 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition).Times(0);
1010 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition).Times(0);
1011 UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1014 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollUnderflow) {
1015 SetDocumentDimensions({100, 200});
1017 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1018 .WillRepeatedly(Return(gfx::PointF(-1.0f, -1.0f)));
1019 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(0));
1020 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(0));
1021 UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1024 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflow) {
1025 SetDocumentDimensions({100, 200});
1027 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1028 .WillRepeatedly(Return(gfx::PointF(96.0f, 195.0f)));
1029 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(95));
1030 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(194));
1031 UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1034 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflowZoomed) {
1035 SetDocumentDimensions({100, 200});
1036 SendViewportMessage(/*zoom=*/2.0);
1038 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1039 .WillRepeatedly(Return(gfx::PointF(196.0f, 395.0f)));
1040 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(195));
1041 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(394));
1042 UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1045 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollScaled) {
1046 SetDocumentDimensions({100, 200});
1048 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1049 .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
1050 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(4));
1051 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(6));
1052 UpdatePluginGeometryWithoutWaiting(2.0f, gfx::Rect(3, 4, 5, 6));
1055 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflowScaled) {
1056 SetDocumentDimensions({100, 200});
1058 EXPECT_CALL(*client_ptr_, GetScrollPosition)
1059 .WillRepeatedly(Return(gfx::PointF(195.0f, 395.0f)));
1060 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(194));
1061 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(394));
1062 UpdatePluginGeometryWithoutWaiting(2.0f, gfx::Rect(3, 4, 5, 6));
1065 TEST_F(PdfViewWebPluginTest, SetCaretPosition) {
1066 SetDocumentDimensions({16, 9});
1067 UpdatePluginGeometryWithoutWaiting(1.0f, {10, 20, 20, 5});
1069 EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(2, 3)));
1070 plugin_->SetCaretPosition({4.0f, 3.0f});
1073 TEST_F(PdfViewWebPluginTest, SetCaretPositionNegativeOrigin) {
1074 SetDocumentDimensions({16, 9});
1075 UpdatePluginGeometryWithoutWaiting(1.0f, {-10, -20, 20, 5});
1077 EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(2, 3)));
1078 plugin_->SetCaretPosition({4.0f, 3.0f});
1081 TEST_F(PdfViewWebPluginTest, SetCaretPositionFractional) {
1082 SetDocumentDimensions({16, 9});
1083 UpdatePluginGeometryWithoutWaiting(1.0f, {10, 20, 20, 5});
1085 EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(1, 2)));
1086 plugin_->SetCaretPosition({3.9f, 2.9f});
1089 TEST_F(PdfViewWebPluginTest, SetCaretPositionScaled) {
1090 SetDocumentDimensions({16, 9});
1091 UpdatePluginGeometryWithoutWaiting(2.0f, {20, 40, 40, 10});
1093 EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(4, 6)));
1094 plugin_->SetCaretPosition({4.0f, 3.0f});
1097 TEST_F(PdfViewWebPluginTest, PaintEmptySnapshots) {
1098 TestPaintEmptySnapshots(/*device_scale=*/4.0f,
1099 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1100 /*paint_rect=*/gfx::Rect(5, 5, 15, 15),
1101 /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1104 TEST_F(PdfViewWebPluginTest, PaintSnapshots) {
1105 TestPaintSnapshots(/*device_scale=*/4.0f,
1106 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1107 /*paint_rect=*/gfx::Rect(5, 5, 15, 15),
1108 /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1111 TEST_F(PdfViewWebPluginTest, PaintSnapshotsWithVariousDeviceScales) {
1112 static constexpr PaintParams kPaintWithVariousScalesParams[] = {
1113 {0.4f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1114 gfx::Rect(10, 10, 28, 28)},
1115 {1.0f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1116 gfx::Rect(10, 10, 28, 28)},
1117 {4.0f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1118 gfx::Rect(10, 10, 28, 28)},
1121 for (const auto& params : kPaintWithVariousScalesParams) {
1122 TestPaintSnapshots(params.device_scale, params.window_rect,
1123 params.paint_rect, params.expected_clipped_rect);
1127 TEST_F(PdfViewWebPluginTest, PaintSnapshotsWithVariousRectPositions) {
1128 static constexpr PaintParams kPaintWithVariousPositionsParams[] = {
1129 // The window origin falls outside the `paint_rect` area.
1130 {4.0f, gfx::Rect(10, 10, 20, 20), gfx::Rect(5, 5, 15, 15),
1131 gfx::Rect(10, 10, 10, 10)},
1132 // The window origin falls within the `paint_rect` area.
1133 {4.0f, gfx::Rect(4, 4, 20, 20), gfx::Rect(8, 8, 15, 15),
1134 gfx::Rect(8, 8, 15, 15)},
1137 for (const auto& params : kPaintWithVariousPositionsParams) {
1138 TestPaintSnapshots(params.device_scale, params.window_rect,
1139 params.paint_rect, params.expected_clipped_rect);
1143 TEST_F(PdfViewWebPluginTest, OnPaintWithMultiplePaintRects) {
1144 SetDocumentDimensions({100, 200});
1145 UpdatePluginGeometryWithoutWaiting(/*device_scale=*/1.0f,
1146 gfx::Rect(0, 0, 40, 40));
1148 EXPECT_CALL(*engine_ptr_, Paint)
1150 [](const gfx::Rect& rect, SkBitmap& /*image_data*/,
1151 std::vector<gfx::Rect>& ready,
1152 std::vector<gfx::Rect>& /*pending*/) { ready.push_back(rect); });
1153 std::vector<PaintReadyRect> ready;
1154 std::vector<gfx::Rect> pending;
1156 /*paint_rects=*/{gfx::Rect(5, 5, 10, 10), gfx::Rect(20, 20, 10, 10)},
1159 // Expect three paints: an initial background-clearing paint, and one for each
1160 // requested paint rectangle.
1161 ASSERT_THAT(ready, SizeIs(3));
1162 EXPECT_THAT(pending, IsEmpty());
1164 EXPECT_EQ(gfx::Rect(0, 0, 90, 90), ready[0].rect());
1165 EXPECT_TRUE(ready[0].flush_now());
1167 EXPECT_EQ(gfx::Rect(5, 5, 10, 10), ready[1].rect());
1168 EXPECT_FALSE(ready[1].flush_now());
1170 EXPECT_EQ(gfx::Rect(20, 20, 10, 10), ready[2].rect());
1171 EXPECT_FALSE(ready[2].flush_now());
1173 // All the requested paints should share the same `SkImage`.
1174 EXPECT_NE(&ready[0].image(), &ready[1].image());
1175 EXPECT_EQ(&ready[1].image(), &ready[2].image());
1178 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithIdentity) {
1179 plugin_->UpdateLayerTransform(1.0f, gfx::Vector2dF());
1180 TestPaintSnapshots(/*device_scale=*/4.0f,
1181 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1182 /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1183 /*expected_clipped_rect=*/gfx::Rect(10, 10, 20, 20));
1186 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithScale) {
1187 plugin_->UpdateLayerTransform(0.5f, gfx::Vector2dF());
1188 TestPaintSnapshots(/*device_scale=*/4.0f,
1189 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1190 /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1191 /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1194 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithTranslate) {
1195 plugin_->UpdateLayerTransform(1.0f, gfx::Vector2dF(-1.25, 1.25));
1196 TestPaintSnapshots(/*device_scale=*/4.0f,
1197 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1198 /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1199 /*expected_clipped_rect=*/gfx::Rect(10, 15, 15, 15));
1202 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithScaleAndTranslate) {
1203 plugin_->UpdateLayerTransform(0.5f, gfx::Vector2dF(-1.25, 1.25));
1204 TestPaintSnapshots(/*device_scale=*/4.0f,
1205 /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1206 /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1207 /*expected_clipped_rect=*/gfx::Rect(10, 15, 5, 10));
1210 TEST_F(PdfViewWebPluginTest, HandleViewportMessageBeforeDocumentLoadComplete) {
1211 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(DocumentLayout::Options()));
1212 EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
1214 plugin_->OnMessage(ParseMessage(R"({
1216 "userInitiated": false,
1220 "defaultPageOrientation": 0,
1221 "twoUpViewEnabled": false,
1229 TEST_F(PdfViewWebPluginTest, HandleViewportMessageAfterDocumentLoadComplete) {
1230 plugin_->DocumentLoadComplete();
1232 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(DocumentLayout::Options()));
1233 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1234 "type": "loadProgress",
1238 plugin_->OnMessage(ParseMessage(R"({
1240 "userInitiated": false,
1244 "defaultPageOrientation": 0,
1245 "twoUpViewEnabled": false,
1253 TEST_F(PdfViewWebPluginTest, HandleViewportMessageSubsequently) {
1254 plugin_->OnMessage(ParseMessage(R"({
1256 "userInitiated": false,
1260 "defaultPageOrientation": 0,
1261 "twoUpViewEnabled": false,
1268 DocumentLayout::Options two_up_options;
1269 two_up_options.set_page_spread(DocumentLayout::PageSpread::kTwoUpOdd);
1270 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(two_up_options));
1271 EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
1273 plugin_->OnMessage(ParseMessage(R"({
1275 "userInitiated": false,
1279 "defaultPageOrientation": 0,
1280 "twoUpViewEnabled": true,
1288 TEST_F(PdfViewWebPluginTest, HandleViewportMessageScroll) {
1289 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
1290 .WillRepeatedly(Return(gfx::Size(16, 9)));
1291 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(2));
1292 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
1294 plugin_->OnMessage(ParseMessage(R"({
1296 "userInitiated": false,
1300 "defaultPageOrientation": 0,
1301 "twoUpViewEnabled": false,
1309 TEST_F(PdfViewWebPluginTest, HandleViewportMessageScrollRightToLeft) {
1310 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
1311 .WillRepeatedly(Return(gfx::Size(16, 9)));
1312 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(2));
1313 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
1315 plugin_->OnMessage(ParseMessage(R"({
1317 "userInitiated": false,
1321 "defaultPageOrientation": 0,
1322 "twoUpViewEnabled": false,
1330 TEST_F(PdfViewWebPluginTest, HandleSetBackgroundColorMessage) {
1331 ASSERT_NE(SK_ColorGREEN, plugin_->GetBackgroundColor());
1333 base::Value::Dict message;
1334 message.Set("type", "setBackgroundColor");
1335 message.Set("color", static_cast<double>(SK_ColorGREEN));
1336 plugin_->OnMessage(message);
1338 EXPECT_EQ(SK_ColorGREEN, plugin_->GetBackgroundColor());
1341 TEST_F(PdfViewWebPluginTest, HandleInputEvent) {
1342 UpdatePluginGeometryWithoutWaiting(2.0f, {0, 0, 20, 20});
1344 EXPECT_CALL(*engine_ptr_, HandleInputEvent)
1345 .WillRepeatedly([](const blink::WebInputEvent& event) {
1346 if (!blink::WebInputEvent::IsMouseEventType(event.GetType())) {
1347 ADD_FAILURE() << "Unexpected event type: " << event.GetType();
1351 const auto& mouse_event =
1352 static_cast<const blink::WebMouseEvent&>(event);
1353 EXPECT_EQ(blink::WebInputEvent::Type::kMouseDown,
1354 mouse_event.GetType());
1355 EXPECT_EQ(gfx::PointF(10.0f, 40.0f), mouse_event.PositionInWidget());
1359 blink::WebMouseEvent mouse_event;
1360 mouse_event.SetType(blink::WebInputEvent::Type::kMouseDown);
1361 mouse_event.SetPositionInWidget(10.0f, 20.0f);
1363 ui::Cursor dummy_cursor;
1364 EXPECT_EQ(blink::WebInputEventResult::kHandledApplication,
1365 plugin_->HandleInputEvent(
1366 blink::WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()),
1370 class PdfViewWebPluginImeTest : public PdfViewWebPluginTest {
1372 void TestImeSetCompositionForPlugin(const blink::WebString& text) {
1373 EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1374 plugin_->ImeSetCompositionForPlugin(text, std::vector<ui::ImeTextSpan>(),
1376 /*selection_start=*/0,
1377 /*selection_end=*/0);
1380 void TestImeFinishComposingTextForPlugin(
1381 const blink::WebString& expected_text) {
1382 InSequence sequence;
1383 std::u16string expected_text16 = expected_text.Utf16();
1384 if (expected_text16.size()) {
1385 for (const auto& c : expected_text16) {
1386 base::StringPiece16 expected_key(&c, 1);
1387 EXPECT_CALL(*engine_ptr_,
1388 HandleInputEvent(IsExpectedImeKeyEvent(expected_key)))
1389 .WillOnce(Return(true));
1392 EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1394 plugin_->ImeFinishComposingTextForPlugin(false);
1397 void TestImeCommitTextForPlugin(const blink::WebString& text) {
1398 InSequence sequence;
1399 std::u16string expected_text16 = text.Utf16();
1400 if (expected_text16.size()) {
1401 for (const auto& c : expected_text16) {
1402 base::StringPiece16 event(&c, 1);
1403 EXPECT_CALL(*engine_ptr_,
1404 HandleInputEvent(IsExpectedImeKeyEvent(event)))
1405 .WillOnce(Return(true));
1408 EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1410 plugin_->ImeCommitTextForPlugin(text, std::vector<ui::ImeTextSpan>(),
1412 /*relative_cursor_pos=*/0);
1416 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishAscii) {
1417 const blink::WebString text = blink::WebString::FromASCII("input");
1418 TestImeSetCompositionForPlugin(text);
1419 TestImeFinishComposingTextForPlugin(text);
1422 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishUnicode) {
1423 const blink::WebString text = blink::WebString::FromUTF16(u"你好");
1424 TestImeSetCompositionForPlugin(text);
1425 TestImeFinishComposingTextForPlugin(text);
1426 // Calling ImeFinishComposingTextForPlugin() again is a no-op.
1427 TestImeFinishComposingTextForPlugin("");
1430 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishEmpty) {
1431 const blink::WebString text;
1432 TestImeSetCompositionForPlugin(text);
1433 TestImeFinishComposingTextForPlugin(text);
1436 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginAscii) {
1437 const blink::WebString text = blink::WebString::FromASCII("a b");
1438 TestImeCommitTextForPlugin(text);
1441 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginUnicode) {
1442 const blink::WebString text = blink::WebString::FromUTF16(u"さようなら");
1443 TestImeCommitTextForPlugin(text);
1446 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginEmpty) {
1447 const blink::WebString text;
1448 TestImeCommitTextForPlugin(text);
1451 TEST_F(PdfViewWebPluginTest, SelectionChanged) {
1452 plugin_->EnableAccessibility();
1453 plugin_->DocumentLoadComplete();
1454 UpdatePluginGeometryWithoutWaiting(1.0f, {300, 56, 20, 5});
1455 SetDocumentDimensions({16, 9});
1457 AccessibilityViewportInfo viewport_info;
1458 EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1459 gfx::PointF(52.0f, 60.0f), 80));
1460 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1461 .WillOnce(SaveArg<0>(&viewport_info));
1462 plugin_->SelectionChanged({-10, -20, 30, 40}, {50, 60, 70, 80});
1464 EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1465 pdf_receiver_.FlushForTesting();
1468 TEST_F(PdfViewWebPluginTest, SelectionChangedNegativeOrigin) {
1469 plugin_->EnableAccessibility();
1470 plugin_->DocumentLoadComplete();
1471 UpdatePluginGeometryWithoutWaiting(1.0f, {-300, -56, 20, 5});
1472 SetDocumentDimensions({16, 9});
1474 AccessibilityViewportInfo viewport_info;
1475 EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1476 gfx::PointF(52.0f, 60.0f), 80));
1477 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1478 .WillOnce(SaveArg<0>(&viewport_info));
1479 plugin_->SelectionChanged({-10, -20, 30, 40}, {50, 60, 70, 80});
1481 EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1482 pdf_receiver_.FlushForTesting();
1485 TEST_F(PdfViewWebPluginTest, SelectionChangedScaled) {
1486 plugin_->EnableAccessibility();
1487 plugin_->DocumentLoadComplete();
1488 UpdatePluginGeometryWithoutWaiting(2.0f, {600, 112, 40, 10});
1489 SetDocumentDimensions({16, 9});
1491 AccessibilityViewportInfo viewport_info;
1492 EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1493 gfx::PointF(52.0f, 60.0f), 80));
1494 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1495 .WillOnce(SaveArg<0>(&viewport_info));
1496 plugin_->SelectionChanged({-20, -40, 60, 80}, {100, 120, 140, 160});
1498 EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1499 pdf_receiver_.FlushForTesting();
1502 TEST_F(PdfViewWebPluginTest, ChangeTextSelection) {
1503 ASSERT_FALSE(plugin_->HasSelection());
1504 ASSERT_TRUE(plugin_->SelectionAsText().IsEmpty());
1505 ASSERT_TRUE(plugin_->SelectionAsMarkup().IsEmpty());
1507 static constexpr char kSelectedText[] = "1234";
1508 EXPECT_CALL(*client_ptr_,
1509 TextSelectionChanged(blink::WebString::FromUTF8(kSelectedText), 0,
1512 plugin_->SetSelectedText(kSelectedText);
1513 EXPECT_TRUE(plugin_->HasSelection());
1514 EXPECT_EQ(kSelectedText, plugin_->SelectionAsText().Utf8());
1515 EXPECT_EQ(kSelectedText, plugin_->SelectionAsMarkup().Utf8());
1517 static constexpr char kEmptyText[] = "";
1518 EXPECT_CALL(*client_ptr_,
1519 TextSelectionChanged(blink::WebString::FromUTF8(kEmptyText), 0,
1521 plugin_->SetSelectedText(kEmptyText);
1522 EXPECT_FALSE(plugin_->HasSelection());
1523 EXPECT_TRUE(plugin_->SelectionAsText().IsEmpty());
1524 EXPECT_TRUE(plugin_->SelectionAsMarkup().IsEmpty());
1527 TEST_F(PdfViewWebPluginTest, FormTextFieldFocusChangeUpdatesTextInputType) {
1528 ASSERT_EQ(blink::WebTextInputType::kWebTextInputTypeNone,
1529 plugin_->GetPluginTextInputType());
1531 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeText);
1532 plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
1534 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1535 plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
1537 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeText);
1538 plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
1540 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1541 plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNonText);
1544 TEST_F(PdfViewWebPluginTest, SearchString) {
1545 static constexpr char16_t kPattern[] = u"fox";
1546 static constexpr char16_t kTarget[] =
1547 u"The quick brown fox jumped over the lazy Fox";
1550 static constexpr PDFEngine::Client::SearchStringResult kExpectation[] = {
1553 plugin_->SearchString(kTarget, kPattern, /*case_sensitive=*/true),
1554 Pointwise(SearchStringResultEq(), kExpectation));
1557 static constexpr PDFEngine::Client::SearchStringResult kExpectation[] = {
1560 plugin_->SearchString(kTarget, kPattern, /*case_sensitive=*/false),
1561 Pointwise(SearchStringResultEq(), kExpectation));
1565 TEST_F(PdfViewWebPluginTest, UpdateFocus) {
1566 MockFunction<void(int checkpoint_num)> checkpoint;
1569 InSequence sequence;
1571 // Focus false -> true: Triggers updates.
1572 EXPECT_CALL(*client_ptr_, UpdateTextInputState);
1573 EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1574 EXPECT_CALL(checkpoint, Call(1));
1576 // Focus true -> true: No updates.
1577 EXPECT_CALL(checkpoint, Call(2));
1579 // Focus true -> false: Triggers updates. `UpdateTextInputState` is called
1580 // twice because it also gets called due to
1581 // `PDFiumEngine::UpdateFocus(false)`.
1582 EXPECT_CALL(*client_ptr_, UpdateTextInputState).Times(2);
1583 EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1584 EXPECT_CALL(checkpoint, Call(3));
1586 // Focus false -> false: No updates.
1587 EXPECT_CALL(checkpoint, Call(4));
1589 // Focus false -> true: Triggers updates.
1590 EXPECT_CALL(*client_ptr_, UpdateTextInputState);
1591 EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1594 // The focus type does not matter in this test.
1595 plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1597 plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1599 plugin_->UpdateFocus(/*focused=*/false, blink::mojom::FocusType::kNone);
1601 plugin_->UpdateFocus(/*focused=*/false, blink::mojom::FocusType::kNone);
1603 plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1606 TEST_F(PdfViewWebPluginTest, ShouldDispatchImeEventsToPlugin) {
1607 ASSERT_TRUE(plugin_->ShouldDispatchImeEventsToPlugin());
1610 TEST_F(PdfViewWebPluginTest, CaretChange) {
1611 EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
1612 UpdatePluginGeometry(
1613 /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(12, 24, 36, 48));
1614 plugin_->CaretChanged(gfx::Rect(10, 20, 30, 40));
1615 EXPECT_EQ(gfx::Rect(28, 20, 30, 40), plugin_->GetPluginCaretBounds());
1618 TEST_F(PdfViewWebPluginTest, EnteredEditMode) {
1619 EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1620 EXPECT_CALL(*client_ptr_, PostMessage).Times(AnyNumber());
1621 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1622 "type": "setIsEditing",
1624 plugin_->EnteredEditMode();
1626 pdf_receiver_.FlushForTesting();
1629 TEST_F(PdfViewWebPluginTest, NotifyNumberOfFindResultsChanged) {
1630 plugin_->StartFind("x", /*case_sensitive=*/false, /*identifier=*/123);
1632 const std::vector<gfx::Rect> tickmarks = {gfx::Rect(1, 2), gfx::Rect(3, 4)};
1633 plugin_->UpdateTickMarks(tickmarks);
1635 EXPECT_CALL(*client_ptr_, ReportFindInPageTickmarks(tickmarks));
1636 EXPECT_CALL(*client_ptr_, ReportFindInPageMatchCount(123, 5, true));
1637 plugin_->NotifyNumberOfFindResultsChanged(/*total=*/5, /*final_result=*/true);
1640 TEST_F(PdfViewWebPluginTest, OnDocumentLoadComplete) {
1641 base::Value::Dict metadata;
1642 metadata.Set("fileSize", "0 B");
1643 metadata.Set("linearized", false);
1644 metadata.Set("pageSize", "Varies");
1645 metadata.Set("canSerializeDocument", true);
1647 base::Value::Dict message;
1648 message.Set("type", "metadata");
1649 message.Set("metadataData", std::move(metadata));
1651 EXPECT_CALL(*client_ptr_, PostMessage);
1652 EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(message))));
1653 plugin_->DocumentLoadComplete();
1656 class PdfViewWebPluginWithDocInfoTest : public PdfViewWebPluginTest {
1658 class TestPDFiumEngineWithDocInfo : public TestPDFiumEngine {
1660 explicit TestPDFiumEngineWithDocInfo(PDFEngine::Client* client)
1661 : TestPDFiumEngine(client) {
1662 InitializeDocumentAttachments();
1663 InitializeDocumentMetadata();
1666 base::Value::List GetBookmarks() override {
1667 // Create `bookmark1` which navigates to an in-doc position. This bookmark
1668 // will be in the top-level bookmark list.
1669 base::Value::Dict bookmark1;
1670 bookmark1.Set("title", "Bookmark 1");
1671 bookmark1.Set("page", 2);
1672 bookmark1.Set("x", 10);
1673 bookmark1.Set("y", 20);
1674 bookmark1.Set("zoom", 2.0);
1676 // Create `bookmark2` which navigates to a web page. This bookmark will be
1677 // a child of `bookmark1`.
1678 base::Value::Dict bookmark2;
1679 bookmark2.Set("title", "Bookmark 2");
1680 bookmark2.Set("uri", "test.com");
1682 base::Value::List children_of_bookmark1;
1683 children_of_bookmark1.Append(std::move(bookmark2));
1684 bookmark1.Set("children", std::move(children_of_bookmark1));
1686 // Create the top-level bookmark list.
1687 base::Value::List bookmarks;
1688 bookmarks.Append(std::move(bookmark1));
1692 absl::optional<gfx::Size> GetUniformPageSizePoints() override {
1693 return gfx::Size(1000, 1200);
1697 void InitializeDocumentAttachments() {
1698 doc_attachment_info_list().resize(3);
1700 // A regular attachment.
1701 doc_attachment_info_list()[0].name = u"attachment1.txt";
1702 doc_attachment_info_list()[0].creation_date = u"D:20170712214438-07'00'";
1703 doc_attachment_info_list()[0].modified_date = u"D:20160115091400";
1704 doc_attachment_info_list()[0].is_readable = true;
1705 doc_attachment_info_list()[0].size_bytes = 13u;
1707 // An unreadable attachment.
1708 doc_attachment_info_list()[1].name = u"attachment2.pdf";
1709 doc_attachment_info_list()[1].is_readable = false;
1711 // A readable attachment that exceeds download size limit.
1712 doc_attachment_info_list()[2].name = u"attachment3.mov";
1713 doc_attachment_info_list()[2].is_readable = true;
1714 doc_attachment_info_list()[2].size_bytes =
1715 PdfViewWebPlugin::kMaximumSavedFileSize + 1;
1718 void InitializeDocumentMetadata() {
1719 metadata().version = PdfVersion::k1_7;
1720 metadata().size_bytes = 13u;
1721 metadata().page_count = 13u;
1722 metadata().linearized = true;
1723 metadata().has_attachments = true;
1724 metadata().form_type = FormType::kAcroForm;
1725 metadata().title = "Title";
1726 metadata().author = "Author";
1727 metadata().subject = "Subject";
1728 metadata().keywords = "Keywords";
1729 metadata().creator = "Creator";
1730 metadata().producer = "Producer";
1731 ASSERT_TRUE(base::Time::FromUTCString("2021-05-04 11:12:13",
1732 &metadata().creation_date));
1733 ASSERT_TRUE(base::Time::FromUTCString("2021-06-04 15:16:17",
1734 &metadata().mod_date));
1738 static base::Value::Dict CreateExpectedAttachmentsResponse() {
1739 base::Value::List attachments;
1741 base::Value::Dict attachment;
1742 attachment.Set("name", "attachment1.txt");
1743 attachment.Set("size", 13);
1744 attachment.Set("readable", true);
1745 attachments.Append(std::move(attachment));
1748 base::Value::Dict attachment;
1749 attachment.Set("name", "attachment2.pdf");
1750 attachment.Set("size", 0);
1751 attachment.Set("readable", false);
1752 attachments.Append(std::move(attachment));
1755 base::Value::Dict attachment;
1756 attachment.Set("name", "attachment3.mov");
1757 attachment.Set("size", -1);
1758 attachment.Set("readable", true);
1759 attachments.Append(std::move(attachment));
1762 base::Value::Dict message;
1763 message.Set("type", "attachments");
1764 message.Set("attachmentsData", std::move(attachments));
1768 static base::Value::Dict CreateExpectedBookmarksResponse(
1769 base::Value::List bookmarks) {
1770 base::Value::Dict message;
1771 message.Set("type", "bookmarks");
1772 message.Set("bookmarksData", std::move(bookmarks));
1776 static base::Value::Dict CreateExpectedMetadataResponse() {
1777 base::Value::Dict metadata;
1778 metadata.Set("version", "1.7");
1779 metadata.Set("fileSize", "13 B");
1780 metadata.Set("linearized", true);
1782 metadata.Set("title", "Title");
1783 metadata.Set("author", "Author");
1784 metadata.Set("subject", "Subject");
1785 metadata.Set("keywords", "Keywords");
1786 metadata.Set("creator", "Creator");
1787 metadata.Set("producer", "Producer");
1788 metadata.Set("creationDate", "5/4/21, 4:12:13 AM");
1789 metadata.Set("modDate", "6/4/21, 8:16:17 AM");
1790 metadata.Set("pageSize", "13.89 × 16.67 in (portrait)");
1791 metadata.Set("canSerializeDocument", true);
1793 base::Value::Dict message;
1794 message.Set("type", "metadata");
1795 message.Set("metadataData", std::move(metadata));
1799 void SetUpClient() override {
1800 EXPECT_CALL(*client_ptr_, CreateEngine).WillOnce([this]() {
1801 auto engine = std::make_unique<NiceMock<TestPDFiumEngineWithDocInfo>>(
1803 engine_ptr_ = engine.get();
1809 TEST_F(PdfViewWebPluginWithDocInfoTest, OnDocumentLoadComplete) {
1810 const base::Value::Dict expect_attachments =
1811 CreateExpectedAttachmentsResponse();
1812 const base::Value::Dict expect_bookmarks =
1813 CreateExpectedBookmarksResponse(engine_ptr_->GetBookmarks());
1814 const base::Value::Dict expect_metadata = CreateExpectedMetadataResponse();
1815 EXPECT_CALL(*client_ptr_, PostMessage);
1816 EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_attachments))));
1817 EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_bookmarks))));
1818 EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_metadata))));
1819 plugin_->DocumentLoadComplete();
1822 class PdfViewWebPluginSaveTest : public PdfViewWebPluginTest {
1824 static void AddDataToValue(base::span<const uint8_t> data,
1825 base::Value& value) {
1826 value.GetDict().Set("dataToSave", base::Value(data));
1829 void SetUpClient() override {
1830 // Ignore non-"saveData" `PdfViewWebPlugin::Client::PostMessage()` calls.
1831 EXPECT_CALL(*client_ptr_, PostMessage)
1832 .WillRepeatedly([](const base::Value::Dict& message) {
1833 EXPECT_NE("saveData", *message.FindString("type"));
1838 #if BUILDFLAG(ENABLE_INK)
1839 TEST_F(PdfViewWebPluginSaveTest, AnnotationInNonEditMode) {
1840 base::Value expected_response = base::test::ParseJson(R"({
1842 "token": "annotation-in-non-edit-mode",
1843 "fileName": "example.pdf",
1844 "editModeForTesting": false,
1846 AddDataToValue(base::make_span(TestPDFiumEngine::kLoadedData),
1849 EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1850 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1851 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1853 plugin_->OnMessage(ParseMessage(R"({
1855 "saveRequestType": 0,
1856 "token": "annotation-in-non-edit-mode",
1859 pdf_receiver_.FlushForTesting();
1862 TEST_F(PdfViewWebPluginSaveTest, AnnotationInEditMode) {
1863 plugin_->EnteredEditMode();
1864 pdf_receiver_.FlushForTesting();
1866 base::Value expected_response = base::test::ParseJson(R"({
1868 "token": "annotation-in-edit-mode",
1869 "fileName": "example.pdf",
1870 "editModeForTesting": true,
1872 AddDataToValue(base::make_span(TestPDFiumEngine::kSaveData),
1875 EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1876 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1877 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1879 plugin_->OnMessage(ParseMessage(R"({
1881 "saveRequestType": 0,
1882 "token": "annotation-in-edit-mode",
1885 pdf_receiver_.FlushForTesting();
1887 #endif // BUILDFLAG(ENABLE_INK)
1889 TEST_F(PdfViewWebPluginSaveTest, OriginalInNonEditMode) {
1891 InSequence pdf_service_sequence;
1893 EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
1896 SaveUrlAs(GURL(kPdfUrl), network::mojom::ReferrerPolicy::kDefault));
1897 EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
1900 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1901 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1902 "type": "consumeSaveToken",
1903 "token": "original-in-non-edit-mode",
1906 plugin_->OnMessage(ParseMessage(R"({
1908 "saveRequestType": 1,
1909 "token": "original-in-non-edit-mode",
1912 pdf_receiver_.FlushForTesting();
1915 TEST_F(PdfViewWebPluginSaveTest, OriginalInEditMode) {
1916 plugin_->EnteredEditMode();
1917 pdf_receiver_.FlushForTesting();
1920 InSequence pdf_service_sequence;
1922 EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
1925 SaveUrlAs(GURL(kPdfUrl), network::mojom::ReferrerPolicy::kDefault));
1926 EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1929 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1930 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1931 "type": "consumeSaveToken",
1932 "token": "original-in-edit-mode",
1935 plugin_->OnMessage(ParseMessage(R"({
1937 "saveRequestType": 1,
1938 "token": "original-in-edit-mode",
1941 pdf_receiver_.FlushForTesting();
1944 #if BUILDFLAG(ENABLE_INK)
1945 TEST_F(PdfViewWebPluginSaveTest, EditedInNonEditMode) {
1946 base::Value expected_response = base::test::ParseJson(R"({
1948 "token": "edited-in-non-edit-mode",
1949 "fileName": "example.pdf",
1950 "editModeForTesting": false,
1952 AddDataToValue(base::make_span(TestPDFiumEngine::kLoadedData),
1955 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1956 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1958 plugin_->OnMessage(ParseMessage(R"({
1960 "saveRequestType": 2,
1961 "token": "edited-in-non-edit-mode",
1964 #endif // BUILDFLAG(ENABLE_INK)
1966 TEST_F(PdfViewWebPluginSaveTest, EditedInEditMode) {
1967 plugin_->EnteredEditMode();
1969 base::Value expected_response = base::test::ParseJson(R"({
1971 "token": "edited-in-edit-mode",
1972 "fileName": "example.pdf",
1973 "editModeForTesting": true,
1975 AddDataToValue(base::make_span(TestPDFiumEngine::kSaveData),
1978 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1979 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1981 plugin_->OnMessage(ParseMessage(R"({
1983 "saveRequestType": 2,
1984 "token": "edited-in-edit-mode",
1988 class PdfViewWebPluginSubmitFormTest
1989 : public PdfViewWebPluginWithoutInitializeTest {
1991 void SubmitForm(const std::string& url,
1992 base::StringPiece form_data = "data") {
1993 EXPECT_TRUE(plugin_->InitializeForTesting());
1995 EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).WillOnce([this]() {
1996 auto associated_loader =
1997 std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
1998 EXPECT_CALL(*associated_loader, LoadAsynchronously)
1999 .WillOnce([this](const blink::WebURLRequest& request,
2000 blink::WebAssociatedURLLoaderClient* /*client*/) {
2001 // TODO(crbug.com/1322928): The `UrlLoader` created by `LoadUrl()`
2002 // and `SubmitForm()` shouldn't use different ownership semantics.
2003 // The loader created by `SubmitForm()` is owned by the plugin, and
2004 // cannot leak past the destruction of the plugin.
2005 request_.CopyFrom(request);
2007 return associated_loader;
2010 plugin_->SubmitForm(url, form_data.data(), form_data.size());
2013 void SubmitFailingForm(const std::string& url) {
2014 EXPECT_TRUE(plugin_->InitializeForTesting());
2016 EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
2018 constexpr base::StringPiece kFormData = "form data";
2019 plugin_->SubmitForm(url, kFormData.data(), kFormData.size());
2022 blink::WebURLRequest request_;
2025 TEST_F(PdfViewWebPluginSubmitFormTest, RequestMethod) {
2026 SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2028 SubmitForm(/*url=*/"");
2030 EXPECT_EQ(request_.HttpMethod().Utf8(), "POST");
2033 TEST_F(PdfViewWebPluginSubmitFormTest, RequestBody) {
2034 SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2036 constexpr base::StringPiece kFormData = "form data";
2037 SubmitForm(/*url=*/"", kFormData);
2039 blink::WebHTTPBody::Element element;
2040 EXPECT_EQ(request_.HttpBody().ElementCount(), 1u);
2041 ASSERT_TRUE(request_.HttpBody().ElementAt(0, element));
2042 ASSERT_EQ(element.type, blink::HTTPBodyElementType::kTypeData);
2043 EXPECT_THAT(element.data.Copy(), testing::ElementsAreArray(kFormData));
2046 TEST_F(PdfViewWebPluginSubmitFormTest, RelativeUrl) {
2047 SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2049 SubmitForm("relative_endpoint");
2051 EXPECT_EQ(request_.Url().GetString().Utf8(),
2052 "https://www.example.com/path/to/relative_endpoint");
2055 TEST_F(PdfViewWebPluginSubmitFormTest, NoRelativeUrl) {
2056 SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2060 EXPECT_EQ(request_.Url().GetString().Utf8(),
2061 "https://www.example.com/path/to/the.pdf");
2064 TEST_F(PdfViewWebPluginSubmitFormTest, AbsoluteUrl) {
2065 SetUpPluginWithUrl("https://a.example.com/path/to/the.pdf");
2067 SubmitForm("https://b.example.com/relative_endpoint");
2069 EXPECT_EQ(request_.Url().GetString().Utf8(),
2070 "https://b.example.com/relative_endpoint");
2073 TEST_F(PdfViewWebPluginSubmitFormTest, RelativeUrlInvalidDocumentUrl) {
2074 SetUpPluginWithUrl("https://www.%B%Ad.com/path/to/the.pdf");
2076 SubmitFailingForm("relative_endpoint");
2079 TEST_F(PdfViewWebPluginSubmitFormTest, AbsoluteUrlInvalidDocumentUrl) {
2080 SetUpPluginWithUrl("https://www.%B%Ad.com/path/to/the.pdf");
2082 SubmitFailingForm("https://wwww.example.com");
2085 class PdfViewWebPluginPrintTest : public PdfViewWebPluginTest {
2087 void SetUp() override {
2088 PdfViewWebPluginTest::SetUp();
2090 // Size must be at least 1 for conversion to `SkMemoryStream`.
2091 ON_CALL(*engine_ptr_, PrintPages)
2092 .WillByDefault(Return(std::vector<uint8_t>(1)));
2094 canvas_.sk_canvas()->SetPrintingMetafile(&metafile_);
2097 printing::MetafileSkia metafile_;
2100 TEST_F(PdfViewWebPluginPrintTest, HighQuality) {
2101 EXPECT_CALL(*engine_ptr_,
2102 HasPermission(DocumentPermission::kPrintHighQuality))
2103 .WillRepeatedly(Return(true));
2104 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2105 .WillRepeatedly(Return(true));
2106 ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2107 plugin_->PrintBegin(blink::WebPrintParams()));
2111 PrintPages(ElementsAre(0),
2112 Field(&blink::WebPrintParams::rasterize_pdf, IsFalse())));
2113 plugin_->PrintPage(0, canvas_.sk_canvas());
2114 plugin_->PrintEnd();
2117 TEST_F(PdfViewWebPluginPrintTest, HighQualityRasterized) {
2118 EXPECT_CALL(*engine_ptr_,
2119 HasPermission(DocumentPermission::kPrintHighQuality))
2120 .WillRepeatedly(Return(true));
2121 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2122 .WillRepeatedly(Return(true));
2124 blink::WebPrintParams params;
2125 params.rasterize_pdf = true;
2126 ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2127 plugin_->PrintBegin(params));
2131 PrintPages(ElementsAre(0),
2132 Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2133 plugin_->PrintPage(0, canvas_.sk_canvas());
2134 plugin_->PrintEnd();
2137 // Regression test for crbug.com/1307219.
2138 TEST_F(PdfViewWebPluginPrintTest, LowQuality) {
2139 EXPECT_CALL(*engine_ptr_,
2140 HasPermission(DocumentPermission::kPrintHighQuality))
2141 .WillRepeatedly(Return(false));
2142 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2143 .WillRepeatedly(Return(true));
2144 ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2145 plugin_->PrintBegin(blink::WebPrintParams()));
2149 PrintPages(ElementsAre(0),
2150 Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2151 plugin_->PrintPage(0, canvas_.sk_canvas());
2152 plugin_->PrintEnd();
2155 // Regression test for crbug.com/1307219.
2156 TEST_F(PdfViewWebPluginPrintTest, LowQualityRasterized) {
2157 EXPECT_CALL(*engine_ptr_,
2158 HasPermission(DocumentPermission::kPrintHighQuality))
2159 .WillRepeatedly(Return(false));
2160 EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2161 .WillRepeatedly(Return(true));
2163 blink::WebPrintParams params;
2164 params.rasterize_pdf = true;
2165 ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2166 plugin_->PrintBegin(params));
2170 PrintPages(ElementsAre(0),
2171 Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2172 plugin_->PrintPage(0, canvas_.sk_canvas());
2173 plugin_->PrintEnd();
2176 TEST_F(PdfViewWebPluginPrintTest, Disabled) {
2177 EXPECT_EQ(0, plugin_->PrintBegin(blink::WebPrintParams()));
2180 TEST_F(PdfViewWebPluginPrintTest, DisabledRasterized) {
2181 blink::WebPrintParams params;
2182 params.rasterize_pdf = true;
2183 EXPECT_EQ(0, plugin_->PrintBegin(params));
2186 class PdfViewWebPluginPrintPreviewTest : public PdfViewWebPluginTest {
2188 void SetUpClient() override {
2189 EXPECT_CALL(*client_ptr_, GetEmbedderOriginString)
2190 .WillRepeatedly(Return("chrome://print/"));
2194 TEST_F(PdfViewWebPluginPrintPreviewTest, HandleResetPrintPreviewModeMessage) {
2195 EXPECT_CALL(*client_ptr_, CreateEngine)
2196 .WillOnce([](PDFEngine::Client* client,
2197 PDFiumFormFiller::ScriptOption script_option) {
2198 EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript, script_option);
2200 auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
2201 EXPECT_CALL(*engine, ZoomUpdated);
2202 EXPECT_CALL(*engine, PageOffsetUpdated);
2203 EXPECT_CALL(*engine, PluginSizeUpdated);
2204 EXPECT_CALL(*engine, SetGrayscale(false));
2208 plugin_->OnMessage(ParseMessage(R"({
2209 "type": "resetPrintPreviewMode",
2210 "url": "chrome-untrusted://print/0/0/print.pdf",
2216 TEST_F(PdfViewWebPluginPrintPreviewTest,
2217 HandleResetPrintPreviewModeMessageSetGrayscale) {
2218 EXPECT_CALL(*client_ptr_, CreateEngine)
2219 .WillOnce([](PDFEngine::Client* client,
2220 PDFiumFormFiller::ScriptOption /*script_option*/) {
2221 auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
2222 EXPECT_CALL(*engine, SetGrayscale(true));
2226 plugin_->OnMessage(ParseMessage(R"({
2227 "type": "resetPrintPreviewMode",
2228 "url": "chrome-untrusted://print/0/0/print.pdf",
2234 TEST_F(PdfViewWebPluginPrintPreviewTest, DocumentLoadComplete) {
2235 plugin_->OnMessage(ParseMessage(R"({
2236 "type": "resetPrintPreviewMode",
2237 "url": "chrome-untrusted://print/0/0/print.pdf",
2242 EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
2243 EXPECT_CALL(*client_ptr_, PostMessage);
2244 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2245 "type": "formFocusChange",
2248 ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
2249 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2250 "type": "printPreviewLoaded",
2252 EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
2254 EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
2255 EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
2256 plugin_->DocumentLoadComplete();
2258 EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
2259 plugin_->document_load_state_for_testing());
2260 pdf_receiver_.FlushForTesting();
2263 TEST_F(PdfViewWebPluginPrintPreviewTest,
2264 DocumentLoadProgressResetByResetPrintPreviewModeMessage) {
2265 plugin_->DocumentLoadProgress(2, 100);
2267 plugin_->OnMessage(ParseMessage(R"({
2268 "type": "resetPrintPreviewMode",
2269 "url": "chrome-untrusted://print/123/0/print.pdf",
2274 EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2275 "type": "loadProgress",
2278 plugin_->DocumentLoadProgress(3, 100);
2281 TEST_F(PdfViewWebPluginPrintPreviewTest,
2282 DocumentLoadProgressNotResetByLoadPreviewPageMessage) {
2283 plugin_->OnMessage(ParseMessage(R"({
2284 "type": "resetPrintPreviewMode",
2285 "url": "chrome-untrusted://print/123/0/print.pdf",
2290 plugin_->DocumentLoadProgress(2, 100);
2292 plugin_->OnMessage(ParseMessage(R"({
2293 "type": "loadPreviewPage",
2294 "url": "chrome-untrusted://print/123/1/print.pdf",
2298 EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
2299 plugin_->DocumentLoadProgress(3, 100);
2302 TEST_F(PdfViewWebPluginPrintPreviewTest,
2303 HandleViewportMessageScrollRightToLeft) {
2304 EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
2305 .WillRepeatedly(Return(gfx::Size(16, 9)));
2306 EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(14));
2307 EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
2309 plugin_->OnMessage(ParseMessage(R"({
2311 "userInitiated": false,
2315 "defaultPageOrientation": 0,
2316 "twoUpViewEnabled": false,
2324 } // namespace chrome_pdf