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.
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"
22 #if defined(OS_MACOSX)
23 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
27 #include "content/browser/renderer_host/ui_events_helper.h"
35 blink::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
;
42 #endif // defined(USE_AURA)
46 RenderWidgetHostViewGuest::RenderWidgetHostViewGuest(
47 RenderWidgetHost
* widget_host
,
48 BrowserPluginGuest
* guest
,
49 base::WeakPtr
<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
) {
55 gesture_recognizer_
.reset(ui::GestureRecognizer::Create());
56 gesture_recognizer_
->AddGestureEventHelper(this);
57 #endif // defined(USE_AURA)
60 RenderWidgetHostViewGuest::~RenderWidgetHostViewGuest() {
62 gesture_recognizer_
->RemoveGestureEventHelper(this);
63 #endif // defined(USE_AURA)
66 bool RenderWidgetHostViewGuest::OnMessageReceivedFromEmbedder(
67 const IPC::Message
& message
,
68 RenderWidgetHostImpl
* embedder
) {
70 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(RenderWidgetHostViewGuest
, message
,
72 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_HandleInputEvent
,
74 IPC_MESSAGE_UNHANDLED(handled
= false)
79 void RenderWidgetHostViewGuest::Show() {
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.
86 // |guest_| is NULL during test.
87 if ((guest_
&& guest_
->is_in_destruction()) || !host_
->is_hidden())
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.
94 SetSize(guest_
->web_contents()->GetViewBounds().size());
95 host_
->WasShown(ui::LatencyInfo());
98 void RenderWidgetHostViewGuest::Hide() {
99 // |guest_| is NULL during test.
100 if ((guest_
&& guest_
->is_in_destruction()) || host_
->is_hidden())
105 void RenderWidgetHostViewGuest::SetSize(const gfx::Size
& size
) {
110 void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect
& rect
) {
111 SetSize(rect
.size());
114 void 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.
119 guest_
->SetFocus(host_
, true, blink::WebFocusTypeNone
);
122 bool RenderWidgetHostViewGuest::HasFocus() const {
125 return guest_
->focused();
128 #if defined(USE_AURA)
129 void 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
))
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))
147 scoped_ptr
<ui::GestureRecognizer::Gestures
> gestures
;
148 gestures
.reset(gesture_recognizer_
->AckSyncTouchEvent(
149 (*iter
)->unique_event_id(), result
, this));
150 ProcessGestures(gestures
.get());
155 gfx::Rect
RenderWidgetHostViewGuest::GetViewBounds() const {
159 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
160 gfx::Rect embedder_bounds
;
162 embedder_bounds
= rwhv
->GetViewBounds();
164 guest_
->GetScreenCoordinates(embedder_bounds
.origin()), size_
);
167 void RenderWidgetHostViewGuest::RenderProcessGone(
168 base::TerminationStatus status
,
170 // The |platform_view_| gets destroyed before we get here if this view
171 // is for an InterstitialPage.
173 platform_view_
->RenderProcessGone(status
, error_code
);
175 // Destroy the guest view instance only, so we don't end up calling
176 // platform_view_->Destroy().
180 void RenderWidgetHostViewGuest::Destroy() {
181 // The RenderWidgetHost's destruction led here, so don't call it.
184 if (platform_view_
) // The platform view might have been destroyed already.
185 platform_view_
->Destroy();
188 gfx::Size
RenderWidgetHostViewGuest::GetPhysicalBackingSize() const {
189 return RenderWidgetHostViewBase::GetPhysicalBackingSize();
192 base::string16
RenderWidgetHostViewGuest::GetSelectedText() const {
193 return platform_view_
->GetSelectedText();
196 void RenderWidgetHostViewGuest::SetTooltipText(
197 const base::string16
& tooltip_text
) {
199 guest_
->SetTooltipText(tooltip_text
);
202 void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
203 uint32 output_surface_id
,
204 scoped_ptr
<cc::CompositorFrame
> frame
) {
208 last_scroll_offset_
= frame
->metadata
.root_scroll_offset
;
209 guest_
->SwapCompositorFrame(output_surface_id
,
210 host_
->GetProcess()->GetID(),
211 host_
->GetRoutingID(),
215 bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message
& msg
) {
216 if (!platform_view_
) {
217 // In theory, we can get here if there's a delay between DestroyGuestView()
218 // being called and when our destructor is invoked.
222 return platform_view_
->OnMessageReceived(msg
);
225 void RenderWidgetHostViewGuest::InitAsChild(
226 gfx::NativeView parent_view
) {
227 platform_view_
->InitAsChild(parent_view
);
230 void RenderWidgetHostViewGuest::InitAsPopup(
231 RenderWidgetHostView
* parent_host_view
, const gfx::Rect
& bounds
) {
232 // This should never get called.
236 void RenderWidgetHostViewGuest::InitAsFullscreen(
237 RenderWidgetHostView
* reference_host_view
) {
238 // This should never get called.
242 gfx::NativeView
RenderWidgetHostViewGuest::GetNativeView() const {
244 return gfx::NativeView();
246 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
248 return gfx::NativeView();
249 return rwhv
->GetNativeView();
252 gfx::NativeViewId
RenderWidgetHostViewGuest::GetNativeViewId() const {
254 return static_cast<gfx::NativeViewId
>(NULL
);
256 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
258 return static_cast<gfx::NativeViewId
>(NULL
);
259 return rwhv
->GetNativeViewId();
262 gfx::NativeViewAccessible
RenderWidgetHostViewGuest::GetNativeViewAccessible() {
264 return gfx::NativeViewAccessible();
266 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
268 return gfx::NativeViewAccessible();
269 return rwhv
->GetNativeViewAccessible();
272 void RenderWidgetHostViewGuest::MovePluginWindows(
273 const std::vector
<WebPluginGeometry
>& moves
) {
274 platform_view_
->MovePluginWindows(moves
);
277 void 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.
284 guest_
->SendMessageToEmbedder(
285 new BrowserPluginMsg_SetCursor(guest_
->browser_plugin_instance_id(),
290 void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading
) {
291 platform_view_
->SetIsLoading(is_loading
);
294 void RenderWidgetHostViewGuest::TextInputTypeChanged(
295 ui::TextInputType type
,
296 ui::TextInputMode input_mode
,
297 bool can_compose_inline
,
302 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
305 // Forward the information to embedding RWHV.
306 rwhv
->TextInputTypeChanged(type
, input_mode
, can_compose_inline
, flags
);
309 void RenderWidgetHostViewGuest::ImeCancelComposition() {
313 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
316 // Forward the information to embedding RWHV.
317 rwhv
->ImeCancelComposition();
320 #if defined(OS_MACOSX) || defined(USE_AURA)
321 void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
322 const gfx::Range
& range
,
323 const std::vector
<gfx::Rect
>& character_bounds
) {
327 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
330 std::vector
<gfx::Rect
> guest_character_bounds
;
331 for (size_t i
= 0; i
< character_bounds
.size(); ++i
) {
332 guest_character_bounds
.push_back(gfx::Rect(
333 guest_
->GetScreenCoordinates(character_bounds
[i
].origin()),
334 character_bounds
[i
].size()));
336 // Forward the information to embedding RWHV.
337 rwhv
->ImeCompositionRangeChanged(range
, guest_character_bounds
);
341 void RenderWidgetHostViewGuest::SelectionChanged(const base::string16
& text
,
343 const gfx::Range
& range
) {
344 platform_view_
->SelectionChanged(text
, offset
, range
);
347 void RenderWidgetHostViewGuest::SelectionBoundsChanged(
348 const ViewHostMsg_SelectionBounds_Params
& params
) {
352 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
355 ViewHostMsg_SelectionBounds_Params
guest_params(params
);
356 guest_params
.anchor_rect
.set_origin(
357 guest_
->GetScreenCoordinates(params
.anchor_rect
.origin()));
358 guest_params
.focus_rect
.set_origin(
359 guest_
->GetScreenCoordinates(params
.focus_rect
.origin()));
360 rwhv
->SelectionBoundsChanged(guest_params
);
363 void RenderWidgetHostViewGuest::SetBackgroundColor(SkColor color
) {
364 // Content embedders can toggle opaque backgrounds through this API.
365 // We plumb the value here so that BrowserPlugin updates its compositing
366 // state in response to this change. We also want to preserve this flag
367 // after recovering from a crash so we let BrowserPluginGuest store it.
370 RenderWidgetHostViewBase::SetBackgroundColor(color
);
371 bool opaque
= GetBackgroundOpaque();
372 host_
->SetBackgroundOpaque(opaque
);
373 guest_
->SetContentsOpaque(opaque
);
376 bool RenderWidgetHostViewGuest::LockMouse() {
377 return platform_view_
->LockMouse();
380 void RenderWidgetHostViewGuest::UnlockMouse() {
381 return platform_view_
->UnlockMouse();
384 void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo
* results
) {
387 RenderWidgetHostViewBase
* embedder_view
= GetOwnerRenderWidgetHostView();
389 embedder_view
->GetScreenInfo(results
);
392 #if defined(OS_MACOSX)
393 void RenderWidgetHostViewGuest::SetActive(bool active
) {
394 platform_view_
->SetActive(active
);
397 void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible
) {
398 platform_view_
->SetWindowVisibility(visible
);
401 void RenderWidgetHostViewGuest::WindowFrameChanged() {
402 platform_view_
->WindowFrameChanged();
405 void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
410 gfx::Rect guest_bounds
= GetViewBounds();
411 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
412 gfx::Rect embedder_bounds
;
414 embedder_bounds
= rwhv
->GetViewBounds();
416 gfx::Vector2d guest_offset
= gfx::Vector2d(
417 // Horizontal offset of guest from embedder.
418 guest_bounds
.x() - embedder_bounds
.x(),
419 // Vertical offset from guest's top to embedder's bottom edge.
420 embedder_bounds
.bottom() - guest_bounds
.y());
422 RenderWidgetHostViewMacDictionaryHelper
helper(platform_view_
.get());
423 helper
.SetTargetView(rwhv
);
424 helper
.set_offset(guest_offset
);
425 helper
.ShowDefinitionForSelection();
428 bool RenderWidgetHostViewGuest::SupportsSpeech() const {
429 return platform_view_
->SupportsSpeech();
432 void RenderWidgetHostViewGuest::SpeakSelection() {
433 platform_view_
->SpeakSelection();
436 bool RenderWidgetHostViewGuest::IsSpeaking() const {
437 return platform_view_
->IsSpeaking();
440 void RenderWidgetHostViewGuest::StopSpeaking() {
441 platform_view_
->StopSpeaking();
444 bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
445 const NativeWebKeyboardEvent
& event
) {
449 #endif // defined(OS_MACOSX)
451 #if defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
452 void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
453 const gfx::Rect
& rect_pixels
,
454 const SkBitmap
& zoomed_bitmap
) {
456 #endif // defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
458 #if defined(OS_ANDROID)
459 void RenderWidgetHostViewGuest::LockCompositingSurface() {
462 void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
464 #endif // defined(OS_ANDROID)
467 void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
468 gfx::NativeViewAccessible accessible_parent
) {
471 gfx::NativeViewId
RenderWidgetHostViewGuest::GetParentForWindowlessPlugin()
477 void RenderWidgetHostViewGuest::DestroyGuestView() {
478 host_
->SetView(NULL
);
480 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
483 bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
484 ui::GestureConsumer
* consumer
) {
485 CHECK_EQ(static_cast<RenderWidgetHostViewGuest
*>(consumer
), this);
489 void RenderWidgetHostViewGuest::DispatchGestureEvent(
490 ui::GestureEvent
* event
) {
491 ForwardGestureEventToRenderer(event
);
494 void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
495 ui::TouchEvent
* event
) {
499 blink::WebTouchEvent cancel_event
;
500 // TODO(rbyers): This event has no touches in it. Don't we need to know what
501 // touches are currently active in order to cancel them all properly?
502 WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel
,
503 event
->time_stamp().InSecondsF(),
506 host_
->ForwardTouchEventWithLatencyInfo(cancel_event
, *event
->latency());
509 bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
510 ui::GestureEvent
* gesture
) {
511 #if defined(USE_AURA)
515 if ((gesture
->type() == ui::ET_GESTURE_PINCH_BEGIN
||
516 gesture
->type() == ui::ET_GESTURE_PINCH_UPDATE
||
517 gesture
->type() == ui::ET_GESTURE_PINCH_END
) && !pinch_zoom_enabled_
) {
521 blink::WebGestureEvent web_gesture
=
522 MakeWebGestureEventFromUIEvent(*gesture
);
523 const gfx::Point
& client_point
= gesture
->location();
524 const gfx::Point
& screen_point
= gesture
->location();
526 web_gesture
.x
= client_point
.x();
527 web_gesture
.y
= client_point
.y();
528 web_gesture
.globalX
= screen_point
.x();
529 web_gesture
.globalY
= screen_point
.y();
531 if (web_gesture
.type
== blink::WebGestureEvent::Undefined
)
533 if (web_gesture
.type
== blink::WebGestureEvent::GestureTapDown
) {
534 host_
->ForwardGestureEvent(
535 CreateFlingCancelEvent(gesture
->time_stamp().InSecondsF()));
537 host_
->ForwardGestureEvent(web_gesture
);
544 void RenderWidgetHostViewGuest::ProcessGestures(
545 ui::GestureRecognizer::Gestures
* gestures
) {
546 if ((gestures
== NULL
) || gestures
->empty())
548 for (ui::GestureRecognizer::Gestures::iterator g_it
= gestures
->begin();
549 g_it
!= gestures
->end();
551 ForwardGestureEventToRenderer(*g_it
);
555 SkColorType
RenderWidgetHostViewGuest::PreferredReadbackFormat() {
556 return kN32_SkColorType
;
559 RenderWidgetHostViewBase
*
560 RenderWidgetHostViewGuest::GetOwnerRenderWidgetHostView() const {
561 return static_cast<RenderWidgetHostViewBase
*>(
562 guest_
->GetOwnerRenderWidgetHostView());
565 void RenderWidgetHostViewGuest::OnHandleInputEvent(
566 RenderWidgetHostImpl
* embedder
,
567 int browser_plugin_instance_id
,
568 const gfx::Rect
& guest_window_rect
,
569 const blink::WebInputEvent
* event
) {
570 if (blink::WebInputEvent::isMouseEventType(event
->type
)) {
571 host_
->ForwardMouseEvent(
572 *static_cast<const blink::WebMouseEvent
*>(event
));
576 if (event
->type
== blink::WebInputEvent::MouseWheel
) {
577 host_
->ForwardWheelEvent(
578 *static_cast<const blink::WebMouseWheelEvent
*>(event
));
582 if (blink::WebInputEvent::isKeyboardEventType(event
->type
)) {
583 if (!embedder
->GetLastKeyboardEvent())
585 NativeWebKeyboardEvent
keyboard_event(*embedder
->GetLastKeyboardEvent());
586 host_
->ForwardKeyboardEvent(keyboard_event
);
590 if (blink::WebInputEvent::isTouchEventType(event
->type
)) {
591 host_
->ForwardTouchEventWithLatencyInfo(
592 *static_cast<const blink::WebTouchEvent
*>(event
),
597 if (blink::WebInputEvent::isGestureEventType(event
->type
)) {
598 host_
->ForwardGestureEvent(
599 *static_cast<const blink::WebGestureEvent
*>(event
));
604 } // namespace content