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
29using blink::WebCanvas;
30using blink::WebCompositionUnderline;
31using blink::WebCursorInfo;
32using blink::WebGestureEvent;
33using blink::WebInputEvent;
34using blink::WebMouseEvent;
35using blink::WebMouseWheelEvent;
36using blink::WebPoint;
37using blink::WebRect;
38using blink::WebSize;
39using blink::WebString;
40using blink::WebTextDirection;
41using blink::WebTextInputType;
42using blink::WebVector;
43using blink::WebWidget;
44using blink::WGC3Dintptr;
45
46namespace content {
47
48namespace {
49
50class 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
65WebMouseEvent 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
115FullscreenMouseLockDispatcher::FullscreenMouseLockDispatcher(
116    RenderWidgetFullscreenPepper* widget) : widget_(widget) {
117}
118
119FullscreenMouseLockDispatcher::~FullscreenMouseLockDispatcher() {
120}
121
122void FullscreenMouseLockDispatcher::SendLockMouseRequest(
123    bool unlocked_by_target) {
124  widget_->Send(new ViewHostMsg_LockMouse(widget_->routing_id(), false,
125                                          unlocked_by_target, true));
126}
127
128void 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.
135class 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
265RenderWidgetFullscreenPepper* 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
278RenderWidgetFullscreenPepper::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
290RenderWidgetFullscreenPepper::~RenderWidgetFullscreenPepper() {
291}
292
293void RenderWidgetFullscreenPepper::Invalidate() {
294  InvalidateRect(gfx::Rect(size_.width(), size_.height()));
295}
296
297void RenderWidgetFullscreenPepper::InvalidateRect(const blink::WebRect& rect) {
298  didInvalidateRect(rect);
299}
300
301void RenderWidgetFullscreenPepper::ScrollRect(
302    int dx, int dy, const blink::WebRect& rect) {
303}
304
305void RenderWidgetFullscreenPepper::Destroy() {
306  // This function is called by the plugin instance as it's going away, so reset
307  // plugin_ to NULL to avoid calling into a dangling pointer e.g. on Close().
308  plugin_ = NULL;
309
310  // After calling Destroy(), the plugin instance assumes that the layer is not
311  // used by us anymore, so it may destroy the layer before this object goes
312  // away.
313  SetLayer(NULL);
314
315  Send(new ViewHostMsg_Close(routing_id_));
316  Release();
317}
318
319void RenderWidgetFullscreenPepper::DidChangeCursor(
320    const blink::WebCursorInfo& cursor) {
321  didChangeCursor(cursor);
322}
323
324void RenderWidgetFullscreenPepper::SetLayer(blink::WebLayer* layer) {
325  layer_ = layer;
326  if (!layer_) {
327    if (compositor_)
328      compositor_->clearRootLayer();
329    return;
330  }
331  if (!layerTreeView())
332    initializeLayerTreeView();
333  layer_->setBounds(blink::WebSize(size()));
334  layer_->setDrawsContent(true);
335  compositor_->setDeviceScaleFactor(device_scale_factor_);
336  compositor_->setRootLayer(*layer_);
337}
338
339bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) {
340  bool handled = true;
341  IPC_BEGIN_MESSAGE_MAP(RenderWidgetFullscreenPepper, msg)
342    IPC_MESSAGE_FORWARD(ViewMsg_LockMouse_ACK,
343                        mouse_lock_dispatcher_.get(),
344                        MouseLockDispatcher::OnLockMouseACK)
345    IPC_MESSAGE_FORWARD(ViewMsg_MouseLockLost,
346                        mouse_lock_dispatcher_.get(),
347                        MouseLockDispatcher::OnMouseLockLost)
348    IPC_MESSAGE_UNHANDLED(handled = false)
349  IPC_END_MESSAGE_MAP()
350  if (handled)
351    return true;
352
353  return RenderWidgetFullscreen::OnMessageReceived(msg);
354}
355
356void RenderWidgetFullscreenPepper::DidInitiatePaint() {
357  if (plugin_)
358    plugin_->ViewInitiatedPaint();
359}
360
361void RenderWidgetFullscreenPepper::DidFlushPaint() {
362  if (plugin_)
363    plugin_->ViewFlushedPaint();
364}
365
366void RenderWidgetFullscreenPepper::Close() {
367  // If the fullscreen window is closed (e.g. user pressed escape), reset to
368  // normal mode.
369  if (plugin_)
370    plugin_->FlashSetFullscreen(false, false);
371
372  // Call Close on the base class to destroy the WebWidget instance.
373  RenderWidget::Close();
374}
375
376void RenderWidgetFullscreenPepper::OnResize(
377    const ViewMsg_Resize_Params& params) {
378  if (layer_)
379    layer_->setBounds(blink::WebSize(params.new_size));
380  RenderWidget::OnResize(params);
381}
382
383WebWidget* RenderWidgetFullscreenPepper::CreateWebWidget() {
384  return new PepperWidget(this);
385}
386
387GURL RenderWidgetFullscreenPepper::GetURLForGraphicsContext3D() {
388  return active_url_;
389}
390
391void RenderWidgetFullscreenPepper::SetDeviceScaleFactor(
392    float device_scale_factor) {
393  RenderWidget::SetDeviceScaleFactor(device_scale_factor);
394  if (compositor_)
395    compositor_->setDeviceScaleFactor(device_scale_factor);
396}
397
398}  // namespace content
399