fixup! [M120 Migration][NaCl][PPFWK] Upgradable pepper plugin requirement
[platform/framework/web/chromium-efl.git] / pdf / pdf_view_web_plugin_unittest.cc
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.
4
5 #include "pdf/pdf_view_web_plugin.h"
6
7 #include <stdint.h>
8
9 #include <functional>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
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/task/single_thread_task_runner.h"
22 #include "base/test/bind.h"
23 #include "base/test/scoped_feature_list.h"
24 #include "base/test/values_test_util.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "cc/paint/paint_canvas.h"
28 #include "cc/test/pixel_comparator.h"
29 #include "cc/test/pixel_test_utils.h"
30 #include "mojo/public/cpp/bindings/associated_receiver.h"
31 #include "mojo/public/cpp/bindings/associated_remote.h"
32 #include "net/cookies/site_for_cookies.h"
33 #include "pdf/accessibility_structs.h"
34 #include "pdf/buildflags.h"
35 #include "pdf/content_restriction.h"
36 #include "pdf/document_layout.h"
37 #include "pdf/mojom/pdf.mojom.h"
38 #include "pdf/paint_ready_rect.h"
39 #include "pdf/pdf_accessibility_data_handler.h"
40 #include "pdf/pdf_accessibility_image_fetcher.h"
41 #include "pdf/pdf_features.h"
42 #include "pdf/test/mock_web_associated_url_loader.h"
43 #include "pdf/test/test_helpers.h"
44 #include "pdf/test/test_pdfium_engine.h"
45 #include "printing/metafile_skia.h"
46 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
47 #include "testing/gmock/include/gmock/gmock.h"
48 #include "testing/gtest/include/gtest/gtest.h"
49 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
50 #include "third_party/blink/public/common/input/web_input_event.h"
51 #include "third_party/blink/public/common/input/web_keyboard_event.h"
52 #include "third_party/blink/public/common/input/web_mouse_event.h"
53 #include "third_party/blink/public/common/loader/http_body_element_type.h"
54 #include "third_party/blink/public/platform/web_data.h"
55 #include "third_party/blink/public/platform/web_http_body.h"
56 #include "third_party/blink/public/platform/web_http_header_visitor.h"
57 #include "third_party/blink/public/platform/web_input_event_result.h"
58 #include "third_party/blink/public/platform/web_string.h"
59 #include "third_party/blink/public/platform/web_text_input_type.h"
60 #include "third_party/blink/public/platform/web_url.h"
61 #include "third_party/blink/public/platform/web_url_request.h"
62 #include "third_party/blink/public/platform/web_url_response.h"
63 #include "third_party/blink/public/web/web_associated_url_loader.h"
64 #include "third_party/blink/public/web/web_associated_url_loader_client.h"
65 #include "third_party/blink/public/web/web_plugin_container.h"
66 #include "third_party/blink/public/web/web_plugin_params.h"
67 #include "third_party/blink/public/web/web_print_params.h"
68 #include "third_party/skia/include/core/SkBitmap.h"
69 #include "third_party/skia/include/core/SkCanvas.h"
70 #include "third_party/skia/include/core/SkColor.h"
71 #include "third_party/skia/include/core/SkRefCnt.h"
72 #include "third_party/skia/include/core/SkSurface.h"
73 #include "ui/base/cursor/cursor.h"
74 #include "ui/events/blink/blink_event_util.h"
75 #include "ui/events/keycodes/dom/dom_code.h"
76 #include "ui/events/keycodes/dom/dom_key.h"
77 #include "ui/gfx/canvas.h"
78 #include "ui/gfx/geometry/point.h"
79 #include "ui/gfx/geometry/point_f.h"
80 #include "ui/gfx/geometry/rect.h"
81 #include "ui/gfx/geometry/size.h"
82 #include "ui/gfx/geometry/skia_conversions.h"
83 #include "ui/gfx/geometry/vector2d_f.h"
84 #include "ui/gfx/range/range.h"
85 #include "ui/latency/latency_info.h"
86 #include "url/gurl.h"
87
88 namespace chrome_pdf {
89
90 namespace {
91
92 using ::testing::AnyNumber;
93 using ::testing::ElementsAre;
94 using ::testing::ElementsAreArray;
95 using ::testing::Eq;
96 using ::testing::InSequence;
97 using ::testing::Invoke;
98 using ::testing::IsEmpty;
99 using ::testing::IsFalse;
100 using ::testing::IsTrue;
101 using ::testing::MockFunction;
102 using ::testing::NiceMock;
103 using ::testing::Pointwise;
104 using ::testing::Return;
105 using ::testing::SaveArg;
106 using ::testing::SizeIs;
107
108 // `kCanvasSize` needs to be big enough to hold plugin's snapshots during
109 // testing.
110 constexpr gfx::Size kCanvasSize(100, 100);
111
112 // Note: Make sure `kDefaultColor` is different from `kPaintColor` and the
113 // plugin's background color. This will help identify bitmap changes after
114 // painting.
115 constexpr SkColor kDefaultColor = SK_ColorGREEN;
116
117 constexpr SkColor kPaintColor = SK_ColorRED;
118
119 struct PaintParams {
120   // The plugin container's device scale.
121   float device_scale;
122
123   // The window area in CSS pixels.
124   gfx::Rect window_rect;
125
126   // The target painting area on the canvas in CSS pixels.
127   gfx::Rect paint_rect;
128
129   // The expected clipped area to be filled with paint color. The clipped area
130   // should be the intersection of `paint_rect` and `window_rect`.
131   gfx::Rect expected_clipped_rect;
132 };
133
134 MATCHER(SearchStringResultEq, "") {
135   PDFEngine::Client::SearchStringResult l = std::get<0>(arg);
136   PDFEngine::Client::SearchStringResult r = std::get<1>(arg);
137   return l.start_index == r.start_index && l.length == r.length;
138 }
139
140 MATCHER_P(IsExpectedImeKeyEvent, expected_text, "") {
141   if (arg.GetType() != blink::WebInputEvent::Type::kChar)
142     return false;
143
144   const auto& event = static_cast<const blink::WebKeyboardEvent&>(arg);
145   return event.GetModifiers() == blink::WebInputEvent::kNoModifiers &&
146          event.windows_key_code == expected_text[0] &&
147          event.native_key_code == expected_text[0] &&
148          event.dom_code == static_cast<int>(ui::DomCode::NONE) &&
149          event.dom_key == ui::DomKey::NONE && !event.is_system_key &&
150          !event.is_browser_shortcut && event.text == expected_text &&
151          event.unmodified_text == expected_text;
152 }
153
154 base::Value::Dict ParseMessage(base::StringPiece json) {
155   return std::move(base::test::ParseJson(json).GetDict());
156 }
157
158 // Generates the expected `SkBitmap` with `paint_color` filled in the expected
159 // clipped area and `kDefaultColor` as the background color.
160 SkBitmap GenerateExpectedBitmapForPaint(const gfx::Rect& expected_clipped_rect,
161                                         SkColor paint_color) {
162   sk_sp<SkSurface> expected_surface =
163       CreateSkiaSurfaceForTesting(kCanvasSize, kDefaultColor);
164   expected_surface->getCanvas()->clipIRect(
165       gfx::RectToSkIRect(expected_clipped_rect));
166   expected_surface->getCanvas()->clear(paint_color);
167
168   SkBitmap expected_bitmap;
169   expected_surface->makeImageSnapshot()->asLegacyBitmap(&expected_bitmap);
170   return expected_bitmap;
171 }
172
173 class MockHeaderVisitor : public blink::WebHTTPHeaderVisitor {
174  public:
175   MOCK_METHOD(void,
176               VisitHeader,
177               (const blink::WebString&, const blink::WebString&),
178               (override));
179 };
180
181 class MockPdfAccessibilityDataHandler : public PdfAccessibilityDataHandler {
182  public:
183   // PdfAccessibilityDataHandler:
184   MOCK_METHOD(void,
185               SetAccessibilityViewportInfo,
186               (AccessibilityViewportInfo),
187               (override));
188   MOCK_METHOD(void,
189               SetAccessibilityDocInfo,
190               (AccessibilityDocInfo),
191               (override));
192   MOCK_METHOD(void,
193               SetAccessibilityPageInfo,
194               (AccessibilityPageInfo,
195                std::vector<AccessibilityTextRunInfo>,
196                std::vector<AccessibilityCharInfo>,
197                AccessibilityPageObjects),
198               (override));
199 };
200
201 class FakePdfViewWebPluginClient : public PdfViewWebPlugin::Client {
202  public:
203   FakePdfViewWebPluginClient() {
204     ON_CALL(*this, CreateAssociatedURLLoader).WillByDefault([]() {
205       auto associated_loader =
206           std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
207       ON_CALL(*associated_loader, LoadAsynchronously)
208           .WillByDefault([](const blink::WebURLRequest& /*request*/,
209                             blink::WebAssociatedURLLoaderClient* client) {
210             // TODO(crbug.com/1322928): Must trigger callback to free
211             // `UrlLoader`.
212             client->DidReceiveResponse(blink::WebURLResponse());
213             client->DidFinishLoading();
214           });
215       return associated_loader;
216     });
217     ON_CALL(*this, GetEmbedderOriginString)
218         .WillByDefault(
219             Return("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/"));
220     ON_CALL(*this, HasFrame).WillByDefault(Return(true));
221   }
222
223   // PdfViewWebPlugin::Client:
224   MOCK_METHOD(std::unique_ptr<base::Value>,
225               FromV8Value,
226               (v8::Local<v8::Value>, v8::Local<v8::Context>),
227               (override));
228
229   MOCK_METHOD(base::WeakPtr<Client>, GetWeakPtr, (), (override));
230
231   MOCK_METHOD(std::unique_ptr<PDFiumEngine>,
232               CreateEngine,
233               (PDFEngine::Client*, PDFiumFormFiller::ScriptOption),
234               (override));
235
236   MOCK_METHOD(void,
237               SetPluginContainer,
238               (blink::WebPluginContainer*),
239               (override));
240   MOCK_METHOD(blink::WebPluginContainer*, PluginContainer, (), (override));
241
242   MOCK_METHOD(net::SiteForCookies, SiteForCookies, (), (const override));
243
244   MOCK_METHOD(blink::WebURL,
245               CompleteURL,
246               (const blink::WebString&),
247               (const override));
248
249   MOCK_METHOD(void, PostMessage, (base::Value::Dict), (override));
250
251   MOCK_METHOD(void, Invalidate, (), (override));
252
253   MOCK_METHOD(void,
254               RequestTouchEventType,
255               (blink::WebPluginContainer::TouchEventRequestType),
256               (override));
257
258   MOCK_METHOD(void, ReportFindInPageMatchCount, (int, int, bool), (override));
259
260   MOCK_METHOD(void, ReportFindInPageSelection, (int, int, bool), (override));
261
262   MOCK_METHOD(void,
263               ReportFindInPageTickmarks,
264               (const std::vector<gfx::Rect>&),
265               (override));
266
267   MOCK_METHOD(float, DeviceScaleFactor, (), (override));
268
269   MOCK_METHOD(gfx::PointF, GetScrollPosition, (), (override));
270
271   MOCK_METHOD(void, UsePluginAsFindHandler, (), (override));
272
273   MOCK_METHOD(void,
274               SetReferrerForRequest,
275               (blink::WebURLRequest&, const blink::WebURL&),
276               (override));
277
278   MOCK_METHOD(void, Alert, (const blink::WebString&), (override));
279
280   MOCK_METHOD(bool, Confirm, (const blink::WebString&), (override));
281
282   MOCK_METHOD(blink::WebString,
283               Prompt,
284               (const blink::WebString&, const blink::WebString&),
285               (override));
286
287   MOCK_METHOD(void,
288               TextSelectionChanged,
289               (const blink::WebString&, uint32_t, const gfx::Range&),
290               (override));
291
292   MOCK_METHOD(std::unique_ptr<blink::WebAssociatedURLLoader>,
293               CreateAssociatedURLLoader,
294               (const blink::WebAssociatedURLLoaderOptions&),
295               (override));
296
297   MOCK_METHOD(void, UpdateTextInputState, (), (override));
298
299   MOCK_METHOD(void, UpdateSelectionBounds, (), (override));
300
301   MOCK_METHOD(std::string, GetEmbedderOriginString, (), (override));
302
303   MOCK_METHOD(bool, HasFrame, (), (const override));
304
305   MOCK_METHOD(void, DidStartLoading, (), (override));
306   MOCK_METHOD(void, DidStopLoading, (), (override));
307
308   MOCK_METHOD(void, RecordComputedAction, (const std::string&), (override));
309
310   MOCK_METHOD(std::unique_ptr<PdfAccessibilityDataHandler>,
311               CreateAccessibilityDataHandler,
312               (PdfAccessibilityActionHandler*, PdfAccessibilityImageFetcher*),
313               (override));
314 };
315
316 class FakePdfService : public pdf::mojom::PdfService {
317  public:
318   MOCK_METHOD(void,
319               SetListener,
320               (mojo::PendingRemote<pdf::mojom::PdfListener>),
321               (override));
322   MOCK_METHOD(void, UpdateContentRestrictions, (int32_t), (override));
323   MOCK_METHOD(void, HasUnsupportedFeature, (), (override));
324   MOCK_METHOD(void,
325               SaveUrlAs,
326               (const GURL&, network::mojom::ReferrerPolicy),
327               (override));
328   MOCK_METHOD(void,
329               SelectionChanged,
330               (const gfx::PointF&, int32_t, const gfx::PointF&, int32_t),
331               (override));
332   MOCK_METHOD(void, SetPluginCanSave, (bool), (override));
333 };
334
335 }  // namespace
336
337 class PdfViewWebPluginWithoutInitializeTest
338     : public testing::TestWithParam<bool> {
339  protected:
340   // Custom deleter for `plugin_`. PdfViewWebPlugin must be destroyed by
341   // PdfViewWebPlugin::Destroy() instead of its destructor.
342   struct PluginDeleter {
343     void operator()(PdfViewWebPlugin* ptr) { ptr->Destroy(); }
344   };
345
346   static void AddToPluginParams(base::StringPiece name,
347                                 base::StringPiece value,
348                                 blink::WebPluginParams& params) {
349     params.attribute_names.push_back(blink::WebString::FromUTF8(name));
350     params.attribute_values.push_back(blink::WebString::FromUTF8(value));
351   }
352
353   void SetUpPlugin(base::StringPiece document_url,
354                    const blink::WebPluginParams& params) {
355     auto client = std::make_unique<NiceMock<FakePdfViewWebPluginClient>>();
356     client_ptr_ = client.get();
357
358     ON_CALL(*client_ptr_, CompleteURL)
359         .WillByDefault([parsed_document_url = GURL(document_url)](
360                            const blink::WebString& partial_url) {
361           return parsed_document_url.Resolve(partial_url.Utf8());
362         });
363     ON_CALL(*client_ptr_, CreateEngine)
364         .WillByDefault([this](
365                            PDFEngine::Client* client,
366                            PDFiumFormFiller::ScriptOption /*script_option*/) {
367           auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
368           engine_ptr_ = engine.get();
369           return engine;
370         });
371     ON_CALL(*client_ptr_, CreateAccessibilityDataHandler)
372         .WillByDefault([this]() {
373           auto handler =
374               std::make_unique<NiceMock<MockPdfAccessibilityDataHandler>>();
375           accessibility_data_handler_ptr_ = handler.get();
376           return handler;
377         });
378     SetUpClient();
379
380     plugin_ =
381         std::unique_ptr<PdfViewWebPlugin, PluginDeleter>(new PdfViewWebPlugin(
382             std::move(client),
383             mojo::AssociatedRemote<pdf::mojom::PdfService>(
384                 pdf_receiver_.BindNewEndpointAndPassDedicatedRemote()),
385             params));
386   }
387
388   void SetUpPluginWithUrl(const std::string& url) {
389     blink::WebPluginParams params;
390     AddToPluginParams("src", url, params);
391     SetUpPluginParams(params);
392
393     SetUpPlugin(url, params);
394   }
395
396   // Allows derived classes to customize plugin parameters within
397   // `SetUpPluginWithUrl()`.
398   virtual void SetUpPluginParams(blink::WebPluginParams& params) {}
399
400   // Allows derived classes to customize `client_ptr_` within `SetUpPlugin()`.
401   virtual void SetUpClient() {}
402
403   void ExpectUpdateTextInputState(
404       blink::WebTextInputType expected_text_input_type) {
405     EXPECT_CALL(*client_ptr_, UpdateTextInputState)
406         .WillOnce([this, expected_text_input_type]() {
407           EXPECT_EQ(expected_text_input_type,
408                     plugin_->GetPluginTextInputType());
409         });
410   }
411
412   void OnMessageWithEngineUpdate(const base::Value::Dict& message) {
413     // New engine will be created making this unowned reference stale.
414     engine_ptr_ = nullptr;
415     plugin_->OnMessage(message);
416   }
417
418   NiceMock<FakePdfService> pdf_service_;
419   mojo::AssociatedReceiver<pdf::mojom::PdfService> pdf_receiver_{&pdf_service_};
420
421   // Must outlive raw_ptrs below.
422   std::unique_ptr<PdfViewWebPlugin, PluginDeleter> plugin_;
423
424   raw_ptr<FakePdfViewWebPluginClient> client_ptr_;
425   raw_ptr<TestPDFiumEngine> engine_ptr_;
426   raw_ptr<MockPdfAccessibilityDataHandler> accessibility_data_handler_ptr_;
427 };
428
429 class PdfViewWebPluginTest : public PdfViewWebPluginWithoutInitializeTest {
430  protected:
431   static constexpr char kPdfUrl[] = "http://localhost/example.pdf";
432
433   void SetUp() override {
434     SetUpPluginWithUrl(kPdfUrl);
435
436     EXPECT_TRUE(plugin_->InitializeForTesting());
437   }
438
439   void SetDocumentDimensions(const gfx::Size& dimensions) {
440     EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
441         .WillRepeatedly(Return(dimensions));
442     SendViewportMessage(/*zoom=*/1.0);
443   }
444
445   void SendViewportMessage(double zoom) {
446     base::Value::Dict message = ParseMessage(R"({
447       "type": "viewport",
448       "userInitiated": false,
449       "zoom": 1,
450       "layoutOptions": {
451         "direction": 2,
452         "defaultPageOrientation": 0,
453         "twoUpViewEnabled": false,
454       },
455       "xOffset": 0,
456       "yOffset": 0,
457       "pinchPhase": 0,
458     })");
459     message.Set("zoom", zoom);
460     plugin_->OnMessage(message);
461   }
462
463   void UpdatePluginGeometry(float device_scale, const gfx::Rect& window_rect) {
464     UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
465
466     // Waits for main thread callback scheduled by `PaintManager`.
467     base::RunLoop run_loop;
468     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
469         FROM_HERE, run_loop.QuitClosure());
470     run_loop.Run();
471   }
472
473   void UpdatePluginGeometryWithoutWaiting(float device_scale,
474                                           const gfx::Rect& window_rect) {
475     // The plugin container's device scale must be set before calling
476     // UpdateGeometry().
477     EXPECT_CALL(*client_ptr_, DeviceScaleFactor)
478         .WillRepeatedly(Return(device_scale));
479     plugin_->UpdateGeometry(window_rect, window_rect, window_rect,
480                             /*is_visible=*/true);
481   }
482
483   void TestUpdateGeometrySetsPluginRect(float device_scale,
484                                         const gfx::Rect& window_rect,
485                                         float expected_device_scale,
486                                         const gfx::Rect& expected_plugin_rect) {
487     UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
488     EXPECT_EQ(expected_device_scale, plugin_->GetDeviceScaleForTesting())
489         << "Device scale comparison failure at device scale of "
490         << device_scale;
491     EXPECT_EQ(expected_plugin_rect, plugin_->GetPluginRectForTesting())
492         << "Plugin rect comparison failure at device scale of " << device_scale
493         << ", window rect of " << window_rect.ToString();
494   }
495
496   void TestPaintEmptySnapshots(float device_scale,
497                                const gfx::Rect& window_rect,
498                                const gfx::Rect& paint_rect,
499                                const gfx::Rect& expected_clipped_rect) {
500     UpdatePluginGeometryWithoutWaiting(device_scale, window_rect);
501     canvas_.DrawColor(kDefaultColor);
502
503     plugin_->Paint(canvas_.sk_canvas(), paint_rect);
504
505     // Expect the clipped area on canvas to be filled with plugin's background
506     // color.
507     SkBitmap expected_bitmap = GenerateExpectedBitmapForPaint(
508         expected_clipped_rect, plugin_->GetBackgroundColor());
509     EXPECT_TRUE(cc::MatchesBitmap(canvas_.GetBitmap(), expected_bitmap,
510                                   cc::ExactPixelComparator()))
511         << "Failure at device scale of " << device_scale << ", window rect of "
512         << window_rect.ToString();
513   }
514
515   void TestPaintSnapshots(float device_scale,
516                           const gfx::Rect& window_rect,
517                           const gfx::Rect& paint_rect,
518                           const gfx::Rect& expected_clipped_rect) {
519     UpdatePluginGeometry(device_scale, window_rect);
520     canvas_.DrawColor(kDefaultColor);
521
522     // Paint the plugin with `kPaintColor`.
523     plugin_->UpdateSnapshot(CreateSkiaImageForTesting(
524         plugin_->GetPluginRectForTesting().size(), kPaintColor));
525     plugin_->Paint(canvas_.sk_canvas(), paint_rect);
526
527     // Expect the clipped area on canvas to be filled with `kPaintColor`.
528     SkBitmap expected_bitmap =
529         GenerateExpectedBitmapForPaint(expected_clipped_rect, kPaintColor);
530     EXPECT_TRUE(cc::MatchesBitmap(canvas_.GetBitmap(), expected_bitmap,
531                                   cc::ExactPixelComparator()))
532         << "Failure at device scale of " << device_scale << ", window rect of "
533         << window_rect.ToString();
534   }
535
536   // Provides the cc::PaintCanvas for painting.
537   gfx::Canvas canvas_{kCanvasSize, /*image_scale=*/1.0f, /*is_opaque=*/true};
538 };
539
540 class PdfViewWebPluginFullFrameTest : public PdfViewWebPluginTest {
541  protected:
542   void SetUpPluginParams(blink::WebPluginParams& params) override {
543     AddToPluginParams("full-frame", "full-frame", params);
544   }
545 };
546
547 TEST_F(PdfViewWebPluginWithoutInitializeTest, Initialize) {
548   SetUpPluginWithUrl("http://localhost/example.pdf");
549
550   EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader)
551       .WillOnce([](const blink::WebAssociatedURLLoaderOptions& options) {
552         EXPECT_TRUE(options.grant_universal_access);
553
554         auto associated_loader =
555             std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
556         EXPECT_CALL(*associated_loader, LoadAsynchronously)
557             .WillOnce([](const blink::WebURLRequest& request,
558                          blink::WebAssociatedURLLoaderClient* client) {
559               EXPECT_EQ("http://localhost/example.pdf",
560                         request.Url().GetString().Utf8());
561               EXPECT_EQ("GET", request.HttpMethod().Utf8());
562               EXPECT_TRUE(request.HttpBody().IsNull());
563
564               NiceMock<MockHeaderVisitor> header_visitor;
565               EXPECT_CALL(header_visitor, VisitHeader).Times(0);
566               request.VisitHttpHeaderFields(&header_visitor);
567
568               EXPECT_FALSE(client->WillFollowRedirect(blink::WebURL(),
569                                                       blink::WebURLResponse()));
570               client->DidReceiveResponse(blink::WebURLResponse());
571               client->DidFinishLoading();
572             });
573         return associated_loader;
574       });
575   EXPECT_CALL(*client_ptr_, SetReferrerForRequest).Times(0);
576
577   EXPECT_TRUE(plugin_->InitializeForTesting());
578 }
579
580 TEST_F(PdfViewWebPluginWithoutInitializeTest, InitializeWithEmptyUrl) {
581   SetUpPluginWithUrl("");
582
583   EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
584
585   EXPECT_FALSE(plugin_->InitializeForTesting());
586 }
587
588 TEST_F(PdfViewWebPluginWithoutInitializeTest, InitializeForPrintPreview) {
589   SetUpPluginWithUrl("about:blank");
590
591   EXPECT_CALL(*client_ptr_, GetEmbedderOriginString)
592       .WillRepeatedly(Return("chrome://print/"));
593   EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
594
595   EXPECT_TRUE(plugin_->InitializeForTesting());
596 }
597
598 TEST_F(PdfViewWebPluginTest, CreateUrlLoader) {
599   EXPECT_CALL(*client_ptr_, DidStartLoading).Times(0);
600   EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
601   plugin_->CreateUrlLoader();
602
603   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kLoading,
604             plugin_->document_load_state_for_testing());
605   pdf_receiver_.FlushForTesting();
606 }
607
608 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoader) {
609   EXPECT_CALL(*client_ptr_, DidStartLoading);
610   EXPECT_CALL(pdf_service_,
611               UpdateContentRestrictions(kContentRestrictionSave |
612                                         kContentRestrictionPrint));
613   plugin_->CreateUrlLoader();
614
615   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kLoading,
616             plugin_->document_load_state_for_testing());
617   pdf_receiver_.FlushForTesting();
618 }
619
620 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoaderMultipleTimes) {
621   plugin_->CreateUrlLoader();
622
623   EXPECT_CALL(*client_ptr_, DidStartLoading).Times(0);
624   plugin_->CreateUrlLoader();
625 }
626
627 TEST_F(PdfViewWebPluginFullFrameTest, CreateUrlLoaderAfterDocumentLoadFailed) {
628   plugin_->CreateUrlLoader();
629   plugin_->DocumentLoadFailed();
630
631   EXPECT_CALL(*client_ptr_, DidStartLoading);
632   plugin_->CreateUrlLoader();
633 }
634
635 TEST_F(PdfViewWebPluginTest, DocumentLoadComplete) {
636   plugin_->CreateUrlLoader();
637
638   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
639   EXPECT_CALL(*client_ptr_, PostMessage);
640   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
641     "type": "formFocusChange",
642     "focused": false,
643   })")));
644   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
645   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
646     "type": "printPreviewLoaded",
647   })")))
648       .Times(0);
649   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
650       .Times(0);
651   EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
652   EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
653   plugin_->DocumentLoadComplete();
654
655   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
656             plugin_->document_load_state_for_testing());
657   pdf_receiver_.FlushForTesting();
658 }
659
660 TEST_F(PdfViewWebPluginFullFrameTest, DocumentLoadComplete) {
661   // Must flush IPCs after `CreateUrlLoader()` in full-frame mode, otherwise
662   // there's an unexpected `UpdateContentRestrictions()` call (see the
663   // `PdfViewWebPluginFullFrameTest.CreateUrlLoader` test).
664   plugin_->CreateUrlLoader();
665   pdf_receiver_.FlushForTesting();
666
667   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
668   EXPECT_CALL(*client_ptr_, PostMessage);
669   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
670     "type": "formFocusChange",
671     "focused": false,
672   })")));
673   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
674   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
675     "type": "printPreviewLoaded",
676   })")))
677       .Times(0);
678   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
679       .Times(0);
680   EXPECT_CALL(*client_ptr_, DidStopLoading);
681   EXPECT_CALL(pdf_service_, UpdateContentRestrictions(kContentRestrictionPrint |
682                                                       kContentRestrictionPaste |
683                                                       kContentRestrictionCut |
684                                                       kContentRestrictionCopy));
685   plugin_->DocumentLoadComplete();
686
687   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
688             plugin_->document_load_state_for_testing());
689   pdf_receiver_.FlushForTesting();
690 }
691
692 TEST_F(PdfViewWebPluginTest, DocumentLoadFailed) {
693   plugin_->CreateUrlLoader();
694
695   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadFailure"));
696   EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
697   plugin_->DocumentLoadFailed();
698
699   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kFailed,
700             plugin_->document_load_state_for_testing());
701 }
702
703 TEST_F(PdfViewWebPluginFullFrameTest, DocumentLoadFailed) {
704   plugin_->CreateUrlLoader();
705
706   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadFailure"));
707   EXPECT_CALL(*client_ptr_, DidStopLoading);
708   plugin_->DocumentLoadFailed();
709
710   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kFailed,
711             plugin_->document_load_state_for_testing());
712 }
713
714 TEST_F(PdfViewWebPluginTest, DocumentHasUnsupportedFeature) {
715   EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
716   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature1"));
717   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature2"));
718
719   // `HasUnsupportedFeature()` is not called if the viewer is not full-frame.
720   EXPECT_CALL(pdf_service_, HasUnsupportedFeature).Times(0);
721
722   plugin_->DocumentHasUnsupportedFeature("feature1");
723   plugin_->DocumentHasUnsupportedFeature("feature2");
724
725   pdf_receiver_.FlushForTesting();
726 }
727
728 TEST_F(PdfViewWebPluginTest, DocumentHasUnsupportedFeatureWithRepeatedFeature) {
729   // Metrics should only be recorded once per feature.
730   EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
731   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature"));
732
733   // `HasUnsupportedFeature()` is not called if the viewer is not full-frame.
734   EXPECT_CALL(pdf_service_, HasUnsupportedFeature).Times(0);
735
736   plugin_->DocumentHasUnsupportedFeature("feature");
737   plugin_->DocumentHasUnsupportedFeature("feature");
738
739   pdf_receiver_.FlushForTesting();
740 }
741
742 TEST_F(PdfViewWebPluginFullFrameTest, DocumentHasUnsupportedFeature) {
743   EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
744   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature1"));
745   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature2"));
746
747   // `HasUnsupportedFeature()` is called once for all features.
748   EXPECT_CALL(pdf_service_, HasUnsupportedFeature);
749
750   plugin_->DocumentHasUnsupportedFeature("feature1");
751   plugin_->DocumentHasUnsupportedFeature("feature2");
752
753   pdf_receiver_.FlushForTesting();
754 }
755
756 TEST_F(PdfViewWebPluginFullFrameTest,
757        DocumentHasUnsupportedFeatureWithRepeatedFeature) {
758   // Metrics should only be recorded once per feature.
759   EXPECT_CALL(*client_ptr_, RecordComputedAction).Times(AnyNumber());
760   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF_Unsupported_feature"));
761
762   // `HasUnsupportedFeature()` is called once for all features.
763   EXPECT_CALL(pdf_service_, HasUnsupportedFeature);
764
765   plugin_->DocumentHasUnsupportedFeature("feature");
766   plugin_->DocumentHasUnsupportedFeature("feature");
767
768   pdf_receiver_.FlushForTesting();
769 }
770
771 TEST_F(PdfViewWebPluginTest, DocumentLoadProgress) {
772   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
773     "type": "loadProgress",
774     "progress": 5.0,
775   })")));
776   plugin_->DocumentLoadProgress(10, 200);
777 }
778
779 TEST_F(PdfViewWebPluginTest, DocumentLoadProgressIgnoreSmall) {
780   plugin_->DocumentLoadProgress(2, 100);
781
782   EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
783   plugin_->DocumentLoadProgress(3, 100);
784 }
785
786 TEST_F(PdfViewWebPluginTest, DocumentLoadProgressMultipleSmall) {
787   plugin_->DocumentLoadProgress(2, 100);
788
789   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
790     "type": "loadProgress",
791     "progress": 4.0,
792   })")));
793   plugin_->DocumentLoadProgress(3, 100);
794   plugin_->DocumentLoadProgress(4, 100);
795 }
796
797 TEST_F(PdfViewWebPluginTest, EnableAccessibilityBeforeDocumentLoadComplete) {
798   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
799       .Times(0);
800   plugin_->EnableAccessibility();
801
802   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
803   plugin_->CreateUrlLoader();
804   plugin_->DocumentLoadComplete();
805 }
806
807 TEST_F(PdfViewWebPluginTest,
808        EnableAccessibilityBeforeDocumentLoadCompleteRepeated) {
809   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
810       .Times(0);
811   plugin_->EnableAccessibility();
812   plugin_->EnableAccessibility();
813
814   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
815   plugin_->CreateUrlLoader();
816   plugin_->DocumentLoadComplete();
817 }
818
819 TEST_F(PdfViewWebPluginTest, EnableAccessibilityAfterDocumentLoadComplete) {
820   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
821       .Times(0);
822   plugin_->CreateUrlLoader();
823   plugin_->DocumentLoadComplete();
824
825   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
826   plugin_->EnableAccessibility();
827 }
828
829 TEST_F(PdfViewWebPluginTest,
830        EnableAccessibilityAfterDocumentLoadCompleteRepeated) {
831   plugin_->CreateUrlLoader();
832   plugin_->DocumentLoadComplete();
833   plugin_->EnableAccessibility();
834
835   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
836       .Times(0);
837   plugin_->EnableAccessibility();
838 }
839
840 TEST_F(PdfViewWebPluginTest,
841        LoadOrReloadAccessibilityBeforeDocumentLoadComplete) {
842   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
843       .Times(0);
844   plugin_->LoadOrReloadAccessibility();
845
846   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
847   plugin_->CreateUrlLoader();
848   plugin_->DocumentLoadComplete();
849 }
850
851 TEST_F(PdfViewWebPluginTest,
852        LoadOrReloadAccessibilityBeforeDocumentLoadCompleteRepeated) {
853   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
854       .Times(0);
855   plugin_->LoadOrReloadAccessibility();
856   plugin_->LoadOrReloadAccessibility();
857
858   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
859   plugin_->CreateUrlLoader();
860   plugin_->DocumentLoadComplete();
861 }
862
863 TEST_F(PdfViewWebPluginTest,
864        LoadOrReloadAccessibilityAfterDocumentLoadComplete) {
865   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
866       .Times(0);
867   plugin_->CreateUrlLoader();
868   plugin_->DocumentLoadComplete();
869
870   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
871   plugin_->LoadOrReloadAccessibility();
872 }
873
874 TEST_F(PdfViewWebPluginTest,
875        LoadOrReloadAccessibilityAfterDocumentLoadCompleteRepeated) {
876   plugin_->CreateUrlLoader();
877   plugin_->DocumentLoadComplete();
878   plugin_->LoadOrReloadAccessibility();
879
880   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
881   plugin_->LoadOrReloadAccessibility();
882 }
883
884 TEST_F(PdfViewWebPluginTest,
885        LoadOrReloadAccessibilityResetsAccessibilityPageIndex) {
886   plugin_->CreateUrlLoader();
887   plugin_->DocumentLoadComplete();
888   plugin_->LoadOrReloadAccessibility();
889   EXPECT_EQ(plugin_->next_accessibility_page_index_for_testing(), 0);
890   plugin_->set_next_accessibility_page_index_for_testing(5);
891
892   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo);
893   plugin_->LoadOrReloadAccessibility();
894   EXPECT_EQ(plugin_->next_accessibility_page_index_for_testing(), 0);
895 }
896
897 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithNoPermissions) {
898   EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
899                 kContentRestrictionPaste | kContentRestrictionPrint,
900             plugin_->GetContentRestrictionsForTesting());
901   EXPECT_FALSE(plugin_->CanCopy());
902 }
903
904 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithCopyAllowed) {
905   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
906   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
907       .WillRepeatedly(Return(true));
908
909   EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste |
910                 kContentRestrictionPrint,
911             plugin_->GetContentRestrictionsForTesting());
912   EXPECT_TRUE(plugin_->CanCopy());
913 }
914
915 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithPrintLowQualityAllowed) {
916   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
917   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
918       .WillRepeatedly(Return(true));
919
920   EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
921                 kContentRestrictionPaste,
922             plugin_->GetContentRestrictionsForTesting());
923 }
924
925 TEST_F(PdfViewWebPluginTest,
926        GetContentRestrictionsWithCopyAndPrintLowQualityAllowed) {
927   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
928   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
929       .WillRepeatedly(Return(true));
930   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
931       .WillRepeatedly(Return(true));
932
933   EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste,
934             plugin_->GetContentRestrictionsForTesting());
935 }
936
937 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithPrintAllowed) {
938   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
939   EXPECT_CALL(*engine_ptr_,
940               HasPermission(DocumentPermission::kPrintHighQuality))
941       .WillRepeatedly(Return(true));
942   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
943       .WillRepeatedly(Return(true));
944
945   EXPECT_EQ(kContentRestrictionCopy | kContentRestrictionCut |
946                 kContentRestrictionPaste,
947             plugin_->GetContentRestrictionsForTesting());
948 }
949
950 TEST_F(PdfViewWebPluginTest, GetContentRestrictionsWithCopyAndPrintAllowed) {
951   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
952   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
953       .WillRepeatedly(Return(true));
954   EXPECT_CALL(*engine_ptr_,
955               HasPermission(DocumentPermission::kPrintHighQuality))
956       .WillRepeatedly(Return(true));
957   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
958       .WillRepeatedly(Return(true));
959
960   EXPECT_EQ(kContentRestrictionCut | kContentRestrictionPaste,
961             plugin_->GetContentRestrictionsForTesting());
962 }
963
964 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithNoPermissions) {
965   AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
966
967   EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
968   EXPECT_FALSE(doc_info.text_accessible);
969   EXPECT_FALSE(doc_info.text_copyable);
970 }
971
972 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithCopyAllowed) {
973   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
974   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
975       .WillRepeatedly(Return(true));
976
977   AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
978
979   EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
980   EXPECT_FALSE(doc_info.text_accessible);
981   EXPECT_TRUE(doc_info.text_copyable);
982 }
983
984 TEST_F(PdfViewWebPluginTest, GetAccessibilityDocInfoWithCopyAccessibleAllowed) {
985   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
986   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopyAccessible))
987       .WillRepeatedly(Return(true));
988
989   AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
990
991   EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
992   EXPECT_TRUE(doc_info.text_accessible);
993   EXPECT_FALSE(doc_info.text_copyable);
994 }
995
996 TEST_F(PdfViewWebPluginTest,
997        GetAccessibilityDocInfoWithCopyAndCopyAccessibleAllowed) {
998   EXPECT_CALL(*engine_ptr_, HasPermission).WillRepeatedly(Return(false));
999   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopy))
1000       .WillRepeatedly(Return(true));
1001   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kCopyAccessible))
1002       .WillRepeatedly(Return(true));
1003
1004   AccessibilityDocInfo doc_info = plugin_->GetAccessibilityDocInfoForTesting();
1005
1006   EXPECT_EQ(TestPDFiumEngine::kPageNumber, doc_info.page_count);
1007   EXPECT_TRUE(doc_info.text_accessible);
1008   EXPECT_TRUE(doc_info.text_copyable);
1009 }
1010
1011 TEST_F(PdfViewWebPluginTest, UpdateGeometrySetsPluginRect) {
1012   EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
1013   TestUpdateGeometrySetsPluginRect(
1014       /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(4, 4, 12, 12),
1015       /*expected_device_scale=*/2.0f,
1016       /*expected_plugin_rect=*/gfx::Rect(4, 4, 12, 12));
1017 }
1018
1019 TEST_F(PdfViewWebPluginTest,
1020        UpdateGeometrySetsPluginRectOnVariousDeviceScales) {
1021   struct UpdateGeometryParams {
1022     // The plugin container's device scale.
1023     float device_scale;
1024
1025     //  The window rect in CSS pixels.
1026     gfx::Rect window_rect;
1027
1028     // The expected plugin device scale.
1029     float expected_device_scale;
1030
1031     // The expected plugin rect in device pixels.
1032     gfx::Rect expected_plugin_rect;
1033   };
1034
1035   static constexpr UpdateGeometryParams kUpdateGeometryParams[] = {
1036       {1.0f, gfx::Rect(3, 4, 5, 6), 1.0f, gfx::Rect(3, 4, 5, 6)},
1037       {2.0f, gfx::Rect(3, 4, 5, 6), 2.0f, gfx::Rect(3, 4, 5, 6)},
1038   };
1039
1040   for (const auto& params : kUpdateGeometryParams) {
1041     TestUpdateGeometrySetsPluginRect(params.device_scale, params.window_rect,
1042                                      params.expected_device_scale,
1043                                      params.expected_plugin_rect);
1044   }
1045 }
1046
1047 TEST_F(PdfViewWebPluginTest, UpdateGeometrySetsPluginRectWithEmptyWindow) {
1048   EXPECT_CALL(*engine_ptr_, ZoomUpdated).Times(0);
1049   TestUpdateGeometrySetsPluginRect(
1050       /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(2, 2, 0, 0),
1051       /*expected_device_scale=*/1.0f, /*expected_plugin_rect=*/gfx::Rect());
1052 }
1053
1054 TEST_F(PdfViewWebPluginTest, UpdateGeometryScroll) {
1055   SetDocumentDimensions({100, 200});
1056
1057   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1058       .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
1059   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(4));
1060   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(6));
1061   UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1062 }
1063
1064 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollStopped) {
1065   SetDocumentDimensions({100, 200});
1066
1067   plugin_->OnMessage(ParseMessage(R"({
1068     "type": "stopScrolling",
1069   })"));
1070
1071   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1072       .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
1073   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition).Times(0);
1074   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition).Times(0);
1075   UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1076 }
1077
1078 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollUnderflow) {
1079   SetDocumentDimensions({100, 200});
1080
1081   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1082       .WillRepeatedly(Return(gfx::PointF(-1.0f, -1.0f)));
1083   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(0));
1084   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(0));
1085   UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1086 }
1087
1088 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflow) {
1089   SetDocumentDimensions({100, 200});
1090
1091   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1092       .WillRepeatedly(Return(gfx::PointF(96.0f, 195.0f)));
1093   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(95));
1094   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(194));
1095   UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1096 }
1097
1098 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflowZoomed) {
1099   SetDocumentDimensions({100, 200});
1100   SendViewportMessage(/*zoom=*/2.0);
1101
1102   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1103       .WillRepeatedly(Return(gfx::PointF(196.0f, 395.0f)));
1104   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(195));
1105   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(394));
1106   UpdatePluginGeometryWithoutWaiting(1.0f, gfx::Rect(3, 4, 5, 6));
1107 }
1108
1109 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollScaled) {
1110   SetDocumentDimensions({100, 200});
1111
1112   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1113       .WillRepeatedly(Return(gfx::PointF(4.0f, 6.0f)));
1114   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(4));
1115   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(6));
1116   UpdatePluginGeometryWithoutWaiting(2.0f, gfx::Rect(3, 4, 5, 6));
1117 }
1118
1119 TEST_F(PdfViewWebPluginTest, UpdateGeometryScrollOverflowScaled) {
1120   SetDocumentDimensions({100, 200});
1121
1122   EXPECT_CALL(*client_ptr_, GetScrollPosition)
1123       .WillRepeatedly(Return(gfx::PointF(195.0f, 395.0f)));
1124   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(194));
1125   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(394));
1126   UpdatePluginGeometryWithoutWaiting(2.0f, gfx::Rect(3, 4, 5, 6));
1127 }
1128
1129 TEST_F(PdfViewWebPluginTest, SetCaretPosition) {
1130   SetDocumentDimensions({16, 9});
1131   UpdatePluginGeometryWithoutWaiting(1.0f, {10, 20, 20, 5});
1132
1133   EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(2, 3)));
1134   plugin_->SetCaretPosition({4.0f, 3.0f});
1135 }
1136
1137 TEST_F(PdfViewWebPluginTest, SetCaretPositionNegativeOrigin) {
1138   SetDocumentDimensions({16, 9});
1139   UpdatePluginGeometryWithoutWaiting(1.0f, {-10, -20, 20, 5});
1140
1141   EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(2, 3)));
1142   plugin_->SetCaretPosition({4.0f, 3.0f});
1143 }
1144
1145 TEST_F(PdfViewWebPluginTest, SetCaretPositionFractional) {
1146   SetDocumentDimensions({16, 9});
1147   UpdatePluginGeometryWithoutWaiting(1.0f, {10, 20, 20, 5});
1148
1149   EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(1, 2)));
1150   plugin_->SetCaretPosition({3.9f, 2.9f});
1151 }
1152
1153 TEST_F(PdfViewWebPluginTest, SetCaretPositionScaled) {
1154   SetDocumentDimensions({16, 9});
1155   UpdatePluginGeometryWithoutWaiting(2.0f, {20, 40, 40, 10});
1156
1157   EXPECT_CALL(*engine_ptr_, SetCaretPosition(gfx::Point(4, 6)));
1158   plugin_->SetCaretPosition({4.0f, 3.0f});
1159 }
1160
1161 TEST_F(PdfViewWebPluginTest, PaintEmptySnapshots) {
1162   TestPaintEmptySnapshots(/*device_scale=*/4.0f,
1163                           /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1164                           /*paint_rect=*/gfx::Rect(5, 5, 15, 15),
1165                           /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1166 }
1167
1168 TEST_F(PdfViewWebPluginTest, PaintSnapshots) {
1169   TestPaintSnapshots(/*device_scale=*/4.0f,
1170                      /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1171                      /*paint_rect=*/gfx::Rect(5, 5, 15, 15),
1172                      /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1173 }
1174
1175 TEST_F(PdfViewWebPluginTest, PaintSnapshotsWithVariousDeviceScales) {
1176   static constexpr PaintParams kPaintWithVariousScalesParams[] = {
1177       {0.4f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1178        gfx::Rect(10, 10, 28, 28)},
1179       {1.0f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1180        gfx::Rect(10, 10, 28, 28)},
1181       {4.0f, gfx::Rect(8, 8, 30, 30), gfx::Rect(10, 10, 30, 30),
1182        gfx::Rect(10, 10, 28, 28)},
1183   };
1184
1185   for (const auto& params : kPaintWithVariousScalesParams) {
1186     TestPaintSnapshots(params.device_scale, params.window_rect,
1187                        params.paint_rect, params.expected_clipped_rect);
1188   }
1189 }
1190
1191 TEST_F(PdfViewWebPluginTest, PaintSnapshotsWithVariousRectPositions) {
1192   static constexpr PaintParams kPaintWithVariousPositionsParams[] = {
1193       // The window origin falls outside the `paint_rect` area.
1194       {4.0f, gfx::Rect(10, 10, 20, 20), gfx::Rect(5, 5, 15, 15),
1195        gfx::Rect(10, 10, 10, 10)},
1196       // The window origin falls within the `paint_rect` area.
1197       {4.0f, gfx::Rect(4, 4, 20, 20), gfx::Rect(8, 8, 15, 15),
1198        gfx::Rect(8, 8, 15, 15)},
1199   };
1200
1201   for (const auto& params : kPaintWithVariousPositionsParams) {
1202     TestPaintSnapshots(params.device_scale, params.window_rect,
1203                        params.paint_rect, params.expected_clipped_rect);
1204   }
1205 }
1206
1207 TEST_F(PdfViewWebPluginTest, OnPaintWithMultiplePaintRects) {
1208   SetDocumentDimensions({100, 200});
1209   UpdatePluginGeometryWithoutWaiting(/*device_scale=*/1.0f,
1210                                      gfx::Rect(0, 0, 40, 40));
1211
1212   EXPECT_CALL(*engine_ptr_, Paint)
1213       .WillRepeatedly(
1214           [](const gfx::Rect& rect, SkBitmap& /*image_data*/,
1215              std::vector<gfx::Rect>& ready,
1216              std::vector<gfx::Rect>& /*pending*/) { ready.push_back(rect); });
1217   std::vector<PaintReadyRect> ready;
1218   std::vector<gfx::Rect> pending;
1219   plugin_->OnPaint(
1220       /*paint_rects=*/{gfx::Rect(5, 5, 10, 10), gfx::Rect(20, 20, 10, 10)},
1221       ready, pending);
1222
1223   // Expect three paints: an initial background-clearing paint, and one for each
1224   // requested paint rectangle.
1225   ASSERT_THAT(ready, SizeIs(3));
1226   EXPECT_THAT(pending, IsEmpty());
1227
1228   EXPECT_EQ(gfx::Rect(0, 0, 90, 90), ready[0].rect());
1229   EXPECT_TRUE(ready[0].flush_now());
1230
1231   EXPECT_EQ(gfx::Rect(5, 5, 10, 10), ready[1].rect());
1232   EXPECT_FALSE(ready[1].flush_now());
1233
1234   EXPECT_EQ(gfx::Rect(20, 20, 10, 10), ready[2].rect());
1235   EXPECT_FALSE(ready[2].flush_now());
1236
1237   // All the requested paints should share the same `SkImage`.
1238   EXPECT_NE(&ready[0].image(), &ready[1].image());
1239   EXPECT_EQ(&ready[1].image(), &ready[2].image());
1240 }
1241
1242 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithIdentity) {
1243   plugin_->UpdateLayerTransform(1.0f, gfx::Vector2dF());
1244   TestPaintSnapshots(/*device_scale=*/4.0f,
1245                      /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1246                      /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1247                      /*expected_clipped_rect=*/gfx::Rect(10, 10, 20, 20));
1248 }
1249
1250 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithScale) {
1251   plugin_->UpdateLayerTransform(0.5f, gfx::Vector2dF());
1252   TestPaintSnapshots(/*device_scale=*/4.0f,
1253                      /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1254                      /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1255                      /*expected_clipped_rect=*/gfx::Rect(10, 10, 10, 10));
1256 }
1257
1258 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithTranslate) {
1259   plugin_->UpdateLayerTransform(1.0f, gfx::Vector2dF(-1.25, 1.25));
1260   TestPaintSnapshots(/*device_scale=*/4.0f,
1261                      /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1262                      /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1263                      /*expected_clipped_rect=*/gfx::Rect(10, 15, 15, 15));
1264 }
1265
1266 TEST_F(PdfViewWebPluginTest, UpdateLayerTransformWithScaleAndTranslate) {
1267   plugin_->UpdateLayerTransform(0.5f, gfx::Vector2dF(-1.25, 1.25));
1268   TestPaintSnapshots(/*device_scale=*/4.0f,
1269                      /*window_rect=*/gfx::Rect(10, 10, 20, 20),
1270                      /*paint_rect=*/gfx::Rect(10, 10, 20, 20),
1271                      /*expected_clipped_rect=*/gfx::Rect(10, 15, 5, 10));
1272 }
1273
1274 TEST_F(PdfViewWebPluginTest, HandleViewportMessageBeforeDocumentLoadComplete) {
1275   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(DocumentLayout::Options()));
1276   EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
1277
1278   plugin_->OnMessage(ParseMessage(R"({
1279     "type": "viewport",
1280     "userInitiated": false,
1281     "zoom": 1,
1282     "layoutOptions": {
1283       "direction": 0,
1284       "defaultPageOrientation": 0,
1285       "twoUpViewEnabled": false,
1286     },
1287     "xOffset": 0,
1288     "yOffset": 0,
1289     "pinchPhase": 0,
1290   })"));
1291 }
1292
1293 TEST_F(PdfViewWebPluginTest, HandleViewportMessageAfterDocumentLoadComplete) {
1294   plugin_->DocumentLoadComplete();
1295
1296   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(DocumentLayout::Options()));
1297   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1298     "type": "loadProgress",
1299     "progress": 100.0,
1300   })")));
1301
1302   plugin_->OnMessage(ParseMessage(R"({
1303     "type": "viewport",
1304     "userInitiated": false,
1305     "zoom": 1,
1306     "layoutOptions": {
1307       "direction": 0,
1308       "defaultPageOrientation": 0,
1309       "twoUpViewEnabled": false,
1310     },
1311     "xOffset": 0,
1312     "yOffset": 0,
1313     "pinchPhase": 0,
1314   })"));
1315 }
1316
1317 TEST_F(PdfViewWebPluginTest, HandleViewportMessageSubsequently) {
1318   plugin_->OnMessage(ParseMessage(R"({
1319     "type": "viewport",
1320     "userInitiated": false,
1321     "zoom": 1,
1322     "layoutOptions": {
1323       "direction": 0,
1324       "defaultPageOrientation": 0,
1325       "twoUpViewEnabled": false,
1326     },
1327     "xOffset": 0,
1328     "yOffset": 0,
1329     "pinchPhase": 0,
1330   })"));
1331
1332   DocumentLayout::Options two_up_options;
1333   two_up_options.set_page_spread(DocumentLayout::PageSpread::kTwoUpOdd);
1334   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout(two_up_options));
1335   EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
1336
1337   plugin_->OnMessage(ParseMessage(R"({
1338     "type": "viewport",
1339     "userInitiated": false,
1340     "zoom": 1,
1341     "layoutOptions": {
1342       "direction": 0,
1343       "defaultPageOrientation": 0,
1344       "twoUpViewEnabled": true,
1345     },
1346     "xOffset": 0,
1347     "yOffset": 0,
1348     "pinchPhase": 0,
1349   })"));
1350 }
1351
1352 TEST_F(PdfViewWebPluginTest, HandleViewportMessageScroll) {
1353   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
1354       .WillRepeatedly(Return(gfx::Size(16, 9)));
1355   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(2));
1356   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
1357
1358   plugin_->OnMessage(ParseMessage(R"({
1359     "type": "viewport",
1360     "userInitiated": false,
1361     "zoom": 1,
1362     "layoutOptions": {
1363       "direction": 2,
1364       "defaultPageOrientation": 0,
1365       "twoUpViewEnabled": false,
1366     },
1367     "xOffset": 2,
1368     "yOffset": 3,
1369     "pinchPhase": 0,
1370   })"));
1371 }
1372
1373 TEST_F(PdfViewWebPluginTest, HandleViewportMessageScrollRightToLeft) {
1374   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
1375       .WillRepeatedly(Return(gfx::Size(16, 9)));
1376   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(2));
1377   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
1378
1379   plugin_->OnMessage(ParseMessage(R"({
1380     "type": "viewport",
1381     "userInitiated": false,
1382     "zoom": 1,
1383     "layoutOptions": {
1384       "direction": 1,
1385       "defaultPageOrientation": 0,
1386       "twoUpViewEnabled": false,
1387     },
1388     "xOffset": 2,
1389     "yOffset": 3,
1390     "pinchPhase": 0,
1391   })"));
1392 }
1393
1394 TEST_F(PdfViewWebPluginTest, HandleSetBackgroundColorMessage) {
1395   ASSERT_NE(SK_ColorGREEN, plugin_->GetBackgroundColor());
1396
1397   base::Value::Dict message;
1398   message.Set("type", "setBackgroundColor");
1399   message.Set("color", static_cast<double>(SK_ColorGREEN));
1400   plugin_->OnMessage(message);
1401
1402   EXPECT_EQ(SK_ColorGREEN, plugin_->GetBackgroundColor());
1403 }
1404
1405 TEST_F(PdfViewWebPluginTest, HandleInputEvent) {
1406   UpdatePluginGeometryWithoutWaiting(2.0f, {0, 0, 20, 20});
1407
1408   EXPECT_CALL(*engine_ptr_, HandleInputEvent)
1409       .WillRepeatedly([](const blink::WebInputEvent& event) {
1410         if (!blink::WebInputEvent::IsMouseEventType(event.GetType())) {
1411           ADD_FAILURE() << "Unexpected event type: " << event.GetType();
1412           return false;
1413         }
1414
1415         const auto& mouse_event =
1416             static_cast<const blink::WebMouseEvent&>(event);
1417         EXPECT_EQ(blink::WebInputEvent::Type::kMouseDown,
1418                   mouse_event.GetType());
1419         EXPECT_EQ(gfx::PointF(10.0f, 40.0f), mouse_event.PositionInWidget());
1420         return true;
1421       });
1422
1423   blink::WebMouseEvent mouse_event;
1424   mouse_event.SetType(blink::WebInputEvent::Type::kMouseDown);
1425   mouse_event.SetPositionInWidget(10.0f, 20.0f);
1426
1427   ui::Cursor dummy_cursor;
1428   EXPECT_EQ(blink::WebInputEventResult::kHandledApplication,
1429             plugin_->HandleInputEvent(
1430                 blink::WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()),
1431                 &dummy_cursor));
1432 }
1433
1434 class PdfViewWebPluginImeTest : public PdfViewWebPluginTest {
1435  public:
1436   void TestImeSetCompositionForPlugin(const blink::WebString& text) {
1437     EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1438     plugin_->ImeSetCompositionForPlugin(text, std::vector<ui::ImeTextSpan>(),
1439                                         gfx::Range(),
1440                                         /*selection_start=*/0,
1441                                         /*selection_end=*/0);
1442   }
1443
1444   void TestImeFinishComposingTextForPlugin(
1445       const blink::WebString& expected_text) {
1446     InSequence sequence;
1447     std::u16string expected_text16 = expected_text.Utf16();
1448     if (expected_text16.size()) {
1449       for (const auto& c : expected_text16) {
1450         base::StringPiece16 expected_key(&c, 1);
1451         EXPECT_CALL(*engine_ptr_,
1452                     HandleInputEvent(IsExpectedImeKeyEvent(expected_key)))
1453             .WillOnce(Return(true));
1454       }
1455     } else {
1456       EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1457     }
1458     plugin_->ImeFinishComposingTextForPlugin(false);
1459   }
1460
1461   void TestImeCommitTextForPlugin(const blink::WebString& text) {
1462     InSequence sequence;
1463     std::u16string expected_text16 = text.Utf16();
1464     if (expected_text16.size()) {
1465       for (const auto& c : expected_text16) {
1466         base::StringPiece16 event(&c, 1);
1467         EXPECT_CALL(*engine_ptr_,
1468                     HandleInputEvent(IsExpectedImeKeyEvent(event)))
1469             .WillOnce(Return(true));
1470       }
1471     } else {
1472       EXPECT_CALL(*engine_ptr_, HandleInputEvent).Times(0);
1473     }
1474     plugin_->ImeCommitTextForPlugin(text, std::vector<ui::ImeTextSpan>(),
1475                                     gfx::Range(),
1476                                     /*relative_cursor_pos=*/0);
1477   }
1478 };
1479
1480 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishAscii) {
1481   const blink::WebString text = blink::WebString::FromASCII("input");
1482   TestImeSetCompositionForPlugin(text);
1483   TestImeFinishComposingTextForPlugin(text);
1484 }
1485
1486 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishUnicode) {
1487   const blink::WebString text = blink::WebString::FromUTF16(u"你好");
1488   TestImeSetCompositionForPlugin(text);
1489   TestImeFinishComposingTextForPlugin(text);
1490   // Calling ImeFinishComposingTextForPlugin() again is a no-op.
1491   TestImeFinishComposingTextForPlugin("");
1492 }
1493
1494 TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishEmpty) {
1495   const blink::WebString text;
1496   TestImeSetCompositionForPlugin(text);
1497   TestImeFinishComposingTextForPlugin(text);
1498 }
1499
1500 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginAscii) {
1501   const blink::WebString text = blink::WebString::FromASCII("a b");
1502   TestImeCommitTextForPlugin(text);
1503 }
1504
1505 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginUnicode) {
1506   const blink::WebString text = blink::WebString::FromUTF16(u"さようなら");
1507   TestImeCommitTextForPlugin(text);
1508 }
1509
1510 TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginEmpty) {
1511   const blink::WebString text;
1512   TestImeCommitTextForPlugin(text);
1513 }
1514
1515 TEST_F(PdfViewWebPluginTest, SelectionChanged) {
1516   plugin_->EnableAccessibility();
1517   plugin_->DocumentLoadComplete();
1518   UpdatePluginGeometryWithoutWaiting(1.0f, {300, 56, 20, 5});
1519   SetDocumentDimensions({16, 9});
1520
1521   AccessibilityViewportInfo viewport_info;
1522   EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1523                                              gfx::PointF(52.0f, 60.0f), 80));
1524   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1525       .WillOnce(SaveArg<0>(&viewport_info));
1526   plugin_->SelectionChanged({-10, -20, 30, 40}, {50, 60, 70, 80});
1527
1528   EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1529   pdf_receiver_.FlushForTesting();
1530 }
1531
1532 TEST_F(PdfViewWebPluginTest, SelectionChangedNegativeOrigin) {
1533   plugin_->EnableAccessibility();
1534   plugin_->DocumentLoadComplete();
1535   UpdatePluginGeometryWithoutWaiting(1.0f, {-300, -56, 20, 5});
1536   SetDocumentDimensions({16, 9});
1537
1538   AccessibilityViewportInfo viewport_info;
1539   EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1540                                              gfx::PointF(52.0f, 60.0f), 80));
1541   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1542       .WillOnce(SaveArg<0>(&viewport_info));
1543   plugin_->SelectionChanged({-10, -20, 30, 40}, {50, 60, 70, 80});
1544
1545   EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1546   pdf_receiver_.FlushForTesting();
1547 }
1548
1549 TEST_F(PdfViewWebPluginTest, SelectionChangedScaled) {
1550   plugin_->EnableAccessibility();
1551   plugin_->DocumentLoadComplete();
1552   UpdatePluginGeometryWithoutWaiting(2.0f, {600, 112, 40, 10});
1553   SetDocumentDimensions({16, 9});
1554
1555   AccessibilityViewportInfo viewport_info;
1556   EXPECT_CALL(pdf_service_, SelectionChanged(gfx::PointF(-8.0f, -20.0f), 40,
1557                                              gfx::PointF(52.0f, 60.0f), 80));
1558   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityViewportInfo)
1559       .WillOnce(SaveArg<0>(&viewport_info));
1560   plugin_->SelectionChanged({-20, -40, 60, 80}, {100, 120, 140, 160});
1561
1562   EXPECT_EQ(gfx::Point(), viewport_info.scroll);
1563   pdf_receiver_.FlushForTesting();
1564 }
1565
1566 TEST_F(PdfViewWebPluginTest, ChangeTextSelection) {
1567   ASSERT_FALSE(plugin_->HasSelection());
1568   ASSERT_TRUE(plugin_->SelectionAsText().IsEmpty());
1569   ASSERT_TRUE(plugin_->SelectionAsMarkup().IsEmpty());
1570
1571   static constexpr char kSelectedText[] = "1234";
1572   EXPECT_CALL(*client_ptr_,
1573               TextSelectionChanged(blink::WebString::FromUTF8(kSelectedText), 0,
1574                                    gfx::Range(0, 4)));
1575
1576   plugin_->SetSelectedText(kSelectedText);
1577   EXPECT_TRUE(plugin_->HasSelection());
1578   EXPECT_EQ(kSelectedText, plugin_->SelectionAsText().Utf8());
1579   EXPECT_EQ(kSelectedText, plugin_->SelectionAsMarkup().Utf8());
1580
1581   static constexpr char kEmptyText[] = "";
1582   EXPECT_CALL(*client_ptr_,
1583               TextSelectionChanged(blink::WebString::FromUTF8(kEmptyText), 0,
1584                                    gfx::Range(0, 0)));
1585   plugin_->SetSelectedText(kEmptyText);
1586   EXPECT_FALSE(plugin_->HasSelection());
1587   EXPECT_TRUE(plugin_->SelectionAsText().IsEmpty());
1588   EXPECT_TRUE(plugin_->SelectionAsMarkup().IsEmpty());
1589 }
1590
1591 TEST_F(PdfViewWebPluginTest, SelectAll) {
1592   EXPECT_CALL(*engine_ptr_, SelectAll);
1593
1594   EXPECT_TRUE(plugin_->ExecuteEditCommand(
1595       /*name=*/blink::WebString::FromASCII("SelectAll"),
1596       /*value=*/blink::WebString()));
1597 }
1598
1599 TEST_F(PdfViewWebPluginTest, FormTextFieldFocusChangeUpdatesTextInputType) {
1600   ASSERT_EQ(blink::WebTextInputType::kWebTextInputTypeNone,
1601             plugin_->GetPluginTextInputType());
1602
1603   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeText);
1604   plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
1605
1606   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1607   plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
1608
1609   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeText);
1610   plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
1611
1612   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1613   plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNonText);
1614 }
1615
1616 TEST_F(PdfViewWebPluginTest, SearchString) {
1617   static constexpr char16_t kPattern[] = u"fox";
1618   static constexpr char16_t kTarget[] =
1619       u"The quick brown fox jumped over the lazy Fox";
1620
1621   {
1622     static constexpr PDFEngine::Client::SearchStringResult kExpectation[] = {
1623         {16, 3}};
1624     EXPECT_THAT(
1625         plugin_->SearchString(kTarget, kPattern, /*case_sensitive=*/true),
1626         Pointwise(SearchStringResultEq(), kExpectation));
1627   }
1628   {
1629     static constexpr PDFEngine::Client::SearchStringResult kExpectation[] = {
1630         {16, 3}, {41, 3}};
1631     EXPECT_THAT(
1632         plugin_->SearchString(kTarget, kPattern, /*case_sensitive=*/false),
1633         Pointwise(SearchStringResultEq(), kExpectation));
1634   }
1635 }
1636
1637 TEST_F(PdfViewWebPluginTest, UpdateFocus) {
1638   MockFunction<void(int checkpoint_num)> checkpoint;
1639
1640   {
1641     InSequence sequence;
1642
1643     // Focus false -> true: Triggers updates.
1644     EXPECT_CALL(*client_ptr_, UpdateTextInputState);
1645     EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1646     EXPECT_CALL(checkpoint, Call(1));
1647
1648     // Focus true -> true: No updates.
1649     EXPECT_CALL(checkpoint, Call(2));
1650
1651     // Focus true -> false: Triggers updates. `UpdateTextInputState` is called
1652     // twice because it also gets called due to
1653     // `PDFiumEngine::UpdateFocus(false)`.
1654     EXPECT_CALL(*client_ptr_, UpdateTextInputState).Times(2);
1655     EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1656     EXPECT_CALL(checkpoint, Call(3));
1657
1658     // Focus false -> false: No updates.
1659     EXPECT_CALL(checkpoint, Call(4));
1660
1661     // Focus false -> true: Triggers updates.
1662     EXPECT_CALL(*client_ptr_, UpdateTextInputState);
1663     EXPECT_CALL(*client_ptr_, UpdateSelectionBounds);
1664   }
1665
1666   // The focus type does not matter in this test.
1667   plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1668   checkpoint.Call(1);
1669   plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1670   checkpoint.Call(2);
1671   plugin_->UpdateFocus(/*focused=*/false, blink::mojom::FocusType::kNone);
1672   checkpoint.Call(3);
1673   plugin_->UpdateFocus(/*focused=*/false, blink::mojom::FocusType::kNone);
1674   checkpoint.Call(4);
1675   plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
1676 }
1677
1678 TEST_F(PdfViewWebPluginTest, ShouldDispatchImeEventsToPlugin) {
1679   ASSERT_TRUE(plugin_->ShouldDispatchImeEventsToPlugin());
1680 }
1681
1682 TEST_F(PdfViewWebPluginTest, CaretChange) {
1683   EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
1684   UpdatePluginGeometry(
1685       /*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(12, 24, 36, 48));
1686   plugin_->CaretChanged(gfx::Rect(10, 20, 30, 40));
1687   EXPECT_EQ(gfx::Rect(28, 20, 30, 40), plugin_->GetPluginCaretBounds());
1688 }
1689
1690 TEST_F(PdfViewWebPluginTest, EnteredEditMode) {
1691   EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1692   EXPECT_CALL(*client_ptr_, PostMessage).Times(AnyNumber());
1693   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1694     "type": "setIsEditing",
1695   })")));
1696   plugin_->EnteredEditMode();
1697
1698   pdf_receiver_.FlushForTesting();
1699 }
1700
1701 TEST_F(PdfViewWebPluginTest, NotifyNumberOfFindResultsChanged) {
1702   plugin_->StartFind("x", /*case_sensitive=*/false, /*identifier=*/123);
1703
1704   const std::vector<gfx::Rect> tickmarks = {gfx::Rect(1, 2), gfx::Rect(3, 4)};
1705   plugin_->UpdateTickMarks(tickmarks);
1706
1707   EXPECT_CALL(*client_ptr_, ReportFindInPageTickmarks(tickmarks));
1708   EXPECT_CALL(*client_ptr_, ReportFindInPageMatchCount(123, 5, true));
1709   plugin_->NotifyNumberOfFindResultsChanged(/*total=*/5, /*final_result=*/true);
1710 }
1711
1712 TEST_F(PdfViewWebPluginTest, OnDocumentLoadComplete) {
1713   base::Value::Dict metadata;
1714   metadata.Set("fileSize", "0 B");
1715   metadata.Set("linearized", false);
1716   metadata.Set("pageSize", "Varies");
1717   metadata.Set("canSerializeDocument", true);
1718
1719   base::Value::Dict message;
1720   message.Set("type", "metadata");
1721   message.Set("metadataData", std::move(metadata));
1722
1723   EXPECT_CALL(*client_ptr_, PostMessage);
1724   EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(message))));
1725   plugin_->DocumentLoadComplete();
1726 }
1727
1728 class PdfViewWebPluginWithDocInfoTest : public PdfViewWebPluginTest {
1729  public:
1730   void SetUp() override {
1731     PdfViewWebPluginTest::SetUp();
1732     if (IsPortfolioEnabled()) {
1733       scoped_feature_list_.InitAndEnableFeature(features::kPdfPortfolio);
1734     }
1735   }
1736
1737   bool IsPortfolioEnabled() { return GetParam(); }
1738
1739  protected:
1740   class TestPDFiumEngineWithDocInfo : public TestPDFiumEngine {
1741    public:
1742     explicit TestPDFiumEngineWithDocInfo(PDFEngine::Client* client)
1743         : TestPDFiumEngine(client) {
1744       InitializeDocumentAttachments();
1745       InitializeDocumentMetadata();
1746     }
1747
1748     base::Value::List GetBookmarks() override {
1749       // Create `bookmark1` which navigates to an in-doc position. This bookmark
1750       // will be in the top-level bookmark list.
1751       base::Value::Dict bookmark1;
1752       bookmark1.Set("title", "Bookmark 1");
1753       bookmark1.Set("page", 2);
1754       bookmark1.Set("x", 10);
1755       bookmark1.Set("y", 20);
1756       bookmark1.Set("zoom", 2.0);
1757
1758       // Create `bookmark2` which navigates to a web page. This bookmark will be
1759       // a child of `bookmark1`.
1760       base::Value::Dict bookmark2;
1761       bookmark2.Set("title", "Bookmark 2");
1762       bookmark2.Set("uri", "test.com");
1763
1764       base::Value::List children_of_bookmark1;
1765       children_of_bookmark1.Append(std::move(bookmark2));
1766       bookmark1.Set("children", std::move(children_of_bookmark1));
1767
1768       // Create the top-level bookmark list.
1769       base::Value::List bookmarks;
1770       bookmarks.Append(std::move(bookmark1));
1771       return bookmarks;
1772     }
1773
1774     absl::optional<gfx::Size> GetUniformPageSizePoints() override {
1775       return gfx::Size(1000, 1200);
1776     }
1777
1778    private:
1779     void InitializeDocumentAttachments() {
1780       doc_attachment_info_list().resize(3);
1781
1782       // A regular attachment.
1783       doc_attachment_info_list()[0].name = u"attachment1.txt";
1784       doc_attachment_info_list()[0].creation_date = u"D:20170712214438-07'00'";
1785       doc_attachment_info_list()[0].modified_date = u"D:20160115091400";
1786       doc_attachment_info_list()[0].is_readable = true;
1787       doc_attachment_info_list()[0].size_bytes = 13u;
1788
1789       // An unreadable attachment.
1790       doc_attachment_info_list()[1].name = u"attachment2.pdf";
1791       doc_attachment_info_list()[1].is_readable = false;
1792
1793       // A readable attachment that exceeds download size limit.
1794       doc_attachment_info_list()[2].name = u"attachment3.mov";
1795       doc_attachment_info_list()[2].is_readable = true;
1796       doc_attachment_info_list()[2].size_bytes =
1797           PdfViewWebPlugin::kMaximumSavedFileSize + 1;
1798     }
1799
1800     void InitializeDocumentMetadata() {
1801       metadata().version = PdfVersion::k1_7;
1802       metadata().size_bytes = 13u;
1803       metadata().page_count = 13u;
1804       metadata().linearized = true;
1805       metadata().has_attachments = true;
1806       metadata().form_type = FormType::kAcroForm;
1807       metadata().title = "Title";
1808       metadata().author = "Author";
1809       metadata().subject = "Subject";
1810       metadata().keywords = "Keywords";
1811       metadata().creator = "Creator";
1812       metadata().producer = "Producer";
1813       ASSERT_TRUE(base::Time::FromUTCString("2021-05-04 11:12:13",
1814                                             &metadata().creation_date));
1815       ASSERT_TRUE(base::Time::FromUTCString("2021-06-04 15:16:17",
1816                                             &metadata().mod_date));
1817     }
1818   };
1819
1820   static base::Value::Dict CreateExpectedAttachmentsResponse() {
1821     base::Value::List attachments;
1822     {
1823       base::Value::Dict attachment;
1824       attachment.Set("name", "attachment1.txt");
1825       attachment.Set("size", 13);
1826       attachment.Set("readable", true);
1827       attachments.Append(std::move(attachment));
1828     }
1829     {
1830       base::Value::Dict attachment;
1831       attachment.Set("name", "attachment2.pdf");
1832       attachment.Set("size", 0);
1833       attachment.Set("readable", false);
1834       attachments.Append(std::move(attachment));
1835     }
1836     {
1837       base::Value::Dict attachment;
1838       attachment.Set("name", "attachment3.mov");
1839       attachment.Set("size", -1);
1840       attachment.Set("readable", true);
1841       attachments.Append(std::move(attachment));
1842     }
1843
1844     base::Value::Dict message;
1845     message.Set("type", "attachments");
1846     message.Set("attachmentsData", std::move(attachments));
1847     return message;
1848   }
1849
1850   static base::Value::Dict CreateExpectedBookmarksResponse(
1851       base::Value::List bookmarks) {
1852     base::Value::Dict message;
1853     message.Set("type", "bookmarks");
1854     message.Set("bookmarksData", std::move(bookmarks));
1855     return message;
1856   }
1857
1858   static base::Value::Dict CreateExpectedMetadataResponse() {
1859     base::Value::Dict metadata;
1860     metadata.Set("version", "1.7");
1861     metadata.Set("fileSize", "13 B");
1862     metadata.Set("linearized", true);
1863
1864     metadata.Set("title", "Title");
1865     metadata.Set("author", "Author");
1866     metadata.Set("subject", "Subject");
1867     metadata.Set("keywords", "Keywords");
1868     metadata.Set("creator", "Creator");
1869     metadata.Set("producer", "Producer");
1870     metadata.Set("creationDate",
1871                  "5/4/21, 4:12:13\xE2\x80\xAF"
1872                  "AM");
1873     metadata.Set("modDate",
1874                  "6/4/21, 8:16:17\xE2\x80\xAF"
1875                  "AM");
1876     metadata.Set("pageSize", "13.89 × 16.67 in (portrait)");
1877     metadata.Set("canSerializeDocument", true);
1878
1879     base::Value::Dict message;
1880     message.Set("type", "metadata");
1881     message.Set("metadataData", std::move(metadata));
1882     return message;
1883   }
1884
1885   void SetUpClient() override {
1886     EXPECT_CALL(*client_ptr_, CreateEngine).WillOnce([this]() {
1887       auto engine = std::make_unique<NiceMock<TestPDFiumEngineWithDocInfo>>(
1888           plugin_.get());
1889       engine_ptr_ = engine.get();
1890       return engine;
1891     });
1892   }
1893
1894   base::test::ScopedFeatureList scoped_feature_list_;
1895 };
1896
1897 TEST_P(PdfViewWebPluginWithDocInfoTest, OnDocumentLoadComplete) {
1898   const base::Value::Dict expect_attachments =
1899       CreateExpectedAttachmentsResponse();
1900   const base::Value::Dict expect_bookmarks =
1901       CreateExpectedBookmarksResponse(engine_ptr_->GetBookmarks());
1902   const base::Value::Dict expect_metadata = CreateExpectedMetadataResponse();
1903   EXPECT_CALL(*client_ptr_, PostMessage);
1904   EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_attachments))))
1905       .Times(IsPortfolioEnabled() ? 1 : 0);
1906   EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_bookmarks))));
1907   EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(expect_metadata))));
1908   plugin_->DocumentLoadComplete();
1909 }
1910
1911 INSTANTIATE_TEST_SUITE_P(All, PdfViewWebPluginWithDocInfoTest, testing::Bool());
1912
1913 class PdfViewWebPluginSaveTest : public PdfViewWebPluginTest {
1914  protected:
1915   static void AddDataToValue(base::span<const uint8_t> data,
1916                              base::Value& value) {
1917     value.GetDict().Set("dataToSave", base::Value(data));
1918   }
1919
1920   void SetUpClient() override {
1921     // Ignore non-"saveData" `PdfViewWebPlugin::Client::PostMessage()` calls.
1922     EXPECT_CALL(*client_ptr_, PostMessage)
1923         .WillRepeatedly([](const base::Value::Dict& message) {
1924           EXPECT_NE("saveData", *message.FindString("type"));
1925         });
1926   }
1927 };
1928
1929 #if BUILDFLAG(ENABLE_INK)
1930 TEST_F(PdfViewWebPluginSaveTest, AnnotationInNonEditMode) {
1931   base::Value expected_response = base::test::ParseJson(R"({
1932     "type": "saveData",
1933     "token": "annotation-in-non-edit-mode",
1934     "fileName": "example.pdf",
1935     "editModeForTesting": false,
1936   })");
1937   AddDataToValue(base::make_span(TestPDFiumEngine::kLoadedData),
1938                  expected_response);
1939
1940   EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1941   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1942   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1943
1944   plugin_->OnMessage(ParseMessage(R"({
1945     "type": "save",
1946     "saveRequestType": 0,
1947     "token": "annotation-in-non-edit-mode",
1948   })"));
1949
1950   pdf_receiver_.FlushForTesting();
1951 }
1952
1953 TEST_F(PdfViewWebPluginSaveTest, AnnotationInEditMode) {
1954   plugin_->EnteredEditMode();
1955   pdf_receiver_.FlushForTesting();
1956
1957   base::Value expected_response = base::test::ParseJson(R"({
1958     "type": "saveData",
1959     "token": "annotation-in-edit-mode",
1960     "fileName": "example.pdf",
1961     "editModeForTesting": true,
1962   })");
1963   AddDataToValue(base::make_span(TestPDFiumEngine::kSaveData),
1964                  expected_response);
1965
1966   EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
1967   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1968   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
1969
1970   plugin_->OnMessage(ParseMessage(R"({
1971     "type": "save",
1972     "saveRequestType": 0,
1973     "token": "annotation-in-edit-mode",
1974   })"));
1975
1976   pdf_receiver_.FlushForTesting();
1977 }
1978 #endif  // BUILDFLAG(ENABLE_INK)
1979
1980 TEST_F(PdfViewWebPluginSaveTest, OriginalInNonEditMode) {
1981   {
1982     InSequence pdf_service_sequence;
1983
1984     EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
1985     EXPECT_CALL(
1986         pdf_service_,
1987         SaveUrlAs(GURL(kPdfUrl), network::mojom::ReferrerPolicy::kDefault));
1988     EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
1989   }
1990
1991   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
1992   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
1993     "type": "consumeSaveToken",
1994     "token": "original-in-non-edit-mode",
1995   })")));
1996
1997   plugin_->OnMessage(ParseMessage(R"({
1998     "type": "save",
1999     "saveRequestType": 1,
2000     "token": "original-in-non-edit-mode",
2001   })"));
2002
2003   pdf_receiver_.FlushForTesting();
2004 }
2005
2006 TEST_F(PdfViewWebPluginSaveTest, OriginalInEditMode) {
2007   plugin_->EnteredEditMode();
2008   pdf_receiver_.FlushForTesting();
2009
2010   {
2011     InSequence pdf_service_sequence;
2012
2013     EXPECT_CALL(pdf_service_, SetPluginCanSave(false));
2014     EXPECT_CALL(
2015         pdf_service_,
2016         SaveUrlAs(GURL(kPdfUrl), network::mojom::ReferrerPolicy::kDefault));
2017     EXPECT_CALL(pdf_service_, SetPluginCanSave(true));
2018   }
2019
2020   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
2021   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2022     "type": "consumeSaveToken",
2023     "token": "original-in-edit-mode",
2024   })")));
2025
2026   plugin_->OnMessage(ParseMessage(R"({
2027     "type": "save",
2028     "saveRequestType": 1,
2029     "token": "original-in-edit-mode",
2030   })"));
2031
2032   pdf_receiver_.FlushForTesting();
2033 }
2034
2035 #if BUILDFLAG(ENABLE_INK)
2036 TEST_F(PdfViewWebPluginSaveTest, EditedInNonEditMode) {
2037   base::Value expected_response = base::test::ParseJson(R"({
2038     "type": "saveData",
2039     "token": "edited-in-non-edit-mode",
2040     "fileName": "example.pdf",
2041     "editModeForTesting": false,
2042   })");
2043   AddDataToValue(base::make_span(TestPDFiumEngine::kLoadedData),
2044                  expected_response);
2045
2046   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
2047   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
2048
2049   plugin_->OnMessage(ParseMessage(R"({
2050     "type": "save",
2051     "saveRequestType": 2,
2052     "token": "edited-in-non-edit-mode",
2053   })"));
2054 }
2055 #endif  // BUILDFLAG(ENABLE_INK)
2056
2057 TEST_F(PdfViewWebPluginSaveTest, EditedInEditMode) {
2058   plugin_->EnteredEditMode();
2059
2060   base::Value expected_response = base::test::ParseJson(R"({
2061     "type": "saveData",
2062     "token": "edited-in-edit-mode",
2063     "fileName": "example.pdf",
2064     "editModeForTesting": true,
2065   })");
2066   AddDataToValue(base::make_span(TestPDFiumEngine::kSaveData),
2067                  expected_response);
2068
2069   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
2070   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(expected_response)));
2071
2072   plugin_->OnMessage(ParseMessage(R"({
2073     "type": "save",
2074     "saveRequestType": 2,
2075     "token": "edited-in-edit-mode",
2076   })"));
2077 }
2078
2079 class PdfViewWebPluginSubmitFormTest
2080     : public PdfViewWebPluginWithoutInitializeTest {
2081  protected:
2082   void SubmitForm(const std::string& url,
2083                   base::StringPiece form_data = "data") {
2084     EXPECT_TRUE(plugin_->InitializeForTesting());
2085
2086     EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).WillOnce([this]() {
2087       auto associated_loader =
2088           std::make_unique<NiceMock<MockWebAssociatedURLLoader>>();
2089       EXPECT_CALL(*associated_loader, LoadAsynchronously)
2090           .WillOnce([this](const blink::WebURLRequest& request,
2091                            blink::WebAssociatedURLLoaderClient* /*client*/) {
2092             // TODO(crbug.com/1322928): The `UrlLoader` created by `LoadUrl()`
2093             // and `SubmitForm()` shouldn't use different ownership semantics.
2094             // The loader created by `SubmitForm()` is owned by the plugin, and
2095             // cannot leak past the destruction of the plugin.
2096             request_.CopyFrom(request);
2097           });
2098       return associated_loader;
2099     });
2100
2101     plugin_->SubmitForm(url, form_data.data(), form_data.size());
2102   }
2103
2104   void SubmitFailingForm(const std::string& url) {
2105     EXPECT_TRUE(plugin_->InitializeForTesting());
2106
2107     EXPECT_CALL(*client_ptr_, CreateAssociatedURLLoader).Times(0);
2108
2109     constexpr base::StringPiece kFormData = "form data";
2110     plugin_->SubmitForm(url, kFormData.data(), kFormData.size());
2111   }
2112
2113   blink::WebURLRequest request_;
2114 };
2115
2116 TEST_F(PdfViewWebPluginSubmitFormTest, RequestMethod) {
2117   SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2118
2119   SubmitForm(/*url=*/"");
2120
2121   EXPECT_EQ(request_.HttpMethod().Utf8(), "POST");
2122 }
2123
2124 TEST_F(PdfViewWebPluginSubmitFormTest, RequestBody) {
2125   SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2126
2127   constexpr base::StringPiece kFormData = "form data";
2128   SubmitForm(/*url=*/"", kFormData);
2129
2130   blink::WebHTTPBody::Element element;
2131   EXPECT_EQ(request_.HttpBody().ElementCount(), 1u);
2132   ASSERT_TRUE(request_.HttpBody().ElementAt(0, element));
2133   ASSERT_EQ(element.type, blink::HTTPBodyElementType::kTypeData);
2134   EXPECT_THAT(element.data.Copy(), testing::ElementsAreArray(kFormData));
2135 }
2136
2137 TEST_F(PdfViewWebPluginSubmitFormTest, RelativeUrl) {
2138   SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2139
2140   SubmitForm("relative_endpoint");
2141
2142   EXPECT_EQ(request_.Url().GetString().Utf8(),
2143             "https://www.example.com/path/to/relative_endpoint");
2144 }
2145
2146 TEST_F(PdfViewWebPluginSubmitFormTest, NoRelativeUrl) {
2147   SetUpPluginWithUrl("https://www.example.com/path/to/the.pdf");
2148
2149   SubmitForm("");
2150
2151   EXPECT_EQ(request_.Url().GetString().Utf8(),
2152             "https://www.example.com/path/to/the.pdf");
2153 }
2154
2155 TEST_F(PdfViewWebPluginSubmitFormTest, AbsoluteUrl) {
2156   SetUpPluginWithUrl("https://a.example.com/path/to/the.pdf");
2157
2158   SubmitForm("https://b.example.com/relative_endpoint");
2159
2160   EXPECT_EQ(request_.Url().GetString().Utf8(),
2161             "https://b.example.com/relative_endpoint");
2162 }
2163
2164 TEST_F(PdfViewWebPluginSubmitFormTest, RelativeUrlInvalidDocumentUrl) {
2165   SetUpPluginWithUrl("https://www.%B%Ad.com/path/to/the.pdf");
2166
2167   SubmitFailingForm("relative_endpoint");
2168 }
2169
2170 TEST_F(PdfViewWebPluginSubmitFormTest, AbsoluteUrlInvalidDocumentUrl) {
2171   SetUpPluginWithUrl("https://www.%B%Ad.com/path/to/the.pdf");
2172
2173   SubmitFailingForm("https://wwww.example.com");
2174 }
2175
2176 class PdfViewWebPluginPrintTest : public PdfViewWebPluginTest {
2177  protected:
2178   void SetUp() override {
2179     PdfViewWebPluginTest::SetUp();
2180
2181     // Size must be at least 1 for conversion to `SkMemoryStream`.
2182     ON_CALL(*engine_ptr_, PrintPages)
2183         .WillByDefault(Return(std::vector<uint8_t>(1)));
2184
2185     canvas_.sk_canvas()->SetPrintingMetafile(&metafile_);
2186   }
2187
2188   printing::MetafileSkia metafile_;
2189 };
2190
2191 TEST_F(PdfViewWebPluginPrintTest, HighQuality) {
2192   EXPECT_CALL(*engine_ptr_,
2193               HasPermission(DocumentPermission::kPrintHighQuality))
2194       .WillRepeatedly(Return(true));
2195   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2196       .WillRepeatedly(Return(true));
2197   ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2198             plugin_->PrintBegin(blink::WebPrintParams()));
2199
2200   EXPECT_CALL(
2201       *engine_ptr_,
2202       PrintPages(ElementsAre(0),
2203                  Field(&blink::WebPrintParams::rasterize_pdf, IsFalse())));
2204   plugin_->PrintPage(0, canvas_.sk_canvas());
2205   plugin_->PrintEnd();
2206 }
2207
2208 TEST_F(PdfViewWebPluginPrintTest, HighQualityRasterized) {
2209   EXPECT_CALL(*engine_ptr_,
2210               HasPermission(DocumentPermission::kPrintHighQuality))
2211       .WillRepeatedly(Return(true));
2212   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2213       .WillRepeatedly(Return(true));
2214
2215   blink::WebPrintParams params;
2216   params.rasterize_pdf = true;
2217   ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2218             plugin_->PrintBegin(params));
2219
2220   EXPECT_CALL(
2221       *engine_ptr_,
2222       PrintPages(ElementsAre(0),
2223                  Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2224   plugin_->PrintPage(0, canvas_.sk_canvas());
2225   plugin_->PrintEnd();
2226 }
2227
2228 // Regression test for crbug.com/1307219.
2229 TEST_F(PdfViewWebPluginPrintTest, LowQuality) {
2230   EXPECT_CALL(*engine_ptr_,
2231               HasPermission(DocumentPermission::kPrintHighQuality))
2232       .WillRepeatedly(Return(false));
2233   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2234       .WillRepeatedly(Return(true));
2235   ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2236             plugin_->PrintBegin(blink::WebPrintParams()));
2237
2238   EXPECT_CALL(
2239       *engine_ptr_,
2240       PrintPages(ElementsAre(0),
2241                  Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2242   plugin_->PrintPage(0, canvas_.sk_canvas());
2243   plugin_->PrintEnd();
2244 }
2245
2246 // Regression test for crbug.com/1307219.
2247 TEST_F(PdfViewWebPluginPrintTest, LowQualityRasterized) {
2248   EXPECT_CALL(*engine_ptr_,
2249               HasPermission(DocumentPermission::kPrintHighQuality))
2250       .WillRepeatedly(Return(false));
2251   EXPECT_CALL(*engine_ptr_, HasPermission(DocumentPermission::kPrintLowQuality))
2252       .WillRepeatedly(Return(true));
2253
2254   blink::WebPrintParams params;
2255   params.rasterize_pdf = true;
2256   ASSERT_EQ(static_cast<int>(TestPDFiumEngine::kPageNumber),
2257             plugin_->PrintBegin(params));
2258
2259   EXPECT_CALL(
2260       *engine_ptr_,
2261       PrintPages(ElementsAre(0),
2262                  Field(&blink::WebPrintParams::rasterize_pdf, IsTrue())));
2263   plugin_->PrintPage(0, canvas_.sk_canvas());
2264   plugin_->PrintEnd();
2265 }
2266
2267 TEST_F(PdfViewWebPluginPrintTest, Disabled) {
2268   EXPECT_EQ(0, plugin_->PrintBegin(blink::WebPrintParams()));
2269 }
2270
2271 TEST_F(PdfViewWebPluginPrintTest, DisabledRasterized) {
2272   blink::WebPrintParams params;
2273   params.rasterize_pdf = true;
2274   EXPECT_EQ(0, plugin_->PrintBegin(params));
2275 }
2276
2277 class PdfViewWebPluginPrintPreviewTest : public PdfViewWebPluginTest {
2278  protected:
2279   void SetUpClient() override {
2280     EXPECT_CALL(*client_ptr_, GetEmbedderOriginString)
2281         .WillRepeatedly(Return("chrome://print/"));
2282   }
2283 };
2284
2285 TEST_F(PdfViewWebPluginPrintPreviewTest, HandleResetPrintPreviewModeMessage) {
2286   EXPECT_CALL(*client_ptr_, CreateEngine)
2287       .WillOnce([](PDFEngine::Client* client,
2288                    PDFiumFormFiller::ScriptOption script_option) {
2289         EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript, script_option);
2290
2291         auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
2292         EXPECT_CALL(*engine, ZoomUpdated);
2293         EXPECT_CALL(*engine, PageOffsetUpdated);
2294         EXPECT_CALL(*engine, PluginSizeUpdated);
2295         EXPECT_CALL(*engine, SetGrayscale(false));
2296         return engine;
2297       });
2298
2299   OnMessageWithEngineUpdate(ParseMessage(R"({
2300     "type": "resetPrintPreviewMode",
2301     "url": "chrome-untrusted://print/0/0/print.pdf",
2302     "grayscale": false,
2303     "pageCount": 1,
2304   })"));
2305 }
2306
2307 TEST_F(PdfViewWebPluginPrintPreviewTest,
2308        HandleResetPrintPreviewModeMessageForPdf) {
2309   EXPECT_CALL(*client_ptr_, CreateEngine)
2310       .WillOnce([](PDFEngine::Client* client,
2311                    PDFiumFormFiller::ScriptOption script_option) {
2312         EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript, script_option);
2313
2314         return std::make_unique<NiceMock<TestPDFiumEngine>>(client);
2315       });
2316
2317   // The UI ID of 1 in the URL is arbitrary.
2318   // The page index value of -1, AKA `kCompletePDFIndex`, is required for PDFs.
2319   OnMessageWithEngineUpdate(ParseMessage(R"({
2320     "type": "resetPrintPreviewMode",
2321     "url": "chrome-untrusted://print/1/-1/print.pdf",
2322     "grayscale": false,
2323     "pageCount": 0,
2324   })"));
2325
2326   EXPECT_CALL(*client_ptr_, PostMessage).Times(AnyNumber());
2327   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2328     "type": "printPreviewLoaded",
2329   })")));
2330   plugin_->DocumentLoadComplete();
2331   pdf_receiver_.FlushForTesting();
2332 }
2333
2334 TEST_F(PdfViewWebPluginPrintPreviewTest,
2335        HandleResetPrintPreviewModeMessageSetGrayscale) {
2336   EXPECT_CALL(*client_ptr_, CreateEngine)
2337       .WillOnce([](PDFEngine::Client* client,
2338                    PDFiumFormFiller::ScriptOption /*script_option*/) {
2339         auto engine = std::make_unique<NiceMock<TestPDFiumEngine>>(client);
2340         EXPECT_CALL(*engine, SetGrayscale(true));
2341         return engine;
2342       });
2343
2344   OnMessageWithEngineUpdate(ParseMessage(R"({
2345     "type": "resetPrintPreviewMode",
2346     "url": "chrome-untrusted://print/0/0/print.pdf",
2347     "grayscale": true,
2348     "pageCount": 1,
2349   })"));
2350 }
2351
2352 TEST_F(PdfViewWebPluginPrintPreviewTest, DocumentLoadComplete) {
2353   OnMessageWithEngineUpdate(ParseMessage(R"({
2354     "type": "resetPrintPreviewMode",
2355     "url": "chrome-untrusted://print/0/0/print.pdf",
2356     "grayscale": false,
2357     "pageCount": 1,
2358   })"));
2359
2360   EXPECT_CALL(*client_ptr_, RecordComputedAction("PDF.LoadSuccess"));
2361   EXPECT_CALL(*client_ptr_, PostMessage);
2362   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2363     "type": "formFocusChange",
2364     "focused": false,
2365   })")));
2366   ExpectUpdateTextInputState(blink::WebTextInputType::kWebTextInputTypeNone);
2367   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2368     "type": "printPreviewLoaded",
2369   })")));
2370   EXPECT_CALL(*accessibility_data_handler_ptr_, SetAccessibilityDocInfo)
2371       .Times(0);
2372   EXPECT_CALL(*client_ptr_, DidStopLoading).Times(0);
2373   EXPECT_CALL(pdf_service_, UpdateContentRestrictions).Times(0);
2374   plugin_->DocumentLoadComplete();
2375
2376   EXPECT_EQ(PdfViewWebPlugin::DocumentLoadState::kComplete,
2377             plugin_->document_load_state_for_testing());
2378   pdf_receiver_.FlushForTesting();
2379 }
2380
2381 TEST_F(PdfViewWebPluginPrintPreviewTest,
2382        DocumentLoadProgressResetByResetPrintPreviewModeMessage) {
2383   plugin_->DocumentLoadProgress(2, 100);
2384
2385   OnMessageWithEngineUpdate(ParseMessage(R"({
2386     "type": "resetPrintPreviewMode",
2387     "url": "chrome-untrusted://print/123/0/print.pdf",
2388     "grayscale": false,
2389     "pageCount": 2,
2390   })"));
2391
2392   EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(R"({
2393     "type": "loadProgress",
2394     "progress": 3.0,
2395   })")));
2396   plugin_->DocumentLoadProgress(3, 100);
2397 }
2398
2399 TEST_F(PdfViewWebPluginPrintPreviewTest,
2400        DocumentLoadProgressNotResetByLoadPreviewPageMessage) {
2401   OnMessageWithEngineUpdate(ParseMessage(R"({
2402     "type": "resetPrintPreviewMode",
2403     "url": "chrome-untrusted://print/123/0/print.pdf",
2404     "grayscale": false,
2405     "pageCount": 2,
2406   })"));
2407
2408   plugin_->DocumentLoadProgress(2, 100);
2409
2410   plugin_->OnMessage(ParseMessage(R"({
2411     "type": "loadPreviewPage",
2412     "url": "chrome-untrusted://print/123/1/print.pdf",
2413     "index": 1,
2414   })"));
2415
2416   EXPECT_CALL(*client_ptr_, PostMessage).Times(0);
2417   plugin_->DocumentLoadProgress(3, 100);
2418 }
2419
2420 TEST_F(PdfViewWebPluginPrintPreviewTest,
2421        HandleViewportMessageScrollRightToLeft) {
2422   EXPECT_CALL(*engine_ptr_, ApplyDocumentLayout)
2423       .WillRepeatedly(Return(gfx::Size(16, 9)));
2424   EXPECT_CALL(*engine_ptr_, ScrolledToXPosition(14));
2425   EXPECT_CALL(*engine_ptr_, ScrolledToYPosition(3));
2426
2427   plugin_->OnMessage(ParseMessage(R"({
2428     "type": "viewport",
2429     "userInitiated": false,
2430     "zoom": 1,
2431     "layoutOptions": {
2432       "direction": 1,
2433       "defaultPageOrientation": 0,
2434       "twoUpViewEnabled": false,
2435     },
2436     "xOffset": -2,
2437     "yOffset": 3,
2438     "pinchPhase": 0,
2439   })"));
2440 }
2441
2442 }  // namespace chrome_pdf