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