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::TextInputTypeChanged( 295 ui::TextInputType type, 296 ui::TextInputMode input_mode, 297 bool can_compose_inline) { 298 if (!guest_) 299 return; 300 301 RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView(); 302 if (!rwhv) 303 return; 304 // Forward the information to embedding RWHV. 305 rwhv->TextInputTypeChanged(type, input_mode, can_compose_inline); 306} 307 308void RenderWidgetHostViewGuest::ImeCancelComposition() { 309 if (!guest_) 310 return; 311 312 RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView(); 313 if (!rwhv) 314 return; 315 // Forward the information to embedding RWHV. 316 rwhv->ImeCancelComposition(); 317} 318 319#if defined(OS_MACOSX) || defined(USE_AURA) 320void RenderWidgetHostViewGuest::ImeCompositionRangeChanged( 321 const gfx::Range& range, 322 const std::vector<gfx::Rect>& character_bounds) { 323 if (!guest_) 324 return; 325 326 RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView(); 327 if (!rwhv) 328 return; 329 std::vector<gfx::Rect> guest_character_bounds; 330 for (size_t i = 0; i < character_bounds.size(); ++i) { 331 guest_character_bounds.push_back(gfx::Rect( 332 guest_->GetScreenCoordinates(character_bounds[i].origin()), 333 character_bounds[i].size())); 334 } 335 // Forward the information to embedding RWHV. 336 rwhv->ImeCompositionRangeChanged(range, guest_character_bounds); 337} 338#endif 339 340void RenderWidgetHostViewGuest::SelectionChanged(const base::string16& text, 341 size_t offset, 342 const gfx::Range& range) { 343 platform_view_->SelectionChanged(text, offset, range); 344} 345 346void RenderWidgetHostViewGuest::SelectionBoundsChanged( 347 const ViewHostMsg_SelectionBounds_Params& params) { 348 if (!guest_) 349 return; 350 351 RenderWidgetHostViewBase* rwhv = GetGuestRenderWidgetHostView(); 352 if (!rwhv) 353 return; 354 ViewHostMsg_SelectionBounds_Params guest_params(params); 355 guest_params.anchor_rect.set_origin( 356 guest_->GetScreenCoordinates(params.anchor_rect.origin())); 357 guest_params.focus_rect.set_origin( 358 guest_->GetScreenCoordinates(params.focus_rect.origin())); 359 rwhv->SelectionBoundsChanged(guest_params); 360} 361 362void RenderWidgetHostViewGuest::CopyFromCompositingSurface( 363 const gfx::Rect& src_subrect, 364 const gfx::Size& dst_size, 365 CopyFromCompositingSurfaceCallback& callback, 366 const SkColorType color_type) { 367 CHECK(guest_); 368 guest_->CopyFromCompositingSurface(src_subrect, dst_size, callback); 369} 370 371void RenderWidgetHostViewGuest::SetBackgroundOpaque(bool opaque) { 372 // Content embedders can toggle opaque backgrounds through this API. 373 // We plumb the value here so that BrowserPlugin updates its compositing 374 // state in response to this change. We also want to preserve this flag 375 // after recovering from a crash so we let BrowserPluginGuest store it. 376 if (!guest_) 377 return; 378 RenderWidgetHostViewBase::SetBackgroundOpaque(opaque); 379 host_->SetBackgroundOpaque(opaque); 380 guest_->SetContentsOpaque(opaque); 381} 382 383bool RenderWidgetHostViewGuest::LockMouse() { 384 return platform_view_->LockMouse(); 385} 386 387void RenderWidgetHostViewGuest::UnlockMouse() { 388 return platform_view_->UnlockMouse(); 389} 390 391void RenderWidgetHostViewGuest::GetScreenInfo(blink::WebScreenInfo* results) { 392 if (!guest_) 393 return; 394 RenderWidgetHostViewBase* embedder_view = GetGuestRenderWidgetHostView(); 395 if (embedder_view) 396 embedder_view->GetScreenInfo(results); 397} 398 399#if defined(OS_MACOSX) 400void RenderWidgetHostViewGuest::SetActive(bool active) { 401 platform_view_->SetActive(active); 402} 403 404void RenderWidgetHostViewGuest::SetTakesFocusOnlyOnMouseDown(bool flag) { 405 platform_view_->SetTakesFocusOnlyOnMouseDown(flag); 406} 407 408void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible) { 409 platform_view_->SetWindowVisibility(visible); 410} 411 412void RenderWidgetHostViewGuest::WindowFrameChanged() { 413 platform_view_->WindowFrameChanged(); 414} 415 416void RenderWidgetHostViewGuest::ShowDefinitionForSelection() { 417 if (!guest_) 418 return; 419 420 gfx::Point origin; 421 gfx::Rect guest_bounds = GetViewBounds(); 422 RenderWidgetHostView* rwhv = guest_->GetEmbedderRenderWidgetHostView(); 423 gfx::Rect embedder_bounds; 424 if (rwhv) 425 embedder_bounds = rwhv->GetViewBounds(); 426 427 gfx::Vector2d guest_offset = gfx::Vector2d( 428 // Horizontal offset of guest from embedder. 429 guest_bounds.x() - embedder_bounds.x(), 430 // Vertical offset from guest's top to embedder's bottom edge. 431 embedder_bounds.bottom() - guest_bounds.y()); 432 433 RenderWidgetHostViewMacDictionaryHelper helper(platform_view_); 434 helper.SetTargetView(rwhv); 435 helper.set_offset(guest_offset); 436 helper.ShowDefinitionForSelection(); 437} 438 439bool RenderWidgetHostViewGuest::SupportsSpeech() const { 440 return platform_view_->SupportsSpeech(); 441} 442 443void RenderWidgetHostViewGuest::SpeakSelection() { 444 platform_view_->SpeakSelection(); 445} 446 447bool RenderWidgetHostViewGuest::IsSpeaking() const { 448 return platform_view_->IsSpeaking(); 449} 450 451void RenderWidgetHostViewGuest::StopSpeaking() { 452 platform_view_->StopSpeaking(); 453} 454 455bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme( 456 const NativeWebKeyboardEvent& event) { 457 return false; 458} 459 460#endif // defined(OS_MACOSX) 461 462#if defined(OS_ANDROID) || defined(TOOLKIT_VIEWS) 463void RenderWidgetHostViewGuest::ShowDisambiguationPopup( 464 const gfx::Rect& rect_pixels, 465 const SkBitmap& zoomed_bitmap) { 466} 467#endif // defined(OS_ANDROID) || defined(TOOLKIT_VIEWS) 468 469#if defined(OS_ANDROID) 470void RenderWidgetHostViewGuest::LockCompositingSurface() { 471} 472 473void RenderWidgetHostViewGuest::UnlockCompositingSurface() { 474} 475#endif // defined(OS_ANDROID) 476 477#if defined(OS_WIN) 478void RenderWidgetHostViewGuest::SetParentNativeViewAccessible( 479 gfx::NativeViewAccessible accessible_parent) { 480} 481 482gfx::NativeViewId RenderWidgetHostViewGuest::GetParentForWindowlessPlugin() 483 const { 484 return NULL; 485} 486#endif 487 488void RenderWidgetHostViewGuest::DestroyGuestView() { 489 host_->SetView(NULL); 490 host_ = NULL; 491 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 492} 493 494bool RenderWidgetHostViewGuest::CanDispatchToConsumer( 495 ui::GestureConsumer* consumer) { 496 CHECK_EQ(static_cast<RenderWidgetHostViewGuest*>(consumer), this); 497 return true; 498} 499 500void RenderWidgetHostViewGuest::DispatchGestureEvent( 501 ui::GestureEvent* event) { 502 ForwardGestureEventToRenderer(event); 503} 504 505void RenderWidgetHostViewGuest::DispatchCancelTouchEvent( 506 ui::TouchEvent* event) { 507 if (!host_) 508 return; 509 510 blink::WebTouchEvent cancel_event; 511 // TODO(rbyers): This event has no touches in it. Don't we need to know what 512 // touches are currently active in order to cancel them all properly? 513 WebTouchEventTraits::ResetType(blink::WebInputEvent::TouchCancel, 514 event->time_stamp().InSecondsF(), 515 &cancel_event); 516 517 host_->ForwardTouchEventWithLatencyInfo(cancel_event, *event->latency()); 518} 519 520bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer( 521 ui::GestureEvent* gesture) { 522#if defined(USE_AURA) 523 if (!host_) 524 return false; 525 526 if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN || 527 gesture->type() == ui::ET_GESTURE_PINCH_UPDATE || 528 gesture->type() == ui::ET_GESTURE_PINCH_END) && !pinch_zoom_enabled_) { 529 return true; 530 } 531 532 blink::WebGestureEvent web_gesture = 533 MakeWebGestureEventFromUIEvent(*gesture); 534 const gfx::Point& client_point = gesture->location(); 535 const gfx::Point& screen_point = gesture->location(); 536 537 web_gesture.x = client_point.x(); 538 web_gesture.y = client_point.y(); 539 web_gesture.globalX = screen_point.x(); 540 web_gesture.globalY = screen_point.y(); 541 542 if (web_gesture.type == blink::WebGestureEvent::Undefined) 543 return false; 544 if (web_gesture.type == blink::WebGestureEvent::GestureTapDown) { 545 host_->ForwardGestureEvent( 546 CreateFlingCancelEvent(gesture->time_stamp().InSecondsF())); 547 } 548 host_->ForwardGestureEvent(web_gesture); 549 return true; 550#else 551 return false; 552#endif 553} 554 555void RenderWidgetHostViewGuest::ProcessGestures( 556 ui::GestureRecognizer::Gestures* gestures) { 557 if ((gestures == NULL) || gestures->empty()) 558 return; 559 for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin(); 560 g_it != gestures->end(); 561 ++g_it) { 562 ForwardGestureEventToRenderer(*g_it); 563 } 564} 565 566SkColorType RenderWidgetHostViewGuest::PreferredReadbackFormat() { 567 return kN32_SkColorType; 568} 569 570RenderWidgetHostViewBase* 571RenderWidgetHostViewGuest::GetGuestRenderWidgetHostView() const { 572 return static_cast<RenderWidgetHostViewBase*>( 573 guest_->GetEmbedderRenderWidgetHostView()); 574} 575 576void RenderWidgetHostViewGuest::OnHandleInputEvent( 577 RenderWidgetHostImpl* embedder, 578 int browser_plugin_instance_id, 579 const gfx::Rect& guest_window_rect, 580 const blink::WebInputEvent* event) { 581 if (blink::WebInputEvent::isMouseEventType(event->type)) { 582 host_->ForwardMouseEvent( 583 *static_cast<const blink::WebMouseEvent*>(event)); 584 return; 585 } 586 587 if (event->type == blink::WebInputEvent::MouseWheel) { 588 host_->ForwardWheelEvent( 589 *static_cast<const blink::WebMouseWheelEvent*>(event)); 590 return; 591 } 592 593 if (blink::WebInputEvent::isKeyboardEventType(event->type)) { 594 if (!embedder->GetLastKeyboardEvent()) 595 return; 596 NativeWebKeyboardEvent keyboard_event(*embedder->GetLastKeyboardEvent()); 597 host_->ForwardKeyboardEvent(keyboard_event); 598 return; 599 } 600 601 if (blink::WebInputEvent::isTouchEventType(event->type)) { 602 host_->ForwardTouchEventWithLatencyInfo( 603 *static_cast<const blink::WebTouchEvent*>(event), 604 ui::LatencyInfo()); 605 return; 606 } 607 608 if (blink::WebInputEvent::isGestureEventType(event->type)) { 609 host_->ForwardGestureEvent( 610 *static_cast<const blink::WebGestureEvent*>(event)); 611 return; 612 } 613} 614 615} // namespace content 616