| OLD | NEW |
| (Empty) |
| 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 "content/renderer/npapi/webplugin_delegate_proxy.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/auto_reset.h" | |
| 13 #include "base/command_line.h" | |
| 14 #include "base/files/file_util.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/macros.h" | |
| 17 #include "base/memory/ref_counted.h" | |
| 18 #include "base/memory/scoped_ptr.h" | |
| 19 #include "base/process/process.h" | |
| 20 #include "base/strings/string_split.h" | |
| 21 #include "base/strings/string_util.h" | |
| 22 #include "base/strings/utf_string_conversions.h" | |
| 23 #include "base/version.h" | |
| 24 #include "build/build_config.h" | |
| 25 #include "cc/resources/shared_bitmap.h" | |
| 26 #include "content/child/child_process.h" | |
| 27 #include "content/child/child_shared_bitmap_manager.h" | |
| 28 #include "content/child/npapi/webplugin_resource_client.h" | |
| 29 #include "content/child/plugin_messages.h" | |
| 30 #include "content/common/content_constants_internal.h" | |
| 31 #include "content/common/cursors/webcursor.h" | |
| 32 #include "content/common/frame_messages.h" | |
| 33 #include "content/common/view_messages.h" | |
| 34 #include "content/public/renderer/content_renderer_client.h" | |
| 35 #include "content/renderer/npapi/plugin_channel_host.h" | |
| 36 #include "content/renderer/npapi/webplugin_impl.h" | |
| 37 #include "content/renderer/render_thread_impl.h" | |
| 38 #include "content/renderer/render_view_impl.h" | |
| 39 #include "content/renderer/sad_plugin.h" | |
| 40 #include "ipc/ipc_channel_handle.h" | |
| 41 #include "net/base/mime_util.h" | |
| 42 #include "skia/ext/platform_canvas.h" | |
| 43 #include "third_party/WebKit/public/platform/WebDragData.h" | |
| 44 #include "third_party/WebKit/public/platform/WebString.h" | |
| 45 #include "third_party/WebKit/public/web/WebDocument.h" | |
| 46 #include "third_party/WebKit/public/web/WebFrame.h" | |
| 47 #include "third_party/WebKit/public/web/WebView.h" | |
| 48 #include "ui/gfx/blit.h" | |
| 49 #include "ui/gfx/canvas.h" | |
| 50 #include "ui/gfx/geometry/size.h" | |
| 51 #include "ui/gfx/native_widget_types.h" | |
| 52 #include "ui/gfx/skia_util.h" | |
| 53 | |
| 54 #if defined(OS_POSIX) | |
| 55 #include "ipc/ipc_channel_posix.h" | |
| 56 #endif | |
| 57 | |
| 58 #if defined(OS_WIN) | |
| 59 #include "base/win/scoped_handle.h" | |
| 60 #include "content/public/common/sandbox_init.h" | |
| 61 #endif | |
| 62 | |
| 63 using blink::WebCursorInfo; | |
| 64 using blink::WebDragData; | |
| 65 using blink::WebInputEvent; | |
| 66 using blink::WebString; | |
| 67 using blink::WebView; | |
| 68 | |
| 69 namespace content { | |
| 70 | |
| 71 namespace { | |
| 72 | |
| 73 class ScopedLogLevel { | |
| 74 public: | |
| 75 explicit ScopedLogLevel(int level); | |
| 76 ~ScopedLogLevel(); | |
| 77 | |
| 78 private: | |
| 79 int old_level_; | |
| 80 | |
| 81 DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel); | |
| 82 }; | |
| 83 | |
| 84 ScopedLogLevel::ScopedLogLevel(int level) | |
| 85 : old_level_(logging::GetMinLogLevel()) { | |
| 86 logging::SetMinLogLevel(level); | |
| 87 } | |
| 88 | |
| 89 ScopedLogLevel::~ScopedLogLevel() { | |
| 90 logging::SetMinLogLevel(old_level_); | |
| 91 } | |
| 92 | |
| 93 } // namespace | |
| 94 | |
| 95 WebPluginDelegateProxy::WebPluginDelegateProxy( | |
| 96 WebPluginImpl* plugin, | |
| 97 const std::string& mime_type, | |
| 98 const base::WeakPtr<RenderViewImpl>& render_view, | |
| 99 RenderFrameImpl* render_frame) | |
| 100 : render_view_(render_view), | |
| 101 render_frame_(render_frame), | |
| 102 plugin_(plugin), | |
| 103 uses_shared_bitmaps_(true), | |
| 104 #if defined(OS_MACOSX) | |
| 105 uses_compositor_(false), | |
| 106 #endif | |
| 107 mime_type_(mime_type), | |
| 108 instance_id_(MSG_ROUTING_NONE), | |
| 109 sad_plugin_(NULL), | |
| 110 invalidate_pending_(false), | |
| 111 transparent_(false), | |
| 112 front_buffer_index_(0), | |
| 113 page_url_(render_view_->webview()->mainFrame()->document().url()) { | |
| 114 } | |
| 115 | |
| 116 WebPluginDelegateProxy::~WebPluginDelegateProxy() { | |
| 117 } | |
| 118 | |
| 119 WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {} | |
| 120 | |
| 121 WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {} | |
| 122 | |
| 123 void WebPluginDelegateProxy::PluginDestroyed() { | |
| 124 #if defined(OS_MACOSX) | |
| 125 // Ensure that the renderer doesn't think the plugin still has focus. | |
| 126 if (render_view_) | |
| 127 render_view_->PluginFocusChanged(false, instance_id_); | |
| 128 #endif | |
| 129 | |
| 130 if (render_view_.get()) | |
| 131 render_view_->UnregisterPluginDelegate(this); | |
| 132 | |
| 133 if (channel_host_.get()) { | |
| 134 Send(new PluginMsg_DestroyInstance(instance_id_)); | |
| 135 | |
| 136 // Must remove the route after sending the destroy message, rather than | |
| 137 // before, since RemoveRoute can lead to all the outstanding NPObjects | |
| 138 // being told the channel went away if this was the last instance. | |
| 139 channel_host_->RemoveRoute(instance_id_); | |
| 140 | |
| 141 // Release the channel host now. If we are is the last reference to the | |
| 142 // channel, this avoids a race where this renderer asks a new connection to | |
| 143 // the same plugin between now and the time 'this' is actually deleted. | |
| 144 // Destroying the channel host is what releases the channel name -> FD | |
| 145 // association on POSIX, and if we ask for a new connection before it is | |
| 146 // released, the plugin will give us a new FD, and we'll assert when trying | |
| 147 // to associate it with the channel name. | |
| 148 channel_host_ = NULL; | |
| 149 } | |
| 150 | |
| 151 plugin_ = NULL; | |
| 152 | |
| 153 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
| 154 } | |
| 155 | |
| 156 bool WebPluginDelegateProxy::Initialize( | |
| 157 const GURL& url, | |
| 158 const std::vector<std::string>& arg_names, | |
| 159 const std::vector<std::string>& arg_values, | |
| 160 bool load_manually) { | |
| 161 // TODO(shess): Attempt to work around http://crbug.com/97285 and | |
| 162 // http://crbug.com/141055 by retrying the connection. Reports seem | |
| 163 // to indicate that the plugin hasn't crashed, and that the problem | |
| 164 // is not 100% persistent. | |
| 165 const size_t kAttempts = 2; | |
| 166 | |
| 167 bool result = false; | |
| 168 scoped_refptr<PluginChannelHost> channel_host; | |
| 169 int instance_id = 0; | |
| 170 | |
| 171 for (size_t attempt = 0; !result && attempt < kAttempts; attempt++) { | |
| 172 #if defined(OS_MACOSX) | |
| 173 // TODO(shess): Debugging for http://crbug.com/97285 . See comment | |
| 174 // in plugin_channel_host.cc. | |
| 175 scoped_ptr<base::AutoReset<bool> > track_nested_removes( | |
| 176 new base::AutoReset<bool>(PluginChannelHost::GetRemoveTrackingFlag(), | |
| 177 true)); | |
| 178 #endif | |
| 179 | |
| 180 IPC::ChannelHandle channel_handle; | |
| 181 if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin( | |
| 182 render_frame_->GetRoutingID(), url, page_url_, mime_type_, | |
| 183 &channel_handle, &info_))) { | |
| 184 continue; | |
| 185 } | |
| 186 | |
| 187 if (channel_handle.name.empty()) { | |
| 188 // We got an invalid handle. Either the plugin couldn't be found (which | |
| 189 // shouldn't happen, since if we got here the plugin should exist) or the | |
| 190 // plugin crashed on initialization. | |
| 191 if (!info_.path.empty()) { | |
| 192 render_view_->GetMainRenderFrame()->PluginCrashed( | |
| 193 info_.path, base::kNullProcessId); | |
| 194 LOG(ERROR) << "Plugin crashed on start"; | |
| 195 | |
| 196 // Return true so that the plugin widget is created and we can paint the | |
| 197 // crashed plugin there. | |
| 198 return true; | |
| 199 } | |
| 200 LOG(ERROR) << "Plugin couldn't be found"; | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 channel_host = PluginChannelHost::GetPluginChannelHost( | |
| 205 channel_handle, ChildProcess::current()->io_task_runner()); | |
| 206 if (!channel_host.get()) { | |
| 207 LOG(ERROR) << "Couldn't get PluginChannelHost"; | |
| 208 continue; | |
| 209 } | |
| 210 #if defined(OS_MACOSX) | |
| 211 track_nested_removes.reset(); | |
| 212 #endif | |
| 213 | |
| 214 { | |
| 215 // TODO(bauerb): Debugging for http://crbug.com/141055. | |
| 216 ScopedLogLevel log_level(-2); // Equivalent to --v=2 | |
| 217 result = channel_host->Send(new PluginMsg_CreateInstance( | |
| 218 mime_type_, &instance_id)); | |
| 219 if (!result) { | |
| 220 LOG(ERROR) << "Couldn't send PluginMsg_CreateInstance"; | |
| 221 continue; | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // Failed too often, give up. | |
| 227 if (!result) | |
| 228 return false; | |
| 229 | |
| 230 channel_host_ = channel_host; | |
| 231 instance_id_ = instance_id; | |
| 232 | |
| 233 channel_host_->AddRoute(instance_id_, this); | |
| 234 | |
| 235 // Now tell the PluginInstance in the plugin process to initialize. | |
| 236 PluginMsg_Init_Params params; | |
| 237 params.url = url; | |
| 238 params.page_url = page_url_; | |
| 239 params.arg_names = arg_names; | |
| 240 params.arg_values = arg_values; | |
| 241 params.host_render_view_routing_id = render_view_->GetRoutingID(); | |
| 242 params.load_manually = load_manually; | |
| 243 | |
| 244 result = false; | |
| 245 Send(new PluginMsg_Init(instance_id_, params, &transparent_, &result)); | |
| 246 | |
| 247 if (!result) | |
| 248 LOG(WARNING) << "PluginMsg_Init returned false"; | |
| 249 | |
| 250 render_view_->RegisterPluginDelegate(this); | |
| 251 | |
| 252 return result; | |
| 253 } | |
| 254 | |
| 255 bool WebPluginDelegateProxy::Send(IPC::Message* msg) { | |
| 256 if (!channel_host_.get()) { | |
| 257 DLOG(WARNING) << "dropping message because channel host is null"; | |
| 258 delete msg; | |
| 259 return false; | |
| 260 } | |
| 261 | |
| 262 return channel_host_->Send(msg); | |
| 263 } | |
| 264 | |
| 265 bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) { | |
| 266 GetContentClient()->SetActiveURL(page_url_); | |
| 267 | |
| 268 bool handled = true; | |
| 269 IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy, msg) | |
| 270 IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect) | |
| 271 IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy, OnResolveProxy) | |
| 272 IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie, OnSetCookie) | |
| 273 IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies, OnGetCookies) | |
| 274 IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad, OnCancelDocumentLoad) | |
| 275 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading, OnDidStartLoading) | |
| 276 IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading, OnDidStopLoading) | |
| 277 #if defined(OS_MACOSX) | |
| 278 IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged, | |
| 279 OnFocusChanged); | |
| 280 IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme, | |
| 281 OnStartIme); | |
| 282 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering, | |
| 283 OnAcceleratedPluginEnabledRendering) | |
| 284 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface, | |
| 285 OnAcceleratedPluginAllocatedIOSurface) | |
| 286 IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface, | |
| 287 OnAcceleratedPluginSwappedIOSurface) | |
| 288 #endif | |
| 289 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 290 IPC_END_MESSAGE_MAP() | |
| 291 DCHECK(handled); | |
| 292 return handled; | |
| 293 } | |
| 294 | |
| 295 void WebPluginDelegateProxy::OnChannelError() { | |
| 296 if (plugin_) { | |
| 297 plugin_->Invalidate(); | |
| 298 } | |
| 299 if (channel_host_.get() && !channel_host_->expecting_shutdown()) { | |
| 300 render_view_->GetMainRenderFrame()->PluginCrashed( | |
| 301 info_.path, channel_host_->peer_pid()); | |
| 302 } | |
| 303 | |
| 304 #if defined(OS_MACOSX) | |
| 305 // Ensure that the renderer doesn't think the plugin still has focus. | |
| 306 if (render_view_) | |
| 307 render_view_->PluginFocusChanged(false, instance_id_); | |
| 308 #endif | |
| 309 } | |
| 310 | |
| 311 static void CopySharedMemoryHandleForMessage( | |
| 312 const base::SharedMemoryHandle& handle_in, | |
| 313 base::SharedMemoryHandle* handle_out, | |
| 314 base::ProcessId peer_pid) { | |
| 315 #if defined(OS_POSIX) || defined(OS_WIN) | |
| 316 *handle_out = base::SharedMemory::DuplicateHandle(handle_in); | |
| 317 #else | |
| 318 #error Shared memory copy not implemented. | |
| 319 #endif | |
| 320 } | |
| 321 | |
| 322 void WebPluginDelegateProxy::SendUpdateGeometry( | |
| 323 bool bitmaps_changed) { | |
| 324 if (!channel_host_.get()) | |
| 325 return; | |
| 326 | |
| 327 PluginMsg_UpdateGeometry_Param param; | |
| 328 param.window_rect = plugin_rect_; | |
| 329 param.clip_rect = clip_rect_; | |
| 330 param.windowless_buffer0 = base::SharedMemory::NULLHandle(); | |
| 331 param.windowless_buffer1 = base::SharedMemory::NULLHandle(); | |
| 332 param.windowless_buffer_index = back_buffer_index(); | |
| 333 | |
| 334 #if defined(OS_POSIX) | |
| 335 // If we're using POSIX mmap'd TransportDIBs, sending the handle across | |
| 336 // IPC establishes a new mapping rather than just sending a window ID, | |
| 337 // so only do so if we've actually changed the shared memory bitmaps. | |
| 338 if (bitmaps_changed) | |
| 339 #endif | |
| 340 { | |
| 341 if (transport_stores_[0].bitmap) | |
| 342 CopySharedMemoryHandleForMessage( | |
| 343 transport_stores_[0].bitmap->shared_memory()->handle(), | |
| 344 ¶m.windowless_buffer0, channel_host_->peer_pid()); | |
| 345 | |
| 346 if (transport_stores_[1].bitmap) | |
| 347 CopySharedMemoryHandleForMessage( | |
| 348 transport_stores_[1].bitmap->shared_memory()->handle(), | |
| 349 ¶m.windowless_buffer1, channel_host_->peer_pid()); | |
| 350 } | |
| 351 | |
| 352 IPC::Message* msg; | |
| 353 #if defined(OS_WIN) | |
| 354 if (UseSynchronousGeometryUpdates()) { | |
| 355 msg = new PluginMsg_UpdateGeometrySync(instance_id_, param); | |
| 356 } else // NOLINT | |
| 357 #endif | |
| 358 { | |
| 359 msg = new PluginMsg_UpdateGeometry(instance_id_, param); | |
| 360 msg->set_unblock(true); | |
| 361 } | |
| 362 | |
| 363 Send(msg); | |
| 364 } | |
| 365 | |
| 366 void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect, | |
| 367 const gfx::Rect& clip_rect) { | |
| 368 // window_rect becomes either a window in native windowing system | |
| 369 // coords, or a backing buffer. In either case things will go bad | |
| 370 // if the rectangle is very large. | |
| 371 if (window_rect.width() < 0 || window_rect.width() > kMaxPluginSideLength || | |
| 372 window_rect.height() < 0 || window_rect.height() > kMaxPluginSideLength || | |
| 373 // We know this won't overflow due to above checks. | |
| 374 static_cast<uint32_t>(window_rect.width()) * | |
| 375 static_cast<uint32_t>(window_rect.height()) > | |
| 376 kMaxPluginSize) { | |
| 377 return; | |
| 378 } | |
| 379 | |
| 380 plugin_rect_ = window_rect; | |
| 381 clip_rect_ = clip_rect; | |
| 382 | |
| 383 bool bitmaps_changed = false; | |
| 384 | |
| 385 if (uses_shared_bitmaps_) { | |
| 386 if (!front_buffer_canvas() || | |
| 387 (window_rect.width() != | |
| 388 front_buffer_canvas()->getBaseLayerSize().width() || | |
| 389 window_rect.height() != | |
| 390 front_buffer_canvas()->getBaseLayerSize().height())) | |
| 391 { | |
| 392 bitmaps_changed = true; | |
| 393 | |
| 394 // Create a shared memory section that the plugin paints into | |
| 395 // asynchronously. | |
| 396 ResetWindowlessBitmaps(); | |
| 397 if (!window_rect.IsEmpty()) { | |
| 398 if (!CreateSharedBitmap(&transport_stores_[0].bitmap, | |
| 399 &transport_stores_[0].canvas) || | |
| 400 !CreateSharedBitmap(&transport_stores_[1].bitmap, | |
| 401 &transport_stores_[1].canvas)) { | |
| 402 DCHECK(false); | |
| 403 ResetWindowlessBitmaps(); | |
| 404 return; | |
| 405 } | |
| 406 } | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 SendUpdateGeometry(bitmaps_changed); | |
| 411 } | |
| 412 | |
| 413 void WebPluginDelegateProxy::ResetWindowlessBitmaps() { | |
| 414 transport_stores_[0].bitmap.reset(); | |
| 415 transport_stores_[1].bitmap.reset(); | |
| 416 | |
| 417 transport_stores_[0].canvas.clear(); | |
| 418 transport_stores_[1].canvas.clear(); | |
| 419 transport_store_painted_ = gfx::Rect(); | |
| 420 front_buffer_diff_ = gfx::Rect(); | |
| 421 } | |
| 422 | |
| 423 #if !defined(OS_WIN) | |
| 424 static size_t BitmapSizeForPluginRect(const gfx::Rect& plugin_rect) { | |
| 425 const size_t stride = | |
| 426 skia::PlatformCanvasStrideForWidth(plugin_rect.width()); | |
| 427 return stride * plugin_rect.height(); | |
| 428 } | |
| 429 | |
| 430 bool WebPluginDelegateProxy::CreateLocalBitmap( | |
| 431 std::vector<uint8_t>* memory, | |
| 432 skia::RefPtr<SkCanvas>* canvas) { | |
| 433 const size_t size = BitmapSizeForPluginRect(plugin_rect_); | |
| 434 memory->resize(size); | |
| 435 if (memory->size() != size) | |
| 436 return false; | |
| 437 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
| 438 plugin_rect_.width(), plugin_rect_.height(), true, &((*memory)[0]), | |
| 439 skia::CRASH_ON_FAILURE)); | |
| 440 return true; | |
| 441 } | |
| 442 #endif | |
| 443 | |
| 444 bool WebPluginDelegateProxy::CreateSharedBitmap( | |
| 445 scoped_ptr<SharedMemoryBitmap>* memory, | |
| 446 skia::RefPtr<SkCanvas>* canvas) { | |
| 447 *memory = ChildThreadImpl::current() | |
| 448 ->shared_bitmap_manager() | |
| 449 ->AllocateSharedMemoryBitmap(plugin_rect_.size()); | |
| 450 if (!memory->get()) | |
| 451 return false; | |
| 452 DCHECK((*memory)->shared_memory()); | |
| 453 #if defined(OS_POSIX) | |
| 454 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
| 455 plugin_rect_.width(), plugin_rect_.height(), true, (*memory)->pixels(), | |
| 456 skia::RETURN_NULL_ON_FAILURE)); | |
| 457 #else | |
| 458 *canvas = skia::AdoptRef(skia::CreatePlatformCanvas( | |
| 459 plugin_rect_.width(), plugin_rect_.height(), true, | |
| 460 (*memory)->shared_memory()->handle().GetHandle(), | |
| 461 skia::RETURN_NULL_ON_FAILURE)); | |
| 462 #endif | |
| 463 return !!canvas->get(); | |
| 464 } | |
| 465 | |
| 466 void WebPluginDelegateProxy::Paint(SkCanvas* canvas, | |
| 467 const gfx::Rect& damaged_rect) { | |
| 468 // Limit the damaged rectangle to whatever is contained inside the plugin | |
| 469 // rectangle, as that's the rectangle that we'll actually draw. | |
| 470 gfx::Rect rect = gfx::IntersectRects(damaged_rect, plugin_rect_); | |
| 471 | |
| 472 // If the plugin is no longer connected (channel crashed) draw a crashed | |
| 473 // plugin bitmap | |
| 474 if (!channel_host_.get() || !channel_host_->channel_valid()) { | |
| 475 // Lazily load the sad plugin image. | |
| 476 if (!sad_plugin_) | |
| 477 sad_plugin_ = GetContentClient()->renderer()->GetSadPluginBitmap(); | |
| 478 if (sad_plugin_) | |
| 479 PaintSadPlugin(canvas, plugin_rect_, *sad_plugin_); | |
| 480 return; | |
| 481 } | |
| 482 | |
| 483 if (!uses_shared_bitmaps_) | |
| 484 return; | |
| 485 | |
| 486 // We got a paint before the plugin's coordinates, so there's no buffer to | |
| 487 // copy from. | |
| 488 if (!front_buffer_canvas()) | |
| 489 return; | |
| 490 | |
| 491 gfx::Rect offset_rect = rect; | |
| 492 offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y()); | |
| 493 | |
| 494 // transport_store_painted_ is really a bounding box, so in principle this | |
| 495 // check could falsely indicate that we don't need to paint offset_rect, but | |
| 496 // in practice it works fine. | |
| 497 if (!transport_store_painted_.Contains(offset_rect)) { | |
| 498 Send(new PluginMsg_Paint(instance_id_, offset_rect)); | |
| 499 // Since the plugin is not blocked on the renderer in this context, there is | |
| 500 // a chance that it will begin repainting the back-buffer before we complete | |
| 501 // capturing the data. Buffer flipping would increase that risk because | |
| 502 // geometry update is asynchronous, so we don't want to use buffer flipping | |
| 503 // here. | |
| 504 UpdateFrontBuffer(offset_rect, false); | |
| 505 } | |
| 506 | |
| 507 const SkBitmap bitmap = skia::ReadPixels(front_buffer_canvas()); | |
| 508 SkPaint paint; | |
| 509 paint.setXfermodeMode( | |
| 510 transparent_ ? SkXfermode::kSrcATop_Mode : SkXfermode::kSrc_Mode); | |
| 511 SkRect src_rect = gfx::RectToSkRect(offset_rect); | |
| 512 canvas->drawBitmapRect(bitmap, | |
| 513 src_rect, | |
| 514 gfx::RectToSkRect(rect), | |
| 515 &paint); | |
| 516 | |
| 517 if (invalidate_pending_) { | |
| 518 // Only send the PaintAck message if this paint is in response to an | |
| 519 // invalidate from the plugin, since this message acts as an access token | |
| 520 // to ensure only one process is using the shared bitmap at a time. | |
| 521 invalidate_pending_ = false; | |
| 522 Send(new PluginMsg_DidPaint(instance_id_)); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 void WebPluginDelegateProxy::SetFocus(bool focused) { | |
| 527 Send(new PluginMsg_SetFocus(instance_id_, focused)); | |
| 528 } | |
| 529 | |
| 530 bool WebPluginDelegateProxy::HandleInputEvent( | |
| 531 const WebInputEvent& event, | |
| 532 WebCursor::CursorInfo* cursor_info) { | |
| 533 bool handled = false; | |
| 534 WebCursor cursor; | |
| 535 Send(new PluginMsg_HandleInputEvent(instance_id_, &event, &handled, &cursor)); | |
| 536 return handled; | |
| 537 } | |
| 538 | |
| 539 int WebPluginDelegateProxy::GetProcessId() { | |
| 540 return channel_host_->peer_pid(); | |
| 541 } | |
| 542 | |
| 543 void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus) { | |
| 544 IPC::Message* msg = new PluginMsg_SetContentAreaFocus(instance_id_, | |
| 545 has_focus); | |
| 546 // Make sure focus events are delivered in the right order relative to | |
| 547 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
| 548 msg->set_unblock(true); | |
| 549 Send(msg); | |
| 550 } | |
| 551 | |
| 552 #if defined(OS_MACOSX) | |
| 553 void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus) { | |
| 554 IPC::Message* msg = new PluginMsg_SetWindowFocus(instance_id_, | |
| 555 window_has_focus); | |
| 556 // Make sure focus events are delivered in the right order relative to | |
| 557 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
| 558 msg->set_unblock(true); | |
| 559 Send(msg); | |
| 560 } | |
| 561 | |
| 562 void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible) { | |
| 563 IPC::Message* msg; | |
| 564 if (is_visible) { | |
| 565 gfx::Rect window_frame = render_view_->GetWidget()->rootWindowRect(); | |
| 566 gfx::Rect view_frame = render_view_->GetWidget()->windowRect(); | |
| 567 blink::WebView* webview = render_view_->webview(); | |
| 568 msg = new PluginMsg_ContainerShown(instance_id_, window_frame, view_frame, | |
| 569 webview && webview->isActive()); | |
| 570 } else { | |
| 571 msg = new PluginMsg_ContainerHidden(instance_id_); | |
| 572 } | |
| 573 // Make sure visibility events are delivered in the right order relative to | |
| 574 // sync messages they might interact with (Paint, HandleEvent, etc.). | |
| 575 msg->set_unblock(true); | |
| 576 Send(msg); | |
| 577 } | |
| 578 | |
| 579 void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame, | |
| 580 gfx::Rect view_frame) { | |
| 581 IPC::Message* msg = new PluginMsg_WindowFrameChanged(instance_id_, | |
| 582 window_frame, | |
| 583 view_frame); | |
| 584 // Make sure frame events are delivered in the right order relative to | |
| 585 // sync messages they might interact with (e.g., HandleEvent). | |
| 586 msg->set_unblock(true); | |
| 587 Send(msg); | |
| 588 } | |
| 589 void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16& text, | |
| 590 int plugin_id) { | |
| 591 // If the message isn't intended for this plugin, there's nothing to do. | |
| 592 if (instance_id_ != plugin_id) | |
| 593 return; | |
| 594 | |
| 595 IPC::Message* msg = new PluginMsg_ImeCompositionCompleted(instance_id_, | |
| 596 text); | |
| 597 // Order relative to other key events is important. | |
| 598 msg->set_unblock(true); | |
| 599 Send(msg); | |
| 600 } | |
| 601 #endif // OS_MACOSX | |
| 602 | |
| 603 void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) { | |
| 604 if (!plugin_) | |
| 605 return; | |
| 606 | |
| 607 // Clip the invalidation rect to the plugin bounds; the plugin may have been | |
| 608 // resized since the invalidate message was sent. | |
| 609 gfx::Rect clipped_rect = | |
| 610 gfx::IntersectRects(rect, gfx::Rect(plugin_rect_.size())); | |
| 611 | |
| 612 invalidate_pending_ = true; | |
| 613 // The plugin is blocked on the renderer because the invalidate message it has | |
| 614 // sent us is synchronous, so we can use buffer flipping here if the caller | |
| 615 // allows it. | |
| 616 UpdateFrontBuffer(clipped_rect, true); | |
| 617 plugin_->InvalidateRect(clipped_rect); | |
| 618 } | |
| 619 | |
| 620 void WebPluginDelegateProxy::OnResolveProxy(const GURL& url, | |
| 621 bool* result, | |
| 622 std::string* proxy_list) { | |
| 623 *result = RenderThreadImpl::current()->ResolveProxy(url, proxy_list); | |
| 624 } | |
| 625 | |
| 626 void WebPluginDelegateProxy::OnSetCookie(const GURL& url, | |
| 627 const GURL& first_party_for_cookies, | |
| 628 const std::string& cookie) { | |
| 629 if (plugin_) | |
| 630 plugin_->SetCookie(url, first_party_for_cookies, cookie); | |
| 631 } | |
| 632 | |
| 633 void WebPluginDelegateProxy::OnGetCookies(const GURL& url, | |
| 634 const GURL& first_party_for_cookies, | |
| 635 std::string* cookies) { | |
| 636 DCHECK(cookies); | |
| 637 if (plugin_) | |
| 638 *cookies = plugin_->GetCookies(url, first_party_for_cookies); | |
| 639 } | |
| 640 | |
| 641 void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer( | |
| 642 const gfx::Rect& rect) { | |
| 643 // Blitting the bits directly is much faster than going through CG, and since | |
| 644 // the goal is just to move the raw pixels between two bitmaps with the same | |
| 645 // pixel format (no compositing, color correction, etc.), it's safe. | |
| 646 const size_t stride = | |
| 647 skia::PlatformCanvasStrideForWidth(plugin_rect_.width()); | |
| 648 const size_t chunk_size = 4 * rect.width(); | |
| 649 DCHECK(back_buffer_bitmap() != NULL); | |
| 650 uint8_t* source_data = | |
| 651 back_buffer_bitmap()->pixels() + rect.y() * stride + 4 * rect.x(); | |
| 652 DCHECK(front_buffer_bitmap() != NULL); | |
| 653 uint8_t* target_data = | |
| 654 front_buffer_bitmap()->pixels() + rect.y() * stride + 4 * rect.x(); | |
| 655 for (int row = 0; row < rect.height(); ++row) { | |
| 656 memcpy(target_data, source_data, chunk_size); | |
| 657 source_data += stride; | |
| 658 target_data += stride; | |
| 659 } | |
| 660 } | |
| 661 | |
| 662 void WebPluginDelegateProxy::UpdateFrontBuffer( | |
| 663 const gfx::Rect& rect, | |
| 664 bool allow_buffer_flipping) { | |
| 665 if (!front_buffer_canvas()) { | |
| 666 return; | |
| 667 } | |
| 668 | |
| 669 #if defined(OS_WIN) | |
| 670 // If SendUpdateGeometry() would block on the plugin process then we don't | |
| 671 // want to use buffer flipping at all since it would add extra locking. | |
| 672 // (Alternatively we could probably safely use async updates for buffer | |
| 673 // flipping all the time since the size is not changing.) | |
| 674 if (UseSynchronousGeometryUpdates()) { | |
| 675 allow_buffer_flipping = false; | |
| 676 } | |
| 677 #endif | |
| 678 | |
| 679 // Plugin has just painted "rect" into the back-buffer, so the front-buffer | |
| 680 // no longer holds the latest content for that rectangle. | |
| 681 front_buffer_diff_.Subtract(rect); | |
| 682 if (allow_buffer_flipping && front_buffer_diff_.IsEmpty()) { | |
| 683 // Back-buffer contains the latest content for all areas; simply flip | |
| 684 // the buffers. | |
| 685 front_buffer_index_ = back_buffer_index(); | |
| 686 SendUpdateGeometry(false); | |
| 687 // The front-buffer now holds newer content for this region than the | |
| 688 // back-buffer. | |
| 689 front_buffer_diff_ = rect; | |
| 690 } else { | |
| 691 // Back-buffer contains the latest content for "rect" but the front-buffer | |
| 692 // contains the latest content for some other areas (or buffer flipping not | |
| 693 // allowed); fall back to copying the data. | |
| 694 CopyFromBackBufferToFrontBuffer(rect); | |
| 695 } | |
| 696 transport_store_painted_.Union(rect); | |
| 697 } | |
| 698 | |
| 699 #if defined(OS_MACOSX) | |
| 700 void WebPluginDelegateProxy::OnFocusChanged(bool focused) { | |
| 701 if (render_view_) | |
| 702 render_view_->PluginFocusChanged(focused, instance_id_); | |
| 703 } | |
| 704 | |
| 705 void WebPluginDelegateProxy::OnStartIme() { | |
| 706 if (render_view_) | |
| 707 render_view_->StartPluginIme(); | |
| 708 } | |
| 709 #endif | |
| 710 | |
| 711 void WebPluginDelegateProxy::OnCancelDocumentLoad() { | |
| 712 plugin_->CancelDocumentLoad(); | |
| 713 } | |
| 714 | |
| 715 void WebPluginDelegateProxy::OnDidStartLoading() { | |
| 716 plugin_->DidStartLoading(); | |
| 717 } | |
| 718 | |
| 719 void WebPluginDelegateProxy::OnDidStopLoading() { | |
| 720 plugin_->DidStopLoading(); | |
| 721 } | |
| 722 | |
| 723 #if defined(OS_MACOSX) | |
| 724 void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() { | |
| 725 uses_compositor_ = true; | |
| 726 uses_shared_bitmaps_ = false; | |
| 727 } | |
| 728 | |
| 729 void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface( | |
| 730 int32_t width, | |
| 731 int32_t height, | |
| 732 uint32_t surface_id) { | |
| 733 if (plugin_) | |
| 734 plugin_->AcceleratedPluginAllocatedIOSurface(width, height, surface_id); | |
| 735 } | |
| 736 | |
| 737 void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() { | |
| 738 if (plugin_) | |
| 739 plugin_->AcceleratedPluginSwappedIOSurface(); | |
| 740 } | |
| 741 #endif | |
| 742 | |
| 743 #if defined(OS_WIN) | |
| 744 bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() { | |
| 745 // Need to update geometry synchronously with WMP, otherwise if a site | |
| 746 // scripts the plugin to start playing while it's in the middle of handling | |
| 747 // an update geometry message, videos don't play. See urls in bug 20260. | |
| 748 if (info_.name.find(base::ASCIIToUTF16("Windows Media Player")) != | |
| 749 base::string16::npos) | |
| 750 return true; | |
| 751 | |
| 752 // The move networks plugin needs to be informed of geometry updates | |
| 753 // synchronously. | |
| 754 std::vector<WebPluginMimeType>::iterator index; | |
| 755 for (index = info_.mime_types.begin(); index != info_.mime_types.end(); | |
| 756 index++) { | |
| 757 if (index->mime_type == "application/x-vnd.moveplayer.qm" || | |
| 758 index->mime_type == "application/x-vnd.moveplay2.qm" || | |
| 759 index->mime_type == "application/x-vnd.movenetworks.qm" || | |
| 760 index->mime_type == "application/x-vnd.mnplayer.qm") { | |
| 761 return true; | |
| 762 } | |
| 763 } | |
| 764 return false; | |
| 765 } | |
| 766 #endif | |
| 767 | |
| 768 } // namespace content | |
| OLD | NEW |