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::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.
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::WasHidden() {
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);
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_
->ProcessTouchEventPostDispatch(
149 *(*iter
), result
, this));
150 ProcessGestures(gestures
.get());
155 gfx::Rect
RenderWidgetHostViewGuest::GetViewBounds() const {
159 RenderWidgetHostViewBase
* rwhv
= GetGuestRenderWidgetHostView();
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 platform_view_
->RenderProcessGone(status
, error_code
);
171 // Destroy the guest view instance only, so we don't end up calling
172 // platform_view_->Destroy().
176 void RenderWidgetHostViewGuest::Destroy() {
177 // The RenderWidgetHost's destruction led here, so don't call it.
180 if (platform_view_
) // The platform view might have been destroyed already.
181 platform_view_
->Destroy();
184 gfx::Size
RenderWidgetHostViewGuest::GetPhysicalBackingSize() const {
185 return RenderWidgetHostViewBase::GetPhysicalBackingSize();
188 base::string16
RenderWidgetHostViewGuest::GetSelectedText() const {
189 return platform_view_
->GetSelectedText();
192 void RenderWidgetHostViewGuest::SetTooltipText(
193 const base::string16
& tooltip_text
) {
195 guest_
->SetTooltipText(tooltip_text
);
198 void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
199 uint32 output_surface_id
,
200 scoped_ptr
<cc::CompositorFrame
> frame
) {
204 last_scroll_offset_
= frame
->metadata
.root_scroll_offset
;
205 guest_
->SwapCompositorFrame(output_surface_id
,
206 host_
->GetProcess()->GetID(),
207 host_
->GetRoutingID(),
211 bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message
& msg
) {
212 return platform_view_
->OnMessageReceived(msg
);
215 void RenderWidgetHostViewGuest::InitAsChild(
216 gfx::NativeView parent_view
) {
217 platform_view_
->InitAsChild(parent_view
);
220 void RenderWidgetHostViewGuest::InitAsPopup(
221 RenderWidgetHostView
* parent_host_view
, const gfx::Rect
& pos
) {
222 // This should never get called.
226 void RenderWidgetHostViewGuest::InitAsFullscreen(
227 RenderWidgetHostView
* reference_host_view
) {
228 // This should never get called.
232 gfx::NativeView
RenderWidgetHostViewGuest::GetNativeView() const {
234 return gfx::NativeView();
236 RenderWidgetHostView
* rwhv
= guest_
->GetEmbedderRenderWidgetHostView();
238 return gfx::NativeView();
239 return rwhv
->GetNativeView();
242 gfx::NativeViewId
RenderWidgetHostViewGuest::GetNativeViewId() const {
244 return static_cast<gfx::NativeViewId
>(NULL
);
246 RenderWidgetHostView
* rwhv
= guest_
->GetEmbedderRenderWidgetHostView();
248 return static_cast<gfx::NativeViewId
>(NULL
);
249 return rwhv
->GetNativeViewId();
252 gfx::NativeViewAccessible
RenderWidgetHostViewGuest::GetNativeViewAccessible() {
254 return gfx::NativeViewAccessible();
256 RenderWidgetHostView
* rwhv
= guest_
->GetEmbedderRenderWidgetHostView();
258 return gfx::NativeViewAccessible();
259 return rwhv
->GetNativeViewAccessible();
262 void RenderWidgetHostViewGuest::MovePluginWindows(
263 const std::vector
<WebPluginGeometry
>& moves
) {
264 platform_view_
->MovePluginWindows(moves
);
267 void RenderWidgetHostViewGuest::UpdateCursor(const WebCursor
& cursor
) {
268 // InterstitialPages are not WebContents so we cannot intercept
269 // ViewHostMsg_SetCursor for interstitial pages in BrowserPluginGuest.
270 // All guest RenderViewHosts have RenderWidgetHostViewGuests however,
271 // and so we will always hit this code path.
274 guest_
->SendMessageToEmbedder(
275 new BrowserPluginMsg_SetCursor(guest_
->browser_plugin_instance_id(),
280 void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading
) {
281 platform_view_
->SetIsLoading(is_loading
);
284 void RenderWidgetHostViewGuest::TextInputTypeChanged(
285 ui::TextInputType type
,
286 ui::TextInputMode input_mode
,
287 bool can_compose_inline
,
292 RenderWidgetHostViewBase
* rwhv
= GetGuestRenderWidgetHostView();
295 // Forward the information to embedding RWHV.
296 rwhv
->TextInputTypeChanged(type
, input_mode
, can_compose_inline
, flags
);
299 void RenderWidgetHostViewGuest::ImeCancelComposition() {
303 RenderWidgetHostViewBase
* rwhv
= GetGuestRenderWidgetHostView();
306 // Forward the information to embedding RWHV.
307 rwhv
->ImeCancelComposition();
310 #if defined(OS_MACOSX) || defined(USE_AURA)
311 void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
312 const gfx::Range
& range
,
313 const std::vector
<gfx::Rect
>& character_bounds
) {
317 RenderWidgetHostViewBase
* rwhv
= GetGuestRenderWidgetHostView();
320 std::vector
<gfx::Rect
> guest_character_bounds
;
321 for (size_t i
= 0; i
< character_bounds
.size(); ++i
) {
322 guest_character_bounds
.push_back(gfx::Rect(
323 guest_
->GetScreenCoordinates(character_bounds
[i
].origin()),
324 character_bounds
[i
].size()));
326 // Forward the information to embedding RWHV.
327 rwhv
->ImeCompositionRangeChanged(range
, guest_character_bounds
);
331 void RenderWidgetHostViewGuest::SelectionChanged(const base::string16
& text
,
333 const gfx::Range
& range
) {
334 platform_view_
->SelectionChanged(text
, offset
, range
);
337 void RenderWidgetHostViewGuest::SelectionBoundsChanged(
338 const ViewHostMsg_SelectionBounds_Params
& params
) {
342 RenderWidgetHostViewBase
* rwhv
= GetGuestRenderWidgetHostView();
345 ViewHostMsg_SelectionBounds_Params
guest_params(params
);
346 guest_params
.anchor_rect
.set_origin(
347 guest_
->GetScreenCoordinates(params
.anchor_rect
.origin()));
348 guest_params
.focus_rect
.set_origin(
349 guest_
->GetScreenCoordinates(params
.focus_rect
.origin()));
350 rwhv
->SelectionBoundsChanged(guest_params
);
353 void RenderWidgetHostViewGuest::CopyFromCompositingSurface(
354 const gfx::Rect
& src_subrect
,
355 const gfx::Size
& dst_size
,
356 CopyFromCompositingSurfaceCallback
& callback
,
357 const SkColorType color_type
) {
359 guest_
->CopyFromCompositingSurface(src_subrect
, dst_size
, callback
);
362 void RenderWidgetHostViewGuest::SetBackgroundColor(SkColor color
) {
363 // Content embedders can toggle opaque backgrounds through this API.
364 // We plumb the value here so that BrowserPlugin updates its compositing
365 // state in response to this change. We also want to preserve this flag
366 // after recovering from a crash so we let BrowserPluginGuest store it.
369 RenderWidgetHostViewBase::SetBackgroundColor(color
);
370 bool opaque
= GetBackgroundOpaque();
371 host_
->SetBackgroundOpaque(opaque
);
372 guest_
->SetContentsOpaque(opaque
);
375 bool RenderWidgetHostViewGuest::LockMouse() {
376 return platform_view_
->LockMouse();
379 void RenderWidgetHostViewGuest::UnlockMouse() {
380 return platform_view_
->UnlockMouse();
383 void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo
* results
) {
386 RenderWidgetHostViewBase
* embedder_view
= GetGuestRenderWidgetHostView();
388 embedder_view
->GetScreenInfo(results
);
391 #if defined(OS_MACOSX)
392 void RenderWidgetHostViewGuest::SetActive(bool active
) {
393 platform_view_
->SetActive(active
);
396 void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible
) {
397 platform_view_
->SetWindowVisibility(visible
);
400 void RenderWidgetHostViewGuest::WindowFrameChanged() {
401 platform_view_
->WindowFrameChanged();
404 void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
409 gfx::Rect guest_bounds
= GetViewBounds();
410 RenderWidgetHostView
* rwhv
= guest_
->GetEmbedderRenderWidgetHostView();
411 gfx::Rect embedder_bounds
;
413 embedder_bounds
= rwhv
->GetViewBounds();
415 gfx::Vector2d guest_offset
= gfx::Vector2d(
416 // Horizontal offset of guest from embedder.
417 guest_bounds
.x() - embedder_bounds
.x(),
418 // Vertical offset from guest's top to embedder's bottom edge.
419 embedder_bounds
.bottom() - guest_bounds
.y());
421 RenderWidgetHostViewMacDictionaryHelper
helper(platform_view_
.get());
422 helper
.SetTargetView(rwhv
);
423 helper
.set_offset(guest_offset
);
424 helper
.ShowDefinitionForSelection();
427 bool RenderWidgetHostViewGuest::SupportsSpeech() const {
428 return platform_view_
->SupportsSpeech();
431 void RenderWidgetHostViewGuest::SpeakSelection() {
432 platform_view_
->SpeakSelection();
435 bool RenderWidgetHostViewGuest::IsSpeaking() const {
436 return platform_view_
->IsSpeaking();
439 void RenderWidgetHostViewGuest::StopSpeaking() {
440 platform_view_
->StopSpeaking();
443 bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
444 const NativeWebKeyboardEvent
& event
) {
448 #endif // defined(OS_MACOSX)
450 #if defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
451 void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
452 const gfx::Rect
& rect_pixels
,
453 const SkBitmap
& zoomed_bitmap
) {
455 #endif // defined(OS_ANDROID) || defined(TOOLKIT_VIEWS)
457 #if defined(OS_ANDROID)
458 void RenderWidgetHostViewGuest::LockCompositingSurface() {
461 void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
463 #endif // defined(OS_ANDROID)
466 void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
467 gfx::NativeViewAccessible accessible_parent
) {
470 gfx::NativeViewId
RenderWidgetHostViewGuest::GetParentForWindowlessPlugin()
476 void RenderWidgetHostViewGuest::DestroyGuestView() {
477 host_
->SetView(NULL
);
479 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
482 bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
483 ui::GestureConsumer
* consumer
) {
484 CHECK_EQ(static_cast<RenderWidgetHostViewGuest
*>(consumer
), this);
488 void RenderWidgetHostViewGuest::DispatchGestureEvent(
489 ui::GestureEvent
* event
) {
490 ForwardGestureEventToRenderer(event
);
493 void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
494 ui::TouchEvent
* event
) {
498 blink::WebTouchEvent cancel_event
;
499 // TODO(rbyers): This event has no touches in it. Don't we need to know what
500 // touches are currently active in order to cancel them all properly?
501 WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel
,
502 event
->time_stamp().InSecondsF(),
505 host_
->ForwardTouchEventWithLatencyInfo(cancel_event
, *event
->latency());
508 bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
509 ui::GestureEvent
* gesture
) {
510 #if defined(USE_AURA)
514 if ((gesture
->type() == ui::ET_GESTURE_PINCH_BEGIN
||
515 gesture
->type() == ui::ET_GESTURE_PINCH_UPDATE
||
516 gesture
->type() == ui::ET_GESTURE_PINCH_END
) && !pinch_zoom_enabled_
) {
520 blink::WebGestureEvent web_gesture
=
521 MakeWebGestureEventFromUIEvent(*gesture
);
522 const gfx::Point
& client_point
= gesture
->location();
523 const gfx::Point
& screen_point
= gesture
->location();
525 web_gesture
.x
= client_point
.x();
526 web_gesture
.y
= client_point
.y();
527 web_gesture
.globalX
= screen_point
.x();
528 web_gesture
.globalY
= screen_point
.y();
530 if (web_gesture
.type
== blink::WebGestureEvent::Undefined
)
532 if (web_gesture
.type
== blink::WebGestureEvent::GestureTapDown
) {
533 host_
->ForwardGestureEvent(
534 CreateFlingCancelEvent(gesture
->time_stamp().InSecondsF()));
536 host_
->ForwardGestureEvent(web_gesture
);
543 void RenderWidgetHostViewGuest::ProcessGestures(
544 ui::GestureRecognizer::Gestures
* gestures
) {
545 if ((gestures
== NULL
) || gestures
->empty())
547 for (ui::GestureRecognizer::Gestures::iterator g_it
= gestures
->begin();
548 g_it
!= gestures
->end();
550 ForwardGestureEventToRenderer(*g_it
);
554 SkColorType
RenderWidgetHostViewGuest::PreferredReadbackFormat() {
555 return kN32_SkColorType
;
558 RenderWidgetHostViewBase
*
559 RenderWidgetHostViewGuest::GetGuestRenderWidgetHostView() const {
560 return static_cast<RenderWidgetHostViewBase
*>(
561 guest_
->GetEmbedderRenderWidgetHostView());
564 void RenderWidgetHostViewGuest::OnHandleInputEvent(
565 RenderWidgetHostImpl
* embedder
,
566 int browser_plugin_instance_id
,
567 const gfx::Rect
& guest_window_rect
,
568 const blink::WebInputEvent
* event
) {
569 if (blink::WebInputEvent::isMouseEventType(event
->type
)) {
570 host_
->ForwardMouseEvent(
571 *static_cast<const blink::WebMouseEvent
*>(event
));
575 if (event
->type
== blink::WebInputEvent::MouseWheel
) {
576 host_
->ForwardWheelEvent(
577 *static_cast<const blink::WebMouseWheelEvent
*>(event
));
581 if (blink::WebInputEvent::isKeyboardEventType(event
->type
)) {
582 if (!embedder
->GetLastKeyboardEvent())
584 NativeWebKeyboardEvent
keyboard_event(*embedder
->GetLastKeyboardEvent());
585 host_
->ForwardKeyboardEvent(keyboard_event
);
589 if (blink::WebInputEvent::isTouchEventType(event
->type
)) {
590 host_
->ForwardTouchEventWithLatencyInfo(
591 *static_cast<const blink::WebTouchEvent
*>(event
),
596 if (blink::WebInputEvent::isGestureEventType(event
->type
)) {
597 host_
->ForwardGestureEvent(
598 *static_cast<const blink::WebGestureEvent
*>(event
));
603 } // namespace content