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 "cc/surfaces/surface.h"
10 #include "cc/surfaces/surface_factory.h"
11 #include "cc/surfaces/surface_manager.h"
12 #include "cc/surfaces/surface_sequence.h"
13 #include "content/browser/browser_plugin/browser_plugin_guest.h"
14 #include "content/browser/compositor/surface_utils.h"
15 #include "content/browser/frame_host/render_widget_host_view_guest.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/common/browser_plugin/browser_plugin_messages.h"
18 #include "content/common/frame_messages.h"
19 #include "content/common/gpu/gpu_messages.h"
20 #include "content/common/input/web_touch_event_traits.h"
21 #include "content/common/view_messages.h"
22 #include "content/common/webplugin_geometry.h"
23 #include "content/public/common/content_switches.h"
24 #include "skia/ext/platform_canvas.h"
25 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
27 #if defined(OS_MACOSX)
28 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
32 #include "content/browser/renderer_host/ui_events_helper.h"
40 blink::WebGestureEvent
CreateFlingCancelEvent(double time_stamp
) {
41 blink::WebGestureEvent gesture_event
;
42 gesture_event
.timeStampSeconds
= time_stamp
;
43 gesture_event
.type
= blink::WebGestureEvent::GestureFlingCancel
;
44 gesture_event
.sourceDevice
= blink::WebGestureDeviceTouchscreen
;
47 #endif // defined(USE_AURA)
51 RenderWidgetHostViewGuest::RenderWidgetHostViewGuest(
52 RenderWidgetHost
* widget_host
,
53 BrowserPluginGuest
* guest
,
54 base::WeakPtr
<RenderWidgetHostViewBase
> platform_view
)
55 : RenderWidgetHostViewChildFrame(widget_host
),
56 // |guest| is NULL during test.
57 guest_(guest
? guest
->AsWeakPtr() : base::WeakPtr
<BrowserPluginGuest
>()),
58 platform_view_(platform_view
) {
60 gesture_recognizer_
.reset(ui::GestureRecognizer::Create());
61 gesture_recognizer_
->AddGestureEventHelper(this);
62 #endif // defined(USE_AURA)
65 RenderWidgetHostViewGuest::~RenderWidgetHostViewGuest() {
67 gesture_recognizer_
->RemoveGestureEventHelper(this);
68 #endif // defined(USE_AURA)
71 bool RenderWidgetHostViewGuest::OnMessageReceivedFromEmbedder(
72 const IPC::Message
& message
,
73 RenderWidgetHostImpl
* embedder
) {
75 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(RenderWidgetHostViewGuest
, message
,
77 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_HandleInputEvent
,
79 IPC_MESSAGE_UNHANDLED(handled
= false)
84 void RenderWidgetHostViewGuest::Show() {
85 // If the WebContents associated with us showed an interstitial page in the
86 // beginning, the teardown path might call WasShown() while |host_| is in
87 // the process of destruction. Avoid calling WasShown below in this case.
88 // TODO(lazyboy): We shouldn't be showing interstitial pages in guests in the
89 // first place: http://crbug.com/273089.
91 // |guest_| is NULL during test.
92 if ((guest_
&& guest_
->is_in_destruction()) || !host_
->is_hidden())
94 // Make sure the size of this view matches the size of the WebContentsView.
95 // The two sizes may fall out of sync if we switch RenderWidgetHostViews,
96 // resize, and then switch page, as is the case with interstitial pages.
97 // NOTE: |guest_| is NULL in unit tests.
99 SetSize(guest_
->web_contents()->GetViewBounds().size());
100 host_
->WasShown(ui::LatencyInfo());
103 void RenderWidgetHostViewGuest::Hide() {
104 // |guest_| is NULL during test.
105 if ((guest_
&& guest_
->is_in_destruction()) || host_
->is_hidden())
110 void RenderWidgetHostViewGuest::SetSize(const gfx::Size
& size
) {
115 void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect
& rect
) {
116 SetSize(rect
.size());
119 void RenderWidgetHostViewGuest::Focus() {
120 // InterstitialPageImpl focuses views directly, so we place focus logic here.
121 // InterstitialPages are not WebContents, and so BrowserPluginGuest does not
122 // have direct access to the interstitial page's RenderWidgetHost.
124 guest_
->SetFocus(host_
, true, blink::WebFocusTypeNone
);
127 bool RenderWidgetHostViewGuest::HasFocus() const {
130 return guest_
->focused();
133 #if defined(USE_AURA)
134 void RenderWidgetHostViewGuest::ProcessAckedTouchEvent(
135 const TouchEventWithLatencyInfo
& touch
, InputEventAckState ack_result
) {
136 // TODO(fsamuel): Currently we will only take this codepath if the guest has
137 // requested touch events. A better solution is to always forward touchpresses
138 // to the embedder process to target a BrowserPlugin, and then route all
139 // subsequent touch points of that touchdown to the appropriate guest until
140 // that touch point is released.
141 ScopedVector
<ui::TouchEvent
> events
;
142 if (!MakeUITouchEventsFromWebTouchEvents(touch
, &events
, LOCAL_COORDINATES
))
145 ui::EventResult result
= (ack_result
==
146 INPUT_EVENT_ACK_STATE_CONSUMED
) ? ui::ER_HANDLED
: ui::ER_UNHANDLED
;
147 for (ScopedVector
<ui::TouchEvent
>::iterator iter
= events
.begin(),
148 end
= events
.end(); iter
!= end
; ++iter
) {
149 if (!gesture_recognizer_
->ProcessTouchEventPreDispatch(*iter
, this))
152 scoped_ptr
<ui::GestureRecognizer::Gestures
> gestures
;
153 gestures
.reset(gesture_recognizer_
->AckTouchEvent(
154 (*iter
)->unique_event_id(), result
, this));
155 ProcessGestures(gestures
.get());
160 gfx::Rect
RenderWidgetHostViewGuest::GetViewBounds() const {
164 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
165 gfx::Rect embedder_bounds
;
167 embedder_bounds
= rwhv
->GetViewBounds();
169 guest_
->GetScreenCoordinates(embedder_bounds
.origin()), size_
);
172 void RenderWidgetHostViewGuest::RenderProcessGone(
173 base::TerminationStatus status
,
175 // The |platform_view_| gets destroyed before we get here if this view
176 // is for an InterstitialPage.
178 platform_view_
->RenderProcessGone(status
, error_code
);
180 // Destroy the guest view instance only, so we don't end up calling
181 // platform_view_->Destroy().
185 void RenderWidgetHostViewGuest::Destroy() {
186 // The RenderWidgetHost's destruction led here, so don't call it.
189 if (platform_view_
) // The platform view might have been destroyed already.
190 platform_view_
->Destroy();
193 gfx::Size
RenderWidgetHostViewGuest::GetPhysicalBackingSize() const {
194 return RenderWidgetHostViewBase::GetPhysicalBackingSize();
197 base::string16
RenderWidgetHostViewGuest::GetSelectedText() const {
198 return platform_view_
->GetSelectedText();
201 void RenderWidgetHostViewGuest::SetTooltipText(
202 const base::string16
& tooltip_text
) {
204 guest_
->SetTooltipText(tooltip_text
);
207 void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
208 uint32 output_surface_id
,
209 scoped_ptr
<cc::CompositorFrame
> frame
) {
210 if (!guest_
|| !guest_
->attached()) {
211 // We shouldn't hang on to a surface while we are detached.
212 ClearCompositorSurfaceIfNecessary();
216 last_scroll_offset_
= frame
->metadata
.root_scroll_offset
;
217 // When not using surfaces, the frame just gets proxied to
218 // the embedder's renderer to be composited.
219 if (!frame
->delegated_frame_data
|| !use_surfaces_
) {
220 guest_
->SwapCompositorFrame(output_surface_id
,
221 host_
->GetProcess()->GetID(),
222 host_
->GetRoutingID(),
227 cc::RenderPass
* root_pass
=
228 frame
->delegated_frame_data
->render_pass_list
.back();
230 gfx::Size frame_size
= root_pass
->output_rect
.size();
231 float scale_factor
= frame
->metadata
.device_scale_factor
;
233 // Check whether we need to recreate the cc::Surface, which means the child
234 // frame renderer has changed its output surface, or size, or scale factor.
235 if (output_surface_id
!= last_output_surface_id_
&& surface_factory_
) {
236 surface_factory_
->Destroy(surface_id_
);
237 surface_factory_
.reset();
239 if (output_surface_id
!= last_output_surface_id_
||
240 frame_size
!= current_surface_size_
||
241 scale_factor
!= current_surface_scale_factor_
||
242 guest_
->has_attached_since_surface_set()) {
243 ClearCompositorSurfaceIfNecessary();
244 last_output_surface_id_
= output_surface_id
;
245 current_surface_size_
= frame_size
;
246 current_surface_scale_factor_
= scale_factor
;
249 if (!surface_factory_
) {
250 cc::SurfaceManager
* manager
= GetSurfaceManager();
251 surface_factory_
= make_scoped_ptr(new cc::SurfaceFactory(manager
, this));
254 if (surface_id_
.is_null()) {
255 surface_id_
= id_allocator_
->GenerateId();
256 surface_factory_
->Create(surface_id_
);
258 cc::SurfaceSequence sequence
= cc::SurfaceSequence(
259 id_allocator_
->id_namespace(), next_surface_sequence_
++);
260 // The renderer process will satisfy this dependency when it creates a
262 cc::SurfaceManager
* manager
= GetSurfaceManager();
263 manager
->GetSurfaceForId(surface_id_
)->AddDestructionDependency(sequence
);
264 guest_
->SetChildFrameSurface(surface_id_
, frame_size
, scale_factor
,
268 cc::SurfaceFactory::DrawCallback ack_callback
= base::Bind(
269 &RenderWidgetHostViewChildFrame::SurfaceDrawn
,
270 RenderWidgetHostViewChildFrame::AsWeakPtr(), output_surface_id
);
271 ack_pending_count_
++;
272 // If this value grows very large, something is going wrong.
273 DCHECK(ack_pending_count_
< 1000);
274 surface_factory_
->SubmitCompositorFrame(surface_id_
, frame
.Pass(),
278 bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message
& msg
) {
279 if (!platform_view_
) {
280 // In theory, we can get here if there's a delay between DestroyGuestView()
281 // being called and when our destructor is invoked.
285 return platform_view_
->OnMessageReceived(msg
);
288 void RenderWidgetHostViewGuest::InitAsChild(
289 gfx::NativeView parent_view
) {
290 platform_view_
->InitAsChild(parent_view
);
293 void RenderWidgetHostViewGuest::InitAsPopup(
294 RenderWidgetHostView
* parent_host_view
, const gfx::Rect
& bounds
) {
295 // This should never get called.
299 void RenderWidgetHostViewGuest::InitAsFullscreen(
300 RenderWidgetHostView
* reference_host_view
) {
301 // This should never get called.
305 gfx::NativeView
RenderWidgetHostViewGuest::GetNativeView() const {
307 return gfx::NativeView();
309 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
311 return gfx::NativeView();
312 return rwhv
->GetNativeView();
315 gfx::NativeViewId
RenderWidgetHostViewGuest::GetNativeViewId() const {
317 return static_cast<gfx::NativeViewId
>(NULL
);
319 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
321 return static_cast<gfx::NativeViewId
>(NULL
);
322 return rwhv
->GetNativeViewId();
325 gfx::NativeViewAccessible
RenderWidgetHostViewGuest::GetNativeViewAccessible() {
327 return gfx::NativeViewAccessible();
329 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
331 return gfx::NativeViewAccessible();
332 return rwhv
->GetNativeViewAccessible();
335 void RenderWidgetHostViewGuest::MovePluginWindows(
336 const std::vector
<WebPluginGeometry
>& moves
) {
337 platform_view_
->MovePluginWindows(moves
);
340 void RenderWidgetHostViewGuest::UpdateCursor(const WebCursor
& cursor
) {
341 // InterstitialPages are not WebContents so we cannot intercept
342 // ViewHostMsg_SetCursor for interstitial pages in BrowserPluginGuest.
343 // All guest RenderViewHosts have RenderWidgetHostViewGuests however,
344 // and so we will always hit this code path.
347 guest_
->SendMessageToEmbedder(
348 new BrowserPluginMsg_SetCursor(guest_
->browser_plugin_instance_id(),
353 void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading
) {
354 platform_view_
->SetIsLoading(is_loading
);
357 void RenderWidgetHostViewGuest::TextInputStateChanged(
358 const ViewHostMsg_TextInputState_Params
& params
) {
362 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
365 // Forward the information to embedding RWHV.
366 rwhv
->TextInputStateChanged(params
);
369 void RenderWidgetHostViewGuest::ImeCancelComposition() {
373 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
376 // Forward the information to embedding RWHV.
377 rwhv
->ImeCancelComposition();
380 #if defined(OS_MACOSX) || defined(USE_AURA)
381 void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
382 const gfx::Range
& range
,
383 const std::vector
<gfx::Rect
>& character_bounds
) {
387 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
390 std::vector
<gfx::Rect
> guest_character_bounds
;
391 for (size_t i
= 0; i
< character_bounds
.size(); ++i
) {
392 guest_character_bounds
.push_back(gfx::Rect(
393 guest_
->GetScreenCoordinates(character_bounds
[i
].origin()),
394 character_bounds
[i
].size()));
396 // Forward the information to embedding RWHV.
397 rwhv
->ImeCompositionRangeChanged(range
, guest_character_bounds
);
401 void RenderWidgetHostViewGuest::SelectionChanged(const base::string16
& text
,
403 const gfx::Range
& range
) {
404 platform_view_
->SelectionChanged(text
, offset
, range
);
407 void RenderWidgetHostViewGuest::SelectionBoundsChanged(
408 const ViewHostMsg_SelectionBounds_Params
& params
) {
412 RenderWidgetHostViewBase
* rwhv
= GetOwnerRenderWidgetHostView();
415 ViewHostMsg_SelectionBounds_Params
guest_params(params
);
416 guest_params
.anchor_rect
.set_origin(
417 guest_
->GetScreenCoordinates(params
.anchor_rect
.origin()));
418 guest_params
.focus_rect
.set_origin(
419 guest_
->GetScreenCoordinates(params
.focus_rect
.origin()));
420 rwhv
->SelectionBoundsChanged(guest_params
);
423 void RenderWidgetHostViewGuest::SetBackgroundColor(SkColor color
) {
424 // Content embedders can toggle opaque backgrounds through this API.
425 // We plumb the value here so that BrowserPlugin updates its compositing
426 // state in response to this change. We also want to preserve this flag
427 // after recovering from a crash so we let BrowserPluginGuest store it.
430 RenderWidgetHostViewBase::SetBackgroundColor(color
);
431 bool opaque
= GetBackgroundOpaque();
432 host_
->SetBackgroundOpaque(opaque
);
433 guest_
->SetContentsOpaque(opaque
);
436 bool RenderWidgetHostViewGuest::LockMouse() {
437 return platform_view_
->LockMouse();
440 void RenderWidgetHostViewGuest::UnlockMouse() {
441 return platform_view_
->UnlockMouse();
444 void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo
* results
) {
447 RenderWidgetHostViewBase
* embedder_view
= GetOwnerRenderWidgetHostView();
449 embedder_view
->GetScreenInfo(results
);
452 bool RenderWidgetHostViewGuest::GetScreenColorProfile(
453 std::vector
<char>* color_profile
) {
456 DCHECK(color_profile
->empty());
457 RenderWidgetHostViewBase
* embedder_view
= GetOwnerRenderWidgetHostView();
459 return embedder_view
->GetScreenColorProfile(color_profile
);
463 #if defined(OS_MACOSX)
464 void RenderWidgetHostViewGuest::SetActive(bool active
) {
465 platform_view_
->SetActive(active
);
468 void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible
) {
469 platform_view_
->SetWindowVisibility(visible
);
472 void RenderWidgetHostViewGuest::WindowFrameChanged() {
473 platform_view_
->WindowFrameChanged();
476 void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
481 gfx::Rect guest_bounds
= GetViewBounds();
482 RenderWidgetHostView
* rwhv
= guest_
->GetOwnerRenderWidgetHostView();
483 gfx::Rect embedder_bounds
;
485 embedder_bounds
= rwhv
->GetViewBounds();
487 gfx::Vector2d guest_offset
= gfx::Vector2d(
488 // Horizontal offset of guest from embedder.
489 guest_bounds
.x() - embedder_bounds
.x(),
490 // Vertical offset from guest's top to embedder's bottom edge.
491 embedder_bounds
.bottom() - guest_bounds
.y());
493 RenderWidgetHostViewMacDictionaryHelper
helper(platform_view_
.get());
494 helper
.SetTargetView(rwhv
);
495 helper
.set_offset(guest_offset
);
496 helper
.ShowDefinitionForSelection();
499 bool RenderWidgetHostViewGuest::SupportsSpeech() const {
500 return platform_view_
->SupportsSpeech();
503 void RenderWidgetHostViewGuest::SpeakSelection() {
504 platform_view_
->SpeakSelection();
507 bool RenderWidgetHostViewGuest::IsSpeaking() const {
508 return platform_view_
->IsSpeaking();
511 void RenderWidgetHostViewGuest::StopSpeaking() {
512 platform_view_
->StopSpeaking();
515 bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
516 const NativeWebKeyboardEvent
& event
) {
520 #endif // defined(OS_MACOSX)
522 #if defined(OS_ANDROID) || defined(USE_AURA)
523 void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
524 const gfx::Rect
& rect_pixels
,
525 const SkBitmap
& zoomed_bitmap
) {
527 #endif // defined(OS_ANDROID) || defined(USE_AURA)
529 #if defined(OS_ANDROID)
530 void RenderWidgetHostViewGuest::LockCompositingSurface() {
533 void RenderWidgetHostViewGuest::UnlockCompositingSurface() {
535 #endif // defined(OS_ANDROID)
538 void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
539 gfx::NativeViewAccessible accessible_parent
) {
542 gfx::NativeViewId
RenderWidgetHostViewGuest::GetParentForWindowlessPlugin()
548 void RenderWidgetHostViewGuest::DestroyGuestView() {
549 host_
->SetView(NULL
);
551 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
554 bool RenderWidgetHostViewGuest::CanDispatchToConsumer(
555 ui::GestureConsumer
* consumer
) {
556 CHECK_EQ(static_cast<RenderWidgetHostViewGuest
*>(consumer
), this);
560 void RenderWidgetHostViewGuest::DispatchGestureEvent(
561 ui::GestureEvent
* event
) {
562 ForwardGestureEventToRenderer(event
);
565 void RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
566 ui::TouchEvent
* event
) {
570 blink::WebTouchEvent cancel_event
;
571 // TODO(rbyers): This event has no touches in it. Don't we need to know what
572 // touches are currently active in order to cancel them all properly?
573 WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel
,
574 event
->time_stamp().InSecondsF(),
577 host_
->ForwardTouchEventWithLatencyInfo(cancel_event
, *event
->latency());
580 bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
581 ui::GestureEvent
* gesture
) {
582 #if defined(USE_AURA)
586 if ((gesture
->type() == ui::ET_GESTURE_PINCH_BEGIN
||
587 gesture
->type() == ui::ET_GESTURE_PINCH_UPDATE
||
588 gesture
->type() == ui::ET_GESTURE_PINCH_END
) && !pinch_zoom_enabled_
) {
592 blink::WebGestureEvent web_gesture
=
593 MakeWebGestureEventFromUIEvent(*gesture
);
594 const gfx::Point
& client_point
= gesture
->location();
595 const gfx::Point
& screen_point
= gesture
->location();
597 web_gesture
.x
= client_point
.x();
598 web_gesture
.y
= client_point
.y();
599 web_gesture
.globalX
= screen_point
.x();
600 web_gesture
.globalY
= screen_point
.y();
602 if (web_gesture
.type
== blink::WebGestureEvent::Undefined
)
604 if (web_gesture
.type
== blink::WebGestureEvent::GestureTapDown
) {
605 host_
->ForwardGestureEvent(
606 CreateFlingCancelEvent(gesture
->time_stamp().InSecondsF()));
608 host_
->ForwardGestureEvent(web_gesture
);
615 void RenderWidgetHostViewGuest::ProcessGestures(
616 ui::GestureRecognizer::Gestures
* gestures
) {
617 if ((gestures
== NULL
) || gestures
->empty())
619 for (ui::GestureRecognizer::Gestures::iterator g_it
= gestures
->begin();
620 g_it
!= gestures
->end();
622 ForwardGestureEventToRenderer(*g_it
);
626 RenderWidgetHostViewBase
*
627 RenderWidgetHostViewGuest::GetOwnerRenderWidgetHostView() const {
628 return static_cast<RenderWidgetHostViewBase
*>(
629 guest_
->GetOwnerRenderWidgetHostView());
632 void RenderWidgetHostViewGuest::OnHandleInputEvent(
633 RenderWidgetHostImpl
* embedder
,
634 int browser_plugin_instance_id
,
635 const gfx::Rect
& guest_window_rect
,
636 const blink::WebInputEvent
* event
) {
637 if (blink::WebInputEvent::isMouseEventType(event
->type
)) {
638 // The mouse events for BrowserPlugin are modified by all
639 // the CSS transforms applied on the <object> and embedder. As a result of
640 // this, the coordinates passed on to the guest renderer are potentially
641 // incorrect to determine the position of the context menu(they are not the
642 // actual X, Y of the window). As a hack, we report the last location of a
643 // right mouse up to the BrowserPluginGuest to inform it of the next
644 // potential location for context menu (BUG=470087).
645 // TODO(ekaramad): Find a better and more fundamental solution. Could the
646 // ContextMenuParams be based on global X, Y?
647 const blink::WebMouseEvent
& mouse_event
=
648 static_cast<const blink::WebMouseEvent
&>(*event
);
649 // A MouseDown on the ButtonRight could suggest a ContextMenu.
650 if (guest_
&& mouse_event
.type
== blink::WebInputEvent::MouseDown
&&
651 mouse_event
.button
== blink::WebPointerProperties::ButtonRight
)
652 guest_
->SetContextMenuPosition(
653 gfx::Point(mouse_event
.globalX
- GetViewBounds().x(),
654 mouse_event
.globalY
- GetViewBounds().y()));
655 host_
->ForwardMouseEvent(mouse_event
);
659 if (event
->type
== blink::WebInputEvent::MouseWheel
) {
660 host_
->ForwardWheelEvent(
661 *static_cast<const blink::WebMouseWheelEvent
*>(event
));
665 if (blink::WebInputEvent::isKeyboardEventType(event
->type
)) {
666 if (!embedder
->GetLastKeyboardEvent())
668 NativeWebKeyboardEvent
keyboard_event(*embedder
->GetLastKeyboardEvent());
669 host_
->ForwardKeyboardEvent(keyboard_event
);
673 if (blink::WebInputEvent::isTouchEventType(event
->type
)) {
674 if (event
->type
== blink::WebInputEvent::TouchStart
&&
675 !embedder
->GetView()->HasFocus()) {
676 embedder
->GetView()->Focus();
679 host_
->ForwardTouchEventWithLatencyInfo(
680 *static_cast<const blink::WebTouchEvent
*>(event
),
685 if (blink::WebInputEvent::isGestureEventType(event
->type
)) {
686 host_
->ForwardGestureEvent(
687 *static_cast<const blink::WebGestureEvent
*>(event
));
692 } // namespace content