// Copyright (c) 2013 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 "chrome/renderer/pepper/pepper_pdf_host.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/printing/print_web_view_helper.h"
#include "content/public/common/referrer.h"
#include "content/public/renderer/pepper_plugin_instance.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "grit/webkit_resources.h"
#include "grit/webkit_strings.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/ppb_image_data_proxy.h"
#include "ppapi/shared_impl/ppb_image_data_shared.h"
#include "ppapi/shared_impl/scoped_pp_resource.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_image_data_api.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebPluginContainer.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/point.h"
namespace {
struct ResourceImageInfo {
PP_ResourceImage pp_id;
int res_id;
};
static const ResourceImageInfo kResourceImageMap[] = {
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP },
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW },
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER,
IDR_PDF_BUTTON_ZOOMIN_END_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED,
IDR_PDF_BUTTON_ZOOMIN_END_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED,
IDR_PDF_BUTTON_ZOOMOUT_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE },
{ PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT },
{ PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER },
{ PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9 },
{ PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND,
IDR_PDF_THUMBNAIL_NUM_BACKGROUND },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8 },
{ PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND,
IDR_PDF_PROGRESS_BAR_BACKGROUND },
{ PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND,
IDR_PDF_PAGE_INDICATOR_BACKGROUND },
{ PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW },
{ PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON },
};
// Valid strings for user metrics actions.
static const char* kValidUserMetricsActions[] = {
"PDF.PrintPage",
"PDF.ZoomFromBrowser",
"PDF.FitToPageButton",
"PDF.FitToWidthButton",
"PDF.ZoomOutButton",
"PDF.ZoomInButton",
"PDF.SaveButton",
"PDF.PrintButton",
"PDF.LoadSuccess",
"PDF.LoadFailure",
"PDF.PreviewDocumentLoadFailure",
};
} // namespace
PepperPDFHost::PepperPDFHost(
content::RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource)
: ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource),
host_(host) {
}
PepperPDFHost::~PepperPDFHost() {
}
int32_t PepperPDFHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
IPC_BEGIN_MESSAGE_MAP(PepperPDFHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetLocalizedString,
OnHostMsgGetLocalizedString)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStartLoading,
OnHostMsgDidStartLoading)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStopLoading,
OnHostMsgDidStopLoading)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_UserMetricsRecordAction,
OnHostMsgUserMetricsRecordAction)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_HasUnsupportedFeature,
OnHostMsgHasUnsupportedFeature)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_Print,
OnHostMsgPrint)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_SaveAs,
OnHostMsgSaveAs)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetResourceImage,
OnHostMsgGetResourceImage)
IPC_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperPDFHost::OnHostMsgGetLocalizedString(
ppapi::host::HostMessageContext* context,
PP_ResourceString string_id) {
std::string rv;
if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) {
rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD));
} else if (string_id == PP_RESOURCESTRING_PDFLOADING) {
rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING));
} else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) {
rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED));
} else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) {
rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING));
} else {
NOTREACHED();
return PP_ERROR_FAILED;
}
context->reply_msg = PpapiPluginMsg_PDF_GetLocalizedStringReply(rv);
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgDidStartLoading(
ppapi::host::HostMessageContext* context) {
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
instance->GetRenderView()->DidStartLoading();
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgDidStopLoading(
ppapi::host::HostMessageContext* context) {
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
instance->GetRenderView()->DidStopLoading();
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgSetContentRestriction(
ppapi::host::HostMessageContext* context, int restrictions) {
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
instance->GetRenderView()->Send(
new ChromeViewHostMsg_PDFUpdateContentRestrictions(
instance->GetRenderView()->GetRoutingID(), restrictions));
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgUserMetricsRecordAction(
ppapi::host::HostMessageContext* context,
const std::string& action) {
bool valid = false;
for (size_t i = 0; i < arraysize(kValidUserMetricsActions); ++i) {
if (action == kValidUserMetricsActions[i]) {
valid = true;
break;
}
}
if (!valid) {
NOTREACHED();
return PP_ERROR_FAILED;
}
content::RenderThread::Get()->RecordComputedAction(action);
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgHasUnsupportedFeature(
ppapi::host::HostMessageContext* context) {
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
// Only want to show an info bar if the pdf is the whole tab.
if (!instance->IsFullPagePlugin())
return PP_OK;
blink::WebView* view =
instance->GetContainer()->element().document().frame()->view();
content::RenderView* render_view = content::RenderView::FromWebView(view);
render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature(
render_view->GetRoutingID()));
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgPrint(
ppapi::host::HostMessageContext* context) {
#if defined(ENABLE_FULL_PRINTING)
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
blink::WebElement element = instance->GetContainer()->element();
blink::WebView* view = element.document().frame()->view();
content::RenderView* render_view = content::RenderView::FromWebView(view);
using printing::PrintWebViewHelper;
PrintWebViewHelper* print_view_helper = PrintWebViewHelper::Get(render_view);
if (print_view_helper) {
print_view_helper->PrintNode(element);
return PP_OK;
}
#endif
return PP_ERROR_FAILED;
}
int32_t PepperPDFHost::OnHostMsgSaveAs(
ppapi::host::HostMessageContext* context) {
content::PepperPluginInstance* instance =
host_->GetPluginInstance(pp_instance());
if (!instance)
return PP_ERROR_FAILED;
GURL url = instance->GetPluginURL();
content::RenderView* render_view = instance->GetRenderView();
blink::WebFrame* frame = render_view->GetWebView()->mainFrame();
content::Referrer referrer(frame->document().url(),
frame->document().referrerPolicy());
render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs(
render_view->GetRoutingID(), url, referrer));
return PP_OK;
}
int32_t PepperPDFHost::OnHostMsgGetResourceImage(
ppapi::host::HostMessageContext* context,
PP_ResourceImage image_id,
float scale) {
int res_id = 0;
for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) {
if (kResourceImageMap[i].pp_id == image_id) {
res_id = kResourceImageMap[i].res_id;
break;
}
}
if (res_id == 0)
return PP_ERROR_FAILED;
gfx::ImageSkia* res_image_skia =
ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);
if (!res_image_skia)
return PP_ERROR_FAILED;
gfx::ImageSkiaRep image_skia_rep = res_image_skia->GetRepresentation(scale);
if (image_skia_rep.is_null() || image_skia_rep.scale() != scale)
return PP_ERROR_FAILED;
PP_Size pp_size;
pp_size.width = image_skia_rep.pixel_width();
pp_size.height = image_skia_rep.pixel_height();
ppapi::HostResource host_resource;
PP_ImageDataDesc image_data_desc;
IPC::PlatformFileForTransit image_handle;
uint32_t byte_count = 0;
bool success = CreateImageData(
pp_instance(),
ppapi::PPB_ImageData_Shared::GetNativeImageDataFormat(),
pp_size,
image_skia_rep.sk_bitmap(),
&host_resource,
&image_data_desc,
&image_handle,
&byte_count);
ppapi::ScopedPPResource image_data_resource(
ppapi::ScopedPPResource::PassRef(), host_resource.host_resource());
if (!success)
return PP_ERROR_FAILED;
ppapi::host::ReplyMessageContext reply_context =
context->MakeReplyMessageContext();
ppapi::proxy::SerializedHandle serialized_handle;
serialized_handle.set_shmem(image_handle, byte_count);
reply_context.params.AppendHandle(serialized_handle);
SendReply(reply_context,
PpapiPluginMsg_PDF_GetResourceImageReply(host_resource,
image_data_desc));
// Keep a reference to the resource only if the function succeeds.
image_data_resource.Release();
return PP_OK_COMPLETIONPENDING;
}
// TODO(raymes): This function is mainly copied from ppb_image_data_proxy.cc.
// It's a mess and needs to be fixed in several ways but this is better done
// when we refactor PPB_ImageData. On success, the image handle will be
// non-null.
bool PepperPDFHost::CreateImageData(
PP_Instance instance,
PP_ImageDataFormat format,
const PP_Size& size,
const SkBitmap& pixels_to_write,
ppapi::HostResource* result,
PP_ImageDataDesc* out_image_data_desc,
IPC::PlatformFileForTransit* out_image_handle,
uint32_t* out_byte_count) {
PP_Resource resource = ppapi::proxy::PPB_ImageData_Proxy::CreateImageData(
instance,
ppapi::PPB_ImageData_Shared::SIMPLE,
format, size,
false /* init_to_zero */,
out_image_data_desc, out_image_handle, out_byte_count);
if (!resource)
return false;
result->SetHostResource(instance, resource);
// Write the image to the resource shared memory.
ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_ImageData_API>
enter_resource(resource, false);
if (enter_resource.failed())
return false;
ppapi::thunk::PPB_ImageData_API* image_data =
static_cast<ppapi::thunk::PPB_ImageData_API*>(enter_resource.object());
SkCanvas* canvas = image_data->GetCanvas();
bool needs_unmapping = false;
if (!canvas) {
needs_unmapping = true;
image_data->Map();
canvas = image_data->GetCanvas();
if (!canvas)
return false; // Failure mapping.
}
const SkBitmap* bitmap = &skia::GetTopDevice(*canvas)->accessBitmap(false);
pixels_to_write.copyPixelsTo(bitmap->getPixels(),
bitmap->getSize(),
bitmap->rowBytes());
if (needs_unmapping)
image_data->Unmap();
return true;
}