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.
5 #include "content/renderer/render_widget_fullscreen_pepper.h"
10 #include "base/command_line.h"
11 #include "base/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/pepper/pepper_platform_context_3d_impl.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "gpu/command_buffer/client/gles2_implementation.h"
18 #include "skia/ext/platform_canvas.h"
19 #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCanvas.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h"
24 #include "ui/gfx/size_conversions.h"
25 #include "ui/gl/gpu_preference.h"
26 #include "webkit/plugins/ppapi/plugin_delegate.h"
27 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
29 using WebKit::WebCanvas
;
30 using WebKit::WebCompositionUnderline
;
31 using WebKit::WebCursorInfo
;
32 using WebKit::WebGestureEvent
;
33 using WebKit::WebInputEvent
;
34 using WebKit::WebMouseEvent
;
35 using WebKit::WebMouseWheelEvent
;
36 using WebKit::WebPoint
;
37 using WebKit::WebRect
;
38 using WebKit::WebSize
;
39 using WebKit::WebString
;
40 using WebKit::WebTextDirection
;
41 using WebKit::WebTextInputType
;
42 using WebKit::WebVector
;
43 using WebKit::WebWidget
;
44 using WebKit::WGC3Dintptr
;
50 // See third_party/WebKit/Source/WebCore/dom/WheelEvent.h.
51 const float kTickDivisor
= 120.0f
;
53 class FullscreenMouseLockDispatcher
: public MouseLockDispatcher
{
55 explicit FullscreenMouseLockDispatcher(RenderWidgetFullscreenPepper
* widget
);
56 virtual ~FullscreenMouseLockDispatcher();
59 // MouseLockDispatcher implementation.
60 virtual void SendLockMouseRequest(bool unlocked_by_target
) OVERRIDE
;
61 virtual void SendUnlockMouseRequest() OVERRIDE
;
63 RenderWidgetFullscreenPepper
* widget_
;
65 DISALLOW_COPY_AND_ASSIGN(FullscreenMouseLockDispatcher
);
68 FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher(
69 RenderWidgetFullscreenPepper
* widget
) : widget_(widget
) {
72 FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() {
75 void FullscreenMouseLockDispatcher::SendLockMouseRequest(
76 bool unlocked_by_target
) {
77 widget_
->Send(new ViewHostMsg_LockMouse(widget_
->routing_id(), false,
78 unlocked_by_target
, true));
81 void FullscreenMouseLockDispatcher::SendUnlockMouseRequest() {
82 widget_
->Send(new ViewHostMsg_UnlockMouse(widget_
->routing_id()));
85 // WebWidget that simply wraps the pepper plugin.
86 class PepperWidget
: public WebWidget
{
88 explicit PepperWidget(RenderWidgetFullscreenPepper
* widget
)
92 virtual ~PepperWidget() {}
95 virtual void close() {
99 virtual WebSize
size() {
103 virtual void willStartLiveResize() {
106 virtual void resize(const WebSize
& size
) {
107 if (!widget_
->plugin())
111 WebRect
plugin_rect(0, 0, size_
.width
, size_
.height
);
112 widget_
->plugin()->ViewChanged(plugin_rect
, plugin_rect
,
113 std::vector
<gfx::Rect
>());
114 widget_
->Invalidate();
117 virtual void willEndLiveResize() {
120 virtual void animate(double frameBeginTime
) {
123 virtual void layout() {
126 #if WEBWIDGET_HAS_PAINT_OPTIONS
127 virtual void paint(WebCanvas
* canvas
, const WebRect
& rect
, PaintOptions
) {
129 virtual void paint(WebCanvas
* canvas
, const WebRect
& rect
) {
131 if (!widget_
->plugin())
134 SkAutoCanvasRestore
auto_restore(canvas
, true);
135 float canvas_scale
= widget_
->deviceScaleFactor();
136 canvas
->scale(canvas_scale
, canvas_scale
);
138 WebRect
plugin_rect(0, 0, size_
.width
, size_
.height
);
139 widget_
->plugin()->Paint(canvas
, plugin_rect
, rect
);
142 #if WEBWIDGET_HAS_SETCOMPOSITORSURFACEREADY
143 virtual void setCompositorSurfaceReady() {
147 virtual void composite(bool finish
) {
148 if (!widget_
->plugin())
151 WebGraphicsContext3DCommandBufferImpl
* context
= widget_
->context();
153 unsigned int texture
= widget_
->plugin()->GetBackingTextureId();
154 context
->bindTexture(GL_TEXTURE_2D
, texture
);
155 context
->drawArrays(GL_TRIANGLES
, 0, 3);
156 widget_
->SwapBuffers();
159 virtual void themeChanged() {
163 virtual bool handleInputEvent(const WebInputEvent
& event
) {
164 if (!widget_
->plugin())
167 // This cursor info is ignored, we always set the cursor directly from
168 // RenderWidgetFullscreenPepper::DidChangeCursor.
169 WebCursorInfo cursor
;
170 bool result
= widget_
->plugin()->HandleInputEvent(event
, &cursor
);
172 // For normal web pages, WebCore::EventHandler converts selected
173 // gesture events into mouse and wheel events. We don't have a WebView
174 // so do this translation here.
175 if (!result
&& WebInputEvent::isGestureEventType(event
.type
)) {
176 switch (event
.type
) {
177 case WebInputEvent::GestureScrollUpdate
: {
178 const WebGestureEvent
* gesture_event
=
179 static_cast<const WebGestureEvent
*>(&event
);
180 WebMouseWheelEvent wheel_event
;
181 wheel_event
.timeStampSeconds
= gesture_event
->timeStampSeconds
;
182 wheel_event
.type
= WebInputEvent::MouseWheel
;
183 wheel_event
.modifiers
= gesture_event
->modifiers
;
185 wheel_event
.x
= gesture_event
->x
;
186 wheel_event
.y
= gesture_event
->y
;
187 wheel_event
.windowX
= gesture_event
->globalX
;
188 wheel_event
.windowY
= gesture_event
->globalX
;
189 wheel_event
.globalX
= gesture_event
->globalX
;
190 wheel_event
.globalY
= gesture_event
->globalY
;
191 wheel_event
.movementX
= 0;
192 wheel_event
.movementY
= 0;
194 wheel_event
.deltaX
= gesture_event
->data
.scrollUpdate
.deltaX
;
195 wheel_event
.deltaY
= gesture_event
->data
.scrollUpdate
.deltaY
;
196 wheel_event
.wheelTicksX
=
197 gesture_event
->data
.scrollUpdate
.deltaX
/ kTickDivisor
;
198 wheel_event
.wheelTicksY
=
199 gesture_event
->data
.scrollUpdate
.deltaY
/ kTickDivisor
;
200 wheel_event
.hasPreciseScrollingDeltas
= 1;
201 wheel_event
.phase
= WebMouseWheelEvent::PhaseNone
;
202 wheel_event
.momentumPhase
= WebMouseWheelEvent::PhaseNone
;
204 result
|= widget_
->plugin()->HandleInputEvent(wheel_event
, &cursor
);
207 case WebInputEvent::GestureTap
: {
208 const WebGestureEvent
* gesture_event
=
209 static_cast<const WebGestureEvent
*>(&event
);
210 WebMouseEvent mouseEvent
;
212 mouseEvent
.timeStampSeconds
= gesture_event
->timeStampSeconds
;
213 mouseEvent
.type
= WebInputEvent::MouseMove
;
214 mouseEvent
.modifiers
= gesture_event
->modifiers
;
216 mouseEvent
.x
= gesture_event
->x
;
217 mouseEvent
.y
= gesture_event
->y
;
218 mouseEvent
.windowX
= gesture_event
->globalX
;
219 mouseEvent
.windowY
= gesture_event
->globalX
;
220 mouseEvent
.globalX
= gesture_event
->globalX
;
221 mouseEvent
.globalY
= gesture_event
->globalY
;
222 mouseEvent
.movementX
= 0;
223 mouseEvent
.movementY
= 0;
224 result
|= widget_
->plugin()->HandleInputEvent(mouseEvent
, &cursor
);
226 mouseEvent
.type
= WebInputEvent::MouseDown
;
227 mouseEvent
.button
= WebMouseEvent::ButtonLeft
;
228 mouseEvent
.clickCount
= gesture_event
->data
.tap
.tapCount
;
229 result
|= widget_
->plugin()->HandleInputEvent(mouseEvent
, &cursor
);
231 mouseEvent
.type
= WebInputEvent::MouseUp
;
232 result
|= widget_
->plugin()->HandleInputEvent(mouseEvent
, &cursor
);
240 // For normal web pages, WebViewImpl does input event translations and
241 // generates context menu events. Since we don't have a WebView, we need to
242 // do the necessary translation ourselves.
243 if (WebInputEvent::isMouseEventType(event
.type
)) {
244 const WebMouseEvent
& mouse_event
=
245 reinterpret_cast<const WebMouseEvent
&>(event
);
246 bool send_context_menu_event
= false;
247 // On Mac/Linux, we handle it on mouse down.
248 // On Windows, we handle it on mouse up.
250 send_context_menu_event
=
251 mouse_event
.type
== WebInputEvent::MouseUp
&&
252 mouse_event
.button
== WebMouseEvent::ButtonRight
;
253 #elif defined(OS_MACOSX)
254 send_context_menu_event
=
255 mouse_event
.type
== WebInputEvent::MouseDown
&&
256 (mouse_event
.button
== WebMouseEvent::ButtonRight
||
257 (mouse_event
.button
== WebMouseEvent::ButtonLeft
&&
258 mouse_event
.modifiers
& WebMouseEvent::ControlKey
));
260 send_context_menu_event
=
261 mouse_event
.type
== WebInputEvent::MouseDown
&&
262 mouse_event
.button
== WebMouseEvent::ButtonRight
;
264 if (send_context_menu_event
) {
265 WebMouseEvent
context_menu_event(mouse_event
);
266 context_menu_event
.type
= WebInputEvent::ContextMenu
;
267 widget_
->plugin()->HandleInputEvent(context_menu_event
, &cursor
);
273 virtual void mouseCaptureLost() {
277 virtual void setFocus(bool focus
) {
281 // TODO(piman): figure out IME and implement these if necessary.
282 virtual bool setComposition(
283 const WebString
& text
,
284 const WebVector
<WebCompositionUnderline
>& underlines
,
290 virtual bool confirmComposition() {
294 virtual bool compositionRange(size_t* location
, size_t* length
) {
298 virtual bool confirmComposition(const WebString
& text
) {
302 virtual WebTextInputType
textInputType() {
303 return WebKit::WebTextInputTypeNone
;
306 virtual WebRect
caretOrSelectionBounds() {
310 virtual bool selectionRange(WebPoint
& start
, WebPoint
& end
) const {
314 virtual bool caretOrSelectionRange(size_t* location
, size_t* length
) {
318 virtual void setTextDirection(WebTextDirection
) {
321 virtual bool isAcceleratedCompositingActive() const {
322 return widget_
->context() && widget_
->plugin() &&
323 (widget_
->plugin()->GetBackingTextureId() != 0);
327 RenderWidgetFullscreenPepper
* widget_
;
330 DISALLOW_COPY_AND_ASSIGN(PepperWidget
);
333 void DestroyContext(WebGraphicsContext3DCommandBufferImpl
* context
,
338 context
->deleteProgram(program
);
340 context
->deleteBuffer(buffer
);
344 } // anonymous namespace
347 RenderWidgetFullscreenPepper
* RenderWidgetFullscreenPepper::Create(
348 int32 opener_id
, webkit::ppapi::PluginInstance
* plugin
,
349 const GURL
& active_url
,
350 const WebKit::WebScreenInfo
& screen_info
) {
351 DCHECK_NE(MSG_ROUTING_NONE
, opener_id
);
352 scoped_refptr
<RenderWidgetFullscreenPepper
> widget(
353 new RenderWidgetFullscreenPepper(plugin
, active_url
, screen_info
));
354 widget
->Init(opener_id
);
355 return widget
.release();
358 RenderWidgetFullscreenPepper::RenderWidgetFullscreenPepper(
359 webkit::ppapi::PluginInstance
* plugin
,
360 const GURL
& active_url
,
361 const WebKit::WebScreenInfo
& screen_info
)
362 : RenderWidgetFullscreen(screen_info
),
363 active_url_(active_url
),
368 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
369 mouse_lock_dispatcher_(new FullscreenMouseLockDispatcher(
370 ALLOW_THIS_IN_INITIALIZER_LIST(this))) {
373 RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() {
375 DestroyContext(context_
, program_
, buffer_
);
378 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersPosted() {
379 OnSwapBuffersPosted();
382 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersComplete() {
383 OnSwapBuffersComplete();
386 void RenderWidgetFullscreenPepper::OnViewContextSwapBuffersAborted() {
389 // Destroy the context later, in case we got called from InitContext for
390 // example. We still need to reset context_ now so that a new context gets
391 // created when the plugin recreates its own.
392 MessageLoop::current()->PostTask(
394 base::Bind(&DestroyContext
, context_
, program_
, buffer_
));
398 OnSwapBuffersAborted();
403 void RenderWidgetFullscreenPepper::Invalidate() {
404 InvalidateRect(gfx::Rect(size_
.width(), size_
.height()));
407 void RenderWidgetFullscreenPepper::InvalidateRect(const WebKit::WebRect
& rect
) {
408 if (CheckCompositing()) {
411 didInvalidateRect(rect
);
415 void RenderWidgetFullscreenPepper::ScrollRect(
416 int dx
, int dy
, const WebKit::WebRect
& rect
) {
417 if (CheckCompositing()) {
420 didScrollRect(dx
, dy
, rect
);
424 void RenderWidgetFullscreenPepper::Destroy() {
425 // This function is called by the plugin instance as it's going away, so reset
426 // plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close().
428 Send(new ViewHostMsg_Close(routing_id_
));
432 void RenderWidgetFullscreenPepper::DidChangeCursor(
433 const WebKit::WebCursorInfo
& cursor
) {
434 didChangeCursor(cursor
);
437 webkit::ppapi::PluginDelegate::PlatformContext3D
*
438 RenderWidgetFullscreenPepper::CreateContext3D() {
440 return new PlatformContext3DImpl(this);
446 void RenderWidgetFullscreenPepper::ReparentContext(
447 webkit::ppapi::PluginDelegate::PlatformContext3D
* context
) {
448 static_cast<PlatformContext3DImpl
*>(context
)->SetParentContext(this);
451 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message
& msg
) {
453 IPC_BEGIN_MESSAGE_MAP(RenderWidgetFullscreenPepper
, msg
)
454 IPC_MESSAGE_FORWARD(ViewMsg_LockMouse_ACK
,
455 mouse_lock_dispatcher_
.get(),
456 MouseLockDispatcher::OnLockMouseACK
)
457 IPC_MESSAGE_FORWARD(ViewMsg_MouseLockLost
,
458 mouse_lock_dispatcher_
.get(),
459 MouseLockDispatcher::OnMouseLockLost
)
460 IPC_MESSAGE_UNHANDLED(handled
= false)
461 IPC_END_MESSAGE_MAP()
465 return RenderWidgetFullscreen::OnMessageReceived(msg
);
468 void RenderWidgetFullscreenPepper::WillInitiatePaint() {
470 plugin_
->ViewWillInitiatePaint();
473 void RenderWidgetFullscreenPepper::DidInitiatePaint() {
475 plugin_
->ViewInitiatedPaint();
478 void RenderWidgetFullscreenPepper::DidFlushPaint() {
480 plugin_
->ViewFlushedPaint();
483 void RenderWidgetFullscreenPepper::Close() {
484 // If the fullscreen window is closed (e.g. user pressed escape), reset to
487 plugin_
->FlashSetFullscreen(false, false);
489 // Call Close on the base class to destroy the WebWidget instance.
490 RenderWidget::Close();
493 webkit::ppapi::PluginInstance
*
494 RenderWidgetFullscreenPepper::GetBitmapForOptimizedPluginPaint(
495 const gfx::Rect
& paint_bounds
,
499 float* scale_factor
) {
500 if (plugin_
&& plugin_
->GetBitmapForOptimizedPluginPaint(
501 paint_bounds
, dib
, location
, clip
, scale_factor
)) {
507 void RenderWidgetFullscreenPepper::OnResize(const gfx::Size
& size
,
508 const gfx::Rect
& resizer_rect
,
509 bool is_fullscreen
) {
511 gfx::Size pixel_size
= gfx::ToFlooredSize(
512 gfx::ScaleSize(size
, deviceScaleFactor()));
513 context_
->reshape(pixel_size
.width(), pixel_size
.height());
514 context_
->viewport(0, 0, pixel_size
.width(), pixel_size
.height());
516 RenderWidget::OnResize(size
, resizer_rect
, is_fullscreen
);
519 WebWidget
* RenderWidgetFullscreenPepper::CreateWebWidget() {
520 return new PepperWidget(this);
523 bool RenderWidgetFullscreenPepper::SupportsAsynchronousSwapBuffers() {
524 return context_
!= NULL
;
527 void RenderWidgetFullscreenPepper::CreateContext() {
529 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
530 if (command_line
->HasSwitch(switches::kDisableFlashFullscreen3d
))
532 WebKit::WebGraphicsContext3D::Attributes attributes
;
533 attributes
.depth
= false;
534 attributes
.stencil
= false;
535 attributes
.antialias
= false;
536 attributes
.shareResources
= false;
537 attributes
.preferDiscreteGPU
= true;
538 context_
= WebGraphicsContext3DCommandBufferImpl::CreateViewContext(
539 RenderThreadImpl::current(),
543 true /* bind generates resources */,
545 CAUSE_FOR_GPU_LAUNCH_RENDERWIDGETFULLSCREENPEPPER_CREATECONTEXT
);
549 if (!InitContext()) {
550 DestroyContext(context_
, program_
, buffer_
);
558 const char kVertexShader
[] =
559 "attribute vec2 in_tex_coord;\n"
560 "varying vec2 tex_coord;\n"
562 " gl_Position = vec4(in_tex_coord.x * 2. - 1.,\n"
563 " in_tex_coord.y * 2. - 1.,\n"
566 " tex_coord = vec2(in_tex_coord.x, in_tex_coord.y);\n"
569 const char kFragmentShader
[] =
570 "precision mediump float;\n"
571 "varying vec2 tex_coord;\n"
572 "uniform sampler2D in_texture;\n"
574 " gl_FragColor = texture2D(in_texture, tex_coord);\n"
577 GLuint
CreateShaderFromSource(WebGraphicsContext3DCommandBufferImpl
* context
,
579 const char* source
) {
580 GLuint shader
= context
->createShader(type
);
581 context
->shaderSource(shader
, source
);
582 context
->compileShader(shader
);
583 int status
= GL_FALSE
;
584 context
->getShaderiv(shader
, GL_COMPILE_STATUS
, &status
);
587 context
->getShaderiv(shader
, GL_INFO_LOG_LENGTH
, &size
);
588 std::string log
= context
->getShaderInfoLog(shader
).utf8();
589 DLOG(ERROR
) << "Compilation failed: " << log
;
590 context
->deleteShader(shader
);
596 const float kTexCoords
[] = {
602 } // anonymous namespace
604 bool RenderWidgetFullscreenPepper::InitContext() {
605 gfx::Size pixel_size
= gfx::ToFlooredSize(
606 gfx::ScaleSize(size(), deviceScaleFactor()));
607 context_
->reshape(pixel_size
.width(), pixel_size
.height());
608 context_
->viewport(0, 0, pixel_size
.width(), pixel_size
.height());
610 program_
= context_
->createProgram();
612 GLuint vertex_shader
=
613 CreateShaderFromSource(context_
, GL_VERTEX_SHADER
, kVertexShader
);
616 context_
->attachShader(program_
, vertex_shader
);
617 context_
->deleteShader(vertex_shader
);
619 GLuint fragment_shader
=
620 CreateShaderFromSource(context_
, GL_FRAGMENT_SHADER
, kFragmentShader
);
621 if (!fragment_shader
)
623 context_
->attachShader(program_
, fragment_shader
);
624 context_
->deleteShader(fragment_shader
);
626 context_
->bindAttribLocation(program_
, 0, "in_tex_coord");
627 context_
->linkProgram(program_
);
628 int status
= GL_FALSE
;
629 context_
->getProgramiv(program_
, GL_LINK_STATUS
, &status
);
632 context_
->getProgramiv(program_
, GL_INFO_LOG_LENGTH
, &size
);
633 std::string log
= context_
->getProgramInfoLog(program_
).utf8();
634 DLOG(ERROR
) << "Link failed: " << log
;
637 context_
->useProgram(program_
);
638 int texture_location
= context_
->getUniformLocation(program_
, "in_texture");
639 context_
->uniform1i(texture_location
, 0);
641 buffer_
= context_
->createBuffer();
642 context_
->bindBuffer(GL_ARRAY_BUFFER
, buffer_
);
643 context_
->bufferData(GL_ARRAY_BUFFER
,
647 context_
->vertexAttribPointer(0, 2,
649 0, static_cast<WGC3Dintptr
>(NULL
));
650 context_
->enableVertexAttribArray(0);
654 bool RenderWidgetFullscreenPepper::CheckCompositing() {
656 webwidget_
&& webwidget_
->isAcceleratedCompositingActive();
657 if (compositing
!= is_accelerated_compositing_active_
) {
659 didActivateCompositor(-1);
661 didDeactivateCompositor();
666 void RenderWidgetFullscreenPepper::SwapBuffers() {
668 context_
->prepareTexture();
670 // The compositor isn't actually active in this path, but pretend it is for
671 // scheduling purposes.
672 didCommitAndDrawCompositorFrame();
675 WebGraphicsContext3DCommandBufferImpl
*
676 RenderWidgetFullscreenPepper::GetParentContextForPlatformContext3D() {
685 } // namespace content