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