- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / renderer / pepper / ppb_pdf_impl.cc
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
40 using ppapi::PpapiGlobals;
41 using WebKit::WebElement;
42 using WebKit::WebView;
43 using content::RenderThread;
44
45 namespace {
46
47 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
48 class 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
73 struct ResourceImageInfo {
74   PP_ResourceImage pp_id;
75   int res_id;
76 };
77
78 static 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
135 WebKit::WebElement GetWebElement(PP_Instance instance_id) {
136   content::PepperPluginInstance* instance =
137       content::PepperPluginInstance::Get(instance_id);
138   if (!instance)
139     return WebKit::WebElement();
140   return instance->GetContainer()->element();
141 }
142
143 printing::PrintWebViewHelper* GetPrintWebViewHelper(
144     const WebKit::WebElement& element) {
145   if (element.isNull())
146     return NULL;
147   WebKit::WebView* view = element.document().frame()->view();
148   content::RenderView* render_view = content::RenderView::FromWebView(view);
149   return printing::PrintWebViewHelper::Get(render_view);
150 }
151
152 bool IsPrintingEnabled(PP_Instance instance_id) {
153   WebKit::WebElement element = GetWebElement(instance_id);
154   printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
155   return helper && helper->IsPrintingEnabled();
156 }
157
158 #else  // ENABLE_FULL_PRINTING
159
160 bool IsPrintingEnabled(PP_Instance instance_id) {
161   return false;
162 }
163
164 #endif  // ENABLE_FULL_PRINTING
165
166
167
168 PP_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
191 PP_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
223 bool 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
240 void 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
290 void 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
298 void 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
306 void 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
316 void HistogramPDFPageCount(PP_Instance instance, int count) {
317   UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
318 }
319
320 void 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()->RecordUserMetrics(action_str->value());
325 }
326
327 void 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
343 void 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   WebKit::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
358 PP_Bool IsFeatureEnabled(PP_Instance instance, PP_PDFFeature feature) {
359   switch (feature) {
360     case PP_PDFFEATURE_HIDPI:
361       return PP_TRUE;
362     case PP_PDFFEATURE_PRINTING:
363       return IsPrintingEnabled(instance) ? PP_TRUE : PP_FALSE;
364   }
365   return PP_FALSE;
366 }
367
368 PP_Resource GetResourceImageForScale(PP_Instance instance_id,
369                                      PP_ResourceImage image_id,
370                                      float scale) {
371   int res_id = 0;
372   for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) {
373     if (kResourceImageMap[i].pp_id == image_id) {
374       res_id = kResourceImageMap[i].res_id;
375       break;
376     }
377   }
378   if (res_id == 0)
379     return 0;
380
381   // Validate the instance.
382   content::PepperPluginInstance* instance =
383       content::PepperPluginInstance::Get(instance_id);
384   if (!instance)
385     return 0;
386
387   gfx::ImageSkia* res_image_skia =
388       ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);
389
390   if (!res_image_skia)
391     return 0;
392
393   return instance->CreateImage(res_image_skia, scale);
394 }
395
396 PP_Resource GetResourceImage(PP_Instance instance_id,
397                              PP_ResourceImage image_id) {
398   return GetResourceImageForScale(instance_id, image_id, 1.0f);
399 }
400
401 PP_Var ModalPromptForPassword(PP_Instance instance_id,
402                               PP_Var message) {
403   content::PepperPluginInstance* instance =
404       content::PepperPluginInstance::Get(instance_id);
405   if (!instance)
406     return PP_MakeUndefined();
407
408   std::string actual_value;
409   scoped_refptr<ppapi::StringVar> message_string(
410       ppapi::StringVar::FromPPVar(message));
411
412   IPC::SyncMessage* msg = new ChromeViewHostMsg_PDFModalPromptForPassword(
413       instance->GetRenderView()->GetRoutingID(),
414       message_string->value(),
415       &actual_value);
416   msg->EnableMessagePumping();
417   instance->GetRenderView()->Send(msg);
418
419   return ppapi::StringVar::StringToPPVar(actual_value);
420 }
421
422 const PPB_PDF ppb_pdf = {
423   &GetLocalizedString,
424   &GetResourceImage,
425   &GetFontFileWithFallback,
426   &GetFontTableForPrivateFontFile,
427   &SearchString,
428   &DidStartLoading,
429   &DidStopLoading,
430   &SetContentRestriction,
431   &HistogramPDFPageCount,
432   &UserMetricsRecordAction,
433   &HasUnsupportedFeature,
434   &SaveAs,
435   &PPB_PDF_Impl::InvokePrintingForInstance,
436   &IsFeatureEnabled,
437   &GetResourceImageForScale,
438   &ModalPromptForPassword,
439 };
440
441 }  // namespace
442
443 // static
444 const PPB_PDF* PPB_PDF_Impl::GetInterface() {
445   return &ppb_pdf;
446 }
447
448 // static
449 void PPB_PDF_Impl::InvokePrintingForInstance(PP_Instance instance_id) {
450 #if defined(ENABLE_FULL_PRINTING)
451   WebKit::WebElement element = GetWebElement(instance_id);
452   printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
453   if (helper)
454     helper->PrintNode(element);
455 #endif  // ENABLE_FULL_PRINTING
456 }