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_LINUX) || defined(OS_OPENBSD) 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_LINUX) || defined(OS_OPENBSD) 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_LINUX) || defined(OS_OPENBSD) 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 const PPB_PDF ppb_pdf = { 402 &GetLocalizedString, 403 &GetResourceImage, 404 &GetFontFileWithFallback, 405 &GetFontTableForPrivateFontFile, 406 &SearchString, 407 &DidStartLoading, 408 &DidStopLoading, 409 &SetContentRestriction, 410 &HistogramPDFPageCount, 411 &UserMetricsRecordAction, 412 &HasUnsupportedFeature, 413 &SaveAs, 414 &PPB_PDF_Impl::InvokePrintingForInstance, 415 &IsFeatureEnabled, 416 &GetResourceImageForScale 417 }; 418 419 } // namespace 420 421 // static 422 const PPB_PDF* PPB_PDF_Impl::GetInterface() { 423 return &ppb_pdf; 424 } 425 426 // static 427 void PPB_PDF_Impl::InvokePrintingForInstance(PP_Instance instance_id) { 428 #if defined(ENABLE_FULL_PRINTING) 429 WebKit::WebElement element = GetWebElement(instance_id); 430 printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element); 431 if (helper) 432 helper->PrintNode(element); 433 #endif // ENABLE_FULL_PRINTING 434 } 435