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/render_widget_fullscreen_pepper.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/message_loop/message_loop.h" 12 #include "content/common/gpu/client/gpu_channel_host.h" 13 #include "content/common/view_messages.h" 14 #include "content/public/common/content_switches.h" 15 #include "content/renderer/gpu/render_widget_compositor.h" 16 #include "content/renderer/pepper/pepper_platform_context_3d.h" 17 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 18 #include "content/renderer/render_thread_impl.h" 19 #include "gpu/command_buffer/client/gles2_implementation.h" 20 #include "skia/ext/platform_canvas.h" 21 #include "third_party/WebKit/public/platform/WebCanvas.h" 22 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" 23 #include "third_party/WebKit/public/platform/WebLayer.h" 24 #include "third_party/WebKit/public/platform/WebSize.h" 25 #include "third_party/WebKit/public/web/WebCursorInfo.h" 26 #include "third_party/WebKit/public/web/WebWidget.h" 27 #include "ui/gfx/size_conversions.h" 28 #include "ui/gl/gpu_preference.h" 29 30 using WebKit::WebCanvas; 31 using WebKit::WebCompositionUnderline; 32 using WebKit::WebCursorInfo; 33 using WebKit::WebGestureEvent; 34 using WebKit::WebInputEvent; 35 using WebKit::WebMouseEvent; 36 using WebKit::WebMouseWheelEvent; 37 using WebKit::WebPoint; 38 using WebKit::WebRect; 39 using WebKit::WebSize; 40 using WebKit::WebString; 41 using WebKit::WebTextDirection; 42 using WebKit::WebTextInputType; 43 using WebKit::WebVector; 44 using WebKit::WebWidget; 45 using WebKit::WGC3Dintptr; 46 47 namespace content { 48 49 namespace { 50 51 // See third_party/WebKit/Source/WebCore/dom/WheelEvent.h. 52 const float kTickDivisor = 120.0f; 53 54 class FullscreenMouseLockDispatcher : public MouseLockDispatcher { 55 public: 56 explicit FullscreenMouseLockDispatcher(RenderWidgetFullscreenPepper* widget); 57 virtual ~FullscreenMouseLockDispatcher(); 58 59 private: 60 // MouseLockDispatcher implementation. 61 virtual void SendLockMouseRequest(bool unlocked_by_target) OVERRIDE; 62 virtual void SendUnlockMouseRequest() OVERRIDE; 63 64 RenderWidgetFullscreenPepper* widget_; 65 66 DISALLOW_COPY_AND_ASSIGN(FullscreenMouseLockDispatcher); 67 }; 68 69 WebMouseEvent WebMouseEventFromGestureEvent(const WebGestureEvent& gesture) { 70 WebMouseEvent mouse; 71 72 switch (gesture.type) { 73 case WebInputEvent::GestureScrollBegin: 74 mouse.type = WebInputEvent::MouseDown; 75 break; 76 77 case WebInputEvent::GestureScrollUpdate: 78 mouse.type = WebInputEvent::MouseMove; 79 break; 80 81 case WebInputEvent::GestureFlingStart: 82 if (gesture.sourceDevice == WebGestureEvent::Touchscreen) { 83 // A scroll gesture on the touchscreen may end with a GestureScrollEnd 84 // when there is no velocity, or a GestureFlingStart when it has a 85 // velocity. In both cases, it should end the drag that was initiated by 86 // the GestureScrollBegin (and subsequent GestureScrollUpdate) events. 87 mouse.type = WebInputEvent::MouseUp; 88 break; 89 } else { 90 return mouse; 91 } 92 case WebInputEvent::GestureScrollEnd: 93 mouse.type = WebInputEvent::MouseUp; 94 break; 95 96 default: 97 break; 98 } 99 100 if (mouse.type == WebInputEvent::Undefined) 101 return mouse; 102 103 mouse.timeStampSeconds = gesture.timeStampSeconds; 104 mouse.modifiers = gesture.modifiers | WebInputEvent::LeftButtonDown; 105 mouse.button = WebMouseEvent::ButtonLeft; 106 mouse.clickCount = (mouse.type == WebInputEvent::MouseDown || 107 mouse.type == WebInputEvent::MouseUp); 108 109 mouse.x = gesture.x; 110 mouse.y = gesture.y; 111 mouse.windowX = gesture.globalX; 112 mouse.windowY = gesture.globalY; 113 mouse.globalX = gesture.globalX; 114 mouse.globalY = gesture.globalY; 115 116 return mouse; 117 } 118 119 FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher( 120 RenderWidgetFullscreenPepper* widget) : widget_(widget) { 121 } 122 123 FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() { 124 } 125 126 void FullscreenMouseLockDispatcher::SendLockMouseRequest( 127 bool unlocked_by_target) { 128 widget_->Send(new ViewHostMsg_LockMouse(widget_->routing_id(), false, 129 unlocked_by_target, true)); 130 } 131 132 void FullscreenMouseLockDispatcher::SendUnlockMouseRequest() { 133 widget_->Send(new ViewHostMsg_UnlockMouse(widget_->routing_id())); 134 } 135 136 // WebWidget that simply wraps the pepper plugin. 137 class PepperWidget : public WebWidget { 138 public: 139 explicit PepperWidget(RenderWidgetFullscreenPepper* widget) 140 : widget_(widget) { 141 } 142 143 virtual ~PepperWidget() {} 144 145 // WebWidget API 146 virtual void close() { 147 delete this; 148 } 149 150 virtual WebSize size() { 151 return size_; 152 } 153 154 virtual void willStartLiveResize() { 155 } 156 157 virtual void resize(const WebSize& size) { 158 if (!widget_->plugin()) 159 return; 160 161 size_ = size; 162 WebRect plugin_rect(0, 0, size_.width, size_.height); 163 widget_->plugin()->ViewChanged(plugin_rect, plugin_rect, 164 std::vector<gfx::Rect>()); 165 widget_->Invalidate(); 166 } 167 168 virtual void willEndLiveResize() { 169 } 170 171 virtual void animate(double frameBeginTime) { 172 } 173 174 virtual void layout() { 175 } 176 177 virtual void paint(WebCanvas* canvas, const WebRect& rect, PaintOptions) { 178 if (!widget_->plugin()) 179 return; 180 181 SkAutoCanvasRestore auto_restore(canvas, true); 182 float canvas_scale = widget_->deviceScaleFactor(); 183 canvas->scale(canvas_scale, canvas_scale); 184 185 WebRect plugin_rect(0, 0, size_.width, size_.height); 186 widget_->plugin()->Paint(canvas, plugin_rect, rect); 187 } 188 189 virtual void setCompositorSurfaceReady() { 190 } 191 192 virtual void composite(bool finish) { 193 } 194 195 virtual void themeChanged() { 196 NOTIMPLEMENTED(); 197 } 198 199 virtual bool handleInputEvent(const WebInputEvent& event) { 200 if (!widget_->plugin()) 201 return false; 202 203 // This cursor info is ignored, we always set the cursor directly from 204 // RenderWidgetFullscreenPepper::DidChangeCursor. 205 WebCursorInfo cursor; 206 207 // Pepper plugins do not accept gesture events. So do not send the gesture 208 // events directly to the plugin. Instead, try to convert them to equivalent 209 // mouse events, and then send to the plugin. 210 if (WebInputEvent::isGestureEventType(event.type)) { 211 bool result = false; 212 const WebGestureEvent* gesture_event = 213 static_cast<const WebGestureEvent*>(&event); 214 switch (event.type) { 215 case WebInputEvent::GestureTap: { 216 WebMouseEvent mouse; 217 218 mouse.timeStampSeconds = gesture_event->timeStampSeconds; 219 mouse.type = WebInputEvent::MouseMove; 220 mouse.modifiers = gesture_event->modifiers; 221 222 mouse.x = gesture_event->x; 223 mouse.y = gesture_event->y; 224 mouse.windowX = gesture_event->globalX; 225 mouse.windowY = gesture_event->globalY; 226 mouse.globalX = gesture_event->globalX; 227 mouse.globalY = gesture_event->globalY; 228 mouse.movementX = 0; 229 mouse.movementY = 0; 230 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 231 232 mouse.type = WebInputEvent::MouseDown; 233 mouse.button = WebMouseEvent::ButtonLeft; 234 mouse.clickCount = gesture_event->data.tap.tapCount; 235 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 236 237 mouse.type = WebInputEvent::MouseUp; 238 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 239 break; 240 } 241 242 default: { 243 WebMouseEvent mouse = WebMouseEventFromGestureEvent(*gesture_event); 244 if (mouse.type != WebInputEvent::Undefined) 245 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 246 break; 247 } 248 } 249 return result; 250 } 251 252 bool result = widget_->plugin()->HandleInputEvent(event, &cursor); 253 254 // For normal web pages, WebViewImpl does input event translations and 255 // generates context menu events. Since we don't have a WebView, we need to 256 // do the necessary translation ourselves. 257 if (WebInputEvent::isMouseEventType(event.type)) { 258 const WebMouseEvent& mouse_event = 259 reinterpret_cast<const WebMouseEvent&>(event); 260 bool send_context_menu_event = false; 261 // On Mac/Linux, we handle it on mouse down. 262 // On Windows, we handle it on mouse up. 263 #if defined(OS_WIN) 264 send_context_menu_event = 265 mouse_event.type == WebInputEvent::MouseUp && 266 mouse_event.button == WebMouseEvent::ButtonRight; 267 #elif defined(OS_MACOSX) 268 send_context_menu_event = 269 mouse_event.type == WebInputEvent::MouseDown && 270 (mouse_event.button == WebMouseEvent::ButtonRight || 271 (mouse_event.button == WebMouseEvent::ButtonLeft && 272 mouse_event.modifiers & WebMouseEvent::ControlKey)); 273 #else 274 send_context_menu_event = 275 mouse_event.type == WebInputEvent::MouseDown && 276 mouse_event.button == WebMouseEvent::ButtonRight; 277 #endif 278 if (send_context_menu_event) { 279 WebMouseEvent context_menu_event(mouse_event); 280 context_menu_event.type = WebInputEvent::ContextMenu; 281 widget_->plugin()->HandleInputEvent(context_menu_event, &cursor); 282 } 283 } 284 return result; 285 } 286 287 virtual void mouseCaptureLost() { 288 NOTIMPLEMENTED(); 289 } 290 291 virtual void setFocus(bool focus) { 292 NOTIMPLEMENTED(); 293 } 294 295 // TODO(piman): figure out IME and implement these if necessary. 296 virtual bool setComposition( 297 const WebString& text, 298 const WebVector<WebCompositionUnderline>& underlines, 299 int selectionStart, 300 int selectionEnd) { 301 return false; 302 } 303 304 virtual bool confirmComposition() { 305 return false; 306 } 307 308 virtual bool compositionRange(size_t* location, size_t* length) { 309 return false; 310 } 311 312 virtual bool confirmComposition(const WebString& text) { 313 return false; 314 } 315 316 virtual WebTextInputType textInputType() { 317 return WebKit::WebTextInputTypeNone; 318 } 319 320 virtual WebRect caretOrSelectionBounds() { 321 return WebRect(); 322 } 323 324 virtual bool selectionRange(WebPoint& start, WebPoint& end) const { 325 return false; 326 } 327 328 virtual bool caretOrSelectionRange(size_t* location, size_t* length) { 329 return false; 330 } 331 332 virtual void setTextDirection(WebTextDirection) { 333 } 334 335 virtual bool isAcceleratedCompositingActive() const { 336 return widget_->plugin() && widget_->is_compositing(); 337 } 338 339 private: 340 RenderWidgetFullscreenPepper* widget_; 341 WebSize size_; 342 343 DISALLOW_COPY_AND_ASSIGN(PepperWidget); 344 }; 345 346 } // anonymous namespace 347 348 // static 349 RenderWidgetFullscreenPepper* RenderWidgetFullscreenPepper::Create( 350 int32 opener_id, 351 PepperPluginInstanceImpl* plugin, 352 const GURL& active_url, 353 const WebKit::WebScreenInfo& screen_info) { 354 DCHECK_NE(MSG_ROUTING_NONE, opener_id); 355 scoped_refptr<RenderWidgetFullscreenPepper> widget( 356 new RenderWidgetFullscreenPepper(plugin, active_url, screen_info)); 357 widget->Init(opener_id); 358 widget->AddRef(); 359 return widget.get(); 360 } 361 362 RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper( 363 PepperPluginInstanceImpl* plugin, 364 const GURL& active_url, 365 const WebKit::WebScreenInfo& screen_info) 366 : RenderWidgetFullscreen(screen_info), 367 active_url_(active_url), 368 plugin_(plugin), 369 layer_(NULL), 370 mouse_lock_dispatcher_(new FullscreenMouseLockDispatcher( 371 this)) { 372 } 373 374 RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() { 375 } 376 377 void RenderWidgetFullscreenPepper::Invalidate() { 378 InvalidateRect(gfx::Rect(size_.width(), size_.height())); 379 } 380 381 void RenderWidgetFullscreenPepper::InvalidateRect(const WebKit::WebRect& rect) { 382 didInvalidateRect(rect); 383 } 384 385 void RenderWidgetFullscreenPepper::ScrollRect( 386 int dx, int dy, const WebKit::WebRect& rect) { 387 didScrollRect(dx, dy, rect); 388 } 389 390 void RenderWidgetFullscreenPepper::Destroy() { 391 // This function is called by the plugin instance as it's going away, so reset 392 // plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close(). 393 plugin_ = NULL; 394 395 // After calling Destroy(), the plugin instance assumes that the layer is not 396 // used by us anymore, so it may destroy the layer before this object goes 397 // away. 398 SetLayer(NULL); 399 400 Send(new ViewHostMsg_Close(routing_id_)); 401 Release(); 402 } 403 404 void RenderWidgetFullscreenPepper::DidChangeCursor( 405 const WebKit::WebCursorInfo& cursor) { 406 didChangeCursor(cursor); 407 } 408 409 void RenderWidgetFullscreenPepper::SetLayer(WebKit::WebLayer* layer) { 410 layer_ = layer; 411 bool compositing = !!layer_; 412 if (compositing != is_accelerated_compositing_active_) { 413 if (compositing) { 414 initializeLayerTreeView(); 415 if (!layerTreeView()) 416 return; 417 layer_->setBounds(WebKit::WebSize(size())); 418 layer_->setDrawsContent(true); 419 compositor_->setDeviceScaleFactor(device_scale_factor_); 420 compositor_->setRootLayer(*layer_); 421 didActivateCompositor(-1); 422 } else { 423 didDeactivateCompositor(); 424 } 425 } 426 } 427 428 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) { 429 bool handled = true; 430 IPC_BEGIN_MESSAGE_MAP(RenderWidgetFullscreenPepper, msg) 431 IPC_MESSAGE_FORWARD(ViewMsg_LockMouse_ACK, 432 mouse_lock_dispatcher_.get(), 433 MouseLockDispatcher::OnLockMouseACK) 434 IPC_MESSAGE_FORWARD(ViewMsg_MouseLockLost, 435 mouse_lock_dispatcher_.get(), 436 MouseLockDispatcher::OnMouseLockLost) 437 IPC_MESSAGE_UNHANDLED(handled = false) 438 IPC_END_MESSAGE_MAP() 439 if (handled) 440 return true; 441 442 return RenderWidgetFullscreen::OnMessageReceived(msg); 443 } 444 445 void RenderWidgetFullscreenPepper::WillInitiatePaint() { 446 if (plugin_) 447 plugin_->ViewWillInitiatePaint(); 448 } 449 450 void RenderWidgetFullscreenPepper::DidInitiatePaint() { 451 if (plugin_) 452 plugin_->ViewInitiatedPaint(); 453 } 454 455 void RenderWidgetFullscreenPepper::DidFlushPaint() { 456 if (plugin_) 457 plugin_->ViewFlushedPaint(); 458 } 459 460 void RenderWidgetFullscreenPepper::Close() { 461 // If the fullscreen window is closed (e.g. user pressed escape), reset to 462 // normal mode. 463 if (plugin_) 464 plugin_->FlashSetFullscreen(false, false); 465 466 // Call Close on the base class to destroy the WebWidget instance. 467 RenderWidget::Close(); 468 } 469 470 PepperPluginInstanceImpl* 471 RenderWidgetFullscreenPepper::GetBitmapForOptimizedPluginPaint( 472 const gfx::Rect& paint_bounds, 473 TransportDIB** dib, 474 gfx::Rect* location, 475 gfx::Rect* clip, 476 float* scale_factor) { 477 if (plugin_ && plugin_->GetBitmapForOptimizedPluginPaint( 478 paint_bounds, dib, location, clip, scale_factor)) { 479 return plugin_; 480 } 481 return NULL; 482 } 483 484 void RenderWidgetFullscreenPepper::OnResize( 485 const ViewMsg_Resize_Params& params) { 486 if (layer_) 487 layer_->setBounds(WebKit::WebSize(params.new_size)); 488 RenderWidget::OnResize(params); 489 } 490 491 WebWidget* RenderWidgetFullscreenPepper::CreateWebWidget() { 492 return new PepperWidget(this); 493 } 494 495 GURL RenderWidgetFullscreenPepper::GetURLForGraphicsContext3D() { 496 return active_url_; 497 } 498 499 void RenderWidgetFullscreenPepper::SetDeviceScaleFactor( 500 float device_scale_factor) { 501 RenderWidget::SetDeviceScaleFactor(device_scale_factor); 502 if (compositor_) 503 compositor_->setDeviceScaleFactor(device_scale_factor); 504 } 505 506 } // namespace content 507