ppb_pdf_impl.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
39using ppapi::PpapiGlobals;
40using blink::WebElement;
41using blink::WebView;
42using content::RenderThread;
43
44namespace {
45
46#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
47class 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
68struct ResourceImageInfo {
69  PP_ResourceImage pp_id;
70  int res_id;
71};
72
73static 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
129blink::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
137printing::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
146bool 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
154bool IsPrintingEnabled(PP_Instance instance_id) { return false; }
155
156#endif  // ENABLE_FULL_PRINTING
157
158PP_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
181PP_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
214bool 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
231void 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
287void 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
295void 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
303void 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
313void HistogramPDFPageCount(PP_Instance instance, int count) {
314  UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
315}
316
317void 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
324void 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
341void 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
357PP_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
367PP_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
395PP_Resource GetResourceImage(PP_Instance instance_id,
396                             PP_ResourceImage image_id) {
397  return GetResourceImageForScale(instance_id, image_id, 1.0f);
398}
399
400PP_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
420PP_Bool IsOutOfProcess(PP_Instance instance_id) { return PP_FALSE; }
421
422void SetSelectedText(PP_Instance instance_id, const char* selected_text) {
423  // This function is intended for out of process PDF plugin.
424  NOTIMPLEMENTED();
425}
426
427void 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
435const 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
460const PPB_PDF* PPB_PDF_Impl::GetInterface() { return &ppb_pdf; }
461
462// static
463void 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