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.
5 #include "chrome/renderer/pepper/ppb_pdf_impl.h"
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/layout.h"
38 #include "ui/base/resource/resource_bundle.h"
40 using ppapi::PpapiGlobals;
41 using blink::WebElement;
43 using content::RenderThread;
47 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
48 class PrivateFontFile : public ppapi::Resource {
50 PrivateFontFile(PP_Instance instance, int fd)
51 : Resource(ppapi::OBJECT_IS_IMPL, instance), fd_(fd) {}
53 bool GetFontTable(uint32_t table, void* output, uint32_t* output_length) {
54 size_t temp_size = static_cast<size_t>(*output_length);
55 bool rv = content::GetFontTable(
56 fd_, table, 0 /* offset */, static_cast<uint8_t*>(output), &temp_size);
57 *output_length = base::checked_cast<uint32_t>(temp_size);
62 virtual ~PrivateFontFile() {}
69 struct ResourceImageInfo {
70 PP_ResourceImage pp_id;
74 static const ResourceImageInfo kResourceImageMap[] = {
75 {PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP},
76 {PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER},
77 {PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED},
78 {PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW},
79 {PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER},
80 {PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED},
81 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END},
82 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER,
83 IDR_PDF_BUTTON_ZOOMIN_END_HOVER},
84 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED,
85 IDR_PDF_BUTTON_ZOOMIN_END_PRESSED},
86 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN},
87 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER},
88 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED},
89 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT},
90 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER},
91 {PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED,
92 IDR_PDF_BUTTON_ZOOMOUT_PRESSED},
93 {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE},
94 {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER},
95 {PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED},
96 {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT},
97 {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER},
98 {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED},
99 {PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED},
100 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0},
101 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1},
102 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2},
103 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3},
104 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4},
105 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5},
106 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6},
107 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7},
108 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8},
109 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9},
110 {PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND,
111 IDR_PDF_THUMBNAIL_NUM_BACKGROUND},
112 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0},
113 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1},
114 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2},
115 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3},
116 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4},
117 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5},
118 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6},
119 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7},
120 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8},
121 {PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND,
122 IDR_PDF_PROGRESS_BAR_BACKGROUND},
123 {PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND,
124 IDR_PDF_PAGE_INDICATOR_BACKGROUND},
125 {PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW},
126 {PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON}, };
128 #if defined(ENABLE_FULL_PRINTING)
130 blink::WebElement GetWebElement(PP_Instance instance_id) {
131 content::PepperPluginInstance* instance =
132 content::PepperPluginInstance::Get(instance_id);
134 return blink::WebElement();
135 return instance->GetContainer()->element();
138 printing::PrintWebViewHelper* GetPrintWebViewHelper(
139 const blink::WebElement& element) {
140 if (element.isNull())
142 blink::WebView* view = element.document().frame()->view();
143 content::RenderView* render_view = content::RenderView::FromWebView(view);
144 return printing::PrintWebViewHelper::Get(render_view);
147 bool IsPrintingEnabled(PP_Instance instance_id) {
148 blink::WebElement element = GetWebElement(instance_id);
149 printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
150 return helper && helper->IsPrintingEnabled();
153 #else // ENABLE_FULL_PRINTING
155 bool IsPrintingEnabled(PP_Instance instance_id) { return false; }
157 #endif // ENABLE_FULL_PRINTING
159 PP_Var GetLocalizedString(PP_Instance instance_id,
160 PP_ResourceString string_id) {
161 content::PepperPluginInstance* instance =
162 content::PepperPluginInstance::Get(instance_id);
164 return PP_MakeUndefined();
167 if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) {
168 rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD));
169 } else if (string_id == PP_RESOURCESTRING_PDFLOADING) {
170 rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING));
171 } else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) {
172 rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED));
173 } else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) {
174 rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING));
179 return ppapi::StringVar::StringToPPVar(rv);
182 PP_Resource GetFontFileWithFallback(
183 PP_Instance instance_id,
184 const PP_BrowserFont_Trusted_Description* description,
185 PP_PrivateFontCharset charset) {
186 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
187 // Validate the instance before using it below.
188 if (!content::PepperPluginInstance::Get(instance_id))
191 scoped_refptr<ppapi::StringVar> face_name(
192 ppapi::StringVar::FromPPVar(description->face));
193 if (!face_name.get())
196 int fd = content::MatchFontWithFallback(
197 face_name->value().c_str(),
198 description->weight >= PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD,
201 description->family);
205 scoped_refptr<PrivateFontFile> font(new PrivateFontFile(instance_id, fd));
207 return font->GetReference();
209 // For trusted PPAPI plugins, this is only needed in Linux since font loading
210 // on Windows and Mac works through the renderer sandbox.
215 bool GetFontTableForPrivateFontFile(PP_Resource font_file,
218 uint32_t* output_length) {
219 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
220 ppapi::Resource* resource =
221 PpapiGlobals::Get()->GetResourceTracker()->GetResource(font_file);
225 PrivateFontFile* font = static_cast<PrivateFontFile*>(resource);
226 return font->GetFontTable(table, output, output_length);
232 void SearchString(PP_Instance instance,
233 const unsigned short* input_string,
234 const unsigned short* input_term,
236 PP_PrivateFindResult** results,
238 const base::char16* string =
239 reinterpret_cast<const base::char16*>(input_string);
240 const base::char16* term = reinterpret_cast<const base::char16*>(input_term);
242 UErrorCode status = U_ZERO_ERROR;
243 UStringSearch* searcher =
248 RenderThread::Get()->GetLocale().c_str(),
251 DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING ||
252 status == U_USING_DEFAULT_WARNING);
253 UCollationStrength strength = case_sensitive ? UCOL_TERTIARY : UCOL_PRIMARY;
255 UCollator* collator = usearch_getCollator(searcher);
256 if (ucol_getStrength(collator) != strength) {
257 ucol_setStrength(collator, strength);
258 usearch_reset(searcher);
261 status = U_ZERO_ERROR;
262 int match_start = usearch_first(searcher, &status);
263 DCHECK(status == U_ZERO_ERROR);
265 std::vector<PP_PrivateFindResult> pp_results;
266 while (match_start != USEARCH_DONE) {
267 size_t matched_length = usearch_getMatchedLength(searcher);
268 PP_PrivateFindResult result;
269 result.start_index = match_start;
270 result.length = matched_length;
271 pp_results.push_back(result);
272 match_start = usearch_next(searcher, &status);
273 DCHECK(status == U_ZERO_ERROR);
276 *count = pp_results.size();
278 *results = reinterpret_cast<PP_PrivateFindResult*>(
279 malloc(*count * sizeof(PP_PrivateFindResult)));
280 memcpy(*results, &pp_results[0], *count * sizeof(PP_PrivateFindResult));
285 usearch_close(searcher);
288 void DidStartLoading(PP_Instance instance_id) {
289 content::PepperPluginInstance* instance =
290 content::PepperPluginInstance::Get(instance_id);
293 instance->GetRenderView()->DidStartLoading();
296 void DidStopLoading(PP_Instance instance_id) {
297 content::PepperPluginInstance* instance =
298 content::PepperPluginInstance::Get(instance_id);
301 instance->GetRenderView()->DidStopLoading();
304 void SetContentRestriction(PP_Instance instance_id, int restrictions) {
305 content::PepperPluginInstance* instance =
306 content::PepperPluginInstance::Get(instance_id);
309 instance->GetRenderView()->Send(
310 new ChromeViewHostMsg_PDFUpdateContentRestrictions(
311 instance->GetRenderView()->GetRoutingID(), restrictions));
314 void HistogramPDFPageCount(PP_Instance instance, int count) {
315 UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
318 void UserMetricsRecordAction(PP_Instance instance, PP_Var action) {
319 scoped_refptr<ppapi::StringVar> action_str(
320 ppapi::StringVar::FromPPVar(action));
321 if (action_str.get())
322 RenderThread::Get()->RecordComputedAction(action_str->value());
325 void HasUnsupportedFeature(PP_Instance instance_id) {
326 content::PepperPluginInstance* instance =
327 content::PepperPluginInstance::Get(instance_id);
331 // Only want to show an info bar if the pdf is the whole tab.
332 if (!instance->IsFullPagePlugin())
336 instance->GetContainer()->element().document().frame()->view();
337 content::RenderView* render_view = content::RenderView::FromWebView(view);
338 render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature(
339 render_view->GetRoutingID()));
342 void SaveAs(PP_Instance instance_id) {
343 content::PepperPluginInstance* instance =
344 content::PepperPluginInstance::Get(instance_id);
347 GURL url = instance->GetPluginURL();
349 content::RenderView* render_view = instance->GetRenderView();
350 blink::WebLocalFrame* frame =
351 render_view->GetWebView()->mainFrame()->toWebLocalFrame();
352 content::Referrer referrer(frame->document().url(),
353 frame->document().referrerPolicy());
354 render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs(
355 render_view->GetRoutingID(), url, referrer));
358 PP_Bool IsFeatureEnabled(PP_Instance instance, PP_PDFFeature feature) {
360 case PP_PDFFEATURE_HIDPI:
362 case PP_PDFFEATURE_PRINTING:
363 return IsPrintingEnabled(instance) ? PP_TRUE : PP_FALSE;
368 PP_Resource GetResourceImageForScale(PP_Instance instance_id,
369 PP_ResourceImage image_id,
371 ui::ScaleFactor supported_scale_factor = ui::GetSupportedScaleFactor(scale);
372 DCHECK(supported_scale_factor != ui::SCALE_FACTOR_NONE);
373 float supported_scale = ui::GetImageScale(supported_scale_factor);
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;
385 // Validate the instance.
386 content::PepperPluginInstance* instance =
387 content::PepperPluginInstance::Get(instance_id);
391 gfx::ImageSkia* res_image_skia =
392 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);
397 return instance->CreateImage(res_image_skia, supported_scale);
400 PP_Resource GetResourceImage(PP_Instance instance_id,
401 PP_ResourceImage image_id) {
402 return GetResourceImageForScale(instance_id, image_id, 1.0f);
405 PP_Var ModalPromptForPassword(PP_Instance instance_id, PP_Var message) {
406 content::PepperPluginInstance* instance =
407 content::PepperPluginInstance::Get(instance_id);
409 return PP_MakeUndefined();
411 std::string actual_value;
412 scoped_refptr<ppapi::StringVar> message_string(
413 ppapi::StringVar::FromPPVar(message));
415 IPC::SyncMessage* msg = new ChromeViewHostMsg_PDFModalPromptForPassword(
416 instance->GetRenderView()->GetRoutingID(),
417 message_string->value(),
419 msg->EnableMessagePumping();
420 instance->GetRenderView()->Send(msg);
422 return ppapi::StringVar::StringToPPVar(actual_value);
425 PP_Bool IsOutOfProcess(PP_Instance instance_id) { return PP_FALSE; }
427 void SetSelectedText(PP_Instance instance_id, const char* selected_text) {
428 // This function is intended for out of process PDF plugin.
432 void SetLinkUnderCursor(PP_Instance instance_id, const char* url) {
433 content::PepperPluginInstance* instance =
434 content::PepperPluginInstance::Get(instance_id);
437 instance->SetLinkUnderCursor(url);
440 const PPB_PDF ppb_pdf = { //
441 &GetLocalizedString, //
442 &GetResourceImage, //
443 &GetFontFileWithFallback, //
444 &GetFontTableForPrivateFontFile, //
448 &SetContentRestriction, //
449 &HistogramPDFPageCount, //
450 &UserMetricsRecordAction, //
451 &HasUnsupportedFeature, //
453 &PPB_PDF_Impl::InvokePrintingForInstance, //
454 &IsFeatureEnabled, //
455 &GetResourceImageForScale, //
456 &ModalPromptForPassword, //
459 &SetLinkUnderCursor, //
465 const PPB_PDF* PPB_PDF_Impl::GetInterface() { return &ppb_pdf; }
468 void PPB_PDF_Impl::InvokePrintingForInstance(PP_Instance instance_id) {
469 #if defined(ENABLE_FULL_PRINTING)
470 blink::WebElement element = GetWebElement(instance_id);
471 printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
473 helper->PrintNode(element);
474 #endif // ENABLE_FULL_PRINTING