Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2012 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/ppb_pdf_impl.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/numerics/safe_conversions.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "build/build_config.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/common/render_messages.h"
     14 #include "chrome/renderer/printing/print_web_view_helper.h"
     15 #include "content/public/common/child_process_sandbox_support_linux.h"
     16 #include "content/public/common/referrer.h"
     17 #include "content/public/renderer/pepper_plugin_instance.h"
     18 #include "content/public/renderer/render_thread.h"
     19 #include "content/public/renderer/render_view.h"
     20 #include "grit/webkit_resources.h"
     21 #include "grit/webkit_strings.h"
     22 #include "ppapi/c/pp_resource.h"
     23 #include "ppapi/c/private/ppb_pdf.h"
     24 #include "ppapi/c/trusted/ppb_browser_font_trusted.h"
     25 #include "ppapi/shared_impl/ppapi_globals.h"
     26 #include "ppapi/shared_impl/resource.h"
     27 #include "ppapi/shared_impl/resource_tracker.h"
     28 #include "ppapi/shared_impl/var.h"
     29 #include "third_party/WebKit/public/web/WebDocument.h"
     30 #include "third_party/WebKit/public/web/WebElement.h"
     31 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     32 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     33 #include "third_party/WebKit/public/web/WebView.h"
     34 #include "third_party/icu/source/i18n/unicode/usearch.h"
     35 #include "third_party/skia/include/core/SkBitmap.h"
     36 #include "ui/base/l10n/l10n_util.h"
     37 #include "ui/base/resource/resource_bundle.h"
     38 
     39 using ppapi::PpapiGlobals;
     40 using blink::WebElement;
     41 using blink::WebView;
     42 using content::RenderThread;
     43 
     44 namespace {
     45 
     46 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
     47 class PrivateFontFile : public ppapi::Resource {
     48  public:
     49   PrivateFontFile(PP_Instance instance, int fd)
     50       : Resource(ppapi::OBJECT_IS_IMPL, instance), fd_(fd) {}
     51 
     52   bool GetFontTable(uint32_t table, void* output, uint32_t* output_length) {
     53     size_t temp_size = static_cast<size_t>(*output_length);
     54     bool rv = content::GetFontTable(
     55         fd_, table, 0 /* offset */, static_cast<uint8_t*>(output), &temp_size);
     56     *output_length = base::checked_cast<uint32_t>(temp_size);
     57     return rv;
     58   }
     59 
     60  protected:
     61   virtual ~PrivateFontFile() {}
     62 
     63  private:
     64   int fd_;
     65 };
     66 #endif
     67 
     68 struct ResourceImageInfo {
     69   PP_ResourceImage pp_id;
     70   int res_id;
     71 };
     72 
     73 static const ResourceImageInfo kResourceImageMap[] = {
     74     {PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP},
     75     {PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER},
     76     {PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED},
     77     {PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW},
     78     {PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER},
     79     {PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED},
     80     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END},
     81     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER,
     82      IDR_PDF_BUTTON_ZOOMIN_END_HOVER},
     83     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED,
     84      IDR_PDF_BUTTON_ZOOMIN_END_PRESSED},
     85     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN},
     86     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER},
     87     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED},
     88     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT},
     89     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER},
     90     {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED,
     91      IDR_PDF_BUTTON_ZOOMOUT_PRESSED},
     92     {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE},
     93     {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER},
     94     {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED},
     95     {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT},
     96     {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER},
     97     {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED},
     98     {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED},
     99     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0},
    100     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1},
    101     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2},
    102     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3},
    103     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4},
    104     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5},
    105     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6},
    106     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7},
    107     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8},
    108     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9},
    109     {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND,
    110      IDR_PDF_THUMBNAIL_NUM_BACKGROUND},
    111     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0},
    112     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1},
    113     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2},
    114     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3},
    115     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4},
    116     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5},
    117     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6},
    118     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7},
    119     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8},
    120     {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND,
    121      IDR_PDF_PROGRESS_BAR_BACKGROUND},
    122     {PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND,
    123      IDR_PDF_PAGE_INDICATOR_BACKGROUND},
    124     {PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW},
    125     {PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON}, };
    126 
    127 #if defined(ENABLE_FULL_PRINTING)
    128 
    129 blink::WebElement GetWebElement(PP_Instance instance_id) {
    130   content::PepperPluginInstance* instance =
    131       content::PepperPluginInstance::Get(instance_id);
    132   if (!instance)
    133     return blink::WebElement();
    134   return instance->GetContainer()->element();
    135 }
    136 
    137 printing::PrintWebViewHelper* GetPrintWebViewHelper(
    138     const blink::WebElement& element) {
    139   if (element.isNull())
    140     return NULL;
    141   blink::WebView* view = element.document().frame()->view();
    142   content::RenderView* render_view = content::RenderView::FromWebView(view);
    143   return printing::PrintWebViewHelper::Get(render_view);
    144 }
    145 
    146 bool IsPrintingEnabled(PP_Instance instance_id) {
    147   blink::WebElement element = GetWebElement(instance_id);
    148   printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
    149   return helper && helper->IsPrintingEnabled();
    150 }
    151 
    152 #else  // ENABLE_FULL_PRINTING
    153 
    154 bool IsPrintingEnabled(PP_Instance instance_id) { return false; }
    155 
    156 #endif  // ENABLE_FULL_PRINTING
    157 
    158 PP_Var GetLocalizedString(PP_Instance instance_id,
    159                           PP_ResourceString string_id) {
    160   content::PepperPluginInstance* instance =
    161       content::PepperPluginInstance::Get(instance_id);
    162   if (!instance)
    163     return PP_MakeUndefined();
    164 
    165   std::string rv;
    166   if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) {
    167     rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD));
    168   } else if (string_id == PP_RESOURCESTRING_PDFLOADING) {
    169     rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING));
    170   } else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) {
    171     rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED));
    172   } else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) {
    173     rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING));
    174   } else {
    175     NOTREACHED();
    176   }
    177 
    178   return ppapi::StringVar::StringToPPVar(rv);
    179 }
    180 
    181 PP_Resource GetFontFileWithFallback(
    182     PP_Instance instance_id,
    183     const PP_BrowserFont_Trusted_Description* description,
    184     PP_PrivateFontCharset charset) {
    185 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    186   // Validate the instance before using it below.
    187   if (!content::PepperPluginInstance::Get(instance_id))
    188     return 0;
    189 
    190   scoped_refptr<ppapi::StringVar> face_name(
    191       ppapi::StringVar::FromPPVar(description->face));
    192   if (!face_name.get())
    193     return 0;
    194 
    195   int fd = content::MatchFontWithFallback(
    196       face_name->value().c_str(),
    197       description->weight >= PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD,
    198       description->italic,
    199       charset,
    200       description->family);
    201   if (fd == -1)
    202     return 0;
    203 
    204   scoped_refptr<PrivateFontFile> font(new PrivateFontFile(instance_id, fd));
    205 
    206   return font->GetReference();
    207 #else
    208   // For trusted PPAPI plugins, this is only needed in Linux since font loading
    209   // on Windows and Mac works through the renderer sandbox.
    210   return 0;
    211 #endif
    212 }
    213 
    214 bool GetFontTableForPrivateFontFile(PP_Resource font_file,
    215                                     uint32_t table,
    216                                     void* output,
    217                                     uint32_t* output_length) {
    218 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
    219   ppapi::Resource* resource =
    220       PpapiGlobals::Get()->GetResourceTracker()->GetResource(font_file);
    221   if (!resource)
    222     return false;
    223 
    224   PrivateFontFile* font = static_cast<PrivateFontFile*>(resource);
    225   return font->GetFontTable(table, output, output_length);
    226 #else
    227   return false;
    228 #endif
    229 }
    230 
    231 void SearchString(PP_Instance instance,
    232                   const unsigned short* input_string,
    233                   const unsigned short* input_term,
    234                   bool case_sensitive,
    235                   PP_PrivateFindResult** results,
    236                   int* count) {
    237   const base::char16* string =
    238       reinterpret_cast<const base::char16*>(input_string);
    239   const base::char16* term = reinterpret_cast<const base::char16*>(input_term);
    240 
    241   UErrorCode status = U_ZERO_ERROR;
    242   UStringSearch* searcher =
    243       usearch_open(term,
    244                    -1,
    245                    string,
    246                    -1,
    247                    RenderThread::Get()->GetLocale().c_str(),
    248                    0,
    249                    &status);
    250   DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING ||
    251          status == U_USING_DEFAULT_WARNING);
    252   UCollationStrength strength = case_sensitive ? UCOL_TERTIARY : UCOL_PRIMARY;
    253 
    254   UCollator* collator = usearch_getCollator(searcher);
    255   if (ucol_getStrength(collator) != strength) {
    256     ucol_setStrength(collator, strength);
    257     usearch_reset(searcher);
    258   }
    259 
    260   status = U_ZERO_ERROR;
    261   int match_start = usearch_first(searcher, &status);
    262   DCHECK(status == U_ZERO_ERROR);
    263 
    264   std::vector<PP_PrivateFindResult> pp_results;
    265   while (match_start != USEARCH_DONE) {
    266     size_t matched_length = usearch_getMatchedLength(searcher);
    267     PP_PrivateFindResult result;
    268     result.start_index = match_start;
    269     result.length = matched_length;
    270     pp_results.push_back(result);
    271     match_start = usearch_next(searcher, &status);
    272     DCHECK(status == U_ZERO_ERROR);
    273   }
    274 
    275   *count = pp_results.size();
    276   if (*count) {
    277     *results = reinterpret_cast<PP_PrivateFindResult*>(
    278         malloc(*count * sizeof(PP_PrivateFindResult)));
    279     memcpy(*results, &pp_results[0], *count * sizeof(PP_PrivateFindResult));
    280   } else {
    281     *results = NULL;
    282   }
    283 
    284   usearch_close(searcher);
    285 }
    286 
    287 void DidStartLoading(PP_Instance instance_id) {
    288   content::PepperPluginInstance* instance =
    289       content::PepperPluginInstance::Get(instance_id);
    290   if (!instance)
    291     return;
    292   instance->GetRenderView()->DidStartLoading();
    293 }
    294 
    295 void DidStopLoading(PP_Instance instance_id) {
    296   content::PepperPluginInstance* instance =
    297       content::PepperPluginInstance::Get(instance_id);
    298   if (!instance)
    299     return;
    300   instance->GetRenderView()->DidStopLoading();
    301 }
    302 
    303 void SetContentRestriction(PP_Instance instance_id, int restrictions) {
    304   content::PepperPluginInstance* instance =
    305       content::PepperPluginInstance::Get(instance_id);
    306   if (!instance)
    307     return;
    308   instance->GetRenderView()->Send(
    309       new ChromeViewHostMsg_PDFUpdateContentRestrictions(
    310           instance->GetRenderView()->GetRoutingID(), restrictions));
    311 }
    312 
    313 void HistogramPDFPageCount(PP_Instance instance, int count) {
    314   UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
    315 }
    316 
    317 void UserMetricsRecordAction(PP_Instance instance, PP_Var action) {
    318   scoped_refptr<ppapi::StringVar> action_str(
    319       ppapi::StringVar::FromPPVar(action));
    320   if (action_str.get())
    321     RenderThread::Get()->RecordComputedAction(action_str->value());
    322 }
    323 
    324 void HasUnsupportedFeature(PP_Instance instance_id) {
    325   content::PepperPluginInstance* instance =
    326       content::PepperPluginInstance::Get(instance_id);
    327   if (!instance)
    328     return;
    329 
    330   // Only want to show an info bar if the pdf is the whole tab.
    331   if (!instance->IsFullPagePlugin())
    332     return;
    333 
    334   WebView* view =
    335       instance->GetContainer()->element().document().frame()->view();
    336   content::RenderView* render_view = content::RenderView::FromWebView(view);
    337   render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature(
    338       render_view->GetRoutingID()));
    339 }
    340 
    341 void SaveAs(PP_Instance instance_id) {
    342   content::PepperPluginInstance* instance =
    343       content::PepperPluginInstance::Get(instance_id);
    344   if (!instance)
    345     return;
    346   GURL url = instance->GetPluginURL();
    347 
    348   content::RenderView* render_view = instance->GetRenderView();
    349   blink::WebLocalFrame* frame =
    350       render_view->GetWebView()->mainFrame()->toWebLocalFrame();
    351   content::Referrer referrer(frame->document().url(),
    352                              frame->document().referrerPolicy());
    353   render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs(
    354       render_view->GetRoutingID(), url, referrer));
    355 }
    356 
    357 PP_Bool IsFeatureEnabled(PP_Instance instance, PP_PDFFeature feature) {
    358   switch (feature) {
    359     case PP_PDFFEATURE_HIDPI:
    360       return PP_TRUE;
    361     case PP_PDFFEATURE_PRINTING:
    362       return IsPrintingEnabled(instance) ? PP_TRUE : PP_FALSE;
    363   }
    364   return PP_FALSE;
    365 }
    366 
    367 PP_Resource GetResourceImageForScale(PP_Instance instance_id,
    368                                      PP_ResourceImage image_id,
    369                                      float scale) {
    370   int res_id = 0;
    371   for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) {
    372     if (kResourceImageMap[i].pp_id == image_id) {
    373       res_id = kResourceImageMap[i].res_id;
    374       break;
    375     }
    376   }
    377   if (res_id == 0)
    378     return 0;
    379 
    380   // Validate the instance.
    381   content::PepperPluginInstance* instance =
    382       content::PepperPluginInstance::Get(instance_id);
    383   if (!instance)
    384     return 0;
    385 
    386   gfx::ImageSkia* res_image_skia =
    387       ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);
    388 
    389   if (!res_image_skia)
    390     return 0;
    391 
    392   return instance->CreateImage(res_image_skia, scale);
    393 }
    394 
    395 PP_Resource GetResourceImage(PP_Instance instance_id,
    396                              PP_ResourceImage image_id) {
    397   return GetResourceImageForScale(instance_id, image_id, 1.0f);
    398 }
    399 
    400 PP_Var ModalPromptForPassword(PP_Instance instance_id, PP_Var message) {
    401   content::PepperPluginInstance* instance =
    402       content::PepperPluginInstance::Get(instance_id);
    403   if (!instance)
    404     return PP_MakeUndefined();
    405 
    406   std::string actual_value;
    407   scoped_refptr<ppapi::StringVar> message_string(
    408       ppapi::StringVar::FromPPVar(message));
    409 
    410   IPC::SyncMessage* msg = new ChromeViewHostMsg_PDFModalPromptForPassword(
    411       instance->GetRenderView()->GetRoutingID(),
    412       message_string->value(),
    413       &actual_value);
    414   msg->EnableMessagePumping();
    415   instance->GetRenderView()->Send(msg);
    416 
    417   return ppapi::StringVar::StringToPPVar(actual_value);
    418 }
    419 
    420 PP_Bool IsOutOfProcess(PP_Instance instance_id) { return PP_FALSE; }
    421 
    422 void SetSelectedText(PP_Instance instance_id, const char* selected_text) {
    423   // This function is intended for out of process PDF plugin.
    424   NOTIMPLEMENTED();
    425 }
    426 
    427 void SetLinkUnderCursor(PP_Instance instance_id, const char* url) {
    428   content::PepperPluginInstance* instance =
    429       content::PepperPluginInstance::Get(instance_id);
    430   if (!instance)
    431     return;
    432   instance->SetLinkUnderCursor(url);
    433 }
    434 
    435 const PPB_PDF ppb_pdf = {                      //
    436     &GetLocalizedString,                       //
    437     &GetResourceImage,                         //
    438     &GetFontFileWithFallback,                  //
    439     &GetFontTableForPrivateFontFile,           //
    440     &SearchString,                             //
    441     &DidStartLoading,                          //
    442     &DidStopLoading,                           //
    443     &SetContentRestriction,                    //
    444     &HistogramPDFPageCount,                    //
    445     &UserMetricsRecordAction,                  //
    446     &HasUnsupportedFeature,                    //
    447     &SaveAs,                                   //
    448     &PPB_PDF_Impl::InvokePrintingForInstance,  //
    449     &IsFeatureEnabled,                         //
    450     &GetResourceImageForScale,                 //
    451     &ModalPromptForPassword,                   //
    452     &IsOutOfProcess,                           //
    453     &SetSelectedText,                          //
    454     &SetLinkUnderCursor,                       //
    455 };
    456 
    457 }  // namespace
    458 
    459 // static
    460 const PPB_PDF* PPB_PDF_Impl::GetInterface() { return &ppb_pdf; }
    461 
    462 // static
    463 void PPB_PDF_Impl::InvokePrintingForInstance(PP_Instance instance_id) {
    464 #if defined(ENABLE_FULL_PRINTING)
    465   blink::WebElement element = GetWebElement(instance_id);
    466   printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
    467   if (helper)
    468     helper->PrintNode(element);
    469 #endif  // ENABLE_FULL_PRINTING
    470 }
    471