Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2013 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 
      5 #include "chrome/renderer/pepper/pepper_pdf_host.h"
      6 
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "chrome/common/render_messages.h"
      9 #include "chrome/renderer/printing/print_web_view_helper.h"
     10 #include "content/public/common/referrer.h"
     11 #include "content/public/renderer/pepper_plugin_instance.h"
     12 #include "content/public/renderer/render_thread.h"
     13 #include "content/public/renderer/render_view.h"
     14 #include "content/public/renderer/renderer_ppapi_host.h"
     15 #include "grit/webkit_resources.h"
     16 #include "grit/webkit_strings.h"
     17 #include "ppapi/host/dispatch_host_message.h"
     18 #include "ppapi/host/host_message_context.h"
     19 #include "ppapi/host/ppapi_host.h"
     20 #include "ppapi/proxy/host_dispatcher.h"
     21 #include "ppapi/proxy/ppapi_messages.h"
     22 #include "ppapi/proxy/ppb_image_data_proxy.h"
     23 #include "ppapi/shared_impl/ppb_image_data_shared.h"
     24 #include "ppapi/shared_impl/scoped_pp_resource.h"
     25 #include "ppapi/thunk/enter.h"
     26 #include "ppapi/thunk/ppb_image_data_api.h"
     27 #include "skia/ext/platform_canvas.h"
     28 #include "third_party/WebKit/public/web/WebDocument.h"
     29 #include "third_party/WebKit/public/web/WebElement.h"
     30 #include "third_party/WebKit/public/web/WebFrame.h"
     31 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     32 #include "third_party/WebKit/public/web/WebView.h"
     33 #include "third_party/skia/include/core/SkBitmap.h"
     34 #include "ui/base/l10n/l10n_util.h"
     35 #include "ui/base/layout.h"
     36 #include "ui/base/resource/resource_bundle.h"
     37 #include "ui/gfx/image/image_skia.h"
     38 #include "ui/gfx/image/image_skia_rep.h"
     39 #include "ui/gfx/point.h"
     40 
     41 namespace chrome {
     42 
     43 namespace {
     44 
     45 struct ResourceImageInfo {
     46   PP_ResourceImage pp_id;
     47   int res_id;
     48 };
     49 
     50 static const ResourceImageInfo kResourceImageMap[] = {
     51   { PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP },
     52   { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER },
     53   { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED },
     54   { PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW },
     55   { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER },
     56   { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED },
     57   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END },
     58   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER,
     59       IDR_PDF_BUTTON_ZOOMIN_END_HOVER },
     60   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED,
     61       IDR_PDF_BUTTON_ZOOMIN_END_PRESSED },
     62   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN },
     63   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER },
     64   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED },
     65   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT },
     66   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER },
     67   { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED,
     68       IDR_PDF_BUTTON_ZOOMOUT_PRESSED },
     69   { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE },
     70   { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER },
     71   { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED },
     72   { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT },
     73   { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER },
     74   { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED },
     75   { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED },
     76   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0 },
     77   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1 },
     78   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2 },
     79   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3 },
     80   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4 },
     81   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5 },
     82   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6 },
     83   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7 },
     84   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8 },
     85   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9 },
     86   { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND,
     87       IDR_PDF_THUMBNAIL_NUM_BACKGROUND },
     88   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0 },
     89   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1 },
     90   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2 },
     91   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3 },
     92   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4 },
     93   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5 },
     94   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6 },
     95   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7 },
     96   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8 },
     97   { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND,
     98       IDR_PDF_PROGRESS_BAR_BACKGROUND },
     99   { PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND,
    100       IDR_PDF_PAGE_INDICATOR_BACKGROUND },
    101   { PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW },
    102   { PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON },
    103 };
    104 
    105 // Valid strings for user metrics actions.
    106 static const char* kValidUserMetricsActions[] = {
    107   "PDF.PrintPage",
    108   "PDF.ZoomFromBrowser",
    109   "PDF.FitToPageButton",
    110   "PDF.FitToWidthButton",
    111   "PDF.ZoomOutButton",
    112   "PDF.ZoomInButton",
    113   "PDF.SaveButton",
    114   "PDF.PrintButton",
    115   "PDF.LoadSuccess",
    116   "PDF.LoadFailure",
    117   "PDF.PreviewDocumentLoadFailure",
    118 };
    119 
    120 }  // namespace
    121 
    122 PepperPDFHost::PepperPDFHost(
    123     content::RendererPpapiHost* host,
    124     PP_Instance instance,
    125     PP_Resource resource)
    126     : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource),
    127       host_(host) {
    128 }
    129 
    130 PepperPDFHost::~PepperPDFHost() {
    131 }
    132 
    133 int32_t PepperPDFHost::OnResourceMessageReceived(
    134     const IPC::Message& msg,
    135     ppapi::host::HostMessageContext* context) {
    136   IPC_BEGIN_MESSAGE_MAP(PepperPDFHost, msg)
    137     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetLocalizedString,
    138                                       OnHostMsgGetLocalizedString)
    139     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStartLoading,
    140                                         OnHostMsgDidStartLoading)
    141     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStopLoading,
    142                                       OnHostMsgDidStopLoading)
    143     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_UserMetricsRecordAction,
    144                                       OnHostMsgUserMetricsRecordAction)
    145     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_HasUnsupportedFeature,
    146                                         OnHostMsgHasUnsupportedFeature)
    147     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_Print,
    148                                       OnHostMsgPrint)
    149     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_SaveAs,
    150                                       OnHostMsgSaveAs)
    151     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetResourceImage,
    152                                       OnHostMsgGetResourceImage)
    153   IPC_END_MESSAGE_MAP()
    154   return PP_ERROR_FAILED;
    155 }
    156 
    157 int32_t PepperPDFHost::OnHostMsgGetLocalizedString(
    158     ppapi::host::HostMessageContext* context,
    159     PP_ResourceString string_id) {
    160   std::string rv;
    161   if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) {
    162     rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD));
    163   } else if (string_id == PP_RESOURCESTRING_PDFLOADING) {
    164     rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING));
    165   } else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) {
    166     rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED));
    167   } else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) {
    168     rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING));
    169   } else {
    170     NOTREACHED();
    171     return PP_ERROR_FAILED;
    172   }
    173 
    174   context->reply_msg = PpapiPluginMsg_PDF_GetLocalizedStringReply(rv);
    175   return PP_OK;
    176 }
    177 
    178 int32_t PepperPDFHost::OnHostMsgDidStartLoading(
    179     ppapi::host::HostMessageContext* context) {
    180   content::PepperPluginInstance* instance =
    181       host_->GetPluginInstance(pp_instance());
    182   if (!instance)
    183     return PP_ERROR_FAILED;
    184   instance->GetRenderView()->DidStartLoading();
    185   return PP_OK;
    186 }
    187 
    188 int32_t PepperPDFHost::OnHostMsgDidStopLoading(
    189     ppapi::host::HostMessageContext* context) {
    190   content::PepperPluginInstance* instance =
    191       host_->GetPluginInstance(pp_instance());
    192   if (!instance)
    193     return PP_ERROR_FAILED;
    194   instance->GetRenderView()->DidStopLoading();
    195   return PP_OK;
    196 }
    197 
    198 int32_t PepperPDFHost::OnHostMsgSetContentRestriction(
    199     ppapi::host::HostMessageContext* context, int restrictions) {
    200   content::PepperPluginInstance* instance =
    201       host_->GetPluginInstance(pp_instance());
    202   if (!instance)
    203     return PP_ERROR_FAILED;
    204   instance->GetRenderView()->Send(
    205       new ChromeViewHostMsg_PDFUpdateContentRestrictions(
    206           instance->GetRenderView()->GetRoutingID(), restrictions));
    207   return PP_OK;
    208 }
    209 
    210 int32_t PepperPDFHost::OnHostMsgUserMetricsRecordAction(
    211     ppapi::host::HostMessageContext* context,
    212     const std::string& action) {
    213   bool valid = false;
    214   for (size_t i = 0; i < arraysize(kValidUserMetricsActions); ++i) {
    215     if (action == kValidUserMetricsActions[i]) {
    216       valid = true;
    217       break;
    218     }
    219   }
    220   if (!valid) {
    221     NOTREACHED();
    222     return PP_ERROR_FAILED;
    223   }
    224   content::RenderThread::Get()->RecordUserMetrics(action);
    225   return PP_OK;
    226 }
    227 
    228 int32_t PepperPDFHost::OnHostMsgHasUnsupportedFeature(
    229     ppapi::host::HostMessageContext* context) {
    230   content::PepperPluginInstance* instance =
    231       host_->GetPluginInstance(pp_instance());
    232   if (!instance)
    233     return PP_ERROR_FAILED;
    234 
    235   // Only want to show an info bar if the pdf is the whole tab.
    236   if (!instance->IsFullPagePlugin())
    237     return PP_OK;
    238 
    239   WebKit::WebView* view =
    240       instance->GetContainer()->element().document().frame()->view();
    241   content::RenderView* render_view = content::RenderView::FromWebView(view);
    242   render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature(
    243       render_view->GetRoutingID()));
    244   return PP_OK;
    245 }
    246 
    247 int32_t PepperPDFHost::OnHostMsgPrint(
    248     ppapi::host::HostMessageContext* context) {
    249 #if defined(ENABLE_FULL_PRINTING)
    250   content::PepperPluginInstance* instance =
    251       host_->GetPluginInstance(pp_instance());
    252   if (!instance)
    253     return PP_ERROR_FAILED;
    254 
    255   WebKit::WebElement element = instance->GetContainer()->element();
    256   WebKit::WebView* view = element.document().frame()->view();
    257   content::RenderView* render_view = content::RenderView::FromWebView(view);
    258 
    259   using printing::PrintWebViewHelper;
    260   PrintWebViewHelper* print_view_helper = PrintWebViewHelper::Get(render_view);
    261   if (print_view_helper) {
    262     print_view_helper->PrintNode(element);
    263     return PP_OK;
    264   }
    265 #endif
    266   return PP_ERROR_FAILED;
    267 }
    268 
    269 int32_t PepperPDFHost::OnHostMsgSaveAs(
    270     ppapi::host::HostMessageContext* context) {
    271   content::PepperPluginInstance* instance =
    272       host_->GetPluginInstance(pp_instance());
    273   if (!instance)
    274     return PP_ERROR_FAILED;
    275   GURL url = instance->GetPluginURL();
    276   content::RenderView* render_view = instance->GetRenderView();
    277   WebKit::WebFrame* frame = render_view->GetWebView()->mainFrame();
    278   content::Referrer referrer(frame->document().url(),
    279                              frame->document().referrerPolicy());
    280   render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs(
    281       render_view->GetRoutingID(), url, referrer));
    282   return PP_OK;
    283 }
    284 
    285 int32_t PepperPDFHost::OnHostMsgGetResourceImage(
    286     ppapi::host::HostMessageContext* context,
    287     PP_ResourceImage image_id,
    288     float scale) {
    289   int res_id = 0;
    290   for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) {
    291     if (kResourceImageMap[i].pp_id == image_id) {
    292       res_id = kResourceImageMap[i].res_id;
    293       break;
    294     }
    295   }
    296   if (res_id == 0)
    297     return PP_ERROR_FAILED;
    298 
    299   ui::ScaleFactor scale_factor = ui::GetScaleFactorFromScale(scale);
    300 
    301   gfx::ImageSkia* res_image_skia =
    302       ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);
    303 
    304   if (!res_image_skia)
    305     return PP_ERROR_FAILED;
    306 
    307   gfx::ImageSkiaRep image_skia_rep = res_image_skia->GetRepresentation(
    308       scale_factor);
    309 
    310   if (image_skia_rep.is_null() || image_skia_rep.scale_factor() != scale_factor)
    311     return PP_ERROR_FAILED;
    312 
    313   PP_Size pp_size;
    314   pp_size.width = image_skia_rep.pixel_width();
    315   pp_size.height = image_skia_rep.pixel_height();
    316 
    317   ppapi::HostResource host_resource;
    318   PP_ImageDataDesc image_data_desc;
    319   IPC::PlatformFileForTransit image_handle;
    320   uint32_t byte_count = 0;
    321   bool success = CreateImageData(
    322       pp_instance(),
    323       ppapi::PPB_ImageData_Shared::GetNativeImageDataFormat(),
    324       pp_size,
    325       image_skia_rep.sk_bitmap(),
    326       &host_resource,
    327       &image_data_desc,
    328       &image_handle,
    329       &byte_count);
    330   ppapi::ScopedPPResource image_data_resource(
    331       ppapi::ScopedPPResource::PassRef(), host_resource.host_resource());
    332   if (!success)
    333     return PP_ERROR_FAILED;
    334 
    335   ppapi::host::ReplyMessageContext reply_context =
    336       context->MakeReplyMessageContext();
    337   ppapi::proxy::SerializedHandle serialized_handle;
    338   serialized_handle.set_shmem(image_handle, byte_count);
    339   reply_context.params.AppendHandle(serialized_handle);
    340   SendReply(reply_context,
    341             PpapiPluginMsg_PDF_GetResourceImageReply(host_resource,
    342                                                      image_data_desc));
    343 
    344   // Keep a reference to the resource only if the function succeeds.
    345   image_data_resource.Release();
    346 
    347   return PP_OK_COMPLETIONPENDING;
    348 }
    349 
    350 // TODO(raymes): This function is mainly copied from ppb_image_data_proxy.cc.
    351 // It's a mess and needs to be fixed in several ways but this is better done
    352 // when we refactor PPB_ImageData. On success, the image handle will be
    353 // non-null.
    354 bool PepperPDFHost::CreateImageData(
    355     PP_Instance instance,
    356     PP_ImageDataFormat format,
    357     const PP_Size& size,
    358     const SkBitmap& pixels_to_write,
    359     ppapi::HostResource* result,
    360     PP_ImageDataDesc* out_image_data_desc,
    361     IPC::PlatformFileForTransit* out_image_handle,
    362     uint32_t* out_byte_count) {
    363   PP_Resource resource = ppapi::proxy::PPB_ImageData_Proxy::CreateImageData(
    364       instance,
    365       ppapi::PPB_ImageData_Shared::SIMPLE,
    366       format, size,
    367       false /* init_to_zero */,
    368       out_image_data_desc, out_image_handle, out_byte_count);
    369   if (!resource)
    370     return false;
    371 
    372   result->SetHostResource(instance, resource);
    373 
    374   // Write the image to the resource shared memory.
    375   ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_ImageData_API>
    376       enter_resource(resource, false);
    377   if (enter_resource.failed())
    378     return false;
    379 
    380   ppapi::thunk::PPB_ImageData_API* image_data =
    381       static_cast<ppapi::thunk::PPB_ImageData_API*>(enter_resource.object());
    382   SkCanvas* canvas = image_data->GetCanvas();
    383   bool needs_unmapping = false;
    384   if (!canvas) {
    385     needs_unmapping = true;
    386     image_data->Map();
    387     canvas = image_data->GetCanvas();
    388     if (!canvas)
    389       return false;  // Failure mapping.
    390   }
    391 
    392   const SkBitmap* bitmap = &skia::GetTopDevice(*canvas)->accessBitmap(false);
    393   pixels_to_write.copyPixelsTo(bitmap->getPixels(),
    394                                bitmap->getSize(),
    395                                bitmap->rowBytes());
    396 
    397   if (needs_unmapping)
    398     image_data->Unmap();
    399 
    400   return true;
    401 }
    402 
    403 }  // namespace chrome
    404