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