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 blink::WebElement;
42 using blink::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:
PrivateFontFile(PP_Instance instance,int fd)50 PrivateFontFile(PP_Instance instance, int fd)
51 : Resource(ppapi::OBJECT_IS_IMPL, instance),
52 fd_(fd) {
53 }
54
GetFontTable(uint32_t table,void * output,uint32_t * output_length)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:
~PrivateFontFile()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
GetWebElement(PP_Instance instance_id)135 blink::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
GetPrintWebViewHelper(const blink::WebElement & element)143 printing::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
IsPrintingEnabled(PP_Instance instance_id)152 bool 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
IsPrintingEnabled(PP_Instance instance_id)160 bool IsPrintingEnabled(PP_Instance instance_id) {
161 return false;
162 }
163
164 #endif // ENABLE_FULL_PRINTING
165
166
167
GetLocalizedString(PP_Instance instance_id,PP_ResourceString string_id)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
GetFontFileWithFallback(PP_Instance instance_id,const PP_BrowserFont_Trusted_Description * description,PP_PrivateFontCharset charset)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
GetFontTableForPrivateFontFile(PP_Resource font_file,uint32_t table,void * output,uint32_t * output_length)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
SearchString(PP_Instance instance,const unsigned short * input_string,const unsigned short * input_term,bool case_sensitive,PP_PrivateFindResult ** results,int * count)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
DidStartLoading(PP_Instance instance_id)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
DidStopLoading(PP_Instance instance_id)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
SetContentRestriction(PP_Instance instance_id,int restrictions)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
HistogramPDFPageCount(PP_Instance instance,int count)316 void HistogramPDFPageCount(PP_Instance instance, int count) {
317 UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
318 }
319
UserMetricsRecordAction(PP_Instance instance,PP_Var action)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()->RecordComputedAction(action_str->value());
325 }
326
HasUnsupportedFeature(PP_Instance instance_id)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
SaveAs(PP_Instance instance_id)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 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
IsFeatureEnabled(PP_Instance instance,PP_PDFFeature feature)358 PP_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
GetResourceImageForScale(PP_Instance instance_id,PP_ResourceImage image_id,float scale)372 PP_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
GetResourceImage(PP_Instance instance_id,PP_ResourceImage image_id)400 PP_Resource GetResourceImage(PP_Instance instance_id,
401 PP_ResourceImage image_id) {
402 return GetResourceImageForScale(instance_id, image_id, 1.0f);
403 }
404
ModalPromptForPassword(PP_Instance instance_id,PP_Var message)405 PP_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
IsOutOfProcess(PP_Instance instance_id)426 PP_Bool IsOutOfProcess(PP_Instance instance_id) {
427 return PP_FALSE;
428 }
429
430 const 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
GetInterface()453 const PPB_PDF* PPB_PDF_Impl::GetInterface() {
454 return &ppb_pdf;
455 }
456
457 // static
InvokePrintingForInstance(PP_Instance instance_id)458 void 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