blob: 0711a327651df1c961f5a3508fb1bf795e1ac0d5 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/frame_host/cross_process_frame_connector.h"
#include "cc/surfaces/surface.h"
#include "cc/surfaces/surface_hittest.h"
#include "cc/surfaces/surface_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/frame_messages.h"
#include "gpu/ipc/common/gpu_messages.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/geometry/dip_util.h"
namespace content {
CrossProcessFrameConnector::CrossProcessFrameConnector(
RenderFrameProxyHost* frame_proxy_in_parent_renderer)
: frame_proxy_in_parent_renderer_(frame_proxy_in_parent_renderer),
view_(nullptr),
is_scroll_bubbling_(false) {}
CrossProcessFrameConnector::~CrossProcessFrameConnector() {
// Notify the view of this object being destroyed, if the view still exists.
set_view(nullptr);
}
bool CrossProcessFrameConnector::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg)
IPC_MESSAGE_HANDLER(FrameHostMsg_ForwardInputEvent, OnForwardInputEvent)
IPC_MESSAGE_HANDLER(FrameHostMsg_FrameRectChanged, OnFrameRectChanged)
IPC_MESSAGE_HANDLER(FrameHostMsg_VisibilityChanged, OnVisibilityChanged)
IPC_MESSAGE_HANDLER(FrameHostMsg_SatisfySequence, OnSatisfySequence)
IPC_MESSAGE_HANDLER(FrameHostMsg_RequireSequence, OnRequireSequence)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void CrossProcessFrameConnector::set_view(
RenderWidgetHostViewChildFrame* view) {
// Detach ourselves from the previous |view_|.
if (view_) {
// The RenderWidgetHostDelegate needs to be checked because set_view() can
// be called during nested WebContents destruction. See
// https://crbug.com/644306.
if (is_scroll_bubbling_ && GetParentRenderWidgetHostView() &&
RenderWidgetHostImpl::From(
GetParentRenderWidgetHostView()->GetRenderWidgetHost())
->delegate()) {
RenderWidgetHostImpl::From(
GetParentRenderWidgetHostView()->GetRenderWidgetHost())
->delegate()
->GetInputEventRouter()
->CancelScrollBubbling(view_);
is_scroll_bubbling_ = false;
}
view_->SetCrossProcessFrameConnector(nullptr);
}
view_ = view;
// Attach ourselves to the new view and size it appropriately.
if (view_) {
view_->SetCrossProcessFrameConnector(this);
SetRect(child_frame_rect_);
}
}
void CrossProcessFrameConnector::RenderProcessGone() {
frame_proxy_in_parent_renderer_->Send(new FrameMsg_ChildFrameProcessGone(
frame_proxy_in_parent_renderer_->GetRoutingID()));
}
void CrossProcessFrameConnector::SetChildFrameSurface(
const cc::SurfaceId& surface_id,
const gfx::Size& frame_size,
float scale_factor,
const cc::SurfaceSequence& sequence) {
frame_proxy_in_parent_renderer_->Send(new FrameMsg_SetChildFrameSurface(
frame_proxy_in_parent_renderer_->GetRoutingID(), surface_id, frame_size,
scale_factor, sequence));
}
void CrossProcessFrameConnector::OnSatisfySequence(
const cc::SurfaceSequence& sequence) {
std::vector<uint32_t> sequences;
sequences.push_back(sequence.sequence);
cc::SurfaceManager* manager = GetSurfaceManager();
manager->DidSatisfySequences(sequence.frame_sink_id, &sequences);
}
void CrossProcessFrameConnector::OnRequireSequence(
const cc::SurfaceId& id,
const cc::SurfaceSequence& sequence) {
cc::SurfaceManager* manager = GetSurfaceManager();
cc::Surface* surface = manager->GetSurfaceForId(id);
if (!surface) {
LOG(ERROR) << "Attempting to require callback on nonexistent surface";
return;
}
surface->AddDestructionDependency(sequence);
}
gfx::Rect CrossProcessFrameConnector::ChildFrameRect() {
return child_frame_rect_;
}
void CrossProcessFrameConnector::UpdateCursor(const WebCursor& cursor) {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (root_view)
root_view->UpdateCursor(cursor);
}
gfx::Point CrossProcessFrameConnector::TransformPointToRootCoordSpace(
const gfx::Point& point,
const cc::SurfaceId& surface_id) {
gfx::Point transformed_point;
TransformPointToCoordSpaceForView(point, GetRootRenderWidgetHostView(),
surface_id, &transformed_point);
return transformed_point;
}
bool CrossProcessFrameConnector::TransformPointToLocalCoordSpace(
const gfx::Point& point,
const cc::SurfaceId& original_surface,
const cc::SurfaceId& local_surface_id,
gfx::Point* transformed_point) {
if (original_surface == local_surface_id) {
*transformed_point = point;
return true;
}
// Transformations use physical pixels rather than DIP, so conversion
// is necessary.
*transformed_point =
gfx::ConvertPointToPixel(view_->current_surface_scale_factor(), point);
cc::SurfaceHittest hittest(nullptr, GetSurfaceManager());
if (!hittest.TransformPointToTargetSurface(original_surface, local_surface_id,
transformed_point))
return false;
*transformed_point = gfx::ConvertPointToDIP(
view_->current_surface_scale_factor(), *transformed_point);
return true;
}
bool CrossProcessFrameConnector::TransformPointToCoordSpaceForView(
const gfx::Point& point,
RenderWidgetHostViewBase* target_view,
const cc::SurfaceId& local_surface_id,
gfx::Point* transformed_point) {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (!root_view)
return false;
// It is possible that neither the original surface or target surface is an
// ancestor of the other in the RenderWidgetHostView tree (e.g. they could
// be siblings). To account for this, the point is first transformed into the
// root coordinate space and then the root is asked to perform the conversion.
if (!root_view->TransformPointToLocalCoordSpace(point, local_surface_id,
transformed_point))
return false;
if (target_view == root_view)
return true;
return root_view->TransformPointToCoordSpaceForView(
*transformed_point, target_view, transformed_point);
}
void CrossProcessFrameConnector::ForwardProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch,
InputEventAckState ack_result) {
auto* main_view = GetRootRenderWidgetHostView();
if (main_view)
main_view->ProcessAckedTouchEvent(touch, ack_result);
}
void CrossProcessFrameConnector::BubbleScrollEvent(
const blink::WebGestureEvent& event) {
DCHECK(event.type == blink::WebInputEvent::GestureScrollUpdate ||
event.type == blink::WebInputEvent::GestureScrollEnd);
auto* parent_view = GetParentRenderWidgetHostView();
if (!parent_view)
return;
auto* event_router =
RenderWidgetHostImpl::From(parent_view->GetRenderWidgetHost())
->delegate()
->GetInputEventRouter();
gfx::Vector2d offset_from_parent = child_frame_rect_.OffsetFromOrigin();
blink::WebGestureEvent resent_gesture_event(event);
// TODO(kenrb, wjmaclean): Do we need to account for transforms here?
// See https://crbug.com/626020.
resent_gesture_event.x += offset_from_parent.x();
resent_gesture_event.y += offset_from_parent.y();
if (event.type == blink::WebInputEvent::GestureScrollUpdate) {
event_router->BubbleScrollEvent(parent_view, resent_gesture_event);
is_scroll_bubbling_ = true;
} else if (event.type == blink::WebInputEvent::GestureScrollEnd &&
is_scroll_bubbling_) {
event_router->BubbleScrollEvent(parent_view, resent_gesture_event);
is_scroll_bubbling_ = false;
}
}
bool CrossProcessFrameConnector::HasFocus() {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (root_view)
return root_view->HasFocus();
return false;
}
void CrossProcessFrameConnector::FocusRootView() {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (root_view)
root_view->Focus();
}
bool CrossProcessFrameConnector::LockMouse() {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (root_view)
return root_view->LockMouse();
return false;
}
void CrossProcessFrameConnector::UnlockMouse() {
RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
if (root_view)
root_view->UnlockMouse();
}
void CrossProcessFrameConnector::OnForwardInputEvent(
const blink::WebInputEvent* event) {
if (!view_)
return;
RenderFrameHostManager* manager =
frame_proxy_in_parent_renderer_->frame_tree_node()->render_manager();
RenderWidgetHostImpl* parent_widget =
manager->ForInnerDelegate()
? manager->GetOuterRenderWidgetHostForKeyboardInput()
: frame_proxy_in_parent_renderer_->GetRenderViewHost()->GetWidget();
// TODO(wjmaclean): We should remove these forwarding functions, since they
// are directly target using RenderWidgetHostInputEventRouter. But neither
// pathway is currently handling gesture events, so that needs to be fixed
// in a subsequent CL.
if (blink::WebInputEvent::isKeyboardEventType(event->type)) {
if (!parent_widget->GetLastKeyboardEvent())
return;
NativeWebKeyboardEvent keyboard_event(
*parent_widget->GetLastKeyboardEvent());
view_->ProcessKeyboardEvent(keyboard_event);
return;
}
if (blink::WebInputEvent::isMouseEventType(event->type)) {
// TODO(wjmaclean): Initialize latency info correctly for OOPIFs.
// https://crbug.com/613628
ui::LatencyInfo latency_info;
view_->ProcessMouseEvent(*static_cast<const blink::WebMouseEvent*>(event),
latency_info);
return;
}
if (event->type == blink::WebInputEvent::MouseWheel) {
// TODO(wjmaclean): Initialize latency info correctly for OOPIFs.
// https://crbug.com/613628
ui::LatencyInfo latency_info;
view_->ProcessMouseWheelEvent(
*static_cast<const blink::WebMouseWheelEvent*>(event), latency_info);
return;
}
}
void CrossProcessFrameConnector::OnFrameRectChanged(
const gfx::Rect& frame_rect) {
if (!frame_rect.size().IsEmpty())
SetRect(frame_rect);
}
void CrossProcessFrameConnector::OnVisibilityChanged(bool visible) {
if (!view_)
return;
// If there is an inner WebContents, it should be notified of the change in
// the visibility. The Show/Hide methods will not be called if an inner
// WebContents exists since the corresponding WebContents will itself call
// Show/Hide on all the RenderWidgetHostViews (including this) one.
if (frame_proxy_in_parent_renderer_->frame_tree_node()
->render_manager()
->ForInnerDelegate()) {
RenderWidgetHostImpl::From(view_->GetRenderWidgetHost())
->delegate()
->OnRenderFrameProxyVisibilityChanged(visible);
return;
}
if (visible &&
!RenderWidgetHostImpl::From(view_->GetRenderWidgetHost())
->delegate()
->IsHidden()) {
view_->Show();
} else if (!visible) {
view_->Hide();
}
}
void CrossProcessFrameConnector::SetRect(const gfx::Rect& frame_rect) {
gfx::Rect old_rect = child_frame_rect_;
child_frame_rect_ = frame_rect;
if (view_) {
view_->SetBounds(frame_rect);
// Other local root frames nested underneath this one implicitly have their
// view rects changed when their ancestor is repositioned, and therefore
// need to have their screen rects updated.
FrameTreeNode* proxy_node =
frame_proxy_in_parent_renderer_->frame_tree_node();
if (old_rect.x() != child_frame_rect_.x() ||
old_rect.y() != child_frame_rect_.y()) {
for (FrameTreeNode* node :
proxy_node->frame_tree()->SubtreeNodes(proxy_node)) {
if (node != proxy_node && node->current_frame_host()->is_local_root())
node->current_frame_host()->GetRenderWidgetHost()->SendScreenRects();
}
}
}
}
RenderWidgetHostViewBase*
CrossProcessFrameConnector::GetRootRenderWidgetHostView() {
RenderFrameHostImpl* top_host = frame_proxy_in_parent_renderer_->
frame_tree_node()->frame_tree()->root()->current_frame_host();
// This method should return the root RWHV from the top-level WebContents,
// in the case of nested WebContents.
while (top_host->frame_tree_node()->render_manager()->ForInnerDelegate()) {
top_host = top_host->frame_tree_node()->render_manager()->
GetOuterDelegateNode()->frame_tree()->root()->current_frame_host();
}
return static_cast<RenderWidgetHostViewBase*>(top_host->GetView());
}
RenderWidgetHostViewBase*
CrossProcessFrameConnector::GetParentRenderWidgetHostView() {
FrameTreeNode* parent =
frame_proxy_in_parent_renderer_->frame_tree_node()->parent();
if (!parent &&
frame_proxy_in_parent_renderer_->frame_tree_node()
->render_manager()
->GetOuterDelegateNode()) {
parent = frame_proxy_in_parent_renderer_->frame_tree_node()
->render_manager()
->GetOuterDelegateNode()
->parent();
}
if (parent) {
return static_cast<RenderWidgetHostViewBase*>(
parent->current_frame_host()->GetView());
}
return nullptr;
}
} // namespace content