Fix: warnings in media/capture/filters
[platform/framework/web/chromium-efl.git] / pdf / pdf_view_web_plugin.cc
1 // Copyright 2020 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 <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "base/auto_reset.h"
17 #include "base/check_op.h"
18 #include "base/containers/fixed_flat_map.h"
19 #include "base/containers/queue.h"
20 #include "base/debug/crash_logging.h"
21 #include "base/feature_list.h"
22 #include "base/functional/bind.h"
23 #include "base/functional/callback.h"
24 #include "base/i18n/char_iterator.h"
25 #include "base/i18n/rtl.h"
26 #include "base/i18n/string_search.h"
27 #include "base/i18n/time_formatting.h"
28 #include "base/logging.h"
29 #include "base/memory/raw_ptr.h"
30 #include "base/no_destructor.h"
31 #include "base/notreached.h"
32 #include "base/numerics/safe_conversions.h"
33 #include "base/strings/escape.h"
34 #include "base/strings/strcat.h"
35 #include "base/strings/string_number_conversions.h"
36 #include "base/strings/string_piece.h"
37 #include "base/strings/string_split.h"
38 #include "base/strings/string_util.h"
39 #include "base/task/sequenced_task_runner.h"
40 #include "base/task/single_thread_task_runner.h"
41 #include "base/thread_annotations.h"
42 #include "base/threading/thread_checker.h"
43 #include "base/time/time.h"
44 #include "base/values.h"
45 #include "cc/paint/paint_canvas.h"
46 #include "cc/paint/paint_flags.h"
47 #include "cc/paint/paint_image.h"
48 #include "cc/paint/paint_image_builder.h"
49 #include "net/cookies/site_for_cookies.h"
50 #include "pdf/accessibility.h"
51 #include "pdf/accessibility_structs.h"
52 #include "pdf/buildflags.h"
53 #include "pdf/content_restriction.h"
54 #include "pdf/document_layout.h"
55 #include "pdf/loader/result_codes.h"
56 #include "pdf/loader/url_loader.h"
57 #include "pdf/metrics_handler.h"
58 #include "pdf/mojom/pdf.mojom.h"
59 #include "pdf/paint_manager.h"
60 #include "pdf/paint_ready_rect.h"
61 #include "pdf/parsed_params.h"
62 #include "pdf/pdf_accessibility_data_handler.h"
63 #include "pdf/pdf_engine.h"
64 #include "pdf/pdf_features.h"
65 #include "pdf/pdf_init.h"
66 #include "pdf/pdfium/pdfium_engine.h"
67 #include "pdf/post_message_receiver.h"
68 #include "pdf/ui/document_properties.h"
69 #include "pdf/ui/file_name.h"
70 #include "pdf/ui/thumbnail.h"
71 #include "printing/metafile_skia.h"
72 #include "printing/units.h"
73 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
74 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
75 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
76 #include "third_party/blink/public/common/input/web_input_event.h"
77 #include "third_party/blink/public/common/input/web_keyboard_event.h"
78 #include "third_party/blink/public/common/metrics/document_update_reason.h"
79 #include "third_party/blink/public/mojom/input/focus_type.mojom-shared.h"
80 #include "third_party/blink/public/platform/platform.h"
81 #include "third_party/blink/public/platform/web_input_event_result.h"
82 #include "third_party/blink/public/platform/web_string.h"
83 #include "third_party/blink/public/platform/web_text_input_type.h"
84 #include "third_party/blink/public/platform/web_url.h"
85 #include "third_party/blink/public/platform/web_url_error.h"
86 #include "third_party/blink/public/platform/web_url_response.h"
87 #include "third_party/blink/public/web/web_associated_url_loader.h"
88 #include "third_party/blink/public/web/web_associated_url_loader_options.h"
89 #include "third_party/blink/public/web/web_document.h"
90 #include "third_party/blink/public/web/web_frame.h"
91 #include "third_party/blink/public/web/web_plugin_container.h"
92 #include "third_party/blink/public/web/web_plugin_params.h"
93 #include "third_party/blink/public/web/web_print_params.h"
94 #include "third_party/blink/public/web/web_print_preset_options.h"
95 #include "third_party/blink/public/web/web_view.h"
96 #include "third_party/blink/public/web/web_widget.h"
97 #include "third_party/skia/include/core/SkBitmap.h"
98 #include "third_party/skia/include/core/SkColor.h"
99 #include "third_party/skia/include/core/SkImage.h"
100 #include "third_party/skia/include/core/SkImageInfo.h"
101 #include "third_party/skia/include/core/SkRect.h"
102 #include "third_party/skia/include/core/SkRefCnt.h"
103 #include "third_party/skia/include/core/SkSize.h"
104 #include "ui/base/cursor/cursor.h"
105 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
106 #include "ui/base/text/bytes_formatting.h"
107 #include "ui/events/base_event_utils.h"
108 #include "ui/events/blink/blink_event_util.h"
109 #include "ui/events/keycodes/keyboard_codes.h"
110 #include "ui/gfx/geometry/point.h"
111 #include "ui/gfx/geometry/point_conversions.h"
112 #include "ui/gfx/geometry/point_f.h"
113 #include "ui/gfx/geometry/rect.h"
114 #include "ui/gfx/geometry/size.h"
115 #include "ui/gfx/geometry/skia_conversions.h"
116 #include "ui/gfx/geometry/vector2d.h"
117 #include "ui/gfx/geometry/vector2d_f.h"
118 #include "ui/gfx/range/range.h"
119 #include "url/gurl.h"
120 #include "v8/include/v8.h"
121
122 namespace chrome_pdf {
123
124 namespace {
125
126 // The minimum zoom level allowed.
127 constexpr double kMinZoom = 0.01;
128
129 // A delay to wait between each accessibility page to keep the system
130 // responsive.
131 constexpr base::TimeDelta kAccessibilityPageDelay = base::Milliseconds(100);
132
133 constexpr base::TimeDelta kFindResultCooldown = base::Milliseconds(100);
134
135 constexpr base::StringPiece kChromeExtensionHost =
136     "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/";
137
138 // Print Preview base URL.
139 constexpr base::StringPiece kChromePrintHost = "chrome://print/";
140
141 // Untrusted Print Preview base URL.
142 constexpr base::StringPiece kChromeUntrustedPrintHost =
143     "chrome-untrusted://print/";
144
145 // Same value as `printing::COMPLETE_PREVIEW_DOCUMENT_INDEX`.
146 constexpr int kCompletePDFIndex = -1;
147
148 // A different negative value to differentiate itself from `kCompletePDFIndex`.
149 constexpr int kInvalidPDFIndex = -2;
150
151 // Enumeration of pinch states.
152 // This should match PinchPhase enum in
153 // chrome/browser/resources/pdf/viewport.ts.
154 enum class PinchPhase {
155   kNone = 0,
156   kStart = 1,
157   kUpdateZoomOut = 2,
158   kUpdateZoomIn = 3,
159   kEnd = 4,
160 };
161
162 // Initialization performed per renderer process. Initialization may be
163 // triggered from multiple plugin instances, but should only execute once.
164 //
165 // TODO(crbug.com/1123621): We may be able to simplify this once we've figured
166 // out exactly which processes need to initialize and shutdown PDFium.
167 class PerProcessInitializer final {
168  public:
169   ~PerProcessInitializer() {
170     // On some configs, thread checker is trivially destructible, which makes
171     // `PerProcessInitializer` trivially destructible as well. This is a problem
172     // because `base::NoDestructor` only allows non-trivially destructible
173     // types. Force `PerProcessInitializer` to be non-trivially destructible by
174     // declaring a non-default destructor.
175   }
176
177   static PerProcessInitializer& GetInstance() {
178     static base::NoDestructor<PerProcessInitializer> instance;
179     return *instance;
180   }
181
182   void Acquire(bool use_skia) {
183     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
184
185     DCHECK_GE(init_count_, 0);
186     if (init_count_++ > 0)
187       return;
188
189     DCHECK(!IsSDKInitializedViaPlugin());
190     InitializeSDK(/*enable_v8=*/true, use_skia, FontMappingMode::kBlink);
191     SetIsSDKInitializedViaPlugin(true);
192   }
193
194   void Release() {
195     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
196
197     DCHECK_GT(init_count_, 0);
198     if (--init_count_ > 0)
199       return;
200
201     DCHECK(IsSDKInitializedViaPlugin());
202     ShutdownSDK();
203     SetIsSDKInitializedViaPlugin(false);
204   }
205
206  private:
207   int init_count_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
208
209   // TODO(crbug.com/1123731): Assuming PDFium is thread-hostile for now, and
210   // must use one thread exclusively.
211   THREAD_CHECKER(thread_checker_);
212 };
213
214 base::Value::Dict DictFromRect(const gfx::Rect& rect) {
215   base::Value::Dict dict;
216   dict.Set("x", rect.x());
217   dict.Set("y", rect.y());
218   dict.Set("width", rect.width());
219   dict.Set("height", rect.height());
220   return dict;
221 }
222
223 bool IsPrintPreviewUrl(base::StringPiece url) {
224   return base::StartsWith(url, kChromeUntrustedPrintHost);
225 }
226
227 int ExtractPrintPreviewPageIndex(base::StringPiece src_url) {
228   // Sample `src_url` format: chrome-untrusted://print/id/page_index/print.pdf
229   // The page_index is zero-based, but can be negative with special meanings.
230   std::vector<base::StringPiece> url_substr =
231       base::SplitStringPiece(src_url.substr(kChromeUntrustedPrintHost.size()),
232                              "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
233   if (url_substr.size() != 3)
234     return kInvalidPDFIndex;
235
236   if (url_substr[2] != "print.pdf")
237     return kInvalidPDFIndex;
238
239   int page_index = 0;
240   if (!base::StringToInt(url_substr[1], &page_index))
241     return kInvalidPDFIndex;
242   return page_index;
243 }
244
245 bool IsPreviewingPDF(int print_preview_page_count) {
246   return print_preview_page_count == 0;
247 }
248
249 // Prepares messages from the plugin that reply to messages from the embedder.
250 // If the "type" value of `message` is "foo", then the `reply_type` must be
251 // "fooReply". The `message` from the embedder must have a "messageId" value
252 // that will be copied to the reply message.
253 base::Value::Dict PrepareReplyMessage(base::StringPiece reply_type,
254                                       const base::Value::Dict& message) {
255   DCHECK_EQ(reply_type, *message.FindString("type") + "Reply");
256
257   base::Value::Dict reply;
258   reply.Set("type", reply_type);
259   reply.Set("messageId", *message.FindString("messageId"));
260   return reply;
261 }
262
263 bool IsSaveDataSizeValid(size_t size) {
264   return size > 0 && size <= PdfViewWebPlugin::kMaximumSavedFileSize;
265 }
266
267 }  // namespace
268
269 std::unique_ptr<PDFiumEngine> PdfViewWebPlugin::Client::CreateEngine(
270     PDFEngine::Client* client,
271     PDFiumFormFiller::ScriptOption script_option) {
272   return std::make_unique<PDFiumEngine>(client, script_option);
273 }
274
275 std::unique_ptr<PdfAccessibilityDataHandler>
276 PdfViewWebPlugin::Client::CreateAccessibilityDataHandler(
277     PdfAccessibilityActionHandler* action_handler,
278     PdfAccessibilityImageFetcher* image_fetcher) {
279   return nullptr;
280 }
281
282 PdfViewWebPlugin::PdfViewWebPlugin(
283     std::unique_ptr<Client> client,
284     mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service,
285     const blink::WebPluginParams& params)
286     : client_(std::move(client)),
287       pdf_service_(std::move(pdf_service)),
288       initial_params_(params),
289       pdf_accessibility_data_handler_(
290           client_->CreateAccessibilityDataHandler(this, this)) {
291   DCHECK(pdf_service_);
292   pdf_service_->SetListener(listener_receiver_.BindNewPipeAndPassRemote());
293 }
294
295 PdfViewWebPlugin::~PdfViewWebPlugin() = default;
296
297 bool PdfViewWebPlugin::Initialize(blink::WebPluginContainer* container) {
298   DCHECK(container);
299   client_->SetPluginContainer(container);
300
301   DCHECK_EQ(container->Plugin(), this);
302   return InitializeCommon();
303 }
304
305 bool PdfViewWebPlugin::InitializeForTesting() {
306   return InitializeCommon();
307 }
308
309 bool PdfViewWebPlugin::InitializeCommon() {
310   // Allow the plugin to handle touch events.
311   client_->RequestTouchEventType(
312       blink::WebPluginContainer::kTouchEventRequestTypeRaw);
313
314   // Allow the plugin to handle find requests.
315   client_->UsePluginAsFindHandler();
316
317   absl::optional<ParsedParams> params = ParseWebPluginParams(initial_params_);
318
319   // The contents of `initial_params_` are no longer needed.
320   initial_params_ = {};
321
322   if (!params.has_value())
323     return false;
324
325   // Sets crash keys like `ppapi::proxy::PDFResource::SetCrashData()`. Note that
326   // we don't set the active URL from the top-level URL, as unlike within a
327   // plugin process, the active URL changes frequently within a renderer process
328   // (see crbug.com/1266050 for details).
329   //
330   // TODO(crbug.com/1266087): If multiple PDF plugin instances share the same
331   // renderer process, the crash key will be overwritten by the newest value.
332   static base::debug::CrashKeyString* subresource_url =
333       base::debug::AllocateCrashKeyString("subresource_url",
334                                           base::debug::CrashKeySize::Size256);
335   base::debug::SetCrashKeyString(subresource_url, params->original_url);
336
337   PerProcessInitializer::GetInstance().Acquire(params->use_skia);
338   initialized_ = true;
339
340   // Check if the PDF is being loaded in the PDF chrome extension. We only allow
341   // the plugin to be loaded in the extension and print preview to avoid
342   // exposing sensitive APIs directly to external websites.
343   //
344   // This is enforced before creating the plugin (see
345   // `pdf::CreateInternalPlugin()`), so we just `CHECK` for defense-in-depth.
346   const std::string& embedder_origin = client_->GetEmbedderOriginString();
347   is_print_preview_ = (embedder_origin == kChromePrintHost);
348   CHECK(IsPrintPreview() || embedder_origin == kChromeExtensionHost);
349
350   full_frame_ = params->full_frame;
351   background_color_ = params->background_color;
352
353   engine_ = client_->CreateEngine(this, params->script_option);
354   DCHECK(engine_);
355
356   SendSetSmoothScrolling();
357
358   // Skip the remaining initialization when in Print Preview mode. Loading will
359   // continue after the plugin receives a "resetPrintPreviewMode" message.
360   if (IsPrintPreview())
361     return true;
362
363   last_progress_sent_ = 0;
364   LoadUrl(params->src_url, base::BindOnce(&PdfViewWebPlugin::DidOpen,
365                                           weak_factory_.GetWeakPtr()));
366   url_ = params->original_url;
367
368   // Not all edits go through the PDF plugin's form filler. The plugin instance
369   // can be restarted by exiting annotation mode on ChromeOS, which can set the
370   // document to an edited state.
371   edit_mode_ = params->has_edits;
372 #if !BUILDFLAG(ENABLE_INK)
373   DCHECK(!edit_mode_);
374 #endif  // !BUILDFLAG(ENABLE_INK)
375
376   metrics_handler_ = std::make_unique<MetricsHandler>();
377   return true;
378 }
379
380 void PdfViewWebPlugin::SendSetSmoothScrolling() {
381   base::Value::Dict message;
382   message.Set("type", "setSmoothScrolling");
383   message.Set("smoothScrolling",
384               blink::Platform::Current()->IsScrollAnimatorEnabled());
385   client_->PostMessage(std::move(message));
386 }
387
388 void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader,
389                                int32_t result) {
390   if (result == kSuccess) {
391     if (!engine_->HandleDocumentLoad(std::move(loader), url_)) {
392       document_load_state_ = DocumentLoadState::kLoading;
393       DocumentLoadFailed();
394     }
395   } else if (result != kErrorAborted) {
396     DocumentLoadFailed();
397   }
398 }
399
400 void PdfViewWebPlugin::Destroy() {
401   if (initialized_) {
402     // Explicitly destroy the PDFEngine during destruction as it may call back
403     // into this object.
404     preview_engine_.reset();
405     engine_.reset();
406     PerProcessInitializer::GetInstance().Release();
407   }
408
409   client_->SetPluginContainer(nullptr);
410
411   delete this;
412 }
413
414 blink::WebPluginContainer* PdfViewWebPlugin::Container() const {
415   return client_->PluginContainer();
416 }
417
418 v8::Local<v8::Object> PdfViewWebPlugin::V8ScriptableObject(
419     v8::Isolate* isolate) {
420   if (scriptable_receiver_.IsEmpty()) {
421     // TODO(crbug.com/1123731): Messages should not be handled on the renderer
422     // main thread.
423     scriptable_receiver_.Reset(
424         isolate, PostMessageReceiver::Create(
425                      isolate, client_->GetWeakPtr(), weak_factory_.GetWeakPtr(),
426                      base::SequencedTaskRunner::GetCurrentDefault()));
427   }
428
429   return scriptable_receiver_.Get(isolate);
430 }
431
432 bool PdfViewWebPlugin::SupportsKeyboardFocus() const {
433   return !IsPrintPreview();
434 }
435
436 void PdfViewWebPlugin::UpdateAllLifecyclePhases(
437     blink::DocumentUpdateReason reason) {}
438
439 void PdfViewWebPlugin::Paint(cc::PaintCanvas* canvas, const gfx::Rect& rect) {
440   // Clip the intersection of the paint rect and the plugin rect, so that
441   // painting outside the plugin or the paint rect area can be avoided.
442   // Note: `rect` is in CSS pixels. We need to use `css_plugin_rect_`
443   // to calculate the intersection.
444   SkRect invalidate_rect =
445       gfx::RectToSkRect(gfx::IntersectRects(css_plugin_rect_, rect));
446   cc::PaintCanvasAutoRestore auto_restore(canvas, /*save=*/true);
447   canvas->clipRect(invalidate_rect);
448
449   // Paint with the plugin's background color if the snapshot is not ready.
450   if (snapshot_.GetSkImageInfo().isEmpty()) {
451     cc::PaintFlags flags;
452     flags.setBlendMode(SkBlendMode::kSrc);
453     flags.setColor(GetBackgroundColor());
454     canvas->drawRect(invalidate_rect, flags);
455     return;
456   }
457
458   // Layer translate is independent of scaling, so apply first.
459   if (!total_translate_.IsZero())
460     canvas->translate(total_translate_.x(), total_translate_.y());
461
462   // Position layer at plugin origin before layer scaling.
463   if (!plugin_rect_.origin().IsOrigin())
464     canvas->translate(plugin_rect_.x(), plugin_rect_.y());
465
466   if (snapshot_scale_ != 1.0f)
467     canvas->scale(snapshot_scale_, snapshot_scale_);
468
469   canvas->drawImage(snapshot_, 0, 0);
470 }
471
472 void PdfViewWebPlugin::UpdateGeometry(const gfx::Rect& window_rect,
473                                       const gfx::Rect& clip_rect,
474                                       const gfx::Rect& unobscured_rect,
475                                       bool is_visible) {
476   // An empty `window_rect` can be received here in the following cases:
477   // - If the embedded plugin size is 0.
478   // - If the embedded plugin size is not 0, it can come from re-layouts during
479   //   the plugin initialization.
480   // For either case, there is no need to create a graphic device to display
481   // a PDF in an empty window. Since an empty `window_rect` can cause failure
482   // to create the graphic device, avoid all updates on the geometries and the
483   // device scales used by the plugin, the PaintManager and the PDFiumEngine
484   // unless a non-empty `window_rect` is received.
485   if (window_rect.IsEmpty())
486     return;
487
488   OnViewportChanged(window_rect, client_->DeviceScaleFactor());
489
490   gfx::PointF scroll_position = client_->GetScrollPosition();
491   // Convert back to CSS pixels.
492   scroll_position.Scale(1.0f / device_scale_);
493   UpdateScroll(scroll_position);
494 }
495
496 void PdfViewWebPlugin::UpdateScroll(const gfx::PointF& scroll_position) {
497   if (stop_scrolling_)
498     return;
499
500   float max_x = std::max(document_size_.width() * static_cast<float>(zoom_) -
501                              plugin_dip_size_.width(),
502                          0.0f);
503   float max_y = std::max(document_size_.height() * static_cast<float>(zoom_) -
504                              plugin_dip_size_.height(),
505                          0.0f);
506
507   gfx::PointF scaled_scroll_position(
508       std::clamp(scroll_position.x(), 0.0f, max_x),
509       std::clamp(scroll_position.y(), 0.0f, max_y));
510   scaled_scroll_position.Scale(device_scale_);
511
512   engine_->ScrolledToXPosition(scaled_scroll_position.x());
513   engine_->ScrolledToYPosition(scaled_scroll_position.y());
514 }
515
516 void PdfViewWebPlugin::UpdateFocus(bool focused,
517                                    blink::mojom::FocusType focus_type) {
518   if (has_focus_ != focused) {
519     engine_->UpdateFocus(focused);
520     client_->UpdateTextInputState();
521
522     // Make sure `this` is still alive after the UpdateSelectionBounds() call.
523     auto weak_this = weak_factory_.GetWeakPtr();
524     client_->UpdateSelectionBounds();
525     if (!weak_this) {
526       return;
527     }
528   }
529   has_focus_ = focused;
530
531   if (!has_focus_ || !SupportsKeyboardFocus())
532     return;
533
534   if (focus_type != blink::mojom::FocusType::kBackward &&
535       focus_type != blink::mojom::FocusType::kForward) {
536     return;
537   }
538
539   const int modifiers = focus_type == blink::mojom::FocusType::kForward
540                             ? blink::WebInputEvent::kNoModifiers
541                             : blink::WebInputEvent::kShiftKey;
542
543   blink::WebKeyboardEvent simulated_event(blink::WebInputEvent::Type::kKeyDown,
544                                           modifiers, base::TimeTicks());
545   simulated_event.windows_key_code = ui::KeyboardCode::VKEY_TAB;
546   HandleWebInputEvent(simulated_event);
547 }
548
549 void PdfViewWebPlugin::UpdateVisibility(bool visibility) {}
550
551 blink::WebInputEventResult PdfViewWebPlugin::HandleInputEvent(
552     const blink::WebCoalescedInputEvent& event,
553     ui::Cursor* cursor) {
554   const blink::WebInputEventResult result =
555       HandleWebInputEvent(event.Event())
556           ? blink::WebInputEventResult::kHandledApplication
557           : blink::WebInputEventResult::kNotHandled;
558
559   *cursor = cursor_type_;
560
561   return result;
562 }
563
564 void PdfViewWebPlugin::DidReceiveResponse(
565     const blink::WebURLResponse& response) {}
566
567 void PdfViewWebPlugin::DidReceiveData(const char* data, size_t data_length) {}
568
569 void PdfViewWebPlugin::DidFinishLoading() {}
570
571 void PdfViewWebPlugin::DidFailLoading(const blink::WebURLError& error) {}
572
573 bool PdfViewWebPlugin::SupportsPaginatedPrint() {
574   return true;
575 }
576
577 bool PdfViewWebPlugin::GetPrintPresetOptionsFromDocument(
578     blink::WebPrintPresetOptions* print_preset_options) {
579   print_preset_options->is_scaling_disabled = !engine_->GetPrintScaling();
580   print_preset_options->copies = engine_->GetCopiesToPrint();
581   print_preset_options->duplex_mode = engine_->GetDuplexMode();
582   print_preset_options->uniform_page_size = engine_->GetUniformPageSizePoints();
583   return true;
584 }
585
586 int PdfViewWebPlugin::PrintBegin(const blink::WebPrintParams& print_params) {
587   // The returned value is always equal to the number of pages in the PDF
588   // document irrespective of the printable area.
589   int32_t ret = engine_->GetNumberOfPages();
590   if (!ret)
591     return 0;
592
593   if (!engine_->HasPermission(DocumentPermission::kPrintLowQuality))
594     return 0;
595
596   print_params_ = print_params;
597   if (!engine_->HasPermission(DocumentPermission::kPrintHighQuality))
598     print_params_->rasterize_pdf = true;
599
600   engine_->PrintBegin();
601   return ret;
602 }
603
604 void PdfViewWebPlugin::PrintPage(int page_number, cc::PaintCanvas* canvas) {
605   // The entire document goes into one metafile. However, it is impossible to
606   // know if a call to `PrintPage()` is the last call. Thus, `PrintPage()` just
607   // stores the pages to print and the metafile. Eventually, the printed output
608   // is generated in `PrintEnd()` and copied over to the metafile.
609
610   // Every `canvas` passed to this method should have a valid `metafile`.
611   printing::MetafileSkia* metafile = canvas->GetPrintingMetafile();
612   DCHECK(metafile);
613
614   // `pages_to_print_` should be empty iff `printing_metafile_` is not set.
615   DCHECK_EQ(pages_to_print_.empty(), !printing_metafile_);
616
617   // The metafile should be the same across all calls for a given print job.
618   DCHECK(!printing_metafile_ || (printing_metafile_ == metafile));
619
620   if (!printing_metafile_)
621     printing_metafile_ = metafile;
622
623   pages_to_print_.push_back(page_number);
624 }
625
626 void PdfViewWebPlugin::PrintEnd() {
627   if (pages_to_print_.empty())
628     return;
629
630   print_pages_called_ = true;
631   printing_metafile_->InitFromData(
632       engine_->PrintPages(pages_to_print_, print_params_.value()));
633
634   if (print_pages_called_)
635     client_->RecordComputedAction("PDF.PrintPage");
636   print_pages_called_ = false;
637   print_params_.reset();
638   engine_->PrintEnd();
639
640   printing_metafile_ = nullptr;
641   pages_to_print_.clear();
642 }
643
644 bool PdfViewWebPlugin::HasSelection() const {
645   return !selected_text_.IsEmpty();
646 }
647
648 blink::WebString PdfViewWebPlugin::SelectionAsText() const {
649   return selected_text_;
650 }
651
652 blink::WebString PdfViewWebPlugin::SelectionAsMarkup() const {
653   return selected_text_;
654 }
655
656 bool PdfViewWebPlugin::CanEditText() const {
657   return engine_->CanEditText();
658 }
659
660 bool PdfViewWebPlugin::HasEditableText() const {
661   return engine_->HasEditableText();
662 }
663
664 bool PdfViewWebPlugin::CanUndo() const {
665   return engine_->CanUndo();
666 }
667
668 bool PdfViewWebPlugin::CanRedo() const {
669   return engine_->CanRedo();
670 }
671
672 bool PdfViewWebPlugin::CanCopy() const {
673   return engine_->HasPermission(DocumentPermission::kCopy);
674 }
675
676 bool PdfViewWebPlugin::ExecuteEditCommand(const blink::WebString& name,
677                                           const blink::WebString& value) {
678   if (name == "SelectAll")
679     return SelectAll();
680
681   if (name == "Cut")
682     return Cut();
683
684   if (name == "Paste" || name == "PasteAndMatchStyle")
685     return Paste(value);
686
687   if (name == "Undo")
688     return Undo();
689
690   if (name == "Redo")
691     return Redo();
692
693   return false;
694 }
695
696 blink::WebURL PdfViewWebPlugin::LinkAtPosition(
697     const gfx::Point& /*position*/) const {
698   return GURL(link_under_cursor_);
699 }
700
701 bool PdfViewWebPlugin::StartFind(const blink::WebString& search_text,
702                                  bool case_sensitive,
703                                  int identifier) {
704   ResetRecentlySentFindUpdate();
705   find_identifier_ = identifier;
706   engine_->StartFind(search_text.Utf16(), case_sensitive);
707   return true;
708 }
709
710 void PdfViewWebPlugin::SelectFindResult(bool forward, int identifier) {
711   find_identifier_ = identifier;
712   engine_->SelectFindResult(forward);
713 }
714
715 void PdfViewWebPlugin::StopFind() {
716   find_identifier_ = -1;
717   engine_->StopFind();
718   tickmarks_.clear();
719   client_->ReportFindInPageTickmarks(tickmarks_);
720 }
721
722 bool PdfViewWebPlugin::CanRotateView() {
723   return !IsPrintPreview();
724 }
725
726 void PdfViewWebPlugin::RotateView(blink::WebPlugin::RotationType type) {
727   DCHECK(CanRotateView());
728
729   switch (type) {
730     case blink::WebPlugin::RotationType::k90Clockwise:
731       engine_->RotateClockwise();
732       break;
733     case blink::WebPlugin::RotationType::k90Counterclockwise:
734       engine_->RotateCounterclockwise();
735       break;
736   }
737 }
738
739 bool PdfViewWebPlugin::ShouldDispatchImeEventsToPlugin() {
740   return true;
741 }
742
743 blink::WebTextInputType PdfViewWebPlugin::GetPluginTextInputType() {
744   return text_input_type_;
745 }
746
747 gfx::Rect PdfViewWebPlugin::GetPluginCaretBounds() {
748   return caret_rect_;
749 }
750
751 void PdfViewWebPlugin::ImeSetCompositionForPlugin(
752     const blink::WebString& text,
753     const std::vector<ui::ImeTextSpan>& /*ime_text_spans*/,
754     const gfx::Range& /*replacement_range*/,
755     int /*selection_start*/,
756     int /*selection_end*/) {
757   composition_text_ = text;
758 }
759
760 void PdfViewWebPlugin::ImeCommitTextForPlugin(
761     const blink::WebString& text,
762     const std::vector<ui::ImeTextSpan>& /*ime_text_spans*/,
763     const gfx::Range& /*replacement_range*/,
764     int /*relative_cursor_pos*/) {
765   HandleImeCommit(text);
766 }
767
768 void PdfViewWebPlugin::ImeFinishComposingTextForPlugin(
769     bool /*keep_selection*/) {
770   HandleImeCommit(composition_text_);
771 }
772
773 void PdfViewWebPlugin::ProposeDocumentLayout(const DocumentLayout& layout) {
774   base::Value::Dict message;
775   message.Set("type", "documentDimensions");
776   message.Set("width", layout.size().width());
777   message.Set("height", layout.size().height());
778   message.Set("layoutOptions", layout.options().ToValue());
779   base::Value::List page_dimensions;
780   for (size_t i = 0; i < layout.page_count(); ++i)
781     page_dimensions.Append(DictFromRect(layout.page_rect(i)));
782   message.Set("pageDimensions", std::move(page_dimensions));
783   client_->PostMessage(std::move(message));
784
785   // Reload the accessibility tree on layout changes because the relative page
786   // bounds are no longer valid.
787   if (layout.dirty() && accessibility_state_ == AccessibilityState::kLoaded)
788     LoadAccessibility();
789 }
790
791 void PdfViewWebPlugin::Invalidate(const gfx::Rect& rect) {
792   if (in_paint_) {
793     deferred_invalidates_.push_back(rect);
794     return;
795   }
796
797   gfx::Rect offset_rect = rect + available_area_.OffsetFromOrigin();
798   paint_manager_.InvalidateRect(offset_rect);
799 }
800
801 void PdfViewWebPlugin::DidScroll(const gfx::Vector2d& offset) {
802   if (!image_data_.drawsNothing())
803     paint_manager_.ScrollRect(available_area_, offset);
804 }
805
806 void PdfViewWebPlugin::ScrollToX(int x_screen_coords) {
807   const float x_scroll_pos = x_screen_coords / device_scale_;
808
809   base::Value::Dict message;
810   message.Set("type", "setScrollPosition");
811   message.Set("x", static_cast<double>(x_scroll_pos));
812   client_->PostMessage(std::move(message));
813 }
814
815 void PdfViewWebPlugin::ScrollToY(int y_screen_coords) {
816   const float y_scroll_pos = y_screen_coords / device_scale_;
817
818   base::Value::Dict message;
819   message.Set("type", "setScrollPosition");
820   message.Set("y", static_cast<double>(y_scroll_pos));
821   client_->PostMessage(std::move(message));
822 }
823
824 void PdfViewWebPlugin::ScrollBy(const gfx::Vector2d& delta) {
825   const float x_delta = delta.x() / device_scale_;
826   const float y_delta = delta.y() / device_scale_;
827
828   base::Value::Dict message;
829   message.Set("type", "scrollBy");
830   message.Set("x", static_cast<double>(x_delta));
831   message.Set("y", static_cast<double>(y_delta));
832   client_->PostMessage(std::move(message));
833 }
834
835 void PdfViewWebPlugin::ScrollToPage(int page) {
836   if (!engine_ || engine_->GetNumberOfPages() == 0)
837     return;
838
839   base::Value::Dict message;
840   message.Set("type", "goToPage");
841   message.Set("page", page);
842   client_->PostMessage(std::move(message));
843 }
844
845 void PdfViewWebPlugin::NavigateTo(const std::string& url,
846                                   WindowOpenDisposition disposition) {
847   base::Value::Dict message;
848   message.Set("type", "navigate");
849   message.Set("url", url);
850   message.Set("disposition", static_cast<int>(disposition));
851   client_->PostMessage(std::move(message));
852 }
853
854 void PdfViewWebPlugin::NavigateToDestination(int page,
855                                              const float* x,
856                                              const float* y,
857                                              const float* zoom) {
858   base::Value::Dict message;
859   message.Set("type", "navigateToDestination");
860   message.Set("page", page);
861   if (x)
862     message.Set("x", static_cast<double>(*x));
863   if (y)
864     message.Set("y", static_cast<double>(*y));
865   if (zoom)
866     message.Set("zoom", static_cast<double>(*zoom));
867   client_->PostMessage(std::move(message));
868 }
869
870 void PdfViewWebPlugin::UpdateCursor(ui::mojom::CursorType new_cursor_type) {
871   cursor_type_ = new_cursor_type;
872 }
873
874 void PdfViewWebPlugin::UpdateTickMarks(
875     const std::vector<gfx::Rect>& tickmarks) {
876   tickmarks_ = tickmarks;
877 }
878
879 void PdfViewWebPlugin::NotifyNumberOfFindResultsChanged(int total,
880                                                         bool final_result) {
881   // We don't want to spam the renderer with too many updates to the number of
882   // find results. Don't send an update if we sent one too recently. If it's the
883   // final update, we always send it though.
884   if (recently_sent_find_update_ && !final_result)
885     return;
886
887   // After stopping search and setting `find_identifier_` to -1 there still may
888   // be a NotifyNumberOfFindResultsChanged notification pending from engine.
889   // Just ignore them.
890   if (find_identifier_ != -1) {
891     client_->ReportFindInPageMatchCount(find_identifier_, total, final_result);
892   }
893
894   client_->ReportFindInPageTickmarks(tickmarks_);
895
896   if (final_result)
897     return;
898
899   recently_sent_find_update_ = true;
900   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
901       FROM_HERE,
902       base::BindOnce(&PdfViewWebPlugin::ResetRecentlySentFindUpdate,
903                      weak_factory_.GetWeakPtr()),
904       kFindResultCooldown);
905 }
906
907 void PdfViewWebPlugin::NotifySelectedFindResultChanged(int current_find_index,
908                                                        bool final_result) {
909   if (find_identifier_ == -1 || !client_->PluginContainer())
910     return;
911
912   DCHECK_GE(current_find_index, -1);
913   client_->ReportFindInPageSelection(find_identifier_, current_find_index + 1,
914                                      final_result);
915 }
916
917 void PdfViewWebPlugin::NotifyTouchSelectionOccurred() {
918   base::Value::Dict message;
919   message.Set("type", "touchSelectionOccurred");
920   client_->PostMessage(std::move(message));
921 }
922
923 void PdfViewWebPlugin::CaretChanged(const gfx::Rect& caret_rect) {
924   caret_rect_ = caret_rect + available_area_.OffsetFromOrigin();
925 }
926
927 void PdfViewWebPlugin::GetDocumentPassword(
928     base::OnceCallback<void(const std::string&)> callback) {
929   DCHECK(password_callback_.is_null());
930   password_callback_ = std::move(callback);
931
932   base::Value::Dict message;
933   message.Set("type", "getPassword");
934   client_->PostMessage(std::move(message));
935 }
936
937 void PdfViewWebPlugin::Beep() {
938   base::Value::Dict message;
939   message.Set("type", "beep");
940   client_->PostMessage(std::move(message));
941 }
942
943 void PdfViewWebPlugin::Alert(const std::string& message) {
944   client_->Alert(blink::WebString::FromUTF8(message));
945 }
946
947 bool PdfViewWebPlugin::Confirm(const std::string& message) {
948   return client_->Confirm(blink::WebString::FromUTF8(message));
949 }
950
951 std::string PdfViewWebPlugin::Prompt(const std::string& question,
952                                      const std::string& default_answer) {
953   return client_
954       ->Prompt(blink::WebString::FromUTF8(question),
955                blink::WebString::FromUTF8(default_answer))
956       .Utf8();
957 }
958
959 std::string PdfViewWebPlugin::GetURL() {
960   return url_;
961 }
962
963 void PdfViewWebPlugin::LoadUrl(base::StringPiece url,
964                                LoadUrlCallback callback) {
965   UrlRequest request;
966   request.url = std::string(url);
967   request.method = "GET";
968   request.ignore_redirects = true;
969
970   auto loader = std::make_unique<UrlLoader>(weak_factory_.GetWeakPtr());
971   UrlLoader* raw_loader = loader.get();
972   raw_loader->Open(request,
973                    base::BindOnce(std::move(callback), std::move(loader)));
974 }
975
976 void PdfViewWebPlugin::Email(const std::string& to,
977                              const std::string& cc,
978                              const std::string& bcc,
979                              const std::string& subject,
980                              const std::string& body) {
981   base::Value::Dict message;
982   message.Set("type", "email");
983   message.Set("to", base::EscapeUrlEncodedData(to, false));
984   message.Set("cc", base::EscapeUrlEncodedData(cc, false));
985   message.Set("bcc", base::EscapeUrlEncodedData(bcc, false));
986   message.Set("subject", base::EscapeUrlEncodedData(subject, false));
987   message.Set("body", base::EscapeUrlEncodedData(body, false));
988   client_->PostMessage(std::move(message));
989 }
990
991 void PdfViewWebPlugin::Print() {
992   if (!engine_)
993     return;
994
995   const bool can_print =
996       engine_->HasPermission(DocumentPermission::kPrintLowQuality) ||
997       engine_->HasPermission(DocumentPermission::kPrintHighQuality);
998   if (!can_print)
999     return;
1000
1001   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1002       FROM_HERE, base::BindOnce(&PdfViewWebPlugin::OnInvokePrintDialog,
1003                                 weak_factory_.GetWeakPtr()));
1004 }
1005
1006 void PdfViewWebPlugin::SubmitForm(const std::string& url,
1007                                   const void* data,
1008                                   int length) {
1009   // `url` might be a relative URL. Resolve it against the document's URL.
1010   // TODO(crbug.com/1322928): Probably redundant with `Client::CompleteURL()`.
1011   GURL resolved_url = GURL(url_).Resolve(url);
1012   if (!resolved_url.is_valid())
1013     return;
1014
1015   UrlRequest request;
1016   request.url = resolved_url.spec();
1017   request.method = "POST";
1018   request.body.assign(static_cast<const char*>(data), length);
1019
1020   form_loader_ = std::make_unique<UrlLoader>(weak_factory_.GetWeakPtr());
1021   form_loader_->Open(request, base::BindOnce(&PdfViewWebPlugin::DidFormOpen,
1022                                              weak_factory_.GetWeakPtr()));
1023 }
1024
1025 void PdfViewWebPlugin::DidFormOpen(int32_t result) {
1026   // TODO(crbug.com/719344): Process response.
1027   LOG_IF(ERROR, result != kSuccess) << "DidFormOpen failed: " << result;
1028   form_loader_.reset();
1029 }
1030
1031 void PdfViewWebPlugin::DidStartLoading() {
1032   if (did_call_start_loading_)
1033     return;
1034
1035   client_->DidStartLoading();
1036   did_call_start_loading_ = true;
1037 }
1038
1039 void PdfViewWebPlugin::DidStopLoading() {
1040   if (!did_call_start_loading_)
1041     return;
1042
1043   client_->DidStopLoading();
1044   did_call_start_loading_ = false;
1045 }
1046
1047 int PdfViewWebPlugin::GetContentRestrictions() const {
1048   int content_restrictions = kContentRestrictionCut | kContentRestrictionPaste;
1049   if (!engine_->HasPermission(DocumentPermission::kCopy))
1050     content_restrictions |= kContentRestrictionCopy;
1051
1052   if (!engine_->HasPermission(DocumentPermission::kPrintLowQuality) &&
1053       !engine_->HasPermission(DocumentPermission::kPrintHighQuality)) {
1054     content_restrictions |= kContentRestrictionPrint;
1055   }
1056
1057   return content_restrictions;
1058 }
1059
1060 std::unique_ptr<UrlLoader> PdfViewWebPlugin::CreateUrlLoader() {
1061   if (full_frame_) {
1062     DidStartLoading();
1063
1064     // Disable save and print until the document is fully loaded, since they
1065     // would generate an incomplete document. This needs to be done each time
1066     // DidStartLoading() is called because that resets the content restrictions.
1067     pdf_service_->UpdateContentRestrictions(kContentRestrictionSave |
1068                                             kContentRestrictionPrint);
1069   }
1070
1071   return std::make_unique<UrlLoader>(weak_factory_.GetWeakPtr());
1072 }
1073
1074 std::vector<PDFEngine::Client::SearchStringResult>
1075 PdfViewWebPlugin::SearchString(const char16_t* string,
1076                                const char16_t* term,
1077                                bool case_sensitive) {
1078   base::i18n::RepeatingStringSearch searcher(
1079       /*find_this=*/term, /*in_this=*/string, case_sensitive);
1080   std::vector<SearchStringResult> results;
1081   int match_index;
1082   int match_length;
1083   while (searcher.NextMatchResult(match_index, match_length))
1084     results.push_back({.start_index = match_index, .length = match_length});
1085   return results;
1086 }
1087
1088 void PdfViewWebPlugin::DocumentLoadComplete() {
1089   DCHECK_EQ(DocumentLoadState::kLoading, document_load_state_);
1090   document_load_state_ = DocumentLoadState::kComplete;
1091
1092   client_->RecordComputedAction("PDF.LoadSuccess");
1093
1094   // Clear the focus state for on-screen keyboards.
1095   FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
1096
1097   if (IsPrintPreview()) {
1098     // Scroll location is retained across document loads in Print Preview, so
1099     // there's no need to override the scroll position by scrolling again.
1100     if (IsPreviewingPDF(print_preview_page_count_)) {
1101       SendPrintPreviewLoadedNotification();
1102     } else {
1103       DCHECK_EQ(0, print_preview_loaded_page_count_);
1104       print_preview_loaded_page_count_ = 1;
1105       engine_->AppendBlankPages(print_preview_page_count_);
1106       LoadNextPreviewPage();
1107     }
1108
1109     OnGeometryChanged(0, 0);
1110     if (!document_size_.IsEmpty())
1111       paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
1112   }
1113
1114   RecordDocumentMetrics();
1115
1116   if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfPortfolio)) {
1117     SendAttachments();
1118   }
1119   SendBookmarks();
1120   SendMetadata();
1121
1122   if (accessibility_state_ == AccessibilityState::kPending)
1123     LoadAccessibility();
1124
1125   if (!full_frame_)
1126     return;
1127
1128   DidStopLoading();
1129   pdf_service_->UpdateContentRestrictions(GetContentRestrictions());
1130 }
1131
1132 void PdfViewWebPlugin::DocumentLoadFailed() {
1133   DCHECK_EQ(DocumentLoadState::kLoading, document_load_state_);
1134   document_load_state_ = DocumentLoadState::kFailed;
1135
1136   client_->RecordComputedAction("PDF.LoadFailure");
1137
1138   // Send a progress value of -1 to indicate a failure.
1139   SendLoadingProgress(-1);
1140
1141   DidStopLoading();
1142
1143   paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
1144 }
1145
1146 void PdfViewWebPlugin::DocumentHasUnsupportedFeature(
1147     const std::string& feature) {
1148   DCHECK(!feature.empty());
1149   std::string metric = base::StrCat({"PDF_Unsupported_", feature});
1150   if (unsupported_features_reported_.insert(metric).second)
1151     client_->RecordComputedAction(metric);
1152
1153   if (!full_frame_ || notified_browser_about_unsupported_feature_)
1154     return;
1155
1156   notified_browser_about_unsupported_feature_ = true;
1157   pdf_service_->HasUnsupportedFeature();
1158 }
1159
1160 void PdfViewWebPlugin::DocumentLoadProgress(uint32_t available,
1161                                             uint32_t doc_size) {
1162   double progress = 0.0;
1163   if (doc_size > 0) {
1164     progress = 100.0 * static_cast<double>(available) / doc_size;
1165   } else {
1166     // Use heuristics when the document size is unknown.
1167     // Progress logarithmically from 0 to 100M.
1168     static const double kFactor = std::log(100'000'000.0) / 100.0;
1169     if (available > 0)
1170       progress =
1171           std::min(std::log(static_cast<double>(available)) / kFactor, 100.0);
1172   }
1173
1174   // DocumentLoadComplete() will send the 100% load progress.
1175   if (progress >= 100)
1176     return;
1177
1178   // Avoid sending too many progress messages over PostMessage.
1179   if (progress <= last_progress_sent_ + 1)
1180     return;
1181
1182   SendLoadingProgress(progress);
1183 }
1184
1185 void PdfViewWebPlugin::FormFieldFocusChange(PDFEngine::FocusFieldType type) {
1186   base::Value::Dict message;
1187   message.Set("type", "formFocusChange");
1188   message.Set("focused", type != PDFEngine::FocusFieldType::kNoFocus);
1189   client_->PostMessage(std::move(message));
1190
1191   text_input_type_ = type == PDFEngine::FocusFieldType::kText
1192                          ? blink::WebTextInputType::kWebTextInputTypeText
1193                          : blink::WebTextInputType::kWebTextInputTypeNone;
1194   client_->UpdateTextInputState();
1195 }
1196
1197 bool PdfViewWebPlugin::IsPrintPreview() const {
1198   return is_print_preview_;
1199 }
1200
1201 SkColor PdfViewWebPlugin::GetBackgroundColor() const {
1202   return background_color_;
1203 }
1204
1205 void PdfViewWebPlugin::SetIsSelecting(bool is_selecting) {
1206   base::Value::Dict message;
1207   message.Set("type", "setIsSelecting");
1208   message.Set("isSelecting", is_selecting);
1209   client_->PostMessage(std::move(message));
1210 }
1211
1212 void PdfViewWebPlugin::SelectionChanged(const gfx::Rect& left,
1213                                         const gfx::Rect& right) {
1214   gfx::PointF left_point(left.x() + available_area_.x(), left.y());
1215   gfx::PointF right_point(right.x() + available_area_.x(), right.y());
1216
1217   const float inverse_scale = 1.0f / device_scale_;
1218   left_point.Scale(inverse_scale);
1219   right_point.Scale(inverse_scale);
1220
1221   pdf_service_->SelectionChanged(left_point, left.height() * inverse_scale,
1222                                  right_point, right.height() * inverse_scale);
1223
1224   if (accessibility_state_ == AccessibilityState::kLoaded)
1225     PrepareAndSetAccessibilityViewportInfo();
1226 }
1227
1228 void PdfViewWebPlugin::EnteredEditMode() {
1229   edit_mode_ = true;
1230   pdf_service_->SetPluginCanSave(true);
1231
1232   base::Value::Dict message;
1233   message.Set("type", "setIsEditing");
1234   client_->PostMessage(std::move(message));
1235 }
1236
1237 void PdfViewWebPlugin::DocumentFocusChanged(bool document_has_focus) {
1238   base::Value::Dict message;
1239   message.Set("type", "documentFocusChanged");
1240   message.Set("hasFocus", document_has_focus);
1241   client_->PostMessage(std::move(message));
1242 }
1243
1244 void PdfViewWebPlugin::SetSelectedText(const std::string& selected_text) {
1245   selected_text_ = blink::WebString::FromUTF8(selected_text);
1246   client_->TextSelectionChanged(selected_text_, /*offset=*/0,
1247                                 gfx::Range(0, selected_text_.length()));
1248 }
1249
1250 void PdfViewWebPlugin::SetLinkUnderCursor(
1251     const std::string& link_under_cursor) {
1252   link_under_cursor_ = link_under_cursor;
1253 }
1254
1255 bool PdfViewWebPlugin::IsValidLink(const std::string& url) {
1256   return base::Value(url).is_string();
1257 }
1258
1259 void PdfViewWebPlugin::SetCaretPosition(const gfx::PointF& position) {
1260   engine_->SetCaretPosition(FrameToPdfCoordinates(position));
1261 }
1262
1263 void PdfViewWebPlugin::MoveRangeSelectionExtent(const gfx::PointF& extent) {
1264   engine_->MoveRangeSelectionExtent(FrameToPdfCoordinates(extent));
1265 }
1266
1267 void PdfViewWebPlugin::SetSelectionBounds(const gfx::PointF& base,
1268                                           const gfx::PointF& extent) {
1269   engine_->SetSelectionBounds(FrameToPdfCoordinates(base),
1270                               FrameToPdfCoordinates(extent));
1271 }
1272
1273 bool PdfViewWebPlugin::IsValid() const {
1274   return client_->HasFrame();
1275 }
1276
1277 blink::WebURL PdfViewWebPlugin::CompleteURL(
1278     const blink::WebString& partial_url) const {
1279   DCHECK(IsValid());
1280   return client_->CompleteURL(partial_url);
1281 }
1282
1283 net::SiteForCookies PdfViewWebPlugin::SiteForCookies() const {
1284   DCHECK(IsValid());
1285   return client_->SiteForCookies();
1286 }
1287
1288 void PdfViewWebPlugin::SetReferrerForRequest(
1289     blink::WebURLRequest& request,
1290     const blink::WebURL& referrer_url) {
1291   client_->SetReferrerForRequest(request, referrer_url);
1292 }
1293
1294 std::unique_ptr<blink::WebAssociatedURLLoader>
1295 PdfViewWebPlugin::CreateAssociatedURLLoader(
1296     const blink::WebAssociatedURLLoaderOptions& options) {
1297   return client_->CreateAssociatedURLLoader(options);
1298 }
1299
1300 void PdfViewWebPlugin::OnMessage(const base::Value::Dict& message) {
1301   using MessageHandler = void (PdfViewWebPlugin::*)(const base::Value::Dict&);
1302
1303   static constexpr auto kMessageHandlers =
1304       base::MakeFixedFlatMap<base::StringPiece, MessageHandler>({
1305           {"displayAnnotations",
1306            &PdfViewWebPlugin::HandleDisplayAnnotationsMessage},
1307           {"getNamedDestination",
1308            &PdfViewWebPlugin::HandleGetNamedDestinationMessage},
1309           {"getPageBoundingBox",
1310            &PdfViewWebPlugin::HandleGetPageBoundingBoxMessage},
1311           {"getPasswordComplete",
1312            &PdfViewWebPlugin::HandleGetPasswordCompleteMessage},
1313           {"getSelectedText", &PdfViewWebPlugin::HandleGetSelectedTextMessage},
1314           {"getThumbnail", &PdfViewWebPlugin::HandleGetThumbnailMessage},
1315           {"print", &PdfViewWebPlugin::HandlePrintMessage},
1316           {"loadPreviewPage", &PdfViewWebPlugin::HandleLoadPreviewPageMessage},
1317           {"resetPrintPreviewMode",
1318            &PdfViewWebPlugin::HandleResetPrintPreviewModeMessage},
1319           {"rotateClockwise", &PdfViewWebPlugin::HandleRotateClockwiseMessage},
1320           {"rotateCounterclockwise",
1321            &PdfViewWebPlugin::HandleRotateCounterclockwiseMessage},
1322           {"save", &PdfViewWebPlugin::HandleSaveMessage},
1323           {"saveAttachment", &PdfViewWebPlugin::HandleSaveAttachmentMessage},
1324           {"selectAll", &PdfViewWebPlugin::HandleSelectAllMessage},
1325           {"setBackgroundColor",
1326            &PdfViewWebPlugin::HandleSetBackgroundColorMessage},
1327           {"setPresentationMode",
1328            &PdfViewWebPlugin::HandleSetPresentationModeMessage},
1329           {"setTwoUpView", &PdfViewWebPlugin::HandleSetTwoUpViewMessage},
1330           {"stopScrolling", &PdfViewWebPlugin::HandleStopScrollingMessage},
1331           {"viewport", &PdfViewWebPlugin::HandleViewportMessage},
1332       });
1333
1334   MessageHandler handler = kMessageHandlers.at(*message.FindString("type"));
1335   (this->*handler)(message);
1336 }
1337
1338 void PdfViewWebPlugin::HandleDisplayAnnotationsMessage(
1339     const base::Value::Dict& message) {
1340   engine_->DisplayAnnotations(message.FindBool("display").value());
1341 }
1342
1343 void PdfViewWebPlugin::HandleGetNamedDestinationMessage(
1344     const base::Value::Dict& message) {
1345   absl::optional<PDFEngine::NamedDestination> named_destination =
1346       engine_->GetNamedDestination(*message.FindString("namedDestination"));
1347
1348   const int page_number = named_destination.has_value()
1349                               ? base::checked_cast<int>(named_destination->page)
1350                               : -1;
1351
1352   base::Value::Dict reply =
1353       PrepareReplyMessage("getNamedDestinationReply", message);
1354   reply.Set("pageNumber", page_number);
1355
1356   if (named_destination.has_value() && !named_destination->view.empty()) {
1357     std::ostringstream view_stream;
1358     view_stream << named_destination->view;
1359     if (named_destination->xyz_params.empty()) {
1360       for (unsigned long i = 0; i < named_destination->num_params; ++i)
1361         view_stream << "," << named_destination->params[i];
1362     } else {
1363       view_stream << "," << named_destination->xyz_params;
1364     }
1365
1366     reply.Set("namedDestinationView", view_stream.str());
1367   }
1368
1369   client_->PostMessage(std::move(reply));
1370 }
1371
1372 void PdfViewWebPlugin::HandleGetPageBoundingBoxMessage(
1373     const base::Value::Dict& message) {
1374   const int page_index = message.FindInt("page").value();
1375   base::Value::Dict reply =
1376       PrepareReplyMessage("getPageBoundingBoxReply", message);
1377
1378   gfx::RectF bounding_box = engine_->GetPageBoundingBox(page_index);
1379   gfx::Rect page_bounds = engine_->GetPageBoundsRect(page_index);
1380
1381   // Flip the origin from bottom-left to top-left.
1382   bounding_box.set_y(static_cast<float>(page_bounds.height()) -
1383                      bounding_box.bottom());
1384   reply.Set("x", bounding_box.x());
1385   reply.Set("y", bounding_box.y());
1386   reply.Set("width", bounding_box.width());
1387   reply.Set("height", bounding_box.height());
1388
1389   client_->PostMessage(std::move(reply));
1390 }
1391
1392 void PdfViewWebPlugin::HandleGetPasswordCompleteMessage(
1393     const base::Value::Dict& message) {
1394   DCHECK(password_callback_);
1395   std::move(password_callback_).Run(*message.FindString("password"));
1396 }
1397
1398 void PdfViewWebPlugin::HandleGetSelectedTextMessage(
1399     const base::Value::Dict& message) {
1400   // Always return unix newlines to JavaScript.
1401   std::string selected_text;
1402   base::RemoveChars(engine_->GetSelectedText(), "\r", &selected_text);
1403
1404   base::Value::Dict reply =
1405       PrepareReplyMessage("getSelectedTextReply", message);
1406   reply.Set("selectedText", selected_text);
1407   client_->PostMessage(std::move(reply));
1408 }
1409
1410 void PdfViewWebPlugin::HandleGetThumbnailMessage(
1411     const base::Value::Dict& message) {
1412   const int page_index = message.FindInt("page").value();
1413   base::Value::Dict reply = PrepareReplyMessage("getThumbnailReply", message);
1414
1415   engine_->RequestThumbnail(
1416       page_index, device_scale_,
1417       base::BindOnce(&PdfViewWebPlugin::SendThumbnail,
1418                      weak_factory_.GetWeakPtr(), std::move(reply)));
1419 }
1420
1421 void PdfViewWebPlugin::HandlePrintMessage(
1422     const base::Value::Dict& /*message*/) {
1423   Print();
1424 }
1425
1426 void PdfViewWebPlugin::HandleRotateClockwiseMessage(
1427     const base::Value::Dict& /*message*/) {
1428   engine_->RotateClockwise();
1429 }
1430
1431 void PdfViewWebPlugin::HandleRotateCounterclockwiseMessage(
1432     const base::Value::Dict& /*message*/) {
1433   engine_->RotateCounterclockwise();
1434 }
1435
1436 void PdfViewWebPlugin::HandleSaveAttachmentMessage(
1437     const base::Value::Dict& message) {
1438   const int index = message.FindInt("attachmentIndex").value();
1439
1440   const std::vector<DocumentAttachmentInfo>& list =
1441       engine_->GetDocumentAttachmentInfoList();
1442   DCHECK_GE(index, 0);
1443   DCHECK_LT(static_cast<size_t>(index), list.size());
1444   DCHECK(list[index].is_readable);
1445   DCHECK(IsSaveDataSizeValid(list[index].size_bytes));
1446
1447   std::vector<uint8_t> data = engine_->GetAttachmentData(index);
1448   base::Value data_to_save(
1449       IsSaveDataSizeValid(data.size()) ? data : std::vector<uint8_t>());
1450
1451   base::Value::Dict reply = PrepareReplyMessage("saveAttachmentReply", message);
1452   reply.Set("dataToSave", std::move(data_to_save));
1453   client_->PostMessage(std::move(reply));
1454 }
1455
1456 void PdfViewWebPlugin::HandleSaveMessage(const base::Value::Dict& message) {
1457   const std::string& token = *message.FindString("token");
1458   int request_type = message.FindInt("saveRequestType").value();
1459   DCHECK_GE(request_type, static_cast<int>(SaveRequestType::kAnnotation));
1460   DCHECK_LE(request_type, static_cast<int>(SaveRequestType::kEdited));
1461
1462   switch (static_cast<SaveRequestType>(request_type)) {
1463     case SaveRequestType::kAnnotation:
1464 #if BUILDFLAG(ENABLE_INK)
1465       // In annotation mode, assume the user will make edits and prefer saving
1466       // using the plugin data.
1467       pdf_service_->SetPluginCanSave(true);
1468       SaveToBuffer(token);
1469 #else
1470       NOTREACHED();
1471 #endif  // BUILDFLAG(ENABLE_INK)
1472       break;
1473     case SaveRequestType::kOriginal:
1474       pdf_service_->SetPluginCanSave(false);
1475       SaveToFile(token);
1476       pdf_service_->SetPluginCanSave(edit_mode_);
1477       break;
1478     case SaveRequestType::kEdited:
1479       SaveToBuffer(token);
1480       break;
1481   }
1482 }
1483
1484 void PdfViewWebPlugin::HandleSelectAllMessage(
1485     const base::Value::Dict& /*message*/) {
1486   engine_->SelectAll();
1487 }
1488
1489 void PdfViewWebPlugin::HandleSetBackgroundColorMessage(
1490     const base::Value::Dict& message) {
1491   background_color_ =
1492       base::checked_cast<SkColor>(message.FindDouble("color").value());
1493 }
1494
1495 void PdfViewWebPlugin::HandleSetPresentationModeMessage(
1496     const base::Value::Dict& message) {
1497   engine_->SetReadOnly(message.FindBool("enablePresentationMode").value());
1498 }
1499
1500 void PdfViewWebPlugin::HandleSetTwoUpViewMessage(
1501     const base::Value::Dict& message) {
1502   engine_->SetDocumentLayout(message.FindBool("enableTwoUpView").value()
1503                                  ? DocumentLayout::PageSpread::kTwoUpOdd
1504                                  : DocumentLayout::PageSpread::kOneUp);
1505 }
1506
1507 void PdfViewWebPlugin::HandleStopScrollingMessage(
1508     const base::Value::Dict& /*message*/) {
1509   stop_scrolling_ = true;
1510 }
1511
1512 void PdfViewWebPlugin::HandleViewportMessage(const base::Value::Dict& message) {
1513   const base::Value::Dict* layout_options_value =
1514       message.FindDict("layoutOptions");
1515   if (layout_options_value) {
1516     DocumentLayout::Options layout_options;
1517     layout_options.FromValue(*layout_options_value);
1518
1519     ui_direction_ = layout_options.direction();
1520
1521     // TODO(crbug.com/1013800): Eliminate need to get document size from here.
1522     document_size_ = engine_->ApplyDocumentLayout(layout_options);
1523
1524     OnGeometryChanged(zoom_, device_scale_);
1525     if (!document_size_.IsEmpty())
1526       paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
1527
1528     // Send 100% loading progress only after initial layout negotiated.
1529     if (last_progress_sent_ < 100 &&
1530         document_load_state_ == DocumentLoadState::kComplete) {
1531       SendLoadingProgress(/*percentage=*/100);
1532     }
1533   }
1534
1535   gfx::Vector2dF scroll_offset(*message.FindDouble("xOffset"),
1536                                *message.FindDouble("yOffset"));
1537   double new_zoom = *message.FindDouble("zoom");
1538   const PinchPhase pinch_phase =
1539       static_cast<PinchPhase>(*message.FindInt("pinchPhase"));
1540
1541   received_viewport_message_ = true;
1542   stop_scrolling_ = false;
1543   const double zoom_ratio = new_zoom / zoom_;
1544
1545   if (pinch_phase == PinchPhase::kStart) {
1546     scroll_offset_at_last_raster_ = scroll_offset;
1547     last_bitmap_smaller_ = false;
1548     needs_reraster_ = false;
1549     return;
1550   }
1551
1552   // When zooming in, we set a layer transform to avoid unneeded rerasters.
1553   // Also, if we're zooming out and the last time we rerastered was when
1554   // we were even further zoomed out (i.e. we pinch zoomed in and are now
1555   // pinch zooming back out in the same gesture), we update the layer
1556   // transform instead of rerastering.
1557   if (pinch_phase == PinchPhase::kUpdateZoomIn ||
1558       (pinch_phase == PinchPhase::kUpdateZoomOut && zoom_ratio > 1.0)) {
1559     // Get the coordinates of the center of the pinch gesture.
1560     const double pinch_x = *message.FindDouble("pinchX");
1561     const double pinch_y = *message.FindDouble("pinchY");
1562     gfx::Point pinch_center(pinch_x, pinch_y);
1563
1564     // Get the pinch vector which represents the panning caused by the change in
1565     // pinch center between the start and the end of the gesture.
1566     const double pinch_vector_x = *message.FindDouble("pinchVectorX");
1567     const double pinch_vector_y = *message.FindDouble("pinchVectorY");
1568     gfx::Vector2d pinch_vector =
1569         gfx::Vector2d(pinch_vector_x * zoom_ratio, pinch_vector_y * zoom_ratio);
1570
1571     gfx::Vector2d scroll_delta;
1572     // If the rendered document doesn't fill the display area we will
1573     // use `paint_offset` to anchor the paint vertically into the same place.
1574     // We use the scroll bars instead of the pinch vector to get the actual
1575     // position on screen of the paint.
1576     gfx::Vector2d paint_offset;
1577
1578     if (plugin_rect_.width() > GetDocumentPixelWidth() * zoom_ratio) {
1579       // We want to keep the paint in the middle but it must stay in the same
1580       // position relative to the scroll bars.
1581       paint_offset = gfx::Vector2d(0, (1 - zoom_ratio) * pinch_center.y());
1582       scroll_delta = gfx::Vector2d(
1583           0,
1584           (scroll_offset.y() - scroll_offset_at_last_raster_.y() * zoom_ratio));
1585
1586       pinch_vector = gfx::Vector2d();
1587       last_bitmap_smaller_ = true;
1588     } else if (last_bitmap_smaller_) {
1589       // When the document width covers the display area's width, we will anchor
1590       // the scroll bars disregarding where the actual pinch certer is.
1591       pinch_center = gfx::Point((plugin_rect_.width() / device_scale_) / 2,
1592                                 (plugin_rect_.height() / device_scale_) / 2);
1593       const double zoom_when_doc_covers_plugin_width =
1594           zoom_ * plugin_rect_.width() / GetDocumentPixelWidth();
1595       paint_offset = gfx::Vector2d(
1596           (1 - new_zoom / zoom_when_doc_covers_plugin_width) * pinch_center.x(),
1597           (1 - zoom_ratio) * pinch_center.y());
1598       pinch_vector = gfx::Vector2d();
1599       scroll_delta = gfx::Vector2d(
1600           (scroll_offset.x() - scroll_offset_at_last_raster_.x() * zoom_ratio),
1601           (scroll_offset.y() - scroll_offset_at_last_raster_.y() * zoom_ratio));
1602     }
1603
1604     paint_manager_.SetTransform(zoom_ratio, pinch_center,
1605                                 pinch_vector + paint_offset + scroll_delta,
1606                                 true);
1607     needs_reraster_ = false;
1608     return;
1609   }
1610
1611   if (pinch_phase == PinchPhase::kUpdateZoomOut ||
1612       pinch_phase == PinchPhase::kEnd) {
1613     // We reraster on pinch zoom out in order to solve the invalid regions
1614     // that appear after zooming out.
1615     // On pinch end the scale is again 1.f and we request a reraster
1616     // in the new position.
1617     paint_manager_.ClearTransform();
1618     last_bitmap_smaller_ = false;
1619     needs_reraster_ = true;
1620
1621     // If we're rerastering due to zooming out, we need to update the scroll
1622     // offset for the last raster, in case the user continues the gesture by
1623     // zooming in.
1624     scroll_offset_at_last_raster_ = scroll_offset;
1625   }
1626
1627   // Bound the input parameters.
1628   new_zoom = std::max(kMinZoom, new_zoom);
1629   DCHECK(message.FindBool("userInitiated").has_value());
1630
1631   double old_zoom = zoom_;
1632   zoom_ = new_zoom;
1633
1634   OnGeometryChanged(old_zoom, device_scale_);
1635   if (!document_size_.IsEmpty())
1636     paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
1637
1638   UpdateScroll(GetScrollPositionFromOffset(scroll_offset));
1639 }
1640
1641 void PdfViewWebPlugin::SaveToBuffer(const std::string& token) {
1642   engine_->KillFormFocus();
1643
1644   base::Value::Dict message;
1645   message.Set("type", "saveData");
1646   message.Set("token", token);
1647   message.Set("fileName", GetFileNameForSaveFromUrl(url_));
1648
1649   // Expose `edit_mode_` state for integration testing.
1650   message.Set("editModeForTesting", edit_mode_);
1651
1652   base::Value data_to_save;
1653   if (edit_mode_) {
1654     base::Value::BlobStorage data = engine_->GetSaveData();
1655     if (IsSaveDataSizeValid(data.size()))
1656       data_to_save = base::Value(std::move(data));
1657   } else {
1658 #if BUILDFLAG(ENABLE_INK)
1659     uint32_t length = engine_->GetLoadedByteSize();
1660     if (IsSaveDataSizeValid(length)) {
1661       base::Value::BlobStorage data(length);
1662       if (engine_->ReadLoadedBytes(length, data.data()))
1663         data_to_save = base::Value(std::move(data));
1664     }
1665 #else
1666     NOTREACHED();
1667 #endif  // BUILDFLAG(ENABLE_INK)
1668   }
1669
1670   message.Set("dataToSave", std::move(data_to_save));
1671   client_->PostMessage(std::move(message));
1672 }
1673
1674 void PdfViewWebPlugin::SaveToFile(const std::string& token) {
1675   engine_->KillFormFocus();
1676
1677   base::Value::Dict message;
1678   message.Set("type", "consumeSaveToken");
1679   message.Set("token", token);
1680   client_->PostMessage(std::move(message));
1681
1682   pdf_service_->SaveUrlAs(GURL(url_), network::mojom::ReferrerPolicy::kDefault);
1683 }
1684
1685 void PdfViewWebPlugin::InvalidatePluginContainer() {
1686   client_->Invalidate();
1687 }
1688
1689 void PdfViewWebPlugin::OnPaint(const std::vector<gfx::Rect>& paint_rects,
1690                                std::vector<PaintReadyRect>& ready,
1691                                std::vector<gfx::Rect>& pending) {
1692   base::AutoReset<bool> auto_reset_in_paint(&in_paint_, true);
1693   DoPaint(paint_rects, ready, pending);
1694 }
1695
1696 gfx::PointF PdfViewWebPlugin::GetScrollPositionFromOffset(
1697     const gfx::Vector2dF& scroll_offset) const {
1698   gfx::PointF scroll_origin;
1699
1700   // TODO(crbug.com/1140374): Right-to-left scrolling currently is not
1701   // compatible with the PDF viewer's sticky "scroller" element.
1702   if (ui_direction_ == base::i18n::RIGHT_TO_LEFT && IsPrintPreview()) {
1703     scroll_origin.set_x(
1704         std::max(document_size_.width() * static_cast<float>(zoom_) -
1705                      plugin_dip_size_.width(),
1706                  0.0f));
1707   }
1708
1709   return scroll_origin + scroll_offset;
1710 }
1711
1712 void PdfViewWebPlugin::DoPaint(const std::vector<gfx::Rect>& paint_rects,
1713                                std::vector<PaintReadyRect>& ready,
1714                                std::vector<gfx::Rect>& pending) {
1715   if (image_data_.drawsNothing()) {
1716     DCHECK(plugin_rect_.IsEmpty());
1717     return;
1718   }
1719
1720   PrepareForFirstPaint(ready);
1721
1722   if (!received_viewport_message_ || !needs_reraster_)
1723     return;
1724
1725   engine_->PrePaint();
1726
1727   std::vector<gfx::Rect> ready_rects;
1728   for (const gfx::Rect& paint_rect : paint_rects) {
1729     // Intersect with plugin area since there could be pending invalidates from
1730     // when the plugin area was larger.
1731     gfx::Rect rect =
1732         gfx::IntersectRects(paint_rect, gfx::Rect(plugin_rect_.size()));
1733     if (rect.IsEmpty())
1734       continue;
1735
1736     // Paint the rendering of the PDF document.
1737     gfx::Rect pdf_rect = gfx::IntersectRects(rect, available_area_);
1738     if (!pdf_rect.IsEmpty()) {
1739       pdf_rect.Offset(-available_area_.x(), 0);
1740
1741       std::vector<gfx::Rect> pdf_ready;
1742       std::vector<gfx::Rect> pdf_pending;
1743       engine_->Paint(pdf_rect, image_data_, pdf_ready, pdf_pending);
1744       for (gfx::Rect& ready_rect : pdf_ready) {
1745         ready_rect.Offset(available_area_.OffsetFromOrigin());
1746         ready_rects.push_back(ready_rect);
1747       }
1748       for (gfx::Rect& pending_rect : pdf_pending) {
1749         pending_rect.Offset(available_area_.OffsetFromOrigin());
1750         pending.push_back(pending_rect);
1751       }
1752     }
1753
1754     // Ensure the region above the first page (if any) is filled;
1755     const int32_t first_page_ypos = 0 == engine_->GetNumberOfPages()
1756                                         ? 0
1757                                         : engine_->GetPageScreenRect(0).y();
1758     if (rect.y() < first_page_ypos) {
1759       gfx::Rect region = gfx::IntersectRects(
1760           rect, gfx::Rect(gfx::Size(plugin_rect_.width(), first_page_ypos)));
1761       image_data_.erase(GetBackgroundColor(), gfx::RectToSkIRect(region));
1762       ready_rects.push_back(region);
1763     }
1764
1765     // Ensure the background parts are filled.
1766     for (const BackgroundPart& background_part : background_parts_) {
1767       gfx::Rect intersection =
1768           gfx::IntersectRects(background_part.location, rect);
1769       if (!intersection.IsEmpty()) {
1770         image_data_.erase(background_part.color,
1771                           gfx::RectToSkIRect(intersection));
1772         ready_rects.push_back(intersection);
1773       }
1774     }
1775   }
1776
1777   engine_->PostPaint();
1778
1779   // TODO(crbug.com/1263614): Write pixels directly to the `SkSurface` in
1780   // `PaintManager`, rather than using an intermediate `SkBitmap` and `SkImage`.
1781   sk_sp<SkImage> painted_image = image_data_.asImage();
1782   for (const gfx::Rect& ready_rect : ready_rects)
1783     ready.emplace_back(ready_rect, painted_image);
1784
1785   InvalidateAfterPaintDone();
1786 }
1787
1788 void PdfViewWebPlugin::PrepareForFirstPaint(
1789     std::vector<PaintReadyRect>& ready) {
1790   if (!first_paint_)
1791     return;
1792
1793   // Fill the image data buffer with the background color.
1794   first_paint_ = false;
1795   image_data_.eraseColor(background_color_);
1796   ready.emplace_back(gfx::SkIRectToRect(image_data_.bounds()),
1797                      image_data_.asImage(), /*flush_now=*/true);
1798 }
1799
1800 void PdfViewWebPlugin::OnGeometryChanged(double old_zoom,
1801                                          float old_device_scale) {
1802   RecalculateAreas(old_zoom, old_device_scale);
1803
1804   if (accessibility_state_ == AccessibilityState::kLoaded)
1805     PrepareAndSetAccessibilityViewportInfo();
1806 }
1807
1808 void PdfViewWebPlugin::RecalculateAreas(double old_zoom,
1809                                         float old_device_scale) {
1810   if (zoom_ != old_zoom || device_scale_ != old_device_scale)
1811     engine_->ZoomUpdated(zoom_ * device_scale_);
1812
1813   available_area_ = gfx::Rect(plugin_rect_.size());
1814   int doc_width = GetDocumentPixelWidth();
1815   if (doc_width < available_area_.width()) {
1816     // Center the document horizontally inside the plugin rectangle.
1817     available_area_.Offset((plugin_rect_.width() - doc_width) / 2, 0);
1818     available_area_.set_width(doc_width);
1819   }
1820
1821   // The distance between top of the plugin and the bottom of the document in
1822   // pixels.
1823   int bottom_of_document = GetDocumentPixelHeight();
1824   if (bottom_of_document < plugin_rect_.height())
1825     available_area_.set_height(bottom_of_document);
1826
1827   CalculateBackgroundParts();
1828
1829   engine_->PageOffsetUpdated(available_area_.OffsetFromOrigin());
1830   engine_->PluginSizeUpdated(available_area_.size());
1831 }
1832
1833 void PdfViewWebPlugin::CalculateBackgroundParts() {
1834   background_parts_.clear();
1835   int left_width = available_area_.x();
1836   int right_start = available_area_.right();
1837   int right_width = std::abs(plugin_rect_.width() - available_area_.right());
1838   int bottom = std::min(available_area_.bottom(), plugin_rect_.height());
1839
1840   // Note: we assume the display of the PDF document is always centered
1841   // horizontally, but not necessarily centered vertically.
1842   // Add the left rectangle.
1843   BackgroundPart part = {gfx::Rect(left_width, bottom), GetBackgroundColor()};
1844   if (!part.location.IsEmpty())
1845     background_parts_.push_back(part);
1846
1847   // Add the right rectangle.
1848   part.location = gfx::Rect(right_start, 0, right_width, bottom);
1849   if (!part.location.IsEmpty())
1850     background_parts_.push_back(part);
1851
1852   // Add the bottom rectangle.
1853   part.location = gfx::Rect(0, bottom, plugin_rect_.width(),
1854                             plugin_rect_.height() - bottom);
1855   if (!part.location.IsEmpty())
1856     background_parts_.push_back(part);
1857 }
1858
1859 int PdfViewWebPlugin::GetDocumentPixelWidth() const {
1860   return static_cast<int>(
1861       std::ceil(document_size_.width() * zoom_ * device_scale_));
1862 }
1863
1864 int PdfViewWebPlugin::GetDocumentPixelHeight() const {
1865   return static_cast<int>(
1866       std::ceil(document_size_.height() * zoom_ * device_scale_));
1867 }
1868
1869 void PdfViewWebPlugin::InvalidateAfterPaintDone() {
1870   if (deferred_invalidates_.empty())
1871     return;
1872
1873   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
1874       FROM_HERE, base::BindOnce(&PdfViewWebPlugin::ClearDeferredInvalidates,
1875                                 weak_factory_.GetWeakPtr()));
1876 }
1877
1878 void PdfViewWebPlugin::ClearDeferredInvalidates() {
1879   DCHECK(!in_paint_);
1880   for (const gfx::Rect& rect : deferred_invalidates_)
1881     Invalidate(rect);
1882   deferred_invalidates_.clear();
1883 }
1884
1885 void PdfViewWebPlugin::UpdateSnapshot(sk_sp<SkImage> snapshot) {
1886   // Every time something changes (e.g. scale or scroll position),
1887   // `UpdateSnapshot()` is called, so the snapshot is effectively used only
1888   // once. Make it "no-cache" so that the old snapshots are not cached
1889   // downstream.
1890   //
1891   // Otherwise, for instance when scrolling, all the previous snapshots end up
1892   // accumulating in the (for the GPU path) GpuImageDecodeCache, and then in the
1893   // service transfer cache. The size of the service transfer cache is bounded,
1894   // so on desktop this "only" causes a 256MiB memory spike, but it's completely
1895   // wasted memory nonetheless.
1896   snapshot_ =
1897       cc::PaintImageBuilder::WithDefault()
1898           .set_image(std::move(snapshot), cc::PaintImage::GetNextContentId())
1899           .set_id(cc::PaintImage::GetNextId())
1900           .set_no_cache(true)
1901           .TakePaintImage();
1902
1903   if (!plugin_rect_.IsEmpty())
1904     InvalidatePluginContainer();
1905 }
1906
1907 void PdfViewWebPlugin::UpdateScaledValues() {
1908   total_translate_ = snapshot_translate_;
1909
1910   if (viewport_to_dip_scale_ != 1.0f)
1911     total_translate_.Scale(1.0f / viewport_to_dip_scale_);
1912 }
1913
1914 void PdfViewWebPlugin::UpdateScale(float scale) {
1915   if (scale <= 0.0f) {
1916     NOTREACHED();
1917     return;
1918   }
1919
1920   viewport_to_dip_scale_ = scale;
1921   UpdateScaledValues();
1922 }
1923
1924 void PdfViewWebPlugin::UpdateLayerTransform(float scale,
1925                                             const gfx::Vector2dF& translate) {
1926   snapshot_translate_ = translate;
1927   snapshot_scale_ = scale;
1928   UpdateScaledValues();
1929 }
1930
1931 void PdfViewWebPlugin::EnableAccessibility() {
1932   if (accessibility_state_ == AccessibilityState::kLoaded)
1933     return;
1934
1935   LoadOrReloadAccessibility();
1936 }
1937
1938 SkBitmap PdfViewWebPlugin::GetImageForOcr(int32_t page_index,
1939                                           int32_t page_object_index) {
1940   return engine_->GetImageForOcr(page_index, page_object_index);
1941 }
1942
1943 void PdfViewWebPlugin::HandleAccessibilityAction(
1944     const AccessibilityActionData& action_data) {
1945   engine_->HandleAccessibilityAction(action_data);
1946 }
1947
1948 void PdfViewWebPlugin::LoadOrReloadAccessibility() {
1949   if (accessibility_state_ == AccessibilityState::kOff) {
1950     accessibility_state_ = AccessibilityState::kPending;
1951   }
1952
1953   if (document_load_state_ == DocumentLoadState::kComplete) {
1954     LoadAccessibility();
1955   }
1956 }
1957
1958 void PdfViewWebPlugin::OnViewportChanged(
1959     const gfx::Rect& new_plugin_rect_in_css_pixel,
1960     float new_device_scale) {
1961   DCHECK_GT(new_device_scale, 0.0f);
1962
1963   css_plugin_rect_ = new_plugin_rect_in_css_pixel;
1964
1965   if (new_device_scale == device_scale_ &&
1966       new_plugin_rect_in_css_pixel == plugin_rect_) {
1967     return;
1968   }
1969
1970   const float old_device_scale = device_scale_;
1971   device_scale_ = new_device_scale;
1972   plugin_rect_ = new_plugin_rect_in_css_pixel;
1973
1974   // TODO(crbug.com/1250173): We should try to avoid the downscaling in this
1975   // calculation, perhaps by migrating off `plugin_dip_size_`.
1976   plugin_dip_size_ = gfx::ScaleToEnclosingRect(new_plugin_rect_in_css_pixel,
1977                                                1.0f / new_device_scale)
1978                          .size();
1979
1980   paint_manager_.SetSize(plugin_rect_.size(), device_scale_);
1981
1982   // Initialize the image data buffer if the context size changes.
1983   const gfx::Size old_image_size = gfx::SkISizeToSize(image_data_.dimensions());
1984   const gfx::Size new_image_size =
1985       PaintManager::GetNewContextSize(old_image_size, plugin_rect_.size());
1986   if (new_image_size != old_image_size) {
1987     image_data_.allocPixels(
1988         SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(new_image_size)));
1989     first_paint_ = true;
1990   }
1991
1992   // Skip updating the geometry if the new image data buffer is empty.
1993   if (image_data_.drawsNothing())
1994     return;
1995
1996   OnGeometryChanged(zoom_, old_device_scale);
1997 }
1998
1999 bool PdfViewWebPlugin::SelectAll() {
2000   engine_->SelectAll();
2001   return true;
2002 }
2003
2004 bool PdfViewWebPlugin::Cut() {
2005   if (!HasSelection() || !CanEditText())
2006     return false;
2007
2008   engine_->ReplaceSelection("");
2009   return true;
2010 }
2011
2012 bool PdfViewWebPlugin::Paste(const blink::WebString& value) {
2013   if (!CanEditText())
2014     return false;
2015
2016   engine_->ReplaceSelection(value.Utf8());
2017   return true;
2018 }
2019
2020 bool PdfViewWebPlugin::Undo() {
2021   if (!CanUndo())
2022     return false;
2023
2024   engine_->Undo();
2025   return true;
2026 }
2027
2028 bool PdfViewWebPlugin::Redo() {
2029   if (!CanRedo())
2030     return false;
2031
2032   engine_->Redo();
2033   return true;
2034 }
2035
2036 bool PdfViewWebPlugin::HandleWebInputEvent(const blink::WebInputEvent& event) {
2037   // Ignore user input in read-only mode.
2038   if (engine_->IsReadOnly())
2039     return false;
2040
2041   // `engine_` expects input events in device coordinates.
2042   float viewport_to_device_scale = viewport_to_dip_scale_ * device_scale_;
2043   std::unique_ptr<blink::WebInputEvent> transformed_event =
2044       ui::TranslateAndScaleWebInputEvent(
2045           event,
2046           gfx::Vector2dF(-available_area_.x() / viewport_to_device_scale, 0),
2047           viewport_to_device_scale);
2048
2049   const blink::WebInputEvent& event_to_handle =
2050       transformed_event ? *transformed_event : event;
2051
2052   if (engine_->HandleInputEvent(event_to_handle))
2053     return true;
2054
2055   // Middle click is used for scrolling and is handled by the container page.
2056   if (blink::WebInputEvent::IsMouseEventType(event_to_handle.GetType()) &&
2057       static_cast<const blink::WebMouseEvent&>(event_to_handle).button ==
2058           blink::WebPointerProperties::Button::kMiddle) {
2059     return false;
2060   }
2061
2062   // Return true for unhandled clicks so the plugin takes focus.
2063   return event_to_handle.GetType() == blink::WebInputEvent::Type::kMouseDown;
2064 }
2065
2066 void PdfViewWebPlugin::HandleImeCommit(const blink::WebString& text) {
2067   if (text.IsEmpty())
2068     return;
2069
2070   std::u16string text16 = text.Utf16();
2071   composition_text_.Reset();
2072
2073   size_t i = 0;
2074   for (base::i18n::UTF16CharIterator iterator(text16); iterator.Advance();) {
2075     blink::WebKeyboardEvent char_event(blink::WebInputEvent::Type::kChar,
2076                                        blink::WebInputEvent::kNoModifiers,
2077                                        ui::EventTimeForNow());
2078     char_event.windows_key_code = text16[i];
2079     char_event.native_key_code = text16[i];
2080
2081     for (const size_t char_start = i; i < iterator.array_pos(); ++i) {
2082       char_event.text[i - char_start] = text16[i];
2083       char_event.unmodified_text[i - char_start] = text16[i];
2084     }
2085
2086     blink::WebCoalescedInputEvent input_event(char_event, ui::LatencyInfo());
2087     ui::Cursor dummy_cursor_info;
2088     HandleInputEvent(input_event, &dummy_cursor_info);
2089   }
2090 }
2091
2092 void PdfViewWebPlugin::OnInvokePrintDialog() {
2093   client_->Print();
2094 }
2095
2096 void PdfViewWebPlugin::ResetRecentlySentFindUpdate() {
2097   recently_sent_find_update_ = false;
2098 }
2099
2100 void PdfViewWebPlugin::RecordDocumentMetrics() {
2101   if (!metrics_handler_)
2102     return;
2103
2104   metrics_handler_->RecordDocumentMetrics(engine_->GetDocumentMetadata());
2105   metrics_handler_->RecordAttachmentTypes(
2106       engine_->GetDocumentAttachmentInfoList());
2107 }
2108
2109 void PdfViewWebPlugin::SendAttachments() {
2110   const std::vector<DocumentAttachmentInfo>& attachment_infos =
2111       engine_->GetDocumentAttachmentInfoList();
2112   if (attachment_infos.empty())
2113     return;
2114
2115   base::Value::List attachments;
2116   for (const DocumentAttachmentInfo& attachment_info : attachment_infos) {
2117     // Send `size` as -1 to indicate that the attachment is too large to be
2118     // downloaded.
2119     const int size = attachment_info.size_bytes <= kMaximumSavedFileSize
2120                          ? static_cast<int>(attachment_info.size_bytes)
2121                          : -1;
2122
2123     base::Value::Dict attachment;
2124     attachment.Set("name", attachment_info.name);
2125     attachment.Set("size", size);
2126     attachment.Set("readable", attachment_info.is_readable);
2127     attachments.Append(std::move(attachment));
2128   }
2129
2130   base::Value::Dict message;
2131   message.Set("type", "attachments");
2132   message.Set("attachmentsData", std::move(attachments));
2133   client_->PostMessage(std::move(message));
2134 }
2135
2136 void PdfViewWebPlugin::SendBookmarks() {
2137   base::Value::List bookmarks = engine_->GetBookmarks();
2138   if (bookmarks.empty())
2139     return;
2140
2141   base::Value::Dict message;
2142   message.Set("type", "bookmarks");
2143   message.Set("bookmarksData", std::move(bookmarks));
2144   client_->PostMessage(std::move(message));
2145 }
2146
2147 void PdfViewWebPlugin::SendMetadata() {
2148   base::Value::Dict metadata;
2149   const DocumentMetadata& document_metadata = engine_->GetDocumentMetadata();
2150
2151   const std::string version = FormatPdfVersion(document_metadata.version);
2152   if (!version.empty())
2153     metadata.Set("version", version);
2154
2155   metadata.Set("fileSize", ui::FormatBytes(document_metadata.size_bytes));
2156
2157   metadata.Set("linearized", document_metadata.linearized);
2158
2159   if (!document_metadata.title.empty())
2160     metadata.Set("title", document_metadata.title);
2161
2162   if (!document_metadata.author.empty())
2163     metadata.Set("author", document_metadata.author);
2164
2165   if (!document_metadata.subject.empty())
2166     metadata.Set("subject", document_metadata.subject);
2167
2168   if (!document_metadata.keywords.empty())
2169     metadata.Set("keywords", document_metadata.keywords);
2170
2171   if (!document_metadata.creator.empty())
2172     metadata.Set("creator", document_metadata.creator);
2173
2174   if (!document_metadata.producer.empty())
2175     metadata.Set("producer", document_metadata.producer);
2176
2177   if (!document_metadata.creation_date.is_null()) {
2178     metadata.Set("creationDate", base::TimeFormatShortDateAndTime(
2179                                      document_metadata.creation_date));
2180   }
2181
2182   if (!document_metadata.mod_date.is_null()) {
2183     metadata.Set("modDate",
2184                  base::TimeFormatShortDateAndTime(document_metadata.mod_date));
2185   }
2186
2187   metadata.Set("pageSize", FormatPageSize(engine_->GetUniformPageSizePoints()));
2188
2189   metadata.Set("canSerializeDocument",
2190                IsSaveDataSizeValid(engine_->GetLoadedByteSize()));
2191
2192   base::Value::Dict message;
2193   message.Set("type", "metadata");
2194   message.Set("metadataData", std::move(metadata));
2195   client_->PostMessage(std::move(message));
2196 }
2197
2198 void PdfViewWebPlugin::SendLoadingProgress(double percentage) {
2199   DCHECK(percentage == -1 || (percentage >= 0 && percentage <= 100));
2200   last_progress_sent_ = percentage;
2201
2202   base::Value::Dict message;
2203   message.Set("type", "loadProgress");
2204   message.Set("progress", percentage);
2205   client_->PostMessage(std::move(message));
2206 }
2207
2208 void PdfViewWebPlugin::HandleResetPrintPreviewModeMessage(
2209     const base::Value::Dict& message) {
2210   const std::string& url = *message.FindString("url");
2211   bool is_grayscale = message.FindBool("grayscale").value();
2212   int print_preview_page_count = message.FindInt("pageCount").value();
2213
2214   // For security reasons, crash if `url` is not for Print Preview.
2215   CHECK(IsPrintPreview());
2216   CHECK(IsPrintPreviewUrl(url));
2217
2218   DCHECK_GE(print_preview_page_count, 0);
2219
2220   int page_index = ExtractPrintPreviewPageIndex(url);
2221   if (IsPreviewingPDF(print_preview_page_count)) {
2222     DCHECK_EQ(page_index, kCompletePDFIndex);
2223   } else {
2224     DCHECK_GE(page_index, 0);
2225   }
2226
2227   print_preview_page_count_ = print_preview_page_count;
2228   print_preview_loaded_page_count_ = 0;
2229   url_ = url;
2230   preview_pages_info_ = base::queue<PreviewPageInfo>();
2231   preview_document_load_state_ = DocumentLoadState::kComplete;
2232   document_load_state_ = DocumentLoadState::kLoading;
2233   last_progress_sent_ = 0;
2234   LoadUrl(url_, base::BindOnce(&PdfViewWebPlugin::DidOpen,
2235                                weak_factory_.GetWeakPtr()));
2236   preview_engine_.reset();
2237
2238   // TODO(crbug.com/1237952): Figure out a more consistent way to preserve
2239   // engine settings across a Print Preview reset.
2240   engine_ = client_->CreateEngine(
2241       this, PDFiumFormFiller::ScriptOption::kNoJavaScript);
2242   engine_->ZoomUpdated(zoom_ * device_scale_);
2243   engine_->PageOffsetUpdated(available_area_.OffsetFromOrigin());
2244   engine_->PluginSizeUpdated(available_area_.size());
2245   engine_->SetGrayscale(is_grayscale);
2246
2247   paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
2248 }
2249
2250 void PdfViewWebPlugin::HandleLoadPreviewPageMessage(
2251     const base::Value::Dict& message) {
2252   const std::string& url = *message.FindString("url");
2253   int dest_page_index = message.FindInt("index").value();
2254
2255   // For security reasons, crash if `url` is not for Print Preview.
2256   CHECK(IsPrintPreview());
2257   CHECK(IsPrintPreviewUrl(url));
2258
2259   DCHECK_GE(dest_page_index, 0);
2260   DCHECK_LT(dest_page_index, print_preview_page_count_);
2261
2262   // Print Preview JS will send the loadPreviewPage message for every page,
2263   // including the first page in the print preview, which has already been
2264   // loaded when handing the resetPrintPreviewMode message. Just ignore it.
2265   if (dest_page_index == 0)
2266     return;
2267
2268   int src_page_index = ExtractPrintPreviewPageIndex(url);
2269   DCHECK_GE(src_page_index, 0);
2270
2271   preview_pages_info_.push({.url = url, .dest_page_index = dest_page_index});
2272   LoadAvailablePreviewPage();
2273 }
2274
2275 void PdfViewWebPlugin::LoadAvailablePreviewPage() {
2276   if (preview_pages_info_.empty() ||
2277       document_load_state_ != DocumentLoadState::kComplete ||
2278       preview_document_load_state_ == DocumentLoadState::kLoading) {
2279     return;
2280   }
2281
2282   preview_document_load_state_ = DocumentLoadState::kLoading;
2283   const std::string& url = preview_pages_info_.front().url;
2284
2285   // Note that `last_progress_sent_` is not reset for preview page loads.
2286   LoadUrl(url, base::BindOnce(&PdfViewWebPlugin::DidOpenPreview,
2287                               weak_factory_.GetWeakPtr()));
2288 }
2289
2290 void PdfViewWebPlugin::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
2291                                       int32_t result) {
2292   DCHECK_EQ(result, kSuccess);
2293
2294   // `preview_engine_` holds a `raw_ptr` to `preview_client_`.
2295   // We need to explicitly destroy it before clobbering
2296   // `preview_client_` to dodge lifetime issues.
2297   preview_engine_.reset();
2298
2299   preview_client_ = std::make_unique<PreviewModeClient>(this);
2300   preview_engine_ = client_->CreateEngine(
2301       preview_client_.get(), PDFiumFormFiller::ScriptOption::kNoJavaScript);
2302   preview_engine_->PluginSizeUpdated({});
2303   preview_engine_->HandleDocumentLoad(std::move(loader), url_);
2304 }
2305
2306 void PdfViewWebPlugin::PreviewDocumentLoadComplete() {
2307   if (preview_document_load_state_ != DocumentLoadState::kLoading ||
2308       preview_pages_info_.empty()) {
2309     return;
2310   }
2311
2312   preview_document_load_state_ = DocumentLoadState::kComplete;
2313
2314   int dest_page_index = preview_pages_info_.front().dest_page_index;
2315   preview_pages_info_.pop();
2316   engine_->AppendPage(preview_engine_.get(), dest_page_index);
2317
2318   ++print_preview_loaded_page_count_;
2319   LoadNextPreviewPage();
2320 }
2321
2322 void PdfViewWebPlugin::PreviewDocumentLoadFailed() {
2323   client_->RecordComputedAction("PDF.PreviewDocumentLoadFailure");
2324   if (preview_document_load_state_ != DocumentLoadState::kLoading ||
2325       preview_pages_info_.empty()) {
2326     return;
2327   }
2328
2329   // Even if a print preview page failed to load, keep going.
2330   preview_document_load_state_ = DocumentLoadState::kFailed;
2331   preview_pages_info_.pop();
2332   ++print_preview_loaded_page_count_;
2333   LoadNextPreviewPage();
2334 }
2335
2336 void PdfViewWebPlugin::LoadNextPreviewPage() {
2337   if (!preview_pages_info_.empty()) {
2338     DCHECK_LT(print_preview_loaded_page_count_, print_preview_page_count_);
2339     LoadAvailablePreviewPage();
2340     return;
2341   }
2342
2343   if (print_preview_loaded_page_count_ == print_preview_page_count_)
2344     SendPrintPreviewLoadedNotification();
2345 }
2346
2347 void PdfViewWebPlugin::SendPrintPreviewLoadedNotification() {
2348   base::Value::Dict message;
2349   message.Set("type", "printPreviewLoaded");
2350   client_->PostMessage(std::move(message));
2351 }
2352
2353 void PdfViewWebPlugin::SendThumbnail(base::Value::Dict reply,
2354                                      Thumbnail thumbnail) {
2355   DCHECK_EQ(*reply.FindString("type"), "getThumbnailReply");
2356   DCHECK(reply.FindString("messageId"));
2357
2358   reply.Set("imageData", thumbnail.TakeData());
2359   reply.Set("width", thumbnail.image_size().width());
2360   reply.Set("height", thumbnail.image_size().height());
2361   client_->PostMessage(std::move(reply));
2362 }
2363
2364 gfx::Point PdfViewWebPlugin::FrameToPdfCoordinates(
2365     const gfx::PointF& frame_coordinates) const {
2366   // TODO(crbug.com/1288847): Use methods on `blink::WebPluginContainer`.
2367   return gfx::ToFlooredPoint(
2368              gfx::ScalePoint(frame_coordinates, device_scale_)) -
2369          gfx::Vector2d(available_area_.x(), 0);
2370 }
2371
2372 AccessibilityDocInfo PdfViewWebPlugin::GetAccessibilityDocInfo() const {
2373   AccessibilityDocInfo doc_info;
2374   doc_info.page_count = engine_->GetNumberOfPages();
2375   doc_info.text_accessible =
2376       engine_->HasPermission(DocumentPermission::kCopyAccessible);
2377   doc_info.text_copyable = engine_->HasPermission(DocumentPermission::kCopy);
2378   return doc_info;
2379 }
2380
2381 void PdfViewWebPlugin::PrepareAndSetAccessibilityPageInfo(int32_t page_index) {
2382   // Outdated calls are ignored.
2383   if (page_index != next_accessibility_page_index_)
2384     return;
2385   ++next_accessibility_page_index_;
2386
2387   AccessibilityPageInfo page_info;
2388   std::vector<AccessibilityTextRunInfo> text_runs;
2389   std::vector<AccessibilityCharInfo> chars;
2390   AccessibilityPageObjects page_objects;
2391
2392   if (!GetAccessibilityInfo(engine_.get(), page_index, page_info, text_runs,
2393                             chars, page_objects)) {
2394     return;
2395   }
2396
2397   pdf_accessibility_data_handler_->SetAccessibilityPageInfo(
2398       std::move(page_info), std::move(text_runs), std::move(chars),
2399       std::move(page_objects));
2400
2401   // Schedule loading the next page.
2402   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
2403       FROM_HERE,
2404       base::BindOnce(&PdfViewWebPlugin::PrepareAndSetAccessibilityPageInfo,
2405                      weak_factory_.GetWeakPtr(), page_index + 1),
2406       kAccessibilityPageDelay);
2407 }
2408
2409 void PdfViewWebPlugin::PrepareAndSetAccessibilityViewportInfo() {
2410   AccessibilityViewportInfo viewport_info;
2411   viewport_info.offset = gfx::ScaleToFlooredPoint(available_area_.origin(),
2412                                                   1 / (device_scale_ * zoom_));
2413   viewport_info.zoom = zoom_;
2414   viewport_info.scale = device_scale_;
2415   viewport_info.focus_info = {FocusObjectType::kNone, 0, 0};
2416
2417   engine_->GetSelection(&viewport_info.selection_start_page_index,
2418                         &viewport_info.selection_start_char_index,
2419                         &viewport_info.selection_end_page_index,
2420                         &viewport_info.selection_end_char_index);
2421
2422   pdf_accessibility_data_handler_->SetAccessibilityViewportInfo(
2423       std::move(viewport_info));
2424 }
2425
2426 void PdfViewWebPlugin::LoadAccessibility() {
2427   accessibility_state_ = AccessibilityState::kLoaded;
2428
2429   // A new document layout will trigger the creation of a new accessibility
2430   // tree, so `next_accessibility_page_index_` should be reset to ignore
2431   // outdated asynchronous calls of PrepareAndSetAccessibilityPageInfo().
2432   next_accessibility_page_index_ = 0;
2433   pdf_accessibility_data_handler_->SetAccessibilityDocInfo(
2434       GetAccessibilityDocInfo());
2435
2436   // If the document contents isn't accessible, don't send anything more.
2437   if (!(engine_->HasPermission(DocumentPermission::kCopy) ||
2438         engine_->HasPermission(DocumentPermission::kCopyAccessible))) {
2439     return;
2440   }
2441
2442   PrepareAndSetAccessibilityViewportInfo();
2443
2444   // Schedule loading the first page.
2445   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
2446       FROM_HERE,
2447       base::BindOnce(&PdfViewWebPlugin::PrepareAndSetAccessibilityPageInfo,
2448                      weak_factory_.GetWeakPtr(), /*page_index=*/0),
2449       kAccessibilityPageDelay);
2450 }
2451
2452 }  // namespace chrome_pdf