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_plugin_instance_impl.h" 17 #include "content/renderer/render_thread_impl.h" 18 #include "gpu/command_buffer/client/gles2_implementation.h" 19 #include "skia/ext/platform_canvas.h" 20 #include "third_party/WebKit/public/platform/WebCanvas.h" 21 #include "third_party/WebKit/public/platform/WebCursorInfo.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/WebWidget.h" 26 #include "ui/gfx/size_conversions.h" 27 #include "ui/gl/gpu_preference.h" 28 29 using blink::WebCanvas; 30 using blink::WebCompositionUnderline; 31 using blink::WebCursorInfo; 32 using blink::WebGestureEvent; 33 using blink::WebInputEvent; 34 using blink::WebMouseEvent; 35 using blink::WebMouseWheelEvent; 36 using blink::WebPoint; 37 using blink::WebRect; 38 using blink::WebSize; 39 using blink::WebString; 40 using blink::WebTextDirection; 41 using blink::WebTextInputType; 42 using blink::WebVector; 43 using blink::WebWidget; 44 using blink::WGC3Dintptr; 45 46 namespace content { 47 48 namespace { 49 50 class FullscreenMouseLockDispatcher : public MouseLockDispatcher { 51 public: 52 explicit FullscreenMouseLockDispatcher(RenderWidgetFullscreenPepper* widget); 53 virtual ~FullscreenMouseLockDispatcher(); 54 55 private: 56 // MouseLockDispatcher implementation. 57 virtual void SendLockMouseRequest(bool unlocked_by_target) OVERRIDE; 58 virtual void SendUnlockMouseRequest() OVERRIDE; 59 60 RenderWidgetFullscreenPepper* widget_; 61 62 DISALLOW_COPY_AND_ASSIGN(FullscreenMouseLockDispatcher); 63 }; 64 65 WebMouseEvent WebMouseEventFromGestureEvent(const WebGestureEvent& gesture) { 66 WebMouseEvent mouse; 67 68 switch (gesture.type) { 69 case WebInputEvent::GestureScrollBegin: 70 mouse.type = WebInputEvent::MouseDown; 71 break; 72 73 case WebInputEvent::GestureScrollUpdate: 74 mouse.type = WebInputEvent::MouseMove; 75 break; 76 77 case WebInputEvent::GestureFlingStart: 78 if (gesture.sourceDevice == blink::WebGestureDeviceTouchscreen) { 79 // A scroll gesture on the touchscreen may end with a GestureScrollEnd 80 // when there is no velocity, or a GestureFlingStart when it has a 81 // velocity. In both cases, it should end the drag that was initiated by 82 // the GestureScrollBegin (and subsequent GestureScrollUpdate) events. 83 mouse.type = WebInputEvent::MouseUp; 84 break; 85 } else { 86 return mouse; 87 } 88 case WebInputEvent::GestureScrollEnd: 89 mouse.type = WebInputEvent::MouseUp; 90 break; 91 92 default: 93 break; 94 } 95 96 if (mouse.type == WebInputEvent::Undefined) 97 return mouse; 98 99 mouse.timeStampSeconds = gesture.timeStampSeconds; 100 mouse.modifiers = gesture.modifiers | WebInputEvent::LeftButtonDown; 101 mouse.button = WebMouseEvent::ButtonLeft; 102 mouse.clickCount = (mouse.type == WebInputEvent::MouseDown || 103 mouse.type == WebInputEvent::MouseUp); 104 105 mouse.x = gesture.x; 106 mouse.y = gesture.y; 107 mouse.windowX = gesture.globalX; 108 mouse.windowY = gesture.globalY; 109 mouse.globalX = gesture.globalX; 110 mouse.globalY = gesture.globalY; 111 112 return mouse; 113 } 114 115 FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher( 116 RenderWidgetFullscreenPepper* widget) : widget_(widget) { 117 } 118 119 FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() { 120 } 121 122 void FullscreenMouseLockDispatcher::SendLockMouseRequest( 123 bool unlocked_by_target) { 124 widget_->Send(new ViewHostMsg_LockMouse(widget_->routing_id(), false, 125 unlocked_by_target, true)); 126 } 127 128 void FullscreenMouseLockDispatcher::SendUnlockMouseRequest() { 129 widget_->Send(new ViewHostMsg_UnlockMouse(widget_->routing_id())); 130 } 131 132 // WebWidget that simply wraps the pepper plugin. 133 // TODO(piman): figure out IME and implement setComposition and friends if 134 // necessary. 135 class PepperWidget : public WebWidget { 136 public: 137 explicit PepperWidget(RenderWidgetFullscreenPepper* widget) 138 : widget_(widget) { 139 } 140 141 virtual ~PepperWidget() {} 142 143 // WebWidget API 144 virtual void close() { 145 delete this; 146 } 147 148 virtual WebSize size() { 149 return size_; 150 } 151 152 virtual void resize(const WebSize& size) { 153 if (!widget_->plugin()) 154 return; 155 156 size_ = size; 157 WebRect plugin_rect(0, 0, size_.width, size_.height); 158 widget_->plugin()->ViewChanged(plugin_rect, plugin_rect, 159 std::vector<gfx::Rect>()); 160 widget_->Invalidate(); 161 } 162 163 virtual void themeChanged() { 164 NOTIMPLEMENTED(); 165 } 166 167 virtual bool handleInputEvent(const WebInputEvent& event) { 168 if (!widget_->plugin()) 169 return false; 170 171 // This cursor info is ignored, we always set the cursor directly from 172 // RenderWidgetFullscreenPepper::DidChangeCursor. 173 WebCursorInfo cursor; 174 175 // Pepper plugins do not accept gesture events. So do not send the gesture 176 // events directly to the plugin. Instead, try to convert them to equivalent 177 // mouse events, and then send to the plugin. 178 if (WebInputEvent::isGestureEventType(event.type)) { 179 bool result = false; 180 const WebGestureEvent* gesture_event = 181 static_cast<const WebGestureEvent*>(&event); 182 switch (event.type) { 183 case WebInputEvent::GestureTap: { 184 WebMouseEvent mouse; 185 186 mouse.timeStampSeconds = gesture_event->timeStampSeconds; 187 mouse.type = WebInputEvent::MouseMove; 188 mouse.modifiers = gesture_event->modifiers; 189 190 mouse.x = gesture_event->x; 191 mouse.y = gesture_event->y; 192 mouse.windowX = gesture_event->globalX; 193 mouse.windowY = gesture_event->globalY; 194 mouse.globalX = gesture_event->globalX; 195 mouse.globalY = gesture_event->globalY; 196 mouse.movementX = 0; 197 mouse.movementY = 0; 198 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 199 200 mouse.type = WebInputEvent::MouseDown; 201 mouse.button = WebMouseEvent::ButtonLeft; 202 mouse.clickCount = gesture_event->data.tap.tapCount; 203 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 204 205 mouse.type = WebInputEvent::MouseUp; 206 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 207 break; 208 } 209 210 default: { 211 WebMouseEvent mouse = WebMouseEventFromGestureEvent(*gesture_event); 212 if (mouse.type != WebInputEvent::Undefined) 213 result |= widget_->plugin()->HandleInputEvent(mouse, &cursor); 214 break; 215 } 216 } 217 return result; 218 } 219 220 bool result = widget_->plugin()->HandleInputEvent(event, &cursor); 221 222 // For normal web pages, WebViewImpl does input event translations and 223 // generates context menu events. Since we don't have a WebView, we need to 224 // do the necessary translation ourselves. 225 if (WebInputEvent::isMouseEventType(event.type)) { 226 const WebMouseEvent& mouse_event = 227 reinterpret_cast<const WebMouseEvent&>(event); 228 bool send_context_menu_event = false; 229 // On Mac/Linux, we handle it on mouse down. 230 // On Windows, we handle it on mouse up. 231 #if defined(OS_WIN) 232 send_context_menu_event = 233 mouse_event.type == WebInputEvent::MouseUp && 234 mouse_event.button == WebMouseEvent::ButtonRight; 235 #elif defined(OS_MACOSX) 236 send_context_menu_event = 237 mouse_event.type == WebInputEvent::MouseDown && 238 (mouse_event.button == WebMouseEvent::ButtonRight || 239 (mouse_event.button == WebMouseEvent::ButtonLeft && 240 mouse_event.modifiers & WebMouseEvent::ControlKey)); 241 #else 242 send_context_menu_event = 243 mouse_event.type == WebInputEvent::MouseDown && 244 mouse_event.button == WebMouseEvent::ButtonRight; 245 #endif 246 if (send_context_menu_event) { 247 WebMouseEvent context_menu_event(mouse_event); 248 context_menu_event.type = WebInputEvent::ContextMenu; 249 widget_->plugin()->HandleInputEvent(context_menu_event, &cursor); 250 } 251 } 252 return result; 253 } 254 255 private: 256 RenderWidgetFullscreenPepper* widget_; 257 WebSize size_; 258 259 DISALLOW_COPY_AND_ASSIGN(PepperWidget); 260 }; 261 262 } // anonymous namespace 263 264 // static 265 RenderWidgetFullscreenPepper* RenderWidgetFullscreenPepper::Create( 266 int32 opener_id, 267 PepperPluginInstanceImpl* plugin, 268 const GURL& active_url, 269 const blink::WebScreenInfo& screen_info) { 270 DCHECK_NE(MSG_ROUTING_NONE, opener_id); 271 scoped_refptr<RenderWidgetFullscreenPepper> widget( 272 new RenderWidgetFullscreenPepper(plugin, active_url, screen_info)); 273 widget->Init(opener_id); 274 widget->AddRef(); 275 return widget.get(); 276 } 277 278 RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper( 279 PepperPluginInstanceImpl* plugin, 280 const GURL& active_url, 281 const blink::WebScreenInfo& screen_info) 282 : RenderWidgetFullscreen(screen_info), 283 active_url_(active_url), 284 plugin_(plugin), 285 layer_(NULL), 286 mouse_lock_dispatcher_(new FullscreenMouseLockDispatcher( 287 this)) { 288 } 289 290 RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() { 291 } 292 293 void RenderWidgetFullscreenPepper::Invalidate() { 294 InvalidateRect(gfx::Rect(size_.width(), size_.height())); 295 } 296 297 void RenderWidgetFullscreenPepper::InvalidateRect(const blink::WebRect& rect) { 298 didInvalidateRect(rect); 299 } 300 301 void RenderWidgetFullscreenPepper::ScrollRect( 302 int dx, int dy, const blink::WebRect& rect) { 303 didScrollRect(dx, dy, rect); 304 } 305 306 void RenderWidgetFullscreenPepper::Destroy() { 307 // This function is called by the plugin instance as it's going away, so reset 308 // plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close(). 309 plugin_ = NULL; 310 311 // After calling Destroy(), the plugin instance assumes that the layer is not 312 // used by us anymore, so it may destroy the layer before this object goes 313 // away. 314 SetLayer(NULL); 315 316 Send(new ViewHostMsg_Close(routing_id_)); 317 Release(); 318 } 319 320 void RenderWidgetFullscreenPepper::DidChangeCursor( 321 const blink::WebCursorInfo& cursor) { 322 didChangeCursor(cursor); 323 } 324 325 void RenderWidgetFullscreenPepper::SetLayer(blink::WebLayer* layer) { 326 layer_ = layer; 327 if (!layer_) { 328 if (compositor_) 329 compositor_->clearRootLayer(); 330 return; 331 } 332 if (!layerTreeView()) 333 initializeLayerTreeView(); 334 layer_->setBounds(blink::WebSize(size())); 335 layer_->setDrawsContent(true); 336 compositor_->setDeviceScaleFactor(device_scale_factor_); 337 compositor_->setRootLayer(*layer_); 338 } 339 340 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) { 341 bool handled = true; 342 IPC_BEGIN_MESSAGE_MAP(RenderWidgetFullscreenPepper, msg) 343 IPC_MESSAGE_FORWARD(ViewMsg_LockMouse_ACK, 344 mouse_lock_dispatcher_.get(), 345 MouseLockDispatcher::OnLockMouseACK) 346 IPC_MESSAGE_FORWARD(ViewMsg_MouseLockLost, 347 mouse_lock_dispatcher_.get(), 348 MouseLockDispatcher::OnMouseLockLost) 349 IPC_MESSAGE_UNHANDLED(handled = false) 350 IPC_END_MESSAGE_MAP() 351 if (handled) 352 return true; 353 354 return RenderWidgetFullscreen::OnMessageReceived(msg); 355 } 356 357 void RenderWidgetFullscreenPepper::DidInitiatePaint() { 358 if (plugin_) 359 plugin_->ViewInitiatedPaint(); 360 } 361 362 void RenderWidgetFullscreenPepper::DidFlushPaint() { 363 if (plugin_) 364 plugin_->ViewFlushedPaint(); 365 } 366 367 void RenderWidgetFullscreenPepper::Close() { 368 // If the fullscreen window is closed (e.g. user pressed escape), reset to 369 // normal mode. 370 if (plugin_) 371 plugin_->FlashSetFullscreen(false, false); 372 373 // Call Close on the base class to destroy the WebWidget instance. 374 RenderWidget::Close(); 375 } 376 377 void RenderWidgetFullscreenPepper::OnResize( 378 const ViewMsg_Resize_Params& params) { 379 if (layer_) 380 layer_->setBounds(blink::WebSize(params.new_size)); 381 RenderWidget::OnResize(params); 382 } 383 384 WebWidget* RenderWidgetFullscreenPepper::CreateWebWidget() { 385 return new PepperWidget(this); 386 } 387 388 GURL RenderWidgetFullscreenPepper::GetURLForGraphicsContext3D() { 389 return active_url_; 390 } 391 392 void RenderWidgetFullscreenPepper::SetDeviceScaleFactor( 393 float device_scale_factor) { 394 RenderWidget::SetDeviceScaleFactor(device_scale_factor); 395 if (compositor_) 396 compositor_->setDeviceScaleFactor(device_scale_factor); 397 } 398 399 } // namespace content 400