| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 #define PEPPER_APIS_ENABLED 1 | |
| 6 | |
| 7 #include "chrome/renderer/webplugin_delegate_pepper.h" | |
| 8 | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #if defined(OS_LINUX) | |
| 13 #include <unistd.h> | |
| 14 #endif | |
| 15 | |
| 16 #include "base/file_path.h" | |
| 17 #include "base/file_util.h" | |
| 18 #include "base/md5.h" | |
| 19 #include "base/message_loop.h" | |
| 20 #include "base/metrics/histogram.h" | |
| 21 #include "base/metrics/stats_counters.h" | |
| 22 #include "base/path_service.h" | |
| 23 #include "base/process_util.h" | |
| 24 #include "base/scoped_ptr.h" | |
| 25 #include "base/string_number_conversions.h" | |
| 26 #include "base/string_util.h" | |
| 27 #include "base/task.h" | |
| 28 #include "base/time.h" | |
| 29 #include "base/utf_string_conversions.h" | |
| 30 #include "chrome/common/chrome_paths.h" | |
| 31 #include "chrome/common/render_messages.h" | |
| 32 #include "chrome/common/render_messages_params.h" | |
| 33 #include "chrome/renderer/pepper_widget.h" | |
| 34 #include "chrome/renderer/render_thread.h" | |
| 35 #include "chrome/renderer/render_view.h" | |
| 36 #include "chrome/renderer/webplugin_delegate_proxy.h" | |
| 37 #include "ui/gfx/blit.h" | |
| 38 #include "printing/native_metafile_factory.h" | |
| 39 #include "printing/native_metafile.h" | |
| 40 #include "third_party/npapi/bindings/npapi_extensions.h" | |
| 41 #include "third_party/npapi/bindings/npapi_extensions_private.h" | |
| 42 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h" | |
| 43 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
| 44 #include "webkit/glue/webcursor.h" | |
| 45 #include "webkit/glue/webkit_glue.h" | |
| 46 #include "webkit/plugins/npapi/plugin_constants_win.h" | |
| 47 #include "webkit/plugins/npapi/plugin_instance.h" | |
| 48 #include "webkit/plugins/npapi/plugin_lib.h" | |
| 49 #include "webkit/plugins/npapi/plugin_list.h" | |
| 50 #include "webkit/plugins/npapi/plugin_host.h" | |
| 51 #include "webkit/plugins/npapi/plugin_stream_url.h" | |
| 52 | |
| 53 #if defined(OS_MACOSX) | |
| 54 #include "base/mac/mac_util.h" | |
| 55 #include "base/mac/scoped_cftyperef.h" | |
| 56 #elif defined(OS_LINUX) | |
| 57 #include "chrome/renderer/renderer_sandbox_support_linux.h" | |
| 58 #include "printing/pdf_ps_metafile_cairo.h" | |
| 59 #elif defined(OS_WIN) | |
| 60 #include "printing/units.h" | |
| 61 #include "skia/ext/vector_platform_device.h" | |
| 62 #include "ui/gfx/codec/jpeg_codec.h" | |
| 63 #include "ui/gfx/gdi_util.h" | |
| 64 #endif | |
| 65 | |
| 66 using webkit::npapi::WebPlugin; | |
| 67 using webkit::npapi::WebPluginDelegate; | |
| 68 using webkit::npapi::WebPluginResourceClient; | |
| 69 using WebKit::WebCursorInfo; | |
| 70 using WebKit::WebKeyboardEvent; | |
| 71 using WebKit::WebInputEvent; | |
| 72 using WebKit::WebMouseEvent; | |
| 73 using WebKit::WebMouseWheelEvent; | |
| 74 | |
| 75 namespace { | |
| 76 | |
| 77 // Implementation artifacts for a context | |
| 78 struct Device2DImpl { | |
| 79 TransportDIB* dib; | |
| 80 }; | |
| 81 | |
| 82 } // namespace | |
| 83 | |
| 84 static const float kPointsPerInch = 72.0; | |
| 85 | |
| 86 #if defined(OS_WIN) | |
| 87 // Exported by pdf.dll | |
| 88 typedef bool (*RenderPDFPageToDCProc)( | |
| 89 const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc, | |
| 90 int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, | |
| 91 int bounds_width, int bounds_height, bool fit_to_bounds, | |
| 92 bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds); | |
| 93 #endif // defined(OS_WIN) | |
| 94 | |
| 95 WebPluginDelegatePepper* WebPluginDelegatePepper::Create( | |
| 96 const FilePath& filename, | |
| 97 const std::string& mime_type, | |
| 98 const base::WeakPtr<RenderView>& render_view) { | |
| 99 scoped_refptr<webkit::npapi::PluginLib> plugin_lib( | |
| 100 webkit::npapi::PluginLib::CreatePluginLib(filename)); | |
| 101 if (plugin_lib.get() == NULL) | |
| 102 return NULL; | |
| 103 | |
| 104 NPError err = plugin_lib->NP_Initialize(); | |
| 105 if (err != NPERR_NO_ERROR) | |
| 106 return NULL; | |
| 107 | |
| 108 scoped_refptr<webkit::npapi::PluginInstance> instance( | |
| 109 plugin_lib->CreateInstance(mime_type)); | |
| 110 return new WebPluginDelegatePepper(render_view, | |
| 111 instance.get()); | |
| 112 } | |
| 113 | |
| 114 void WebPluginDelegatePepper::didChooseFile( | |
| 115 const WebKit::WebVector<WebKit::WebString>& file_names) { | |
| 116 if (file_names.isEmpty()) { | |
| 117 current_choose_file_callback_(NULL, 0, current_choose_file_user_data_); | |
| 118 } else { | |
| 119 // Construct a bunch of 8-bit strings for the callback. | |
| 120 std::vector<std::string> file_strings; | |
| 121 file_strings.resize(file_names.size()); | |
| 122 for (size_t i = 0; i < file_names.size(); i++) | |
| 123 file_strings[i] = file_names[0].utf8(); | |
| 124 | |
| 125 // Construct an array of pointers to each of the strings. | |
| 126 std::vector<const char*> pointers_to_strings; | |
| 127 pointers_to_strings.resize(file_strings.size()); | |
| 128 for (size_t i = 0; i < file_strings.size(); i++) | |
| 129 pointers_to_strings[i] = file_strings[i].c_str(); | |
| 130 | |
| 131 current_choose_file_callback_( | |
| 132 &pointers_to_strings[0], | |
| 133 static_cast<int>(pointers_to_strings.size()), | |
| 134 current_choose_file_user_data_); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 bool WebPluginDelegatePepper::Initialize( | |
| 139 const GURL& url, | |
| 140 const std::vector<std::string>& arg_names, | |
| 141 const std::vector<std::string>& arg_values, | |
| 142 WebPlugin* plugin, | |
| 143 bool load_manually) { | |
| 144 plugin_ = plugin; | |
| 145 | |
| 146 instance_->set_web_plugin(plugin_); | |
| 147 int argc = 0; | |
| 148 scoped_array<char*> argn(new char*[arg_names.size()]); | |
| 149 scoped_array<char*> argv(new char*[arg_names.size()]); | |
| 150 for (size_t i = 0; i < arg_names.size(); ++i) { | |
| 151 argn[argc] = const_cast<char*>(arg_names[i].c_str()); | |
| 152 argv[argc] = const_cast<char*>(arg_values[i].c_str()); | |
| 153 argc++; | |
| 154 } | |
| 155 | |
| 156 bool start_result = instance_->Start( | |
| 157 url, argn.get(), argv.get(), argc, load_manually); | |
| 158 if (!start_result) | |
| 159 return false; | |
| 160 | |
| 161 plugin_url_ = url.spec(); | |
| 162 | |
| 163 return true; | |
| 164 } | |
| 165 | |
| 166 void WebPluginDelegatePepper::DestroyInstance() { | |
| 167 if (instance_ && (instance_->npp()->ndata != NULL)) { | |
| 168 // Shutdown all streams before destroying so that | |
| 169 // no streams are left "in progress". Need to do | |
| 170 // this before calling set_web_plugin(NULL) because the | |
| 171 // instance uses the helper to do the download. | |
| 172 instance_->CloseStreams(); | |
| 173 | |
| 174 window_.window = NULL; | |
| 175 instance_->NPP_SetWindow(&window_); | |
| 176 | |
| 177 instance_->NPP_Destroy(); | |
| 178 | |
| 179 instance_->set_web_plugin(NULL); | |
| 180 | |
| 181 instance_ = 0; | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 void WebPluginDelegatePepper::UpdateGeometry( | |
| 186 const gfx::Rect& window_rect, | |
| 187 const gfx::Rect& clip_rect) { | |
| 188 // Only resend to the instance if the geometry has changed. | |
| 189 if (window_rect == window_rect_ && clip_rect == clip_rect_) | |
| 190 return; | |
| 191 | |
| 192 clip_rect_ = clip_rect; | |
| 193 cutout_rects_.clear(); | |
| 194 | |
| 195 if (window_rect_ == window_rect) | |
| 196 return; | |
| 197 window_rect_ = window_rect; | |
| 198 | |
| 199 // TODO(brettw) figure out how to tell the plugin that the size changed and it | |
| 200 // needs to repaint? | |
| 201 SkBitmap new_committed; | |
| 202 new_committed.setConfig(SkBitmap::kARGB_8888_Config, | |
| 203 window_rect_.width(), window_rect_.height()); | |
| 204 new_committed.allocPixels(); | |
| 205 committed_bitmap_ = new_committed; | |
| 206 | |
| 207 if (!instance()) | |
| 208 return; | |
| 209 | |
| 210 ForwardSetWindow(); | |
| 211 } | |
| 212 | |
| 213 NPObject* WebPluginDelegatePepper::GetPluginScriptableObject() { | |
| 214 return instance_->GetPluginScriptableObject(); | |
| 215 } | |
| 216 | |
| 217 void WebPluginDelegatePepper::DidFinishLoadWithReason( | |
| 218 const GURL& url, NPReason reason, int notify_id) { | |
| 219 instance()->DidFinishLoadWithReason(url, reason, notify_id); | |
| 220 } | |
| 221 | |
| 222 int WebPluginDelegatePepper::GetProcessId() { | |
| 223 // We are in process, so the plugin pid is this current process pid. | |
| 224 return base::GetCurrentProcId(); | |
| 225 } | |
| 226 | |
| 227 void WebPluginDelegatePepper::SendJavaScriptStream( | |
| 228 const GURL& url, | |
| 229 const std::string& result, | |
| 230 bool success, | |
| 231 int notify_id) { | |
| 232 instance()->SendJavaScriptStream(url, result, success, notify_id); | |
| 233 } | |
| 234 | |
| 235 void WebPluginDelegatePepper::DidReceiveManualResponse( | |
| 236 const GURL& url, const std::string& mime_type, | |
| 237 const std::string& headers, uint32 expected_length, uint32 last_modified) { | |
| 238 instance()->DidReceiveManualResponse(url, mime_type, headers, | |
| 239 expected_length, last_modified); | |
| 240 } | |
| 241 | |
| 242 void WebPluginDelegatePepper::DidReceiveManualData(const char* buffer, | |
| 243 int length) { | |
| 244 instance()->DidReceiveManualData(buffer, length); | |
| 245 } | |
| 246 | |
| 247 void WebPluginDelegatePepper::DidFinishManualLoading() { | |
| 248 instance()->DidFinishManualLoading(); | |
| 249 } | |
| 250 | |
| 251 void WebPluginDelegatePepper::DidManualLoadFail() { | |
| 252 instance()->DidManualLoadFail(); | |
| 253 } | |
| 254 | |
| 255 FilePath WebPluginDelegatePepper::GetPluginPath() { | |
| 256 return instance()->plugin_lib()->plugin_info().path; | |
| 257 } | |
| 258 | |
| 259 void WebPluginDelegatePepper::RenderViewInitiatedPaint() { | |
| 260 // Broadcast event to all 2D contexts. | |
| 261 Graphics2DMap::iterator iter2d(&graphic2d_contexts_); | |
| 262 while (!iter2d.IsAtEnd()) { | |
| 263 iter2d.GetCurrentValue()->RenderViewInitiatedPaint(); | |
| 264 iter2d.Advance(); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 void WebPluginDelegatePepper::RenderViewFlushedPaint() { | |
| 269 // Broadcast event to all 2D contexts. | |
| 270 Graphics2DMap::iterator iter2d(&graphic2d_contexts_); | |
| 271 while (!iter2d.IsAtEnd()) { | |
| 272 iter2d.GetCurrentValue()->RenderViewFlushedPaint(); | |
| 273 iter2d.Advance(); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 WebPluginResourceClient* WebPluginDelegatePepper::CreateResourceClient( | |
| 278 unsigned long resource_id, const GURL& url, int notify_id) { | |
| 279 return instance()->CreateStream(resource_id, url, std::string(), notify_id); | |
| 280 } | |
| 281 | |
| 282 WebPluginResourceClient* WebPluginDelegatePepper::CreateSeekableResourceClient( | |
| 283 unsigned long resource_id, int range_request_id) { | |
| 284 return instance()->GetRangeRequest(range_request_id); | |
| 285 } | |
| 286 | |
| 287 bool WebPluginDelegatePepper::StartFind(const string16& search_text, | |
| 288 bool case_sensitive, | |
| 289 int identifier) { | |
| 290 if (!GetFindExtensions()) | |
| 291 return false; | |
| 292 find_identifier_ = identifier; | |
| 293 GetFindExtensions()->startFind( | |
| 294 instance()->npp(), UTF16ToUTF8(search_text).c_str(), case_sensitive); | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 void WebPluginDelegatePepper::SelectFindResult(bool forward) { | |
| 299 GetFindExtensions()->selectFindResult(instance()->npp(), forward); | |
| 300 } | |
| 301 | |
| 302 void WebPluginDelegatePepper::StopFind() { | |
| 303 find_identifier_ = -1; | |
| 304 GetFindExtensions()->stopFind(instance()->npp()); | |
| 305 } | |
| 306 | |
| 307 void WebPluginDelegatePepper::NumberOfFindResultsChanged(int total, | |
| 308 bool final_result) { | |
| 309 DCHECK(find_identifier_ != -1); | |
| 310 | |
| 311 render_view_->reportFindInPageMatchCount( | |
| 312 find_identifier_, total, final_result); | |
| 313 } | |
| 314 | |
| 315 void WebPluginDelegatePepper::SelectedFindResultChanged(int index) { | |
| 316 render_view_->reportFindInPageSelection( | |
| 317 find_identifier_, index + 1, WebKit::WebRect()); | |
| 318 } | |
| 319 | |
| 320 bool WebPluginDelegatePepper::ChooseFile(const char* mime_types, | |
| 321 int mode, | |
| 322 NPChooseFileCallback callback, | |
| 323 void* user_data) { | |
| 324 if (!render_view_ || !callback) | |
| 325 return false; | |
| 326 | |
| 327 if (current_choose_file_callback_) | |
| 328 return false; // Reentrant call to browse, only one can be outstanding | |
| 329 // per plugin. | |
| 330 | |
| 331 // TODO(brettw) do something with the mime types! | |
| 332 current_choose_file_callback_ = callback; | |
| 333 current_choose_file_user_data_ = user_data; | |
| 334 | |
| 335 ViewHostMsg_RunFileChooser_Params ipc_params; | |
| 336 switch (mode) { | |
| 337 case NPChooseFile_Open: | |
| 338 ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Open; | |
| 339 break; | |
| 340 case NPChooseFile_OpenMultiple: | |
| 341 ipc_params.mode = ViewHostMsg_RunFileChooser_Params::OpenMultiple; | |
| 342 break; | |
| 343 case NPChooseFile_Save: | |
| 344 ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Save; | |
| 345 break; | |
| 346 default: | |
| 347 return false; | |
| 348 } | |
| 349 return render_view_->ScheduleFileChooser(ipc_params, this); | |
| 350 } | |
| 351 | |
| 352 NPWidgetExtensions* WebPluginDelegatePepper::GetWidgetExtensions() { | |
| 353 return PepperWidget::GetWidgetExtensions(); | |
| 354 } | |
| 355 | |
| 356 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \ | |
| 357 COMPILE_ASSERT(int(WebCursorInfo::webkit_name) == int(np_name), \ | |
| 358 mismatching_enums) | |
| 359 | |
| 360 COMPILE_ASSERT_MATCHING_ENUM(TypePointer, NPCursorTypePointer); | |
| 361 COMPILE_ASSERT_MATCHING_ENUM(TypeCross, NPCursorTypeCross); | |
| 362 COMPILE_ASSERT_MATCHING_ENUM(TypeHand, NPCursorTypeHand); | |
| 363 COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, NPCursorTypeIBeam); | |
| 364 COMPILE_ASSERT_MATCHING_ENUM(TypeWait, NPCursorTypeWait); | |
| 365 COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, NPCursorTypeHelp); | |
| 366 COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, NPCursorTypeEastResize); | |
| 367 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, NPCursorTypeNorthResize); | |
| 368 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize, NPCursorTypeNorthEastResize); | |
| 369 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize, NPCursorTypeNorthWestResize); | |
| 370 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, NPCursorTypeSouthResize); | |
| 371 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize, NPCursorTypeSouthEastResize); | |
| 372 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize, NPCursorTypeSouthWestResize); | |
| 373 COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, NPCursorTypeWestResize); | |
| 374 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize, | |
| 375 NPCursorTypeNorthSouthResize); | |
| 376 COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, NPCursorTypeEastWestResize); | |
| 377 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize, | |
| 378 NPCursorTypeNorthEastSouthWestResize); | |
| 379 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize, | |
| 380 NPCursorTypeNorthWestSouthEastResize); | |
| 381 COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, NPCursorTypeColumnResize); | |
| 382 COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, NPCursorTypeRowResize); | |
| 383 COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, NPCursorTypeMiddlePanning); | |
| 384 COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, NPCursorTypeEastPanning); | |
| 385 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, NPCursorTypeNorthPanning); | |
| 386 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning, | |
| 387 NPCursorTypeNorthEastPanning); | |
| 388 COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning, | |
| 389 NPCursorTypeNorthWestPanning); | |
| 390 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, NPCursorTypeSouthPanning); | |
| 391 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning, | |
| 392 NPCursorTypeSouthEastPanning); | |
| 393 COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning, | |
| 394 NPCursorTypeSouthWestPanning); | |
| 395 COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, NPCursorTypeWestPanning); | |
| 396 COMPILE_ASSERT_MATCHING_ENUM(TypeMove, NPCursorTypeMove); | |
| 397 COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, NPCursorTypeVerticalText); | |
| 398 COMPILE_ASSERT_MATCHING_ENUM(TypeCell, NPCursorTypeCell); | |
| 399 COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, NPCursorTypeContextMenu); | |
| 400 COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, NPCursorTypeAlias); | |
| 401 COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, NPCursorTypeProgress); | |
| 402 COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, NPCursorTypeNoDrop); | |
| 403 COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, NPCursorTypeCopy); | |
| 404 COMPILE_ASSERT_MATCHING_ENUM(TypeNone, NPCursorTypeNone); | |
| 405 COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, NPCursorTypeNotAllowed); | |
| 406 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, NPCursorTypeZoomIn); | |
| 407 COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, NPCursorTypeZoomOut); | |
| 408 | |
| 409 bool WebPluginDelegatePepper::SetCursor(NPCursorType type) { | |
| 410 cursor_.reset(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type))); | |
| 411 return true; | |
| 412 } | |
| 413 | |
| 414 NPError NPMatchFontWithFallback(NPP instance, | |
| 415 const NPFontDescription* description, | |
| 416 NPFontID* id) { | |
| 417 #if defined(OS_LINUX) | |
| 418 int fd = renderer_sandbox_support::MatchFontWithFallback( | |
| 419 description->face, description->weight >= 700, description->italic, | |
| 420 description->charset); | |
| 421 if (fd == -1) | |
| 422 return NPERR_GENERIC_ERROR; | |
| 423 *id = fd; | |
| 424 return NPERR_NO_ERROR; | |
| 425 #else | |
| 426 NOTIMPLEMENTED(); | |
| 427 return NPERR_GENERIC_ERROR; | |
| 428 #endif | |
| 429 } | |
| 430 | |
| 431 NPError GetFontTable(NPP instance, | |
| 432 NPFontID id, | |
| 433 uint32_t table, | |
| 434 void* output, | |
| 435 size_t* output_length) { | |
| 436 #if defined(OS_LINUX) | |
| 437 bool rv = renderer_sandbox_support::GetFontTable( | |
| 438 id, table, static_cast<uint8_t*>(output), output_length); | |
| 439 return rv ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR; | |
| 440 #else | |
| 441 NOTIMPLEMENTED(); | |
| 442 return NPERR_GENERIC_ERROR; | |
| 443 #endif | |
| 444 } | |
| 445 | |
| 446 NPError NPDestroyFont(NPP instance, NPFontID id) { | |
| 447 #if defined(OS_LINUX) | |
| 448 close(id); | |
| 449 return NPERR_NO_ERROR; | |
| 450 #else | |
| 451 NOTIMPLEMENTED(); | |
| 452 return NPERR_GENERIC_ERROR; | |
| 453 #endif | |
| 454 } | |
| 455 | |
| 456 NPFontExtensions g_font_extensions = { | |
| 457 NPMatchFontWithFallback, | |
| 458 GetFontTable, | |
| 459 NPDestroyFont | |
| 460 }; | |
| 461 | |
| 462 NPFontExtensions* WebPluginDelegatePepper::GetFontExtensions() { | |
| 463 return &g_font_extensions; | |
| 464 } | |
| 465 | |
| 466 void WebPluginDelegatePepper::SetZoomFactor(float scale, bool text_only) { | |
| 467 NPPExtensions* extensions = NULL; | |
| 468 instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); | |
| 469 if (extensions && extensions->zoom) | |
| 470 extensions->zoom(instance()->npp(), scale, text_only); | |
| 471 } | |
| 472 | |
| 473 bool WebPluginDelegatePepper::HasSelection() const { | |
| 474 return !GetSelectedText(false).empty(); | |
| 475 } | |
| 476 | |
| 477 string16 WebPluginDelegatePepper::GetSelectionAsText() const { | |
| 478 return GetSelectedText(false); | |
| 479 } | |
| 480 | |
| 481 string16 WebPluginDelegatePepper::GetSelectionAsMarkup() const { | |
| 482 return GetSelectedText(true); | |
| 483 } | |
| 484 | |
| 485 string16 WebPluginDelegatePepper::GetSelectedText(bool html) const { | |
| 486 NPPExtensions* extensions = NULL; | |
| 487 instance_->NPP_GetValue(NPPVPepperExtensions, &extensions); | |
| 488 if (!extensions || !extensions->getSelection) | |
| 489 return string16(); | |
| 490 | |
| 491 void* text; | |
| 492 NPSelectionType type = html ? NPSelectionTypeHTML : NPSelectionTypePlainText; | |
| 493 NPP npp = instance_->npp(); | |
| 494 if (extensions->getSelection(npp, &type, &text) != NPERR_NO_ERROR) | |
| 495 return string16(); | |
| 496 | |
| 497 string16 rv = UTF8ToUTF16(static_cast<char*>(text)); | |
| 498 webkit::npapi::PluginHost::Singleton()->host_functions()->memfree(text); | |
| 499 return rv; | |
| 500 } | |
| 501 | |
| 502 NPError WebPluginDelegatePepper::Device2DQueryCapability(int32 capability, | |
| 503 int32* value) { | |
| 504 return NPERR_GENERIC_ERROR; | |
| 505 } | |
| 506 | |
| 507 NPError WebPluginDelegatePepper::Device2DQueryConfig( | |
| 508 const NPDeviceContext2DConfig* request, | |
| 509 NPDeviceContext2DConfig* obtain) { | |
| 510 return NPERR_GENERIC_ERROR; | |
| 511 } | |
| 512 | |
| 513 NPError WebPluginDelegatePepper::Device2DInitializeContext( | |
| 514 const NPDeviceContext2DConfig* config, | |
| 515 NPDeviceContext2D* context) { | |
| 516 | |
| 517 if (!render_view_) { | |
| 518 return NPERR_GENERIC_ERROR; | |
| 519 } | |
| 520 | |
| 521 // This is a windowless plugin, so set it to have no handle. Defer this | |
| 522 // until we know the plugin will use the 2D device. | |
| 523 plugin_->SetWindow(gfx::kNullPluginWindow); | |
| 524 | |
| 525 scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); | |
| 526 NPError status = g2d->Initialize(window_rect_, config, context); | |
| 527 if (NPERR_NO_ERROR == status) { | |
| 528 context->reserved = reinterpret_cast<void *>( | |
| 529 graphic2d_contexts_.Add(g2d.release())); | |
| 530 } | |
| 531 return status; | |
| 532 } | |
| 533 | |
| 534 NPError WebPluginDelegatePepper::Device2DSetStateContext( | |
| 535 NPDeviceContext2D* context, | |
| 536 int32 state, | |
| 537 intptr_t value) { | |
| 538 return NPERR_GENERIC_ERROR; | |
| 539 } | |
| 540 | |
| 541 NPError WebPluginDelegatePepper::Device2DGetStateContext( | |
| 542 NPDeviceContext2D* context, | |
| 543 int32 state, | |
| 544 intptr_t* value) { | |
| 545 if (state == NPExtensionsReservedStateSharedMemory) { | |
| 546 if (!context) | |
| 547 return NPERR_INVALID_PARAM; | |
| 548 Graphics2DDeviceContext* ctx = GetGraphicsContext(context); | |
| 549 if (!ctx) | |
| 550 return NPERR_INVALID_PARAM; | |
| 551 *value = reinterpret_cast<intptr_t>(ctx->transport_dib()); | |
| 552 return NPERR_NO_ERROR; | |
| 553 } else if (state == NPExtensionsReservedStateSharedMemoryChecksum) { | |
| 554 if (!context) | |
| 555 return NPERR_INVALID_PARAM; | |
| 556 // Bytes per pixel. | |
| 557 static const int kBytesPixel = 4; | |
| 558 int32 row_count = context->dirty.bottom - context->dirty.top; | |
| 559 int32 stride = context->dirty.right - context->dirty.left; | |
| 560 size_t length = row_count * stride * kBytesPixel; | |
| 561 MD5Digest md5_result; // 128-bit digest | |
| 562 MD5Sum(context->region, length, &md5_result); | |
| 563 std::string hex_md5 = MD5DigestToBase16(md5_result); | |
| 564 // Return the least significant 8 characters (i.e. 4 bytes) | |
| 565 // of the 32 character hexadecimal result as an int. | |
| 566 int int_val; | |
| 567 DCHECK_EQ(hex_md5.length(), 32u); | |
| 568 base::HexStringToInt(hex_md5.begin() + 24, hex_md5.end(), &int_val); | |
| 569 *value = int_val; | |
| 570 return NPERR_NO_ERROR; | |
| 571 } | |
| 572 return NPERR_GENERIC_ERROR; | |
| 573 } | |
| 574 | |
| 575 NPError WebPluginDelegatePepper::Device2DFlushContext( | |
| 576 NPP id, | |
| 577 NPDeviceContext2D* context, | |
| 578 NPDeviceFlushContextCallbackPtr callback, | |
| 579 void* user_data) { | |
| 580 if (!context) | |
| 581 return NPERR_INVALID_PARAM; | |
| 582 | |
| 583 Graphics2DDeviceContext* ctx = graphic2d_contexts_.Lookup( | |
| 584 reinterpret_cast<intptr_t>(context->reserved)); | |
| 585 if (!ctx) | |
| 586 return NPERR_INVALID_PARAM; // TODO(brettw) call callback. | |
| 587 | |
| 588 return ctx->Flush(&committed_bitmap_, context, callback, id, user_data); | |
| 589 } | |
| 590 | |
| 591 NPError WebPluginDelegatePepper::Device2DDestroyContext( | |
| 592 NPDeviceContext2D* context) { | |
| 593 | |
| 594 if (!context || !graphic2d_contexts_.Lookup( | |
| 595 reinterpret_cast<intptr_t>(context->reserved))) { | |
| 596 return NPERR_INVALID_PARAM; | |
| 597 } | |
| 598 graphic2d_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); | |
| 599 memset(context, 0, sizeof(NPDeviceContext2D)); | |
| 600 return NPERR_NO_ERROR; | |
| 601 } | |
| 602 | |
| 603 Graphics2DDeviceContext* WebPluginDelegatePepper::GetGraphicsContext( | |
| 604 NPDeviceContext2D* context) { | |
| 605 return graphic2d_contexts_.Lookup( | |
| 606 reinterpret_cast<intptr_t>(context->reserved)); | |
| 607 } | |
| 608 | |
| 609 NPError WebPluginDelegatePepper::Device3DQueryCapability(int32 capability, | |
| 610 int32* value) { | |
| 611 return NPERR_GENERIC_ERROR; | |
| 612 } | |
| 613 | |
| 614 NPError WebPluginDelegatePepper::Device3DQueryConfig( | |
| 615 const NPDeviceContext3DConfig* request, | |
| 616 NPDeviceContext3DConfig* obtain) { | |
| 617 return NPERR_GENERIC_ERROR; | |
| 618 } | |
| 619 | |
| 620 NPError WebPluginDelegatePepper::Device3DInitializeContext( | |
| 621 const NPDeviceContext3DConfig* config, | |
| 622 NPDeviceContext3D* context) { | |
| 623 return NPERR_GENERIC_ERROR; | |
| 624 } | |
| 625 | |
| 626 NPError WebPluginDelegatePepper::Device3DSetStateContext( | |
| 627 NPDeviceContext3D* context, | |
| 628 int32 state, | |
| 629 intptr_t value) { | |
| 630 return NPERR_GENERIC_ERROR; | |
| 631 } | |
| 632 | |
| 633 NPError WebPluginDelegatePepper::Device3DGetStateContext( | |
| 634 NPDeviceContext3D* context, | |
| 635 int32 state, | |
| 636 intptr_t* value) { | |
| 637 return NPERR_GENERIC_ERROR; | |
| 638 } | |
| 639 | |
| 640 NPError WebPluginDelegatePepper::Device3DFlushContext( | |
| 641 NPP id, | |
| 642 NPDeviceContext3D* context, | |
| 643 NPDeviceFlushContextCallbackPtr callback, | |
| 644 void* user_data) { | |
| 645 return NPERR_GENERIC_ERROR; | |
| 646 } | |
| 647 | |
| 648 NPError WebPluginDelegatePepper::Device3DDestroyContext( | |
| 649 NPDeviceContext3D* context) { | |
| 650 return NPERR_GENERIC_ERROR; | |
| 651 } | |
| 652 | |
| 653 NPError WebPluginDelegatePepper::Device3DCreateBuffer( | |
| 654 NPDeviceContext3D* context, | |
| 655 size_t size, | |
| 656 int32* id) { | |
| 657 return NPERR_GENERIC_ERROR; | |
| 658 } | |
| 659 | |
| 660 NPError WebPluginDelegatePepper::Device3DDestroyBuffer( | |
| 661 NPDeviceContext3D* context, | |
| 662 int32 id) { | |
| 663 return NPERR_GENERIC_ERROR; | |
| 664 } | |
| 665 | |
| 666 NPError WebPluginDelegatePepper::Device3DMapBuffer( | |
| 667 NPDeviceContext3D* context, | |
| 668 int32 id, | |
| 669 NPDeviceBuffer* np_buffer) { | |
| 670 return NPERR_GENERIC_ERROR; | |
| 671 } | |
| 672 | |
| 673 NPError WebPluginDelegatePepper::Device3DGetNumConfigs(int32* num_configs) { | |
| 674 return NPERR_GENERIC_ERROR; | |
| 675 } | |
| 676 | |
| 677 NPError WebPluginDelegatePepper::Device3DGetConfigAttribs( | |
| 678 int32 config, | |
| 679 int32* attrib_list) { | |
| 680 return NPERR_GENERIC_ERROR; | |
| 681 } | |
| 682 | |
| 683 NPError WebPluginDelegatePepper::Device3DCreateContext( | |
| 684 int32 config, | |
| 685 const int32* attrib_list, | |
| 686 NPDeviceContext3D** context) { | |
| 687 return NPERR_GENERIC_ERROR; | |
| 688 } | |
| 689 | |
| 690 NPError WebPluginDelegatePepper::Device3DRegisterCallback( | |
| 691 NPP id, | |
| 692 NPDeviceContext3D* context, | |
| 693 int32 callback_type, | |
| 694 NPDeviceGenericCallbackPtr callback, | |
| 695 void* callback_data) { | |
| 696 return NPERR_GENERIC_ERROR; | |
| 697 } | |
| 698 | |
| 699 NPError WebPluginDelegatePepper::Device3DSynchronizeContext( | |
| 700 NPP id, | |
| 701 NPDeviceContext3D* context, | |
| 702 NPDeviceSynchronizationMode mode, | |
| 703 const int32* input_attrib_list, | |
| 704 int32* output_attrib_list, | |
| 705 NPDeviceSynchronizeContextCallbackPtr callback, | |
| 706 void* callback_data) { | |
| 707 return NPERR_GENERIC_ERROR; | |
| 708 } | |
| 709 | |
| 710 NPError WebPluginDelegatePepper::DeviceAudioQueryCapability(int32 capability, | |
| 711 int32* value) { | |
| 712 // TODO(neb,cpu) implement QueryCapability | |
| 713 return NPERR_GENERIC_ERROR; | |
| 714 } | |
| 715 | |
| 716 NPError WebPluginDelegatePepper::DeviceAudioQueryConfig( | |
| 717 const NPDeviceContextAudioConfig* request, | |
| 718 NPDeviceContextAudioConfig* obtain) { | |
| 719 // TODO(neb,cpu) implement QueryConfig | |
| 720 return NPERR_GENERIC_ERROR; | |
| 721 } | |
| 722 | |
| 723 NPError WebPluginDelegatePepper::DeviceAudioInitializeContext( | |
| 724 const NPDeviceContextAudioConfig* config, | |
| 725 NPDeviceContextAudio* context) { | |
| 726 | |
| 727 if (!render_view_) { | |
| 728 return NPERR_GENERIC_ERROR; | |
| 729 } | |
| 730 | |
| 731 scoped_ptr<AudioDeviceContext> audio(new AudioDeviceContext()); | |
| 732 NPError status = audio->Initialize(render_view_->audio_message_filter(), | |
| 733 config, context); | |
| 734 if (NPERR_NO_ERROR == status) { | |
| 735 context->reserved = | |
| 736 reinterpret_cast<void *>(audio_contexts_.Add(audio.release())); | |
| 737 } | |
| 738 return status; | |
| 739 } | |
| 740 | |
| 741 NPError WebPluginDelegatePepper::DeviceAudioSetStateContext( | |
| 742 NPDeviceContextAudio* context, | |
| 743 int32 state, | |
| 744 intptr_t value) { | |
| 745 // TODO(neb,cpu) implement SetStateContext | |
| 746 return NPERR_GENERIC_ERROR; | |
| 747 } | |
| 748 | |
| 749 NPError WebPluginDelegatePepper::DeviceAudioGetStateContext( | |
| 750 NPDeviceContextAudio* context, | |
| 751 int32 state, | |
| 752 intptr_t* value) { | |
| 753 if (state == NPExtensionsReservedStateSharedMemory) { | |
| 754 if (!context) | |
| 755 return NPERR_INVALID_PARAM; | |
| 756 AudioDeviceContext* ctx = audio_contexts_.Lookup( | |
| 757 reinterpret_cast<intptr_t>(context->reserved)); | |
| 758 if (!ctx) | |
| 759 return NPERR_INVALID_PARAM; | |
| 760 *value = reinterpret_cast<intptr_t>(ctx->shared_memory()); | |
| 761 return NPERR_NO_ERROR; | |
| 762 } else if (state == NPExtensionsReservedStateSharedMemorySize) { | |
| 763 if (!context) | |
| 764 return NPERR_INVALID_PARAM; | |
| 765 AudioDeviceContext* ctx = audio_contexts_.Lookup( | |
| 766 reinterpret_cast<intptr_t>(context->reserved)); | |
| 767 if (!ctx) | |
| 768 return NPERR_INVALID_PARAM; | |
| 769 *value = static_cast<intptr_t>(ctx->shared_memory_size()); | |
| 770 return NPERR_NO_ERROR; | |
| 771 } else if (state == NPExtensionsReservedStateSyncChannel) { | |
| 772 if (!context) | |
| 773 return NPERR_INVALID_PARAM; | |
| 774 AudioDeviceContext* ctx = audio_contexts_.Lookup( | |
| 775 reinterpret_cast<intptr_t>(context->reserved)); | |
| 776 if (!ctx) | |
| 777 return NPERR_INVALID_PARAM; | |
| 778 *value = reinterpret_cast<intptr_t>(ctx->socket()); | |
| 779 return NPERR_NO_ERROR; | |
| 780 } | |
| 781 return NPERR_GENERIC_ERROR; | |
| 782 } | |
| 783 | |
| 784 NPError WebPluginDelegatePepper::DeviceAudioFlushContext( | |
| 785 NPP id, | |
| 786 NPDeviceContextAudio* context, | |
| 787 NPDeviceFlushContextCallbackPtr callback, | |
| 788 void* user_data) { | |
| 789 // TODO(neb,cpu) implement FlushContext | |
| 790 return NPERR_GENERIC_ERROR; | |
| 791 } | |
| 792 | |
| 793 NPError WebPluginDelegatePepper::DeviceAudioDestroyContext( | |
| 794 NPDeviceContextAudio* context) { | |
| 795 if (!context || !audio_contexts_.Lookup( | |
| 796 reinterpret_cast<intptr_t>(context->reserved))) { | |
| 797 return NPERR_INVALID_PARAM; | |
| 798 } | |
| 799 audio_contexts_.Remove(reinterpret_cast<intptr_t>(context->reserved)); | |
| 800 memset(context, 0, sizeof(NPDeviceContextAudio)); | |
| 801 return NPERR_NO_ERROR; | |
| 802 } | |
| 803 | |
| 804 bool WebPluginDelegatePepper::PrintSupportsPrintExtension() { | |
| 805 return GetPrintExtensions() != NULL; | |
| 806 } | |
| 807 | |
| 808 int WebPluginDelegatePepper::PrintBegin(const gfx::Rect& printable_area, | |
| 809 int printer_dpi) { | |
| 810 int32 num_pages = 0; | |
| 811 NPPPrintExtensions* print_extensions = GetPrintExtensions(); | |
| 812 if (print_extensions) { | |
| 813 NPRect np_printable_area = {0}; | |
| 814 np_printable_area.left = printable_area.x(); | |
| 815 np_printable_area.top = printable_area.y(); | |
| 816 np_printable_area.right = np_printable_area.left + printable_area.width(); | |
| 817 np_printable_area.bottom = np_printable_area.top + printable_area.height(); | |
| 818 if (NPERR_NO_ERROR == print_extensions->printBegin(instance()->npp(), | |
| 819 &np_printable_area, | |
| 820 printer_dpi, | |
| 821 &num_pages)) { | |
| 822 current_printable_area_ = printable_area; | |
| 823 current_printer_dpi_ = printer_dpi; | |
| 824 } | |
| 825 } | |
| 826 #if defined (OS_LINUX) | |
| 827 num_pages_ = num_pages; | |
| 828 pdf_output_done_ = false; | |
| 829 #endif // (OS_LINUX) | |
| 830 return num_pages; | |
| 831 } | |
| 832 | |
| 833 bool WebPluginDelegatePepper::VectorPrintPage(int page_number, | |
| 834 WebKit::WebCanvas* canvas) { | |
| 835 NPPPrintExtensions* print_extensions = GetPrintExtensions(); | |
| 836 if (!print_extensions) | |
| 837 return false; | |
| 838 #if defined(OS_WIN) | |
| 839 // For Windows, we need the PDF DLL to render the output PDF to a DC. | |
| 840 FilePath pdf_path; | |
| 841 PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path); | |
| 842 HMODULE pdf_module = GetModuleHandle(pdf_path.value().c_str()); | |
| 843 if (!pdf_module) | |
| 844 return false; | |
| 845 RenderPDFPageToDCProc render_proc = | |
| 846 reinterpret_cast<RenderPDFPageToDCProc>( | |
| 847 GetProcAddress(pdf_module, "RenderPDFPageToDC")); | |
| 848 if (!render_proc) | |
| 849 return false; | |
| 850 #endif // defined(OS_WIN) | |
| 851 | |
| 852 unsigned char* pdf_output = NULL; | |
| 853 int32 output_size = 0; | |
| 854 NPPrintPageNumberRange page_range; | |
| 855 #if defined(OS_LINUX) | |
| 856 // On Linux we will try and output all pages as PDF in the first call to | |
| 857 // PrintPage. This is a temporary hack. | |
| 858 // TODO(sanjeevr): Remove this hack and fix this by changing the print | |
| 859 // interfaces for WebFrame and WebPlugin. | |
| 860 if (page_number != 0) | |
| 861 return pdf_output_done_; | |
| 862 page_range.firstPageNumber = 0; | |
| 863 page_range.lastPageNumber = num_pages_ - 1; | |
| 864 #else // defined(OS_LINUX) | |
| 865 page_range.firstPageNumber = page_range.lastPageNumber = page_number; | |
| 866 #endif // defined(OS_LINUX) | |
| 867 NPError err = print_extensions->printPagesAsPDF(instance()->npp(), | |
| 868 &page_range, 1, | |
| 869 &pdf_output, &output_size); | |
| 870 if (err != NPERR_NO_ERROR) | |
| 871 return false; | |
| 872 | |
| 873 bool ret = false; | |
| 874 #if defined(OS_LINUX) | |
| 875 // On Linux we need to get the backing PdfPsMetafile and write the bits | |
| 876 // directly. | |
| 877 cairo_t* context = canvas->beginPlatformPaint(); | |
| 878 printing::NativeMetafile* metafile = | |
| 879 printing::PdfPsMetafile::FromCairoContext(context); | |
| 880 DCHECK(metafile); | |
| 881 if (metafile) { | |
| 882 ret = metafile->SetRawData(pdf_output, output_size); | |
| 883 if (ret) | |
| 884 pdf_output_done_ = true; | |
| 885 } | |
| 886 canvas->endPlatformPaint(); | |
| 887 #elif defined(OS_MACOSX) | |
| 888 scoped_ptr<printing::NativeMetafile> metafile( | |
| 889 printing::NativeMetafileFactory::CreateMetafile()); | |
| 890 // Create a PDF metafile and render from there into the passed in context. | |
| 891 if (metafile->Init(pdf_output, output_size)) { | |
| 892 // Flip the transform. | |
| 893 CGContextSaveGState(canvas); | |
| 894 CGContextTranslateCTM(canvas, 0, current_printable_area_.height()); | |
| 895 CGContextScaleCTM(canvas, 1.0, -1.0); | |
| 896 ret = metafile->RenderPage(1, canvas, current_printable_area_.ToCGRect(), | |
| 897 true, false, true, true); | |
| 898 CGContextRestoreGState(canvas); | |
| 899 } | |
| 900 #elif defined(OS_WIN) | |
| 901 // On Windows, we now need to render the PDF to the DC that backs the | |
| 902 // supplied canvas. | |
| 903 skia::VectorPlatformDevice& device = | |
| 904 static_cast<skia::VectorPlatformDevice&>( | |
| 905 canvas->getTopPlatformDevice()); | |
| 906 HDC dc = device.getBitmapDC(); | |
| 907 gfx::Size size_in_pixels; | |
| 908 size_in_pixels.set_width( | |
| 909 printing::ConvertUnit(current_printable_area_.width(), | |
| 910 static_cast<int>(kPointsPerInch), | |
| 911 current_printer_dpi_)); | |
| 912 size_in_pixels.set_height( | |
| 913 printing::ConvertUnit(current_printable_area_.height(), | |
| 914 static_cast<int>(kPointsPerInch), | |
| 915 current_printer_dpi_)); | |
| 916 // We need to render using the actual printer DPI (rendering to a smaller | |
| 917 // set of pixels leads to a blurry output). However, we need to counter the | |
| 918 // scaling up that will happen in the browser. | |
| 919 XFORM xform = {0}; | |
| 920 xform.eM11 = xform.eM22 = kPointsPerInch / current_printer_dpi_; | |
| 921 ModifyWorldTransform(dc, &xform, MWT_LEFTMULTIPLY); | |
| 922 | |
| 923 ret = render_proc(pdf_output, output_size, 0, dc, current_printer_dpi_, | |
| 924 current_printer_dpi_, 0, 0, size_in_pixels.width(), | |
| 925 size_in_pixels.height(), true, false, true, true); | |
| 926 #endif // defined(OS_WIN) | |
| 927 | |
| 928 webkit::npapi::PluginHost::Singleton()->host_functions()->memfree( | |
| 929 pdf_output); | |
| 930 return ret; | |
| 931 } | |
| 932 | |
| 933 bool WebPluginDelegatePepper::PrintPage(int page_number, | |
| 934 WebKit::WebCanvas* canvas) { | |
| 935 NPPPrintExtensions* print_extensions = GetPrintExtensions(); | |
| 936 if (!print_extensions) | |
| 937 return false; | |
| 938 | |
| 939 // First try and use vector print. | |
| 940 if (VectorPrintPage(page_number, canvas)) | |
| 941 return true; | |
| 942 | |
| 943 DCHECK(!current_printable_area_.IsEmpty()); | |
| 944 | |
| 945 // Calculate the width and height needed for the raster image. | |
| 946 NPRect np_printable_area = {0}; | |
| 947 np_printable_area.left = current_printable_area_.x(); | |
| 948 np_printable_area.top = current_printable_area_.y(); | |
| 949 np_printable_area.right = | |
| 950 current_printable_area_.x() + current_printable_area_.width(); | |
| 951 np_printable_area.bottom = | |
| 952 current_printable_area_.y() + current_printable_area_.height(); | |
| 953 gfx::Size size_in_pixels; | |
| 954 if (!CalculatePrintedPageDimensions(page_number, print_extensions, | |
| 955 &size_in_pixels)) { | |
| 956 return false; | |
| 957 } | |
| 958 | |
| 959 // Now print the page onto a 2d device context. | |
| 960 scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); | |
| 961 NPDeviceContext2DConfig config; | |
| 962 NPDeviceContext2D context; | |
| 963 gfx::Rect surface_rect(size_in_pixels); | |
| 964 NPError err = g2d->Initialize(surface_rect, &config, &context); | |
| 965 if (err != NPERR_NO_ERROR) { | |
| 966 NOTREACHED(); | |
| 967 return false; | |
| 968 } | |
| 969 err = print_extensions->printPageRaster(instance()->npp(), page_number, | |
| 970 &context); | |
| 971 if (err != NPERR_NO_ERROR) | |
| 972 return false; | |
| 973 | |
| 974 SkBitmap committed; | |
| 975 committed.setConfig(SkBitmap::kARGB_8888_Config, size_in_pixels.width(), | |
| 976 size_in_pixels.height()); | |
| 977 committed.allocPixels(); | |
| 978 err = g2d->Flush(&committed, &context, NULL, instance()->npp(), NULL); | |
| 979 if (err != NPERR_NO_ERROR) { | |
| 980 NOTREACHED(); | |
| 981 return false; | |
| 982 } | |
| 983 // Draw the printed image into the supplied canvas. | |
| 984 SkIRect src_rect; | |
| 985 src_rect.set(0, 0, size_in_pixels.width(), size_in_pixels.height()); | |
| 986 SkRect dest_rect; | |
| 987 dest_rect.set(SkIntToScalar(current_printable_area_.x()), | |
| 988 SkIntToScalar(current_printable_area_.y()), | |
| 989 SkIntToScalar(current_printable_area_.x() + | |
| 990 current_printable_area_.width()), | |
| 991 SkIntToScalar(current_printable_area_.y() + | |
| 992 current_printable_area_.height())); | |
| 993 bool draw_to_canvas = true; | |
| 994 #if defined(OS_WIN) | |
| 995 // Since this is a raster output, the size of the bitmap can be | |
| 996 // huge (especially at high printer DPIs). On Windows, this can | |
| 997 // result in a HUGE EMF (on Mac and Linux the output goes to PDF | |
| 998 // which appears to Flate compress the bitmap). So, if this bitmap | |
| 999 // is larger than 20 MB, we save the bitmap as a JPEG into the EMF | |
| 1000 // DC. Note: We chose JPEG over PNG because JPEG compression seems | |
| 1001 // way faster (about 4 times faster). | |
| 1002 static const int kCompressionThreshold = 20 * 1024 * 1024; | |
| 1003 if (committed.getSize() > kCompressionThreshold) { | |
| 1004 DrawJPEGToPlatformDC(committed, current_printable_area_, canvas); | |
| 1005 draw_to_canvas = false; | |
| 1006 } | |
| 1007 #endif // defined(OS_WIN) | |
| 1008 #if defined(OS_MACOSX) | |
| 1009 draw_to_canvas = false; | |
| 1010 DrawSkBitmapToCanvas(committed, canvas, current_printable_area_, | |
| 1011 current_printable_area_.height()); | |
| 1012 // See comments in the header file. | |
| 1013 last_printed_page_ = committed; | |
| 1014 #else // defined(OS_MACOSX) | |
| 1015 if (draw_to_canvas) | |
| 1016 canvas->drawBitmapRect(committed, &src_rect, dest_rect); | |
| 1017 #endif // defined(OS_MACOSX) | |
| 1018 return true; | |
| 1019 } | |
| 1020 | |
| 1021 void WebPluginDelegatePepper::PrintEnd() { | |
| 1022 NPPPrintExtensions* print_extensions = GetPrintExtensions(); | |
| 1023 if (print_extensions) | |
| 1024 print_extensions->printEnd(instance()->npp()); | |
| 1025 current_printable_area_ = gfx::Rect(); | |
| 1026 current_printer_dpi_ = -1; | |
| 1027 #if defined(OS_MACOSX) | |
| 1028 last_printed_page_ = SkBitmap(); | |
| 1029 #elif defined(OS_LINUX) | |
| 1030 num_pages_ = 0; | |
| 1031 pdf_output_done_ = false; | |
| 1032 #endif // defined(OS_LINUX) | |
| 1033 } | |
| 1034 | |
| 1035 WebPluginDelegatePepper::WebPluginDelegatePepper( | |
| 1036 const base::WeakPtr<RenderView>& render_view, | |
| 1037 webkit::npapi::PluginInstance *instance) | |
| 1038 : render_view_(render_view), | |
| 1039 plugin_(NULL), | |
| 1040 instance_(instance), | |
| 1041 current_printer_dpi_(-1), | |
| 1042 #if defined (OS_LINUX) | |
| 1043 num_pages_(0), | |
| 1044 pdf_output_done_(false), | |
| 1045 #endif // (OS_LINUX) | |
| 1046 find_identifier_(-1), | |
| 1047 current_choose_file_callback_(NULL), | |
| 1048 current_choose_file_user_data_(NULL) { | |
| 1049 // For now we keep a window struct, although it isn't used. | |
| 1050 memset(&window_, 0, sizeof(window_)); | |
| 1051 // All Pepper plugins are windowless and transparent. | |
| 1052 // TODO(sehr): disable resetting these NPPVs by plugins. | |
| 1053 instance->set_windowless(true); | |
| 1054 instance->set_transparent(true); | |
| 1055 } | |
| 1056 | |
| 1057 WebPluginDelegatePepper::~WebPluginDelegatePepper() { | |
| 1058 DestroyInstance(); | |
| 1059 | |
| 1060 if (render_view_) | |
| 1061 render_view_->OnPepperPluginDestroy(this); | |
| 1062 } | |
| 1063 | |
| 1064 void WebPluginDelegatePepper::ForwardSetWindow() { | |
| 1065 window_.clipRect.top = clip_rect_.y(); | |
| 1066 window_.clipRect.left = clip_rect_.x(); | |
| 1067 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); | |
| 1068 window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); | |
| 1069 window_.height = window_rect_.height(); | |
| 1070 window_.width = window_rect_.width(); | |
| 1071 window_.x = window_rect_.x(); | |
| 1072 window_.y = window_rect_.y(); | |
| 1073 window_.type = NPWindowTypeDrawable; | |
| 1074 instance()->NPP_SetWindow(&window_); | |
| 1075 } | |
| 1076 | |
| 1077 void WebPluginDelegatePepper::PluginDestroyed() { | |
| 1078 delete this; | |
| 1079 } | |
| 1080 | |
| 1081 void WebPluginDelegatePepper::Paint(WebKit::WebCanvas* canvas, | |
| 1082 const gfx::Rect& rect) { | |
| 1083 // Blit from background_context to context. | |
| 1084 if (!committed_bitmap_.isNull()) { | |
| 1085 #if defined(OS_MACOSX) | |
| 1086 DrawSkBitmapToCanvas(committed_bitmap_, canvas, window_rect_, | |
| 1087 static_cast<int>(CGBitmapContextGetHeight(canvas))); | |
| 1088 #else | |
| 1089 canvas->drawBitmap(committed_bitmap_, | |
| 1090 SkIntToScalar(window_rect_.origin().x()), | |
| 1091 SkIntToScalar(window_rect_.origin().y())); | |
| 1092 #endif | |
| 1093 } | |
| 1094 } | |
| 1095 | |
| 1096 void WebPluginDelegatePepper::Print(gfx::NativeDrawingContext context) { | |
| 1097 NOTIMPLEMENTED(); | |
| 1098 } | |
| 1099 | |
| 1100 void WebPluginDelegatePepper::InstallMissingPlugin() { | |
| 1101 NOTIMPLEMENTED(); | |
| 1102 } | |
| 1103 | |
| 1104 void WebPluginDelegatePepper::SetFocus(bool focused) { | |
| 1105 if (!focused) | |
| 1106 return; | |
| 1107 | |
| 1108 NPPepperEvent npevent; | |
| 1109 | |
| 1110 npevent.type = NPEventType_Focus; | |
| 1111 npevent.size = sizeof(npevent); | |
| 1112 // TODO(sehr): what timestamp should this have? | |
| 1113 npevent.timeStampSeconds = 0.0; | |
| 1114 // Currently this API only supports gaining focus. | |
| 1115 npevent.u.focus.value = 1; | |
| 1116 instance()->NPP_HandleEvent(&npevent); | |
| 1117 } | |
| 1118 | |
| 1119 // Anonymous namespace for functions converting WebInputEvents to NPAPI types. | |
| 1120 namespace { | |
| 1121 NPEventTypes ConvertEventTypes(WebInputEvent::Type wetype) { | |
| 1122 switch (wetype) { | |
| 1123 case WebInputEvent::MouseDown: | |
| 1124 return NPEventType_MouseDown; | |
| 1125 case WebInputEvent::MouseUp: | |
| 1126 return NPEventType_MouseUp; | |
| 1127 case WebInputEvent::MouseMove: | |
| 1128 return NPEventType_MouseMove; | |
| 1129 case WebInputEvent::MouseEnter: | |
| 1130 return NPEventType_MouseEnter; | |
| 1131 case WebInputEvent::MouseLeave: | |
| 1132 return NPEventType_MouseLeave; | |
| 1133 case WebInputEvent::MouseWheel: | |
| 1134 return NPEventType_MouseWheel; | |
| 1135 case WebInputEvent::RawKeyDown: | |
| 1136 return NPEventType_RawKeyDown; | |
| 1137 case WebInputEvent::KeyDown: | |
| 1138 return NPEventType_KeyDown; | |
| 1139 case WebInputEvent::KeyUp: | |
| 1140 return NPEventType_KeyUp; | |
| 1141 case WebInputEvent::Char: | |
| 1142 return NPEventType_Char; | |
| 1143 case WebInputEvent::Undefined: | |
| 1144 default: | |
| 1145 return NPEventType_Undefined; | |
| 1146 } | |
| 1147 } | |
| 1148 | |
| 1149 void BuildKeyEvent(const WebInputEvent* event, NPPepperEvent* npevent) { | |
| 1150 const WebKeyboardEvent* key_event = | |
| 1151 reinterpret_cast<const WebKeyboardEvent*>(event); | |
| 1152 npevent->u.key.modifier = key_event->modifiers; | |
| 1153 npevent->u.key.normalizedKeyCode = key_event->windowsKeyCode; | |
| 1154 } | |
| 1155 | |
| 1156 void BuildCharEvent(const WebInputEvent* event, NPPepperEvent* npevent) { | |
| 1157 const WebKeyboardEvent* key_event = | |
| 1158 reinterpret_cast<const WebKeyboardEvent*>(event); | |
| 1159 npevent->u.character.modifier = key_event->modifiers; | |
| 1160 // For consistency, check that the sizes of the texts agree. | |
| 1161 DCHECK(sizeof(npevent->u.character.text) == sizeof(key_event->text)); | |
| 1162 DCHECK(sizeof(npevent->u.character.unmodifiedText) == | |
| 1163 sizeof(key_event->unmodifiedText)); | |
| 1164 for (size_t i = 0; i < WebKeyboardEvent::textLengthCap; ++i) { | |
| 1165 npevent->u.character.text[i] = key_event->text[i]; | |
| 1166 npevent->u.character.unmodifiedText[i] = key_event->unmodifiedText[i]; | |
| 1167 } | |
| 1168 } | |
| 1169 | |
| 1170 void BuildMouseEvent(const WebInputEvent* event, NPPepperEvent* npevent) { | |
| 1171 const WebMouseEvent* mouse_event = | |
| 1172 reinterpret_cast<const WebMouseEvent*>(event); | |
| 1173 npevent->u.mouse.modifier = mouse_event->modifiers; | |
| 1174 npevent->u.mouse.button = mouse_event->button; | |
| 1175 npevent->u.mouse.x = mouse_event->x; | |
| 1176 npevent->u.mouse.y = mouse_event->y; | |
| 1177 npevent->u.mouse.clickCount = mouse_event->clickCount; | |
| 1178 } | |
| 1179 | |
| 1180 void BuildMouseWheelEvent(const WebInputEvent* event, NPPepperEvent* npevent) { | |
| 1181 const WebMouseWheelEvent* mouse_wheel_event = | |
| 1182 reinterpret_cast<const WebMouseWheelEvent*>(event); | |
| 1183 npevent->u.wheel.modifier = mouse_wheel_event->modifiers; | |
| 1184 npevent->u.wheel.deltaX = mouse_wheel_event->deltaX; | |
| 1185 npevent->u.wheel.deltaY = mouse_wheel_event->deltaY; | |
| 1186 npevent->u.wheel.wheelTicksX = mouse_wheel_event->wheelTicksX; | |
| 1187 npevent->u.wheel.wheelTicksY = mouse_wheel_event->wheelTicksY; | |
| 1188 npevent->u.wheel.scrollByPage = mouse_wheel_event->scrollByPage; | |
| 1189 } | |
| 1190 } // namespace | |
| 1191 | |
| 1192 bool WebPluginDelegatePepper::HandleInputEvent(const WebInputEvent& event, | |
| 1193 WebCursorInfo* cursor_info) { | |
| 1194 NPPepperEvent npevent; | |
| 1195 | |
| 1196 npevent.type = ConvertEventTypes(event.type); | |
| 1197 npevent.size = sizeof(npevent); | |
| 1198 npevent.timeStampSeconds = event.timeStampSeconds; | |
| 1199 switch (npevent.type) { | |
| 1200 case NPEventType_Undefined: | |
| 1201 return false; | |
| 1202 case NPEventType_MouseDown: | |
| 1203 case NPEventType_MouseUp: | |
| 1204 case NPEventType_MouseMove: | |
| 1205 case NPEventType_MouseEnter: | |
| 1206 case NPEventType_MouseLeave: | |
| 1207 BuildMouseEvent(&event, &npevent); | |
| 1208 break; | |
| 1209 case NPEventType_MouseWheel: | |
| 1210 BuildMouseWheelEvent(&event, &npevent); | |
| 1211 break; | |
| 1212 case NPEventType_RawKeyDown: | |
| 1213 case NPEventType_KeyDown: | |
| 1214 case NPEventType_KeyUp: | |
| 1215 BuildKeyEvent(&event, &npevent); | |
| 1216 break; | |
| 1217 case NPEventType_Char: | |
| 1218 BuildCharEvent(&event, &npevent); | |
| 1219 break; | |
| 1220 case NPEventType_Minimize: | |
| 1221 case NPEventType_Focus: | |
| 1222 case NPEventType_Device: | |
| 1223 // NOTIMPLEMENTED(); | |
| 1224 break; | |
| 1225 } | |
| 1226 bool rv = instance()->NPP_HandleEvent(&npevent) != 0; | |
| 1227 if (cursor_.get()) | |
| 1228 *cursor_info = *cursor_; | |
| 1229 return rv; | |
| 1230 } | |
| 1231 | |
| 1232 bool WebPluginDelegatePepper::CalculatePrintedPageDimensions( | |
| 1233 int page_number, | |
| 1234 NPPPrintExtensions* print_extensions, | |
| 1235 gfx::Size* page_dimensions) { | |
| 1236 int32 width_in_pixels = 0; | |
| 1237 int32 height_in_pixels = 0; | |
| 1238 NPError err = print_extensions->getRasterDimensions( | |
| 1239 instance()->npp(), page_number, &width_in_pixels, &height_in_pixels); | |
| 1240 if (err != NPERR_NO_ERROR) | |
| 1241 return false; | |
| 1242 | |
| 1243 DCHECK(width_in_pixels && height_in_pixels); | |
| 1244 page_dimensions->SetSize(width_in_pixels, height_in_pixels); | |
| 1245 return true; | |
| 1246 } | |
| 1247 | |
| 1248 NPPPrintExtensions* WebPluginDelegatePepper::GetPrintExtensions() { | |
| 1249 NPPPrintExtensions* ret = NULL; | |
| 1250 NPPExtensions* extensions = NULL; | |
| 1251 instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); | |
| 1252 if (extensions && extensions->getPrintExtensions) | |
| 1253 ret = extensions->getPrintExtensions(instance()->npp()); | |
| 1254 return ret; | |
| 1255 } | |
| 1256 | |
| 1257 NPPFindExtensions* WebPluginDelegatePepper::GetFindExtensions() { | |
| 1258 NPPFindExtensions* ret = NULL; | |
| 1259 NPPExtensions* extensions = NULL; | |
| 1260 instance()->NPP_GetValue(NPPVPepperExtensions, &extensions); | |
| 1261 if (extensions && extensions->getFindExtensions) | |
| 1262 ret = extensions->getFindExtensions(instance()->npp()); | |
| 1263 return ret; | |
| 1264 } | |
| 1265 | |
| 1266 #if defined(OS_WIN) | |
| 1267 bool WebPluginDelegatePepper::DrawJPEGToPlatformDC( | |
| 1268 const SkBitmap& bitmap, | |
| 1269 const gfx::Rect& printable_area, | |
| 1270 WebKit::WebCanvas* canvas) { | |
| 1271 skia::VectorPlatformDevice& device = | |
| 1272 static_cast<skia::VectorPlatformDevice&>( | |
| 1273 canvas->getTopPlatformDevice()); | |
| 1274 HDC dc = device.getBitmapDC(); | |
| 1275 // TODO(sanjeevr): This is a temporary hack. If we output a JPEG | |
| 1276 // to the EMF, the EnumEnhMetaFile call fails in the browser | |
| 1277 // process. The failure also happens if we output nothing here. | |
| 1278 // We need to investigate the reason for this failure and fix it. | |
| 1279 // In the meantime this temporary hack of drawing an empty | |
| 1280 // rectangle in the DC gets us by. | |
| 1281 Rectangle(dc, 0, 0, 0, 0); | |
| 1282 | |
| 1283 // Ideally we should add JPEG compression to the VectorPlatformDevice class | |
| 1284 // However, Skia currently has no JPEG compression code and we cannot | |
| 1285 // depend on gfx/jpeg_codec.h in Skia. So we do the compression here. | |
| 1286 SkAutoLockPixels lock(bitmap); | |
| 1287 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
| 1288 const uint32_t* pixels = | |
| 1289 static_cast<const uint32_t*>(bitmap.getPixels()); | |
| 1290 std::vector<unsigned char> compressed_image; | |
| 1291 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 1292 bool encoded = gfx::JPEGCodec::Encode( | |
| 1293 reinterpret_cast<const unsigned char*>(pixels), | |
| 1294 gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(), | |
| 1295 static_cast<int>(bitmap.rowBytes()), 100, &compressed_image); | |
| 1296 UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime", | |
| 1297 base::TimeTicks::Now() - start_time); | |
| 1298 if (!encoded) { | |
| 1299 NOTREACHED(); | |
| 1300 return false; | |
| 1301 } | |
| 1302 BITMAPINFOHEADER bmi = {0}; | |
| 1303 gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi); | |
| 1304 bmi.biCompression = BI_JPEG; | |
| 1305 bmi.biSizeImage = compressed_image.size(); | |
| 1306 bmi.biHeight = -bmi.biHeight; | |
| 1307 StretchDIBits(dc, printable_area.x(), printable_area.y(), | |
| 1308 printable_area.width(), printable_area.height(), | |
| 1309 0, 0, bitmap.width(), bitmap.height(), | |
| 1310 &compressed_image.front(), | |
| 1311 reinterpret_cast<const BITMAPINFO*>(&bmi), | |
| 1312 DIB_RGB_COLORS, SRCCOPY); | |
| 1313 return true; | |
| 1314 } | |
| 1315 #endif // OS_WIN | |
| 1316 | |
| 1317 #if defined(OS_MACOSX) | |
| 1318 void WebPluginDelegatePepper::DrawSkBitmapToCanvas( | |
| 1319 const SkBitmap& bitmap, WebKit::WebCanvas* canvas, | |
| 1320 const gfx::Rect& dest_rect, | |
| 1321 int canvas_height) { | |
| 1322 SkAutoLockPixels lock(bitmap); | |
| 1323 DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config); | |
| 1324 base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider( | |
| 1325 CGDataProviderCreateWithData( | |
| 1326 NULL, bitmap.getAddr32(0, 0), | |
| 1327 bitmap.rowBytes() * bitmap.height(), NULL)); | |
| 1328 base::mac::ScopedCFTypeRef<CGImageRef> image( | |
| 1329 CGImageCreate( | |
| 1330 bitmap.width(), bitmap.height(), | |
| 1331 8, 32, bitmap.rowBytes(), | |
| 1332 base::mac::GetSystemColorSpace(), | |
| 1333 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, | |
| 1334 data_provider, NULL, false, kCGRenderingIntentDefault)); | |
| 1335 | |
| 1336 // Flip the transform | |
| 1337 CGContextSaveGState(canvas); | |
| 1338 CGContextTranslateCTM(canvas, 0, canvas_height); | |
| 1339 CGContextScaleCTM(canvas, 1.0, -1.0); | |
| 1340 | |
| 1341 CGRect bounds; | |
| 1342 bounds.origin.x = dest_rect.x(); | |
| 1343 bounds.origin.y = canvas_height - dest_rect.y() - dest_rect.height(); | |
| 1344 bounds.size.width = dest_rect.width(); | |
| 1345 bounds.size.height = dest_rect.height(); | |
| 1346 | |
| 1347 CGContextDrawImage(canvas, bounds, image); | |
| 1348 CGContextRestoreGState(canvas); | |
| 1349 } | |
| 1350 #endif // defined(OS_MACOSX) | |
| OLD | NEW |