render_widget_host_view_guest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 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 "base/bind_helpers.h"
6#include "base/command_line.h"
7#include "base/logging.h"
8#include "base/message_loop/message_loop.h"
9#include "content/browser/browser_plugin/browser_plugin_guest.h"
10#include "content/browser/frame_host/render_widget_host_view_guest.h"
11#include "content/browser/renderer_host/render_view_host_impl.h"
12#include "content/common/browser_plugin/browser_plugin_messages.h"
13#include "content/common/frame_messages.h"
14#include "content/common/gpu/gpu_messages.h"
15#include "content/common/input/web_touch_event_traits.h"
16#include "content/common/view_messages.h"
17#include "content/common/webplugin_geometry.h"
18#include "content/public/common/content_switches.h"
19#include "skia/ext/platform_canvas.h"
20#include "third_party/WebKit/public/platform/WebScreenInfo.h"
21
22#if defined(OS_MACOSX)
23#import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
24#endif
25
26#if defined(USE_AURA)
27#include "content/browser/renderer_host/ui_events_helper.h"
28#endif
29
30namespace content {
31
32namespace {
33
34#if defined(USE_AURA)
35blink::WebGestureEvent CreateFlingCancelEvent(double time_stamp) {
36  blink::WebGestureEvent gesture_event;
37  gesture_event.timeStampSeconds = time_stamp;
38  gesture_event.type = blink::WebGestureEvent::GestureFlingCancel;
39  gesture_event.sourceDevice = blink::WebGestureDeviceTouchscreen;
40  return gesture_event;
41}
42#endif  // defined(USE_AURA)
43
44}  // namespace
45
46RenderWidgetHostViewGuest::RenderWidgetHostViewGuest(
47    RenderWidgetHost* widget_host,
48    BrowserPluginGuest* guest,
49    RenderWidgetHostViewBase* platform_view)
50    : RenderWidgetHostViewChildFrame(widget_host),
51      // |guest| is NULL during test.
52      guest_(guest ? guest->AsWeakPtr() : base::WeakPtr<BrowserPluginGuest>()),
53      platform_view_(platform_view) {
54#if defined(USE_AURA)
55  gesture_recognizer_.reset(ui::GestureRecognizer::Create());
56  gesture_recognizer_->AddGestureEventHelper(this);
57#endif  // defined(USE_AURA)
58}
59
60RenderWidgetHostViewGuest::~RenderWidgetHostViewGuest() {
61#if defined(USE_AURA)
62  gesture_recognizer_->RemoveGestureEventHelper(this);
63#endif  // defined(USE_AURA)
64}
65
66bool RenderWidgetHostViewGuest::OnMessageReceivedFromEmbedder(
67    const IPC::Message& message,
68    RenderWidgetHostImpl* embedder) {
69  bool handled = true;
70  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(RenderWidgetHostViewGuest, message,
71                                   embedder)
72    IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_HandleInputEvent,
73                        OnHandleInputEvent)
74    IPC_MESSAGE_UNHANDLED(handled = false)
75  IPC_END_MESSAGE_MAP()
76  return handled;
77}
78
79void RenderWidgetHostViewGuest::WasShown() {
80  // If the WebContents associated with us showed an interstitial page in the
81  // beginning, the teardown path might call WasShown() while |host_| is in
82  // the process of destruction. Avoid calling WasShown below in this case.
83  // TODO(lazyboy): We shouldn't be showing interstitial pages in guests in the
84  // first place: http://crbug.com/273089.
85  //
86  // |guest_| is NULL during test.
87  if ((guest_ && guest_->is_in_destruction()) || !host_->is_hidden())
88    return;
89  // Make sure the size of this view matches the size of the WebContentsView.
90  // The two sizes may fall out of sync if we switch RenderWidgetHostViews,
91  // resize, and then switch page, as is the case with interstitial pages.
92  // NOTE: |guest_| is NULL in unit tests.
93  if (guest_)
94    SetSize(guest_->web_contents()->GetViewBounds().size());
95  host_->WasShown(ui::LatencyInfo());
96}
97
98void RenderWidgetHostViewGuest::WasHidden() {
99  // |guest_| is NULL during test.
100  if ((guest_ && guest_->is_in_destruction()) || host_->is_hidden())
101    return;
102  host_->WasHidden();
103}
104
105void RenderWidgetHostViewGuest::SetSize(const gfx::Size& size) {
106  size_ = size;
107  host_->WasResized();
108}
109
110void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect& rect) {
111  SetSize(rect.size());
112}
113
114void RenderWidgetHostViewGuest::Focus() {
115  // InterstitialPageImpl focuses views directly, so we place focus logic here.
116  // InterstitialPages are not WebContents, and so BrowserPluginGuest does not
117  // have direct access to the interstitial page's RenderWidgetHost.
118  if (guest_)
119    guest_->SetFocus(host_, true);
120}
121
122bool RenderWidgetHostViewGuest::HasFocus() const {
123  if (!guest_)
124    return false;
125  return guest_->focused();
126}
127
128#if defined(USE_AURA)
129void RenderWidgetHostViewGuest::ProcessAckedTouchEvent(
130    const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
131  // TODO(fsamuel): Currently we will only take this codepath if the guest has
132  // requested touch events. A better solution is to always forward touchpresses
133  // to the embedder process to target a BrowserPlugin, and then route all
134  // subsequent touch points of that touchdown to the appropriate guest until
135  // that touch point is released.
136  ScopedVector<ui::TouchEvent> events;
137  if (!MakeUITouchEventsFromWebTouchEvents(touch, &events, LOCAL_COORDINATES))
138    return;
139
140  ui::EventResult result = (ack_result ==
141      INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
142  for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
143      end = events.end(); iter != end; ++iter)  {
144    if (!gesture_recognizer_->ProcessTouchEventPreDispatch(*(*iter), this))
145      continue;
146
147    scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
148    gestures.reset(gesture_recognizer_->ProcessTouchEventPostDispatch(
149        *(*iter), result, this));
150    ProcessGestures(gestures.get());
151  }
152}
153#endif
154
155gfx::Rect RenderWidgetHostViewGuest::GetViewBounds() const {
156  if (!guest_)
157    return gfx::Rect();
158
159  RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
160  gfx::Rect embedder_bounds;
161  if (rwhv)
162    embedder_bounds = rwhv->GetViewBounds();
163  return gfx::Rect(
164      guest_->GetScreenCoordinates(embedder_bounds.origin()), size_);
165}
166
167void RenderWidgetHostViewGuest::RenderProcessGone(
168    base::TerminationStatus status,
169    int error_code) {
170  platform_view_->RenderProcessGone(status, error_code);
171  // Destroy the guest view instance only, so we don't end up calling
172  // platform_view_->Destroy().
173  DestroyGuestView();
174}
175
176void RenderWidgetHostViewGuest::Destroy() {
177  // The RenderWidgetHost's destruction led here, so don't call it.
178  DestroyGuestView();
179
180  platform_view_->Destroy();
181}
182
183gfx::Size RenderWidgetHostViewGuest::GetPhysicalBackingSize() const {
184  return RenderWidgetHostViewBase::GetPhysicalBackingSize();
185}
186
187base::string16 RenderWidgetHostViewGuest::GetSelectedText() const {
188  return platform_view_->GetSelectedText();
189}
190
191void RenderWidgetHostViewGuest::SetTooltipText(
192    const base::string16& tooltip_text) {
193  platform_view_->SetTooltipText(tooltip_text);
194}
195
196void RenderWidgetHostViewGuest::AcceleratedSurfaceBuffersSwapped(
197    const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
198    int gpu_host_id) {
199  NOTREACHED();
200}
201
202void RenderWidgetHostViewGuest::AcceleratedSurfacePostSubBuffer(
203    const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
204    int gpu_host_id) {
205  NOTREACHED();
206}
207
208void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
209    uint32 output_surface_id,
210    scoped_ptr<cc::CompositorFrame> frame) {
211  if (!guest_)
212    return;
213
214  last_scroll_offset_ = frame->metadata.root_scroll_offset;
215  guest_->SwapCompositorFrame(output_surface_id,
216                              host_->GetProcess()->GetID(),
217                              host_->GetRoutingID(),
218                              frame.Pass());
219}
220
221bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message& msg) {
222  return platform_view_->OnMessageReceived(msg);
223}
224
225void RenderWidgetHostViewGuest::InitAsChild(
226    gfx::NativeView parent_view) {
227  platform_view_->InitAsChild(parent_view);
228}
229
230void RenderWidgetHostViewGuest::InitAsPopup(
231    RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
232  // This should never get called.
233  NOTREACHED();
234}
235
236void RenderWidgetHostViewGuest::InitAsFullscreen(
237    RenderWidgetHostView* reference_host_view) {
238  // This should never get called.
239  NOTREACHED();
240}
241
242gfx::NativeView RenderWidgetHostViewGuest::GetNativeView() const {
243  if (!guest_)
244    return gfx::NativeView();
245
246  RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
247  if (!rwhv)
248    return gfx::NativeView();
249  return rwhv->GetNativeView();
250}
251
252gfx::NativeViewId RenderWidgetHostViewGuest::GetNativeViewId() const {
253  if (!guest_)
254    return static_cast<gfx::NativeViewId>(NULL);
255
256  RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
257  if (!rwhv)
258    return static_cast<gfx::NativeViewId>(NULL);
259  return rwhv->GetNativeViewId();
260}
261
262gfx::NativeViewAccessible RenderWidgetHostViewGuest::GetNativeViewAccessible() {
263  if (!guest_)
264    return gfx::NativeViewAccessible();
265
266  RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
267  if (!rwhv)
268    return gfx::NativeViewAccessible();
269  return rwhv->GetNativeViewAccessible();
270}
271
272void RenderWidgetHostViewGuest::MovePluginWindows(
273    const std::vector<WebPluginGeometry>& moves) {
274  platform_view_->MovePluginWindows(moves);
275}
276
277void RenderWidgetHostViewGuest::UpdateCursor(const WebCursor& cursor) {
278  // InterstitialPages are not WebContents so we cannot intercept
279  // ViewHostMsg_SetCursor for interstitial pages in BrowserPluginGuest.
280  // All guest RenderViewHosts have RenderWidgetHostViewGuests however,
281  // and so we will always hit this code path.
282  if (!guest_)
283    return;
284  guest_->SendMessageToEmbedder(
285      new BrowserPluginMsg_SetCursor(guest_->browser_plugin_instance_id(),
286                                     cursor));
287
288}
289
290void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading) {
291  platform_view_->SetIsLoading(is_loading);
292}
293
294void RenderWidgetHostViewGuest::TextInputStateChanged(
295    const ViewHostMsg_TextInputState_Params& params) {
296  if (!guest_)
297    return;
298
299  RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
300  if (!rwhv)
301    return;
302  // Forward the information to embedding RWHV.
303  rwhv->TextInputStateChanged(params);
304}
305
306void RenderWidgetHostViewGuest::ImeCancelComposition() {
307  if (!guest_)
308    return;
309
310  RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
311  if (!rwhv)
312    return;
313  // Forward the information to embedding RWHV.
314  rwhv->ImeCancelComposition();
315}
316
317#if defined(OS_MACOSX) || defined(USE_AURA)
318void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
319    const gfx::Range& range,
320    const std::vector<gfx::Rect>& character_bounds) {
321  if (!guest_)
322    return;
323
324  RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
325  if (!rwhv)
326    return;
327  std::vector<gfx::Rect> guest_character_bounds;
328  for (size_t i = 0; i < character_bounds.size(); ++i) {
329    guest_character_bounds.push_back(gfx::Rect(
330        guest_->GetScreenCoordinates(character_bounds[i].origin()),
331        character_bounds[i].size()));
332  }
333  // Forward the information to embedding RWHV.
334  rwhv->ImeCompositionRangeChanged(range, guest_character_bounds);
335}
336#endif
337
338void RenderWidgetHostViewGuest::SelectionChanged(const base::string16& text,
339                                                 size_t offset,
340                                                 const gfx::Range& range) {
341  platform_view_->SelectionChanged(text, offset, range);
342}
343
344void RenderWidgetHostViewGuest::SelectionBoundsChanged(
345    const ViewHostMsg_SelectionBounds_Params& params) {
346  if (!guest_)
347    return;
348
349  RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView();
350  if (!rwhv)
351    return;
352  ViewHostMsg_SelectionBounds_Params guest_params(params);
353  guest_params.anchor_rect.set_origin(
354      guest_->GetScreenCoordinates(params.anchor_rect.origin()));
355  guest_params.focus_rect.set_origin(
356      guest_->GetScreenCoordinates(params.focus_rect.origin()));
357  rwhv->SelectionBoundsChanged(guest_params);
358}
359
360void RenderWidgetHostViewGuest::CopyFromCompositingSurface(
361    const gfx::Rect& src_subrect,
362    const gfx::Size& dst_size,
363    CopyFromCompositingSurfaceCallback& callback,
364    const SkColorType color_type) {
365  CHECK(guest_);
366  guest_->CopyFromCompositingSurface(src_subrect, dst_size, callback);
367}
368
369void RenderWidgetHostViewGuest::SetBackgroundOpaque(bool opaque) {
370  // Content embedders can toggle opaque backgrounds through this API.
371  // We plumb the value here so that BrowserPlugin updates its compositing
372  // state in response to this change. We also want to preserve this flag
373  // after recovering from a crash so we let BrowserPluginGuest store it.
374  if (!guest_)
375    return;
376  RenderWidgetHostViewBase::SetBackgroundOpaque(opaque);
377  host_->SetBackgroundOpaque(opaque);
378  guest_->SetContentsOpaque(opaque);
379}
380
381bool RenderWidgetHostViewGuest::LockMouse() {
382  return platform_view_->LockMouse();
383}
384
385void RenderWidgetHostViewGuest::UnlockMouse() {
386  return platform_view_->UnlockMouse();
387}
388
389void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo* results) {
390  if (!guest_)
391    return;
392  RenderWidgetHostViewBase* embedder_view = GetGuestRenderWidgetHostView();
393  if (embedder_view)
394    embedder_view->GetScreenInfo(results);
395}
396
397#if defined(OS_MACOSX)
398void RenderWidgetHostViewGuest::SetActive(bool active) {
399  platform_view_->SetActive(active);
400}
401
402void RenderWidgetHostViewGuest::SetTakesFocusOnlyOnMouseDown(bool flag) {
403  platform_view_->SetTakesFocusOnlyOnMouseDown(flag);
404}
405
406void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible) {
407  platform_view_->SetWindowVisibility(visible);
408}
409
410void RenderWidgetHostViewGuest::WindowFrameChanged() {
411  platform_view_->WindowFrameChanged();
412}
413
414void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
415  if (!guest_)
416    return;
417
418  gfx::Point origin;
419  gfx::Rect guest_bounds = GetViewBounds();
420  RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView();
421  gfx::Rect embedder_bounds;
422  if (rwhv)
423    embedder_bounds = rwhv->GetViewBounds();
424
425  gfx::Vector2d guest_offset = gfx::Vector2d(
426      // Horizontal offset of guest from embedder.
427      guest_bounds.x() - embedder_bounds.x(),
428      // Vertical offset from guest's top to embedder's bottom edge.
429      embedder_bounds.bottom() - guest_bounds.y());
430
431  RenderWidgetHostViewMacDictionaryHelper helper(platform_view_);
432  helper.SetTargetView(rwhv);
433  helper.set_offset(guest_offset);
434  helper.ShowDefinitionForSelection();
435}
436
437bool RenderWidgetHostViewGuest::SupportsSpeech() const {
438  return platform_view_->SupportsSpeech();
439}
440
441void RenderWidgetHostViewGuest::SpeakSelection() {
442  platform_view_->SpeakSelection();
443}
444
445bool RenderWidgetHostViewGuest::IsSpeaking() const {
446  return platform_view_->IsSpeaking();
447}
448
449void RenderWidgetHostViewGuest::StopSpeaking() {
450  platform_view_->StopSpeaking();
451}
452
453bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
454    const NativeWebKeyboardEvent& event) {
455  return false;
456}
457
458#endif  // defined(OS_MACOSX)
459
460#if defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
461void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
462    const gfx::Rect& rect_pixels,
463    const SkBitmap& zoomed_bitmap) {
464}
465#endif  // defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
466
467#if defined(OS_ANDROID)
468void RenderWidgetHostViewGuest::LockCompositingSurface() {
469}
470
471void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
472}
473#endif  // defined(OS_ANDROID)
474
475#if defined(OS_WIN)
476void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
477    gfx::NativeViewAccessible accessible_parent) {
478}
479
480gfx::NativeViewId RenderWidgetHostViewGuest::GetParentForWindowlessPlugin()
481    const {
482  return NULL;
483}
484#endif
485
486void RenderWidgetHostViewGuest::DestroyGuestView() {
487  host_->SetView(NULL);
488  host_ = NULL;
489  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
490}
491
492bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
493    ui::GestureConsumer* consumer) {
494  CHECK_EQ(static_cast<RenderWidgetHostViewGuest*>(consumer), this);
495  return true;
496}
497
498void RenderWidgetHostViewGuest::DispatchGestureEvent(
499    ui::GestureEvent* event) {
500  ForwardGestureEventToRenderer(event);
501}
502
503void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
504    ui::TouchEvent* event) {
505  if (!host_)
506    return;
507
508  blink::WebTouchEvent cancel_event;
509  // TODO(rbyers): This event has no touches in it.  Don't we need to know what
510  // touches are currently active in order to cancel them all properly?
511  WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel,
512                                 event->time_stamp().InSecondsF(),
513                                 &cancel_event);
514
515  host_->ForwardTouchEventWithLatencyInfo(cancel_event, *event->latency());
516}
517
518bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
519    ui::GestureEvent* gesture) {
520#if defined(USE_AURA)
521  if (!host_)
522    return false;
523
524  if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN ||
525      gesture->type() == ui::ET_GESTURE_PINCH_UPDATE ||
526      gesture->type() == ui::ET_GESTURE_PINCH_END) && !pinch_zoom_enabled_) {
527    return true;
528  }
529
530  blink::WebGestureEvent web_gesture =
531      MakeWebGestureEventFromUIEvent(*gesture);
532  const gfx::Point& client_point = gesture->location();
533  const gfx::Point& screen_point = gesture->location();
534
535  web_gesture.x = client_point.x();
536  web_gesture.y = client_point.y();
537  web_gesture.globalX = screen_point.x();
538  web_gesture.globalY = screen_point.y();
539
540  if (web_gesture.type == blink::WebGestureEvent::Undefined)
541    return false;
542  if (web_gesture.type == blink::WebGestureEvent::GestureTapDown) {
543    host_->ForwardGestureEvent(
544        CreateFlingCancelEvent(gesture->time_stamp().InSecondsF()));
545  }
546  host_->ForwardGestureEvent(web_gesture);
547  return true;
548#else
549  return false;
550#endif
551}
552
553void RenderWidgetHostViewGuest::ProcessGestures(
554    ui::GestureRecognizer::Gestures* gestures) {
555  if ((gestures == NULL) || gestures->empty())
556    return;
557  for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin();
558      g_it != gestures->end();
559      ++g_it) {
560    ForwardGestureEventToRenderer(*g_it);
561  }
562}
563
564SkColorType RenderWidgetHostViewGuest::PreferredReadbackFormat() {
565  return kN32_SkColorType;
566}
567
568RenderWidgetHostViewBase*
569RenderWidgetHostViewGuest::GetGuestRenderWidgetHostView() const {
570  return static_cast<RenderWidgetHostViewBase*>(
571      guest_->GetEmbedderRenderWidgetHostView());
572}
573
574void RenderWidgetHostViewGuest::OnHandleInputEvent(
575    RenderWidgetHostImpl* embedder,
576    int browser_plugin_instance_id,
577    const gfx::Rect& guest_window_rect,
578    const blink::WebInputEvent* event) {
579  if (blink::WebInputEvent::isMouseEventType(event->type)) {
580    host_->ForwardMouseEvent(
581        *static_cast<const blink::WebMouseEvent*>(event));
582    return;
583  }
584
585  if (event->type == blink::WebInputEvent::MouseWheel) {
586    host_->ForwardWheelEvent(
587        *static_cast<const blink::WebMouseWheelEvent*>(event));
588    return;
589  }
590
591  if (blink::WebInputEvent::isKeyboardEventType(event->type)) {
592    if (!embedder->GetLastKeyboardEvent())
593      return;
594    NativeWebKeyboardEvent keyboard_event(*embedder->GetLastKeyboardEvent());
595    host_->ForwardKeyboardEvent(keyboard_event);
596    return;
597  }
598
599  if (blink::WebInputEvent::isTouchEventType(event->type)) {
600    host_->ForwardTouchEventWithLatencyInfo(
601        *static_cast<const blink::WebTouchEvent*>(event),
602        ui::LatencyInfo());
603    return;
604  }
605
606  if (blink::WebInputEvent::isGestureEventType(event->type)) {
607    host_->ForwardGestureEvent(
608        *static_cast<const blink::WebGestureEvent*>(event));
609    return;
610  }
611}
612
613}  // namespace content
614