1 // Copyright (c) 2013 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/pepper_pdf_host.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/common/render_messages.h" 9 #include "chrome/renderer/printing/print_web_view_helper.h" 10 #include "content/public/common/referrer.h" 11 #include "content/public/renderer/pepper_plugin_instance.h" 12 #include "content/public/renderer/render_thread.h" 13 #include "content/public/renderer/render_view.h" 14 #include "content/public/renderer/renderer_ppapi_host.h" 15 #include "grit/webkit_resources.h" 16 #include "grit/webkit_strings.h" 17 #include "ppapi/host/dispatch_host_message.h" 18 #include "ppapi/host/host_message_context.h" 19 #include "ppapi/host/ppapi_host.h" 20 #include "ppapi/proxy/host_dispatcher.h" 21 #include "ppapi/proxy/ppapi_messages.h" 22 #include "ppapi/proxy/ppb_image_data_proxy.h" 23 #include "ppapi/shared_impl/ppb_image_data_shared.h" 24 #include "ppapi/shared_impl/scoped_pp_resource.h" 25 #include "ppapi/thunk/enter.h" 26 #include "ppapi/thunk/ppb_image_data_api.h" 27 #include "skia/ext/platform_canvas.h" 28 #include "third_party/WebKit/public/web/WebDocument.h" 29 #include "third_party/WebKit/public/web/WebElement.h" 30 #include "third_party/WebKit/public/web/WebFrame.h" 31 #include "third_party/WebKit/public/web/WebPluginContainer.h" 32 #include "third_party/WebKit/public/web/WebView.h" 33 #include "third_party/skia/include/core/SkBitmap.h" 34 #include "ui/base/l10n/l10n_util.h" 35 #include "ui/base/layout.h" 36 #include "ui/base/resource/resource_bundle.h" 37 #include "ui/gfx/image/image_skia.h" 38 #include "ui/gfx/image/image_skia_rep.h" 39 #include "ui/gfx/point.h" 40 41 namespace { 42 43 struct ResourceImageInfo { 44 PP_ResourceImage pp_id; 45 int res_id; 46 }; 47 48 static const ResourceImageInfo kResourceImageMap[] = { 49 { PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP }, 50 { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER }, 51 { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED }, 52 { PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW }, 53 { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER }, 54 { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED }, 55 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END }, 56 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER, 57 IDR_PDF_BUTTON_ZOOMIN_END_HOVER }, 58 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED, 59 IDR_PDF_BUTTON_ZOOMIN_END_PRESSED }, 60 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN }, 61 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER }, 62 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED }, 63 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT }, 64 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER }, 65 { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED, 66 IDR_PDF_BUTTON_ZOOMOUT_PRESSED }, 67 { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE }, 68 { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER }, 69 { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED }, 70 { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT }, 71 { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER }, 72 { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED }, 73 { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED }, 74 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0 }, 75 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1 }, 76 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2 }, 77 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3 }, 78 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4 }, 79 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5 }, 80 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6 }, 81 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7 }, 82 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8 }, 83 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9 }, 84 { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND, 85 IDR_PDF_THUMBNAIL_NUM_BACKGROUND }, 86 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0 }, 87 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1 }, 88 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2 }, 89 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3 }, 90 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4 }, 91 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5 }, 92 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6 }, 93 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7 }, 94 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8 }, 95 { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND, 96 IDR_PDF_PROGRESS_BAR_BACKGROUND }, 97 { PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND, 98 IDR_PDF_PAGE_INDICATOR_BACKGROUND }, 99 { PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW }, 100 { PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON }, 101 }; 102 103 // Valid strings for user metrics actions. 104 static const char* kValidUserMetricsActions[] = { 105 "PDF.PrintPage", 106 "PDF.ZoomFromBrowser", 107 "PDF.FitToPageButton", 108 "PDF.FitToWidthButton", 109 "PDF.ZoomOutButton", 110 "PDF.ZoomInButton", 111 "PDF.SaveButton", 112 "PDF.PrintButton", 113 "PDF.LoadSuccess", 114 "PDF.LoadFailure", 115 "PDF.PreviewDocumentLoadFailure", 116 }; 117 118 } // namespace 119 120 PepperPDFHost::PepperPDFHost( 121 content::RendererPpapiHost* host, 122 PP_Instance instance, 123 PP_Resource resource) 124 : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource), 125 host_(host) { 126 } 127 128 PepperPDFHost::~PepperPDFHost() { 129 } 130 131 int32_t PepperPDFHost::OnResourceMessageReceived( 132 const IPC::Message& msg, 133 ppapi::host::HostMessageContext* context) { 134 IPC_BEGIN_MESSAGE_MAP(PepperPDFHost, msg) 135 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetLocalizedString, 136 OnHostMsgGetLocalizedString) 137 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStartLoading, 138 OnHostMsgDidStartLoading) 139 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStopLoading, 140 OnHostMsgDidStopLoading) 141 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_UserMetricsRecordAction, 142 OnHostMsgUserMetricsRecordAction) 143 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_HasUnsupportedFeature, 144 OnHostMsgHasUnsupportedFeature) 145 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_Print, 146 OnHostMsgPrint) 147 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_SaveAs, 148 OnHostMsgSaveAs) 149 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_GetResourceImage, 150 OnHostMsgGetResourceImage) 151 IPC_END_MESSAGE_MAP() 152 return PP_ERROR_FAILED; 153 } 154 155 int32_t PepperPDFHost::OnHostMsgGetLocalizedString( 156 ppapi::host::HostMessageContext* context, 157 PP_ResourceString string_id) { 158 std::string rv; 159 if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) { 160 rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD)); 161 } else if (string_id == PP_RESOURCESTRING_PDFLOADING) { 162 rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING)); 163 } else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) { 164 rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED)); 165 } else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) { 166 rv = UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING)); 167 } else { 168 NOTREACHED(); 169 return PP_ERROR_FAILED; 170 } 171 172 context->reply_msg = PpapiPluginMsg_PDF_GetLocalizedStringReply(rv); 173 return PP_OK; 174 } 175 176 int32_t PepperPDFHost::OnHostMsgDidStartLoading( 177 ppapi::host::HostMessageContext* context) { 178 content::PepperPluginInstance* instance = 179 host_->GetPluginInstance(pp_instance()); 180 if (!instance) 181 return PP_ERROR_FAILED; 182 instance->GetRenderView()->DidStartLoading(); 183 return PP_OK; 184 } 185 186 int32_t PepperPDFHost::OnHostMsgDidStopLoading( 187 ppapi::host::HostMessageContext* context) { 188 content::PepperPluginInstance* instance = 189 host_->GetPluginInstance(pp_instance()); 190 if (!instance) 191 return PP_ERROR_FAILED; 192 instance->GetRenderView()->DidStopLoading(); 193 return PP_OK; 194 } 195 196 int32_t PepperPDFHost::OnHostMsgSetContentRestriction( 197 ppapi::host::HostMessageContext* context, int restrictions) { 198 content::PepperPluginInstance* instance = 199 host_->GetPluginInstance(pp_instance()); 200 if (!instance) 201 return PP_ERROR_FAILED; 202 instance->GetRenderView()->Send( 203 new ChromeViewHostMsg_PDFUpdateContentRestrictions( 204 instance->GetRenderView()->GetRoutingID(), restrictions)); 205 return PP_OK; 206 } 207 208 int32_t PepperPDFHost::OnHostMsgUserMetricsRecordAction( 209 ppapi::host::HostMessageContext* context, 210 const std::string& action) { 211 bool valid = false; 212 for (size_t i = 0; i < arraysize(kValidUserMetricsActions); ++i) { 213 if (action == kValidUserMetricsActions[i]) { 214 valid = true; 215 break; 216 } 217 } 218 if (!valid) { 219 NOTREACHED(); 220 return PP_ERROR_FAILED; 221 } 222 content::RenderThread::Get()->RecordComputedAction(action); 223 return PP_OK; 224 } 225 226 int32_t PepperPDFHost::OnHostMsgHasUnsupportedFeature( 227 ppapi::host::HostMessageContext* context) { 228 content::PepperPluginInstance* instance = 229 host_->GetPluginInstance(pp_instance()); 230 if (!instance) 231 return PP_ERROR_FAILED; 232 233 // Only want to show an info bar if the pdf is the whole tab. 234 if (!instance->IsFullPagePlugin()) 235 return PP_OK; 236 237 blink::WebView* view = 238 instance->GetContainer()->element().document().frame()->view(); 239 content::RenderView* render_view = content::RenderView::FromWebView(view); 240 render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature( 241 render_view->GetRoutingID())); 242 return PP_OK; 243 } 244 245 int32_t PepperPDFHost::OnHostMsgPrint( 246 ppapi::host::HostMessageContext* context) { 247 #if defined(ENABLE_FULL_PRINTING) 248 content::PepperPluginInstance* instance = 249 host_->GetPluginInstance(pp_instance()); 250 if (!instance) 251 return PP_ERROR_FAILED; 252 253 blink::WebElement element = instance->GetContainer()->element(); 254 blink::WebView* view = element.document().frame()->view(); 255 content::RenderView* render_view = content::RenderView::FromWebView(view); 256 257 using printing::PrintWebViewHelper; 258 PrintWebViewHelper* print_view_helper = PrintWebViewHelper::Get(render_view); 259 if (print_view_helper) { 260 print_view_helper->PrintNode(element); 261 return PP_OK; 262 } 263 #endif 264 return PP_ERROR_FAILED; 265 } 266 267 int32_t PepperPDFHost::OnHostMsgSaveAs( 268 ppapi::host::HostMessageContext* context) { 269 content::PepperPluginInstance* instance = 270 host_->GetPluginInstance(pp_instance()); 271 if (!instance) 272 return PP_ERROR_FAILED; 273 GURL url = instance->GetPluginURL(); 274 content::RenderView* render_view = instance->GetRenderView(); 275 blink::WebFrame* frame = render_view->GetWebView()->mainFrame(); 276 content::Referrer referrer(frame->document().url(), 277 frame->document().referrerPolicy()); 278 render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs( 279 render_view->GetRoutingID(), url, referrer)); 280 return PP_OK; 281 } 282 283 int32_t PepperPDFHost::OnHostMsgGetResourceImage( 284 ppapi::host::HostMessageContext* context, 285 PP_ResourceImage image_id, 286 float scale) { 287 int res_id = 0; 288 for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) { 289 if (kResourceImageMap[i].pp_id == image_id) { 290 res_id = kResourceImageMap[i].res_id; 291 break; 292 } 293 } 294 if (res_id == 0) 295 return PP_ERROR_FAILED; 296 297 gfx::ImageSkia* res_image_skia = 298 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id); 299 300 if (!res_image_skia) 301 return PP_ERROR_FAILED; 302 303 gfx::ImageSkiaRep image_skia_rep = res_image_skia->GetRepresentation(scale); 304 305 if (image_skia_rep.is_null() || image_skia_rep.scale() != scale) 306 return PP_ERROR_FAILED; 307 308 PP_Size pp_size; 309 pp_size.width = image_skia_rep.pixel_width(); 310 pp_size.height = image_skia_rep.pixel_height(); 311 312 ppapi::HostResource host_resource; 313 PP_ImageDataDesc image_data_desc; 314 IPC::PlatformFileForTransit image_handle; 315 uint32_t byte_count = 0; 316 bool success = CreateImageData( 317 pp_instance(), 318 ppapi::PPB_ImageData_Shared::GetNativeImageDataFormat(), 319 pp_size, 320 image_skia_rep.sk_bitmap(), 321 &host_resource, 322 &image_data_desc, 323 &image_handle, 324 &byte_count); 325 ppapi::ScopedPPResource image_data_resource( 326 ppapi::ScopedPPResource::PassRef(), host_resource.host_resource()); 327 if (!success) 328 return PP_ERROR_FAILED; 329 330 ppapi::host::ReplyMessageContext reply_context = 331 context->MakeReplyMessageContext(); 332 ppapi::proxy::SerializedHandle serialized_handle; 333 serialized_handle.set_shmem(image_handle, byte_count); 334 reply_context.params.AppendHandle(serialized_handle); 335 SendReply(reply_context, 336 PpapiPluginMsg_PDF_GetResourceImageReply(host_resource, 337 image_data_desc)); 338 339 // Keep a reference to the resource only if the function succeeds. 340 image_data_resource.Release(); 341 342 return PP_OK_COMPLETIONPENDING; 343 } 344 345 // TODO(raymes): This function is mainly copied from ppb_image_data_proxy.cc. 346 // It's a mess and needs to be fixed in several ways but this is better done 347 // when we refactor PPB_ImageData. On success, the image handle will be 348 // non-null. 349 bool PepperPDFHost::CreateImageData( 350 PP_Instance instance, 351 PP_ImageDataFormat format, 352 const PP_Size& size, 353 const SkBitmap& pixels_to_write, 354 ppapi::HostResource* result, 355 PP_ImageDataDesc* out_image_data_desc, 356 IPC::PlatformFileForTransit* out_image_handle, 357 uint32_t* out_byte_count) { 358 PP_Resource resource = ppapi::proxy::PPB_ImageData_Proxy::CreateImageData( 359 instance, 360 ppapi::PPB_ImageData_Shared::SIMPLE, 361 format, size, 362 false /* init_to_zero */, 363 out_image_data_desc, out_image_handle, out_byte_count); 364 if (!resource) 365 return false; 366 367 result->SetHostResource(instance, resource); 368 369 // Write the image to the resource shared memory. 370 ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_ImageData_API> 371 enter_resource(resource, false); 372 if (enter_resource.failed()) 373 return false; 374 375 ppapi::thunk::PPB_ImageData_API* image_data = 376 static_cast<ppapi::thunk::PPB_ImageData_API*>(enter_resource.object()); 377 SkCanvas* canvas = image_data->GetCanvas(); 378 bool needs_unmapping = false; 379 if (!canvas) { 380 needs_unmapping = true; 381 image_data->Map(); 382 canvas = image_data->GetCanvas(); 383 if (!canvas) 384 return false; // Failure mapping. 385 } 386 387 const SkBitmap* bitmap = &skia::GetTopDevice(*canvas)->accessBitmap(false); 388 pixels_to_write.copyPixelsTo(bitmap->getPixels(), 389 bitmap->getSize(), 390 bitmap->rowBytes()); 391 392 if (needs_unmapping) 393 image_data->Unmap(); 394 395 return true; 396 } 397