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