pdf_view_plugin_base.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "pdf/pdf_view_plugin_base.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <iterator>
  8. #include <memory>
  9. #include <sstream>
  10. #include <string>
  11. #include <utility>
  12. #include "base/auto_reset.h"
  13. #include "base/bind.h"
  14. #include "base/callback.h"
  15. #include "base/check.h"
  16. #include "base/check_op.h"
  17. #include "base/containers/span.h"
  18. #include "base/cxx17_backports.h"
  19. #include "base/feature_list.h"
  20. #include "base/i18n/rtl.h"
  21. #include "base/location.h"
  22. #include "base/memory/weak_ptr.h"
  23. #include "base/notreached.h"
  24. #include "base/numerics/safe_conversions.h"
  25. #include "base/strings/escape.h"
  26. #include "base/strings/string_piece.h"
  27. #include "base/strings/string_util.h"
  28. #include "base/threading/thread_task_runner_handle.h"
  29. #include "base/time/time.h"
  30. #include "base/values.h"
  31. #include "pdf/accessibility.h"
  32. #include "pdf/accessibility_structs.h"
  33. #include "pdf/buildflags.h"
  34. #include "pdf/content_restriction.h"
  35. #include "pdf/document_layout.h"
  36. #include "pdf/document_metadata.h"
  37. #include "pdf/paint_ready_rect.h"
  38. #include "pdf/pdf_engine.h"
  39. #include "pdf/pdf_features.h"
  40. #include "pdf/pdfium/pdfium_engine.h"
  41. #include "pdf/pdfium/pdfium_form_filler.h"
  42. #include "pdf/ui/file_name.h"
  43. #include "third_party/abseil-cpp/absl/types/optional.h"
  44. #include "third_party/blink/public/common/input/web_input_event.h"
  45. #include "third_party/blink/public/common/input/web_mouse_event.h"
  46. #include "third_party/blink/public/common/input/web_touch_event.h"
  47. #include "third_party/skia/include/core/SkBitmap.h"
  48. #include "third_party/skia/include/core/SkImage.h"
  49. #include "third_party/skia/include/core/SkRefCnt.h"
  50. #include "ui/events/blink/blink_event_util.h"
  51. #include "ui/gfx/geometry/point.h"
  52. #include "ui/gfx/geometry/point_conversions.h"
  53. #include "ui/gfx/geometry/point_f.h"
  54. #include "ui/gfx/geometry/rect.h"
  55. #include "ui/gfx/geometry/size.h"
  56. #include "ui/gfx/geometry/skia_conversions.h"
  57. #include "ui/gfx/geometry/vector2d.h"
  58. #include "ui/gfx/geometry/vector2d_f.h"
  59. #include "url/gurl.h"
  60. namespace chrome_pdf {
  61. namespace {
  62. // A delay to wait between each accessibility page to keep the system
  63. // responsive.
  64. constexpr base::TimeDelta kAccessibilityPageDelay = base::Milliseconds(100);
  65. } // namespace
  66. PdfViewPluginBase::PdfViewPluginBase() = default;
  67. PdfViewPluginBase::~PdfViewPluginBase() = default;
  68. void PdfViewPluginBase::Invalidate(const gfx::Rect& rect) {
  69. if (in_paint_) {
  70. deferred_invalidates_.push_back(rect);
  71. return;
  72. }
  73. gfx::Rect offset_rect = rect + available_area_.OffsetFromOrigin();
  74. paint_manager_.InvalidateRect(offset_rect);
  75. }
  76. void PdfViewPluginBase::DidScroll(const gfx::Vector2d& offset) {
  77. if (!image_data_.drawsNothing())
  78. paint_manager_.ScrollRect(available_area_, offset);
  79. }
  80. void PdfViewPluginBase::ScrollToX(int x_screen_coords) {
  81. const float x_scroll_pos = x_screen_coords / device_scale();
  82. base::Value::Dict message;
  83. message.Set("type", "setScrollPosition");
  84. message.Set("x", static_cast<double>(x_scroll_pos));
  85. SendMessage(std::move(message));
  86. }
  87. void PdfViewPluginBase::ScrollToY(int y_screen_coords) {
  88. const float y_scroll_pos = y_screen_coords / device_scale();
  89. base::Value::Dict message;
  90. message.Set("type", "setScrollPosition");
  91. message.Set("y", static_cast<double>(y_scroll_pos));
  92. SendMessage(std::move(message));
  93. }
  94. void PdfViewPluginBase::ScrollBy(const gfx::Vector2d& delta) {
  95. const float x_delta = delta.x() / device_scale();
  96. const float y_delta = delta.y() / device_scale();
  97. base::Value::Dict message;
  98. message.Set("type", "scrollBy");
  99. message.Set("x", static_cast<double>(x_delta));
  100. message.Set("y", static_cast<double>(y_delta));
  101. SendMessage(std::move(message));
  102. }
  103. void PdfViewPluginBase::ScrollToPage(int page) {
  104. if (!engine() || engine()->GetNumberOfPages() == 0)
  105. return;
  106. base::Value::Dict message;
  107. message.Set("type", "goToPage");
  108. message.Set("page", page);
  109. SendMessage(std::move(message));
  110. }
  111. void PdfViewPluginBase::NavigateTo(const std::string& url,
  112. WindowOpenDisposition disposition) {
  113. base::Value::Dict message;
  114. message.Set("type", "navigate");
  115. message.Set("url", url);
  116. message.Set("disposition", static_cast<int>(disposition));
  117. SendMessage(std::move(message));
  118. }
  119. void PdfViewPluginBase::NavigateToDestination(int page,
  120. const float* x,
  121. const float* y,
  122. const float* zoom) {
  123. base::Value::Dict message;
  124. message.Set("type", "navigateToDestination");
  125. message.Set("page", page);
  126. if (x)
  127. message.Set("x", static_cast<double>(*x));
  128. if (y)
  129. message.Set("y", static_cast<double>(*y));
  130. if (zoom)
  131. message.Set("zoom", static_cast<double>(*zoom));
  132. SendMessage(std::move(message));
  133. }
  134. void PdfViewPluginBase::NotifyTouchSelectionOccurred() {
  135. base::Value::Dict message;
  136. message.Set("type", "touchSelectionOccurred");
  137. SendMessage(std::move(message));
  138. }
  139. void PdfViewPluginBase::Email(const std::string& to,
  140. const std::string& cc,
  141. const std::string& bcc,
  142. const std::string& subject,
  143. const std::string& body) {
  144. base::Value::Dict message;
  145. message.Set("type", "email");
  146. message.Set("to", base::EscapeUrlEncodedData(to, false));
  147. message.Set("cc", base::EscapeUrlEncodedData(cc, false));
  148. message.Set("bcc", base::EscapeUrlEncodedData(bcc, false));
  149. message.Set("subject", base::EscapeUrlEncodedData(subject, false));
  150. message.Set("body", base::EscapeUrlEncodedData(body, false));
  151. SendMessage(std::move(message));
  152. }
  153. void PdfViewPluginBase::DocumentLoadComplete() {
  154. DCHECK_EQ(DocumentLoadState::kLoading, document_load_state_);
  155. document_load_state_ = DocumentLoadState::kComplete;
  156. UserMetricsRecordAction("PDF.LoadSuccess");
  157. // Clear the focus state for on-screen keyboards.
  158. FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
  159. if (IsPrintPreview())
  160. OnPrintPreviewLoaded();
  161. OnDocumentLoadComplete();
  162. if (!full_frame())
  163. return;
  164. DidStopLoading();
  165. SetContentRestrictions(GetContentRestrictions());
  166. }
  167. void PdfViewPluginBase::DocumentLoadFailed() {
  168. DCHECK_EQ(DocumentLoadState::kLoading, document_load_state_);
  169. document_load_state_ = DocumentLoadState::kFailed;
  170. UserMetricsRecordAction("PDF.LoadFailure");
  171. // Send a progress value of -1 to indicate a failure.
  172. SendLoadingProgress(-1);
  173. DidStopLoading();
  174. paint_manager_.InvalidateRect(gfx::Rect(plugin_rect().size()));
  175. }
  176. void PdfViewPluginBase::DocumentLoadProgress(uint32_t available,
  177. uint32_t doc_size) {
  178. double progress = 0.0;
  179. if (doc_size > 0) {
  180. progress = 100.0 * static_cast<double>(available) / doc_size;
  181. } else {
  182. // Use heuristics when the document size is unknown.
  183. // Progress logarithmically from 0 to 100M.
  184. static const double kFactor = std::log(100'000'000.0) / 100.0;
  185. if (available > 0)
  186. progress =
  187. std::min(std::log(static_cast<double>(available)) / kFactor, 100.0);
  188. }
  189. // DocumentLoadComplete() will send the 100% load progress.
  190. if (progress >= 100)
  191. return;
  192. // Avoid sending too many progress messages over PostMessage.
  193. if (progress <= last_progress_sent_ + 1)
  194. return;
  195. SendLoadingProgress(progress);
  196. }
  197. void PdfViewPluginBase::FormFieldFocusChange(PDFEngine::FocusFieldType type) {
  198. base::Value::Dict message;
  199. message.Set("type", "formFocusChange");
  200. message.Set("focused", type != PDFEngine::FocusFieldType::kNoFocus);
  201. SendMessage(std::move(message));
  202. SetFormTextFieldInFocus(type == PDFEngine::FocusFieldType::kText);
  203. }
  204. void PdfViewPluginBase::SetIsSelecting(bool is_selecting) {
  205. base::Value::Dict message;
  206. message.Set("type", "setIsSelecting");
  207. message.Set("isSelecting", is_selecting);
  208. SendMessage(std::move(message));
  209. }
  210. void PdfViewPluginBase::SelectionChanged(const gfx::Rect& left,
  211. const gfx::Rect& right) {
  212. gfx::PointF left_point(left.x() + available_area_.x(), left.y());
  213. gfx::PointF right_point(right.x() + available_area_.x(), right.y());
  214. const float inverse_scale = 1.0f / device_scale();
  215. left_point.Scale(inverse_scale);
  216. right_point.Scale(inverse_scale);
  217. NotifySelectionChanged(left_point, left.height() * inverse_scale, right_point,
  218. right.height() * inverse_scale);
  219. if (accessibility_state_ == AccessibilityState::kLoaded)
  220. PrepareAndSetAccessibilityViewportInfo();
  221. }
  222. void PdfViewPluginBase::DocumentFocusChanged(bool document_has_focus) {
  223. base::Value::Dict message;
  224. message.Set("type", "documentFocusChanged");
  225. message.Set("hasFocus", document_has_focus);
  226. SendMessage(std::move(message));
  227. }
  228. void PdfViewPluginBase::SendLoadingProgress(double percentage) {
  229. DCHECK(percentage == -1 || (percentage >= 0 && percentage <= 100));
  230. last_progress_sent_ = percentage;
  231. base::Value::Dict message;
  232. message.Set("type", "loadProgress");
  233. message.Set("progress", percentage);
  234. SendMessage(std::move(message));
  235. }
  236. void PdfViewPluginBase::OnPaint(const std::vector<gfx::Rect>& paint_rects,
  237. std::vector<PaintReadyRect>& ready,
  238. std::vector<gfx::Rect>& pending) {
  239. base::AutoReset<bool> auto_reset_in_paint(&in_paint_, true);
  240. DoPaint(paint_rects, ready, pending);
  241. }
  242. int PdfViewPluginBase::GetContentRestrictions() const {
  243. int content_restrictions = kContentRestrictionCut | kContentRestrictionPaste;
  244. if (!engine()->HasPermission(DocumentPermission::kCopy))
  245. content_restrictions |= kContentRestrictionCopy;
  246. if (!engine()->HasPermission(DocumentPermission::kPrintLowQuality) &&
  247. !engine()->HasPermission(DocumentPermission::kPrintHighQuality)) {
  248. content_restrictions |= kContentRestrictionPrint;
  249. }
  250. return content_restrictions;
  251. }
  252. AccessibilityDocInfo PdfViewPluginBase::GetAccessibilityDocInfo() const {
  253. AccessibilityDocInfo doc_info;
  254. doc_info.page_count = engine()->GetNumberOfPages();
  255. doc_info.text_accessible =
  256. engine()->HasPermission(DocumentPermission::kCopyAccessible);
  257. doc_info.text_copyable = engine()->HasPermission(DocumentPermission::kCopy);
  258. return doc_info;
  259. }
  260. void PdfViewPluginBase::InvalidateAfterPaintDone() {
  261. if (deferred_invalidates_.empty())
  262. return;
  263. base::ThreadTaskRunnerHandle::Get()->PostTask(
  264. FROM_HERE, base::BindOnce(&PdfViewPluginBase::ClearDeferredInvalidates,
  265. GetWeakPtr()));
  266. }
  267. void PdfViewPluginBase::OnGeometryChanged(double old_zoom,
  268. float old_device_scale) {
  269. RecalculateAreas(old_zoom, old_device_scale);
  270. if (accessibility_state_ == AccessibilityState::kLoaded)
  271. PrepareAndSetAccessibilityViewportInfo();
  272. }
  273. void PdfViewPluginBase::RecalculateAreas(double old_zoom,
  274. float old_device_scale) {
  275. if (zoom_ != old_zoom || device_scale() != old_device_scale)
  276. engine()->ZoomUpdated(zoom_ * device_scale());
  277. available_area_ = gfx::Rect(plugin_rect().size());
  278. int doc_width = GetDocumentPixelWidth();
  279. if (doc_width < available_area_.width()) {
  280. // Center the document horizontally inside the plugin rectangle.
  281. available_area_.Offset((plugin_rect().width() - doc_width) / 2, 0);
  282. available_area_.set_width(doc_width);
  283. }
  284. // The distance between top of the plugin and the bottom of the document in
  285. // pixels.
  286. int bottom_of_document = GetDocumentPixelHeight();
  287. if (bottom_of_document < plugin_rect().height())
  288. available_area_.set_height(bottom_of_document);
  289. CalculateBackgroundParts();
  290. engine()->PageOffsetUpdated(available_area_.OffsetFromOrigin());
  291. engine()->PluginSizeUpdated(available_area_.size());
  292. }
  293. void PdfViewPluginBase::CalculateBackgroundParts() {
  294. background_parts_.clear();
  295. int left_width = available_area_.x();
  296. int right_start = available_area_.right();
  297. int right_width = std::abs(plugin_rect().width() - available_area_.right());
  298. int bottom = std::min(available_area_.bottom(), plugin_rect().height());
  299. // Note: we assume the display of the PDF document is always centered
  300. // horizontally, but not necessarily centered vertically.
  301. // Add the left rectangle.
  302. BackgroundPart part = {gfx::Rect(left_width, bottom), GetBackgroundColor()};
  303. if (!part.location.IsEmpty())
  304. background_parts_.push_back(part);
  305. // Add the right rectangle.
  306. part.location = gfx::Rect(right_start, 0, right_width, bottom);
  307. if (!part.location.IsEmpty())
  308. background_parts_.push_back(part);
  309. // Add the bottom rectangle.
  310. part.location = gfx::Rect(0, bottom, plugin_rect().width(),
  311. plugin_rect().height() - bottom);
  312. if (!part.location.IsEmpty())
  313. background_parts_.push_back(part);
  314. }
  315. gfx::PointF PdfViewPluginBase::GetScrollPositionFromOffset(
  316. const gfx::Vector2dF& scroll_offset) const {
  317. gfx::PointF scroll_origin;
  318. // TODO(crbug.com/1140374): Right-to-left scrolling currently is not
  319. // compatible with the PDF viewer's sticky "scroller" element.
  320. if (ui_direction() == base::i18n::RIGHT_TO_LEFT && IsPrintPreview()) {
  321. scroll_origin.set_x(
  322. std::max(document_size_.width() * static_cast<float>(zoom_) -
  323. plugin_dip_size().width(),
  324. 0.0f));
  325. }
  326. return scroll_origin + scroll_offset;
  327. }
  328. int PdfViewPluginBase::GetDocumentPixelWidth() const {
  329. return static_cast<int>(
  330. std::ceil(document_size_.width() * zoom() * device_scale()));
  331. }
  332. int PdfViewPluginBase::GetDocumentPixelHeight() const {
  333. return static_cast<int>(
  334. std::ceil(document_size_.height() * zoom() * device_scale()));
  335. }
  336. void PdfViewPluginBase::PrepareAndSetAccessibilityPageInfo(int32_t page_index) {
  337. // Outdated calls are ignored.
  338. if (page_index != next_accessibility_page_index_)
  339. return;
  340. ++next_accessibility_page_index_;
  341. AccessibilityPageInfo page_info;
  342. std::vector<AccessibilityTextRunInfo> text_runs;
  343. std::vector<AccessibilityCharInfo> chars;
  344. AccessibilityPageObjects page_objects;
  345. if (!GetAccessibilityInfo(engine(), page_index, page_info, text_runs, chars,
  346. page_objects)) {
  347. return;
  348. }
  349. SetAccessibilityPageInfo(std::move(page_info), std::move(text_runs),
  350. std::move(chars), std::move(page_objects));
  351. // Schedule loading the next page.
  352. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  353. FROM_HERE,
  354. base::BindOnce(&PdfViewPluginBase::PrepareAndSetAccessibilityPageInfo,
  355. GetWeakPtr(), page_index + 1),
  356. kAccessibilityPageDelay);
  357. }
  358. void PdfViewPluginBase::PrepareAndSetAccessibilityViewportInfo() {
  359. AccessibilityViewportInfo viewport_info;
  360. viewport_info.offset = gfx::ScaleToFlooredPoint(available_area_.origin(),
  361. 1 / (device_scale() * zoom_));
  362. viewport_info.zoom = zoom_;
  363. viewport_info.scale = device_scale();
  364. viewport_info.focus_info = {FocusObjectType::kNone, 0, 0};
  365. engine()->GetSelection(&viewport_info.selection_start_page_index,
  366. &viewport_info.selection_start_char_index,
  367. &viewport_info.selection_end_page_index,
  368. &viewport_info.selection_end_char_index);
  369. SetAccessibilityViewportInfo(std::move(viewport_info));
  370. }
  371. void PdfViewPluginBase::SetZoom(double scale) {
  372. double old_zoom = zoom_;
  373. zoom_ = scale;
  374. OnGeometryChanged(old_zoom, device_scale());
  375. if (!document_size_.IsEmpty())
  376. paint_manager_.InvalidateRect(gfx::Rect(plugin_rect().size()));
  377. }
  378. void PdfViewPluginBase::DoPaint(const std::vector<gfx::Rect>& paint_rects,
  379. std::vector<PaintReadyRect>& ready,
  380. std::vector<gfx::Rect>& pending) {
  381. if (image_data_.drawsNothing()) {
  382. DCHECK(plugin_rect().IsEmpty());
  383. return;
  384. }
  385. PrepareForFirstPaint(ready);
  386. if (!received_viewport_message() || !needs_reraster())
  387. return;
  388. engine()->PrePaint();
  389. std::vector<gfx::Rect> ready_rects;
  390. for (const gfx::Rect& paint_rect : paint_rects) {
  391. // Intersect with plugin area since there could be pending invalidates from
  392. // when the plugin area was larger.
  393. gfx::Rect rect =
  394. gfx::IntersectRects(paint_rect, gfx::Rect(plugin_rect().size()));
  395. if (rect.IsEmpty())
  396. continue;
  397. // Paint the rendering of the PDF document.
  398. gfx::Rect pdf_rect = gfx::IntersectRects(rect, available_area_);
  399. if (!pdf_rect.IsEmpty()) {
  400. pdf_rect.Offset(-available_area_.x(), 0);
  401. std::vector<gfx::Rect> pdf_ready;
  402. std::vector<gfx::Rect> pdf_pending;
  403. engine()->Paint(pdf_rect, image_data_, pdf_ready, pdf_pending);
  404. for (gfx::Rect& ready_rect : pdf_ready) {
  405. ready_rect.Offset(available_area_.OffsetFromOrigin());
  406. ready_rects.push_back(ready_rect);
  407. }
  408. for (gfx::Rect& pending_rect : pdf_pending) {
  409. pending_rect.Offset(available_area_.OffsetFromOrigin());
  410. pending.push_back(pending_rect);
  411. }
  412. }
  413. // Ensure the region above the first page (if any) is filled;
  414. const int32_t first_page_ypos = 0 == engine()->GetNumberOfPages()
  415. ? 0
  416. : engine()->GetPageScreenRect(0).y();
  417. if (rect.y() < first_page_ypos) {
  418. gfx::Rect region = gfx::IntersectRects(
  419. rect, gfx::Rect(gfx::Size(plugin_rect().width(), first_page_ypos)));
  420. image_data_.erase(GetBackgroundColor(), gfx::RectToSkIRect(region));
  421. ready_rects.push_back(region);
  422. }
  423. // Ensure the background parts are filled.
  424. for (const BackgroundPart& background_part : background_parts_) {
  425. gfx::Rect intersection =
  426. gfx::IntersectRects(background_part.location, rect);
  427. if (!intersection.IsEmpty()) {
  428. image_data_.erase(background_part.color,
  429. gfx::RectToSkIRect(intersection));
  430. ready_rects.push_back(intersection);
  431. }
  432. }
  433. }
  434. engine()->PostPaint();
  435. // TODO(crbug.com/1263614): Write pixels directly to the `SkSurface` in
  436. // `PaintManager`, rather than using an intermediate `SkBitmap` and `SkImage`.
  437. sk_sp<SkImage> painted_image = image_data_.asImage();
  438. for (const gfx::Rect& ready_rect : ready_rects)
  439. ready.emplace_back(ready_rect, painted_image);
  440. InvalidateAfterPaintDone();
  441. }
  442. void PdfViewPluginBase::ClearDeferredInvalidates() {
  443. DCHECK(!in_paint_);
  444. for (const gfx::Rect& rect : deferred_invalidates_)
  445. Invalidate(rect);
  446. deferred_invalidates_.clear();
  447. }
  448. void PdfViewPluginBase::LoadAccessibility() {
  449. accessibility_state_ = AccessibilityState::kLoaded;
  450. // A new document layout will trigger the creation of a new accessibility
  451. // tree, so `next_accessibility_page_index_` should be reset to ignore
  452. // outdated asynchronous calls of PrepareAndSetAccessibilityPageInfo().
  453. next_accessibility_page_index_ = 0;
  454. SetAccessibilityDocInfo(GetAccessibilityDocInfo());
  455. // If the document contents isn't accessible, don't send anything more.
  456. if (!(engine()->HasPermission(DocumentPermission::kCopy) ||
  457. engine()->HasPermission(DocumentPermission::kCopyAccessible))) {
  458. return;
  459. }
  460. PrepareAndSetAccessibilityViewportInfo();
  461. // Schedule loading the first page.
  462. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  463. FROM_HERE,
  464. base::BindOnce(&PdfViewPluginBase::PrepareAndSetAccessibilityPageInfo,
  465. GetWeakPtr(), /*page_index=*/0),
  466. kAccessibilityPageDelay);
  467. }
  468. gfx::Point PdfViewPluginBase::FrameToPdfCoordinates(
  469. const gfx::PointF& frame_coordinates) const {
  470. // TODO(crbug.com/1288847): Use methods on `blink::WebPluginContainer`.
  471. return gfx::ToFlooredPoint(
  472. gfx::ScalePoint(frame_coordinates, device_scale())) -
  473. gfx::Vector2d(available_area_.x(), 0);
  474. }
  475. } // namespace chrome_pdf