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 39 using ppapi::PpapiGlobals; 40 using blink::WebElement; 41 using blink::WebView; 42 using content::RenderThread; 43 44 namespace { 45 46 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 47 class 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 68 struct ResourceImageInfo { 69 PP_ResourceImage pp_id; 70 int res_id; 71 }; 72 73 static 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 129 blink::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 137 printing::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 146 bool 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 154 bool IsPrintingEnabled(PP_Instance instance_id) { return false; } 155 156 #endif // ENABLE_FULL_PRINTING 157 158 PP_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 181 PP_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 214 bool 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 231 void 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 287 void 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 295 void 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 303 void 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 313 void HistogramPDFPageCount(PP_Instance instance, int count) { 314 UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count); 315 } 316 317 void 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 324 void 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 341 void 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 357 PP_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 367 PP_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 395 PP_Resource GetResourceImage(PP_Instance instance_id, 396 PP_ResourceImage image_id) { 397 return GetResourceImageForScale(instance_id, image_id, 1.0f); 398 } 399 400 PP_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 420 PP_Bool IsOutOfProcess(PP_Instance instance_id) { return PP_FALSE; } 421 422 void SetSelectedText(PP_Instance instance_id, const char* selected_text) { 423 // This function is intended for out of process PDF plugin. 424 NOTIMPLEMENTED(); 425 } 426 427 void 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 435 const 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 460 const PPB_PDF* PPB_PDF_Impl::GetInterface() { return &ppb_pdf; } 461 462 // static 463 void 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