blob: 0551bb7d954fed3212097d958f12d7f47cd3efef [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/pdf_view_web_plugin.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/i18n/string_search.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/thread_annotations.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/values.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "net/cookies/site_for_cookies.h"
#include "pdf/accessibility_structs.h"
#include "pdf/mojom/pdf.mojom.h"
#include "pdf/parsed_params.h"
#include "pdf/pdf_engine.h"
#include "pdf/pdf_init.h"
#include "pdf/pdfium/pdfium_engine.h"
#include "pdf/post_message_receiver.h"
#include "pdf/ppapi_migration/bitmap.h"
#include "pdf/ppapi_migration/graphics.h"
#include "pdf/ppapi_migration/result_codes.h"
#include "pdf/ppapi_migration/url_loader.h"
#include "printing/metafile_skia.h"
#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/metrics/document_update_reason.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-shared.h"
#include "third_party/blink/public/platform/web_input_event_result.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_text_input_type.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/web_associated_url_loader.h"
#include "third_party/blink/public/web/web_associated_url_loader_options.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_plugin_container.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#include "third_party/blink/public/web/web_print_preset_options.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/skia_util.h"
#include "url/gurl.h"
#include "v8/include/v8.h"
namespace chrome_pdf {
namespace {
// Initialization performed per renderer process. Initialization may be
// triggered from multiple plugin instances, but should only execute once.
//
// TODO(crbug.com/1123621): We may be able to simplify this once we've figured
// out exactly which processes need to initialize and shutdown PDFium.
class PerProcessInitializer final {
public:
static PerProcessInitializer& GetInstance() {
static base::NoDestructor<PerProcessInitializer,
base::AllowForTriviallyDestructibleType>
instance;
return *instance;
}
void Acquire() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_GE(init_count_, 0);
if (init_count_++ > 0)
return;
DCHECK(!IsSDKInitializedViaPlugin());
InitializeSDK(/*enable_v8=*/true, FontMappingMode::kBlink);
SetIsSDKInitializedViaPlugin(true);
}
void Release() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_GT(init_count_, 0);
if (--init_count_ > 0)
return;
DCHECK(IsSDKInitializedViaPlugin());
ShutdownSDK();
SetIsSDKInitializedViaPlugin(false);
}
private:
int init_count_ GUARDED_BY_CONTEXT(thread_checker_) = 0;
// TODO(crbug.com/1123731): Assuming PDFium is thread-hostile for now, and
// must use one thread exclusively.
THREAD_CHECKER(thread_checker_);
};
class BlinkContainerWrapper final : public PdfViewWebPlugin::ContainerWrapper {
public:
explicit BlinkContainerWrapper(blink::WebPluginContainer* container)
: container_(container) {
DCHECK(container_);
}
BlinkContainerWrapper(const BlinkContainerWrapper&) = delete;
BlinkContainerWrapper& operator=(const BlinkContainerWrapper&) = delete;
~BlinkContainerWrapper() override = default;
void Invalidate() override { container_->Invalidate(); }
void ReportFindInPageMatchCount(int identifier,
int total,
bool final_update) override {
container_->ReportFindInPageMatchCount(identifier, total, final_update);
}
void ReportFindInPageSelection(int identifier, int index) override {
container_->ReportFindInPageSelection(identifier, index);
}
float DeviceScaleFactor() const override {
return container_->DeviceScaleFactor();
}
void SetReferrerForRequest(blink::WebURLRequest& request,
const blink::WebURL& referrer_url) override {
GetFrame()->SetReferrerForRequest(request, referrer_url);
}
void Alert(const blink::WebString& message) override {
GetFrame()->Alert(message);
}
bool Confirm(const blink::WebString& message) override {
return GetFrame()->Confirm(message);
}
blink::WebString Prompt(const blink::WebString& message,
const blink::WebString& default_value) override {
return GetFrame()->Prompt(message, default_value);
}
void TextSelectionChanged(const blink::WebString& selection_text,
uint32_t offset,
const gfx::Range& range) override {
GetFrame()->TextSelectionChanged(selection_text, offset, range);
}
std::unique_ptr<blink::WebAssociatedURLLoader> CreateAssociatedURLLoader(
const blink::WebAssociatedURLLoaderOptions& options) override {
return GetFrame()->CreateAssociatedURLLoader(options);
}
void UpdateTextInputState() override {
// `widget` is null in Print Preview.
auto* widget = GetFrame()->FrameWidget();
if (widget)
widget->UpdateTextInputState();
}
blink::WebLocalFrame* GetFrame() override {
return container_->GetDocument().GetFrame();
}
blink::WebLocalFrameClient* GetWebLocalFrameClient() override {
return GetFrame()->Client();
}
blink::WebPluginContainer* Container() override { return container_; }
private:
blink::WebPluginContainer* const container_;
};
} // namespace
PdfViewWebPlugin::PdfViewWebPlugin(
mojo::AssociatedRemote<pdf::mojom::PdfService> pdf_service_remote,
std::unique_ptr<PrintClient> print_client,
const blink::WebPluginParams& params)
: pdf_service_remote_(std::move(pdf_service_remote)),
print_client_(std::move(print_client)),
initial_params_(params) {}
PdfViewWebPlugin::~PdfViewWebPlugin() = default;
bool PdfViewWebPlugin::Initialize(blink::WebPluginContainer* container) {
DCHECK_EQ(container->Plugin(), this);
return InitializeCommon(std::make_unique<BlinkContainerWrapper>(container));
}
bool PdfViewWebPlugin::InitializeForTesting(
std::unique_ptr<ContainerWrapper> container_wrapper) {
return InitializeCommon(std::move(container_wrapper));
}
// Modeled on `OutOfProcessInstance::Init()`.
bool PdfViewWebPlugin::InitializeCommon(
std::unique_ptr<ContainerWrapper> container_wrapper) {
container_wrapper_ = std::move(container_wrapper);
// Check if the PDF is being loaded in the PDF chrome extension. We only allow
// the plugin to be loaded in the extension and print preview to avoid
// exposing sensitive APIs directly to external websites.
std::string document_url;
auto* container = Container();
if (container) {
GURL maybe_url(container->GetDocument().Url());
if (maybe_url.is_valid())
document_url = maybe_url.possibly_invalid_spec();
}
base::StringPiece document_url_piece(document_url);
set_is_print_preview(IsPrintPreviewUrl(document_url_piece));
// TODO(crbug.com/1123621): Consider calling ValidateDocumentUrl() or
// something like it once the process model has been finalized.
// Allow the plugin to handle find requests.
if (container)
container->UsePluginAsFindHandler();
absl::optional<ParsedParams> params = ParseWebPluginParams(initial_params_);
// The contents of `initial_params_` are no longer needed.
initial_params_ = {};
if (!params.has_value())
return false;
set_full_frame(params->full_frame);
if (params->background_color.has_value())
SetBackgroundColor(params->background_color.value());
PerProcessInitializer::GetInstance().Acquire();
InitializeEngine(std::make_unique<PDFiumEngine>(this, params->script_option));
LoadUrl(params->src_url, /*is_print_preview=*/false);
set_url(params->original_url);
post_message_sender_.set_container(Container());
return true;
}
void PdfViewWebPlugin::Destroy() {
if (container_wrapper_) {
// Explicitly destroy the PDFEngine during destruction as it may call back
// into this object.
DestroyPreviewEngine();
DestroyEngine();
PerProcessInitializer::GetInstance().Release();
container_wrapper_.reset();
post_message_sender_.set_container(nullptr);
}
delete this;
}
blink::WebPluginContainer* PdfViewWebPlugin::Container() const {
return container_wrapper_ ? container_wrapper_->Container() : nullptr;
}
v8::Local<v8::Object> PdfViewWebPlugin::V8ScriptableObject(
v8::Isolate* isolate) {
if (scriptable_receiver_.IsEmpty()) {
// TODO(crbug.com/1123731): Messages should not be handled on the renderer
// main thread.
scriptable_receiver_.Reset(
isolate,
PostMessageReceiver::Create(isolate, weak_factory_.GetWeakPtr(),
base::SequencedTaskRunnerHandle::Get()));
}
return scriptable_receiver_.Get(isolate);
}
void PdfViewWebPlugin::UpdateAllLifecyclePhases(
blink::DocumentUpdateReason reason) {}
void PdfViewWebPlugin::Paint(cc::PaintCanvas* canvas, const gfx::Rect& rect) {
const float inverse_scale = 1.0f / device_scale();
// Clip the intersection of the paint rect and the plugin rect, so that
// painting outside the plugin or the paint rect area can be avoided.
// Note: `invalidate_rect` and `rect` are in CSS pixels. The plugin rect (with
// the device scale applied) must be converted to CSS pixels as well before
// calculating `invalidate_rect`.
SkRect invalidate_rect = gfx::RectToSkRect(gfx::IntersectRects(
gfx::ScaleToEnclosingRectSafe(plugin_rect(), inverse_scale), rect));
cc::PaintCanvasAutoRestore auto_restore(canvas, /*save=*/true);
canvas->clipRect(invalidate_rect);
// Paint with the plugin's background color if the snapshot is not ready.
if (snapshot_.GetSkImageInfo().isEmpty()) {
cc::PaintFlags flags;
flags.setBlendMode(SkBlendMode::kSrc);
flags.setColor(GetBackgroundColor());
canvas->drawRect(invalidate_rect, flags);
return;
}
if (inverse_scale != 1.0f)
canvas->scale(inverse_scale, inverse_scale);
canvas->drawImage(snapshot_, plugin_rect().x(), plugin_rect().y());
}
void PdfViewWebPlugin::UpdateGeometry(const gfx::Rect& window_rect,
const gfx::Rect& clip_rect,
const gfx::Rect& unobscured_rect,
bool is_visible) {
OnViewportChanged(window_rect, container_wrapper_->DeviceScaleFactor());
}
void PdfViewWebPlugin::UpdateFocus(bool focused,
blink::mojom::FocusType focus_type) {}
void PdfViewWebPlugin::UpdateVisibility(bool visibility) {}
blink::WebInputEventResult PdfViewWebPlugin::HandleInputEvent(
const blink::WebCoalescedInputEvent& event,
ui::Cursor* cursor) {
// TODO(crbug.com/702993): The input events received by the Pepper plugin
// already have the device scale applied. The scaling done here should be
// moved into `PdfViewPluginBase::HandleInputEvent()` once the Pepper plugin
// is removed.
std::unique_ptr<blink::WebInputEvent> scaled_event =
ui::ScaleWebInputEvent(event.Event(), device_scale());
const blink::WebInputEvent& event_to_handle =
scaled_event ? *scaled_event : event.Event();
const blink::WebInputEventResult result =
PdfViewPluginBase::HandleInputEvent(event_to_handle)
? blink::WebInputEventResult::kHandledApplication
: blink::WebInputEventResult::kNotHandled;
*cursor = cursor_type();
return result;
}
void PdfViewWebPlugin::DidReceiveResponse(
const blink::WebURLResponse& response) {}
void PdfViewWebPlugin::DidReceiveData(const char* data, size_t data_length) {}
void PdfViewWebPlugin::DidFinishLoading() {}
void PdfViewWebPlugin::DidFailLoading(const blink::WebURLError& error) {}
bool PdfViewWebPlugin::SupportsPaginatedPrint() {
return true;
}
bool PdfViewWebPlugin::GetPrintPresetOptionsFromDocument(
blink::WebPrintPresetOptions* print_preset_options) {
*print_preset_options = GetPrintPresetOptions();
return true;
}
int PdfViewWebPlugin::PrintBegin(const blink::WebPrintParams& print_params) {
return PdfViewPluginBase::PrintBegin(print_params);
}
void PdfViewWebPlugin::PrintPage(int page_number, cc::PaintCanvas* canvas) {
// The entire document goes into one metafile. However, it is impossible to
// know if a call to `PrintPage()` is the last call. Thus, `PrintPage()` just
// stores the pages to print and the metafile. Eventually, the printed output
// is generated in `PrintEnd()` and copied over to the metafile.
// Every `canvas` passed to this method should have a valid `metafile`.
printing::MetafileSkia* metafile = canvas->GetPrintingMetafile();
DCHECK(metafile);
// `pages_to_print_` should be empty iff `printing_metafile_` is not set.
DCHECK_EQ(pages_to_print_.empty(), !printing_metafile_);
// The metafile should be the same across all calls for a given print job.
DCHECK(!printing_metafile_ || (printing_metafile_ == metafile));
if (!printing_metafile_)
printing_metafile_ = metafile;
pages_to_print_.push_back(page_number);
}
void PdfViewWebPlugin::PrintEnd() {
if (pages_to_print_.empty())
return;
printing_metafile_->InitFromData(PrintPages(pages_to_print_));
PdfViewPluginBase::PrintEnd();
printing_metafile_ = nullptr;
pages_to_print_.clear();
}
bool PdfViewWebPlugin::HasSelection() const {
return !selected_text_.IsEmpty();
}
blink::WebString PdfViewWebPlugin::SelectionAsText() const {
return selected_text_;
}
blink::WebString PdfViewWebPlugin::SelectionAsMarkup() const {
return selected_text_;
}
bool PdfViewWebPlugin::CanEditText() const {
return engine()->CanEditText();
}
bool PdfViewWebPlugin::HasEditableText() const {
return engine()->HasEditableText();
}
bool PdfViewWebPlugin::CanUndo() const {
return engine()->CanUndo();
}
bool PdfViewWebPlugin::CanRedo() const {
return engine()->CanRedo();
}
bool PdfViewWebPlugin::ExecuteEditCommand(const blink::WebString& name,
const blink::WebString& value) {
if (name == "SelectAll")
return SelectAll();
if (name == "Cut")
return Cut();
if (name == "Paste" || name == "PasteAndMatchStyle")
return Paste(value);
if (name == "Undo")
return Undo();
if (name == "Redo")
return Redo();
return false;
}
blink::WebURL PdfViewWebPlugin::LinkAtPosition(
const gfx::Point& /*position*/) const {
return GURL(link_under_cursor());
}
bool PdfViewWebPlugin::StartFind(const blink::WebString& search_text,
bool case_sensitive,
int identifier) {
find_identifier_ = identifier;
engine()->StartFind(search_text.Utf8(), case_sensitive);
return true;
}
void PdfViewWebPlugin::SelectFindResult(bool forward, int identifier) {
find_identifier_ = identifier;
engine()->SelectFindResult(forward);
}
void PdfViewWebPlugin::StopFind() {
find_identifier_ = -1;
engine()->StopFind();
// TODO(crbug.com/1199999): Clear tickmarks on scroller when find is
// dismissed.
}
bool PdfViewWebPlugin::CanRotateView() {
return !IsPrintPreview();
}
void PdfViewWebPlugin::RotateView(blink::WebPlugin::RotationType type) {
DCHECK(CanRotateView());
switch (type) {
case blink::WebPlugin::RotationType::k90Clockwise:
engine()->RotateClockwise();
break;
case blink::WebPlugin::RotationType::k90Counterclockwise:
engine()->RotateCounterclockwise();
break;
}
}
blink::WebTextInputType PdfViewWebPlugin::GetPluginTextInputType() {
return text_input_type_;
}
void PdfViewWebPlugin::UpdateCursor(ui::mojom::CursorType new_cursor_type) {
set_cursor_type(new_cursor_type);
}
void PdfViewWebPlugin::UpdateTickMarks(
const std::vector<gfx::Rect>& tickmarks) {}
void PdfViewWebPlugin::NotifyNumberOfFindResultsChanged(int total,
bool final_result) {
// After stopping search and setting `find_identifier_` to -1 there still may
// be a NotifyNumberOfFindResultsChanged notification pending from engine.
// Just ignore them.
if (find_identifier_ == -1 || !container_wrapper_)
return;
container_wrapper_->ReportFindInPageMatchCount(find_identifier_, total,
final_result);
// TODO(crbug.com/1199999): Set tickmarks on scroller.
}
void PdfViewWebPlugin::NotifySelectedFindResultChanged(int current_find_index) {
if (find_identifier_ == -1 || !container_wrapper_)
return;
DCHECK_GE(current_find_index, -1);
container_wrapper_->ReportFindInPageSelection(find_identifier_,
current_find_index + 1);
}
void PdfViewWebPlugin::Alert(const std::string& message) {
container_wrapper_->Alert(blink::WebString::FromUTF8(message));
}
bool PdfViewWebPlugin::Confirm(const std::string& message) {
return container_wrapper_->Confirm(blink::WebString::FromUTF8(message));
}
std::string PdfViewWebPlugin::Prompt(const std::string& question,
const std::string& default_answer) {
return container_wrapper_
->Prompt(blink::WebString::FromUTF8(question),
blink::WebString::FromUTF8(default_answer))
.Utf8();
}
void PdfViewWebPlugin::SubmitForm(const std::string& url,
const void* data,
int length) {}
std::vector<PDFEngine::Client::SearchStringResult>
PdfViewWebPlugin::SearchString(const char16_t* string,
const char16_t* term,
bool case_sensitive) {
base::i18n::RepeatingStringSearch searcher(
/*find_this=*/term, /*in_this=*/string, case_sensitive);
std::vector<SearchStringResult> results;
int match_index;
int match_length;
while (searcher.NextMatchResult(match_index, match_length))
results.push_back({.start_index = match_index, .length = match_length});
return results;
}
void PdfViewWebPlugin::SetSelectedText(const std::string& selected_text) {
selected_text_ = blink::WebString::FromUTF8(selected_text);
container_wrapper_->TextSelectionChanged(
selected_text_, /*offset=*/0, gfx::Range(0, selected_text_.length()));
}
bool PdfViewWebPlugin::IsValidLink(const std::string& url) {
return base::Value(url).is_string();
}
std::unique_ptr<Graphics> PdfViewWebPlugin::CreatePaintGraphics(
const gfx::Size& size) {
// `this` must be valid when creating new graphics. `this` is guaranteed to
// outlive `graphics`; the implemented client interface owns the paint manager
// in which the graphics device exists.
auto graphics = SkiaGraphics::Create(this, size);
DCHECK(graphics);
return graphics;
}
bool PdfViewWebPlugin::BindPaintGraphics(Graphics& graphics) {
InvalidatePluginContainer();
return false;
}
void PdfViewWebPlugin::ScheduleTaskOnMainThread(const base::Location& from_here,
ResultCallback callback,
int32_t result,
base::TimeDelta delay) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
from_here, base::BindOnce(std::move(callback), result), delay);
}
bool PdfViewWebPlugin::IsValid() const {
return container_wrapper_ && container_wrapper_->GetFrame();
}
blink::WebURL PdfViewWebPlugin::CompleteURL(
const blink::WebString& partial_url) const {
DCHECK(IsValid());
return Container()->GetDocument().CompleteURL(partial_url);
}
net::SiteForCookies PdfViewWebPlugin::SiteForCookies() const {
DCHECK(IsValid());
return Container()->GetDocument().SiteForCookies();
}
void PdfViewWebPlugin::SetReferrerForRequest(
blink::WebURLRequest& request,
const blink::WebURL& referrer_url) {
container_wrapper_->SetReferrerForRequest(request, referrer_url);
}
std::unique_ptr<blink::WebAssociatedURLLoader>
PdfViewWebPlugin::CreateAssociatedURLLoader(
const blink::WebAssociatedURLLoaderOptions& options) {
return container_wrapper_->CreateAssociatedURLLoader(options);
}
void PdfViewWebPlugin::OnMessage(const base::Value& message) {
PdfViewPluginBase::HandleMessage(message);
}
void PdfViewWebPlugin::UpdateSnapshot(sk_sp<SkImage> snapshot) {
snapshot_ =
cc::PaintImageBuilder::WithDefault()
.set_image(std::move(snapshot), cc::PaintImage::GetNextContentId())
.set_id(cc::PaintImage::GetNextId())
.TakePaintImage();
if (!plugin_rect().IsEmpty())
InvalidatePluginContainer();
}
base::WeakPtr<PdfViewPluginBase> PdfViewWebPlugin::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
std::unique_ptr<UrlLoader> PdfViewWebPlugin::CreateUrlLoaderInternal() {
auto loader = std::make_unique<BlinkUrlLoader>(weak_factory_.GetWeakPtr());
loader->GrantUniversalAccess();
return loader;
}
// Modeled on `OutOfProcessInstance::DidOpen()`.
void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader,
int32_t result) {
if (result == Result::kSuccess) {
if (!engine()->HandleDocumentLoad(std::move(loader)))
DocumentLoadFailed();
} else {
NOTIMPLEMENTED();
}
}
void PdfViewWebPlugin::SendMessage(base::Value message) {
post_message_sender_.Post(std::move(message));
}
void PdfViewWebPlugin::SaveAs() {
auto* service = GetPdfService();
if (!service)
return;
service->SaveUrlAs(GURL(GetURL().c_str()),
network::mojom::ReferrerPolicy::kDefault);
}
void PdfViewWebPlugin::InitImageData(const gfx::Size& size) {
mutable_image_data() = CreateN32PremulSkBitmap(gfx::SizeToSkISize(size));
}
void PdfViewWebPlugin::SetFormFieldInFocus(bool in_focus) {
text_input_type_ = in_focus ? blink::WebTextInputType::kWebTextInputTypeText
: blink::WebTextInputType::kWebTextInputTypeNone;
container_wrapper_->UpdateTextInputState();
}
// TODO(https://crbug.com/1144444): Add a Pepper-free implementation to set
// accessibility document information.
void PdfViewWebPlugin::SetAccessibilityDocInfo(
const AccessibilityDocInfo& doc_info) {
NOTIMPLEMENTED();
}
// TODO(https://crbug.com/1144444): Add a Pepper-free implementation to set
// accessibility page information.
void PdfViewWebPlugin::SetAccessibilityPageInfo(
AccessibilityPageInfo page_info,
std::vector<AccessibilityTextRunInfo> text_runs,
std::vector<AccessibilityCharInfo> chars,
AccessibilityPageObjects page_objects) {
NOTIMPLEMENTED();
}
// TODO(https://crbug.com/1144444): Add a Pepper-free implementation to set
// accessibility viewport information.
void PdfViewWebPlugin::SetAccessibilityViewportInfo(
const AccessibilityViewportInfo& viewport_info) {
NOTIMPLEMENTED();
}
void PdfViewWebPlugin::SetContentRestrictions(int content_restrictions) {
auto* service = GetPdfService();
if (!service)
return;
service->UpdateContentRestrictions(content_restrictions);
}
void PdfViewWebPlugin::SetPluginCanSave(bool can_save) {
auto* service = GetPdfService();
if (!service)
return;
service->SetPluginCanSave(can_save);
}
void PdfViewWebPlugin::PluginDidStartLoading() {
auto* client = container_wrapper_->GetWebLocalFrameClient();
if (!client)
return;
client->DidStartLoading();
}
void PdfViewWebPlugin::PluginDidStopLoading() {
auto* client = container_wrapper_->GetWebLocalFrameClient();
if (!client)
return;
client->DidStopLoading();
}
void PdfViewWebPlugin::InvokePrintDialog() {
ScheduleTaskOnMainThread(
FROM_HERE,
base::BindOnce(&PdfViewWebPlugin::OnInvokePrintDialog,
weak_factory_.GetWeakPtr()),
/*result=*/0, base::TimeDelta());
}
void PdfViewWebPlugin::NotifySelectionChanged(const gfx::PointF& left,
int left_height,
const gfx::PointF& right,
int right_height) {
auto* service = GetPdfService();
if (!service)
return;
service->SelectionChanged(left, left_height, right, right_height);
}
void PdfViewWebPlugin::NotifyUnsupportedFeature() {
DCHECK(full_frame());
GetPdfService()->HasUnsupportedFeature();
}
void PdfViewWebPlugin::UserMetricsRecordAction(const std::string& action) {
base::RecordAction(base::UserMetricsAction(action.c_str()));
}
void PdfViewWebPlugin::OnViewportChanged(const gfx::Rect& view_rect,
float new_device_scale) {
UpdateGeometryOnViewChanged(view_rect, new_device_scale);
if (IsPrintPreview() && !stop_scrolling()) {
DCHECK_EQ(new_device_scale, device_scale());
gfx::ScrollOffset scroll_offset =
container_wrapper_->GetFrame()->GetScrollOffset();
scroll_offset.Scale(device_scale());
set_scroll_position(gfx::Point(scroll_offset.x(), scroll_offset.y()));
UpdateScroll();
}
// Scrolling in the main PDF Viewer UI is already handled by
// `HandleUpdateScrollMessage()`.
}
void PdfViewWebPlugin::InvalidatePluginContainer() {
container_wrapper_->Invalidate();
}
bool PdfViewWebPlugin::SelectAll() {
if (!CanEditText())
return false;
engine()->SelectAll();
return true;
}
bool PdfViewWebPlugin::Cut() {
if (!HasSelection() || !CanEditText())
return false;
engine()->ReplaceSelection("");
return true;
}
bool PdfViewWebPlugin::Paste(const blink::WebString& value) {
if (!CanEditText())
return false;
engine()->ReplaceSelection(value.Utf8());
return true;
}
bool PdfViewWebPlugin::Undo() {
if (!CanUndo())
return false;
engine()->Undo();
return true;
}
bool PdfViewWebPlugin::Redo() {
if (!CanRedo())
return false;
engine()->Redo();
return true;
}
void PdfViewWebPlugin::OnInvokePrintDialog(int32_t /*result*/) {
if (!print_client_)
return;
print_client_->Print(Container()->GetElement());
}
pdf::mojom::PdfService* PdfViewWebPlugin::GetPdfService() {
return pdf_service_remote_.is_bound() ? pdf_service_remote_.get() : nullptr;
}
} // namespace chrome_pdf