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_
->AckTouchEvent(
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 uint32_t RenderWidgetHostViewGuest::GetSurfaceIdNamespace() {
393 // Compositing surfaces not supported.
397 #if defined(OS_MACOSX)
398 void RenderWidgetHostViewGuest::SetActive(bool active
) {
399 platform_view_
->SetActive(active
);
402 void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible
) {
403 platform_view_
->SetWindowVisibility(visible
);
406 void RenderWidgetHostViewGuest::WindowFrameChanged() {
407 platform_view_
->WindowFrameChanged();
410 void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
415 gfx::Rect guest_bounds
= GetViewBounds();
416 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
417 gfx::Rect embedder_bounds
;
419 embedder_bounds
= rwhv
->GetViewBounds();
421 gfx::Vector2d guest_offset
= gfx::Vector2d(
422 // Horizontal offset of guest from embedder.
423 guest_bounds
.x() - embedder_bounds
.x(),
424 // Vertical offset from guest's top to embedder's bottom edge.
425 embedder_bounds
.bottom() - guest_bounds
.y());
427 RenderWidgetHostViewMacDictionaryHelper
helper(platform_view_
.get());
428 helper
.SetTargetView(rwhv
);
429 helper
.set_offset(guest_offset
);
430 helper
.ShowDefinitionForSelection();
433 bool RenderWidgetHostViewGuest::SupportsSpeech() const {
434 return platform_view_
->SupportsSpeech();
437 void RenderWidgetHostViewGuest::SpeakSelection() {
438 platform_view_
->SpeakSelection();
441 bool RenderWidgetHostViewGuest::IsSpeaking() const {
442 return platform_view_
->IsSpeaking();
445 void RenderWidgetHostViewGuest::StopSpeaking() {
446 platform_view_
->StopSpeaking();
449 bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
450 const NativeWebKeyboardEvent
& event
) {
454 #endif // defined(OS_MACOSX)
456 #if defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
457 void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
458 const gfx::Rect
& rect_pixels
,
459 const SkBitmap
& zoomed_bitmap
) {
461 #endif // defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
463 #if defined(OS_ANDROID)
464 void RenderWidgetHostViewGuest::LockCompositingSurface() {
467 void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
469 #endif // defined(OS_ANDROID)
472 void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
473 gfx::NativeViewAccessible accessible_parent
) {
476 gfx::NativeViewId
RenderWidgetHostViewGuest::GetParentForWindowlessPlugin()
482 void RenderWidgetHostViewGuest::DestroyGuestView() {
483 host_
->SetView(NULL
);
485 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
488 bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
489 ui::GestureConsumer
* consumer
) {
490 CHECK_EQ(static_cast<RenderWidgetHostViewGuest
*>(consumer
), this);
494 void RenderWidgetHostViewGuest::DispatchGestureEvent(
495 ui::GestureEvent
* event
) {
496 ForwardGestureEventToRenderer(event
);
499 void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
500 ui::TouchEvent
* event
) {
504 blink::WebTouchEvent cancel_event
;
505 // TODO(rbyers): This event has no touches in it. Don't we need to know what
506 // touches are currently active in order to cancel them all properly?
507 WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel
,
508 event
->time_stamp().InSecondsF(),
511 host_
->ForwardTouchEventWithLatencyInfo(cancel_event
, *event
->latency());
514 bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
515 ui::GestureEvent
* gesture
) {
516 #if defined(USE_AURA)
520 if ((gesture
->type() == ui::ET_GESTURE_PINCH_BEGIN
||
521 gesture
->type() == ui::ET_GESTURE_PINCH_UPDATE
||
522 gesture
->type() == ui::ET_GESTURE_PINCH_END
) && !pinch_zoom_enabled_
) {
526 blink::WebGestureEvent web_gesture
=
527 MakeWebGestureEventFromUIEvent(*gesture
);
528 const gfx::Point
& client_point
= gesture
->location();
529 const gfx::Point
& screen_point
= gesture
->location();
531 web_gesture
.x
= client_point
.x();
532 web_gesture
.y
= client_point
.y();
533 web_gesture
.globalX
= screen_point
.x();
534 web_gesture
.globalY
= screen_point
.y();
536 if (web_gesture
.type
== blink::WebGestureEvent::Undefined
)
538 if (web_gesture
.type
== blink::WebGestureEvent::GestureTapDown
) {
539 host_
->ForwardGestureEvent(
540 CreateFlingCancelEvent(gesture
->time_stamp().InSecondsF()));
542 host_
->ForwardGestureEvent(web_gesture
);
549 void RenderWidgetHostViewGuest::ProcessGestures(
550 ui::GestureRecognizer::Gestures
* gestures
) {
551 if ((gestures
== NULL
) || gestures
->empty())
553 for (ui::GestureRecognizer::Gestures::iterator g_it
= gestures
->begin();
554 g_it
!= gestures
->end();
556 ForwardGestureEventToRenderer(*g_it
);
560 RenderWidgetHostViewBase
*
561 RenderWidgetHostViewGuest::GetOwnerRenderWidgetHostView() const {
562 return static_cast<RenderWidgetHostViewBase
*>(
563 guest_
->GetOwnerRenderWidgetHostView());
566 void RenderWidgetHostViewGuest::OnHandleInputEvent(
567 RenderWidgetHostImpl
* embedder
,
568 int browser_plugin_instance_id
,
569 const gfx::Rect
& guest_window_rect
,
570 const blink::WebInputEvent
* event
) {
571 if (blink::WebInputEvent::isMouseEventType(event
->type
)) {
572 host_
->ForwardMouseEvent(
573 *static_cast<const blink::WebMouseEvent
*>(event
));
577 if (event
->type
== blink::WebInputEvent::MouseWheel
) {
578 host_
->ForwardWheelEvent(
579 *static_cast<const blink::WebMouseWheelEvent
*>(event
));
583 if (blink::WebInputEvent::isKeyboardEventType(event
->type
)) {
584 if (!embedder
->GetLastKeyboardEvent())
586 NativeWebKeyboardEvent
keyboard_event(*embedder
->GetLastKeyboardEvent());
587 host_
->ForwardKeyboardEvent(keyboard_event
);
591 if (blink::WebInputEvent::isTouchEventType(event
->type
)) {
592 host_
->ForwardTouchEventWithLatencyInfo(
593 *static_cast<const blink::WebTouchEvent
*>(event
),
598 if (blink::WebInputEvent::isGestureEventType(event
->type
)) {
599 host_
->ForwardGestureEvent(
600 *static_cast<const blink::WebGestureEvent
*>(event
));
605 } // namespace content