[go: nahoru, domu]

blob: 4e8bc7661bf0d51f54f4c0f00dfc839e6d6ea658 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/agent_scheduling_group.h"
#include <map>
#include <utility>
#include "base/containers/map_util.h"
#include "base/feature_list.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_bound.h"
#include "base/types/pass_key.h"
#include "content/common/agent_scheduling_group.mojom.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/renderer/render_frame_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "ipc/ipc_channel_mojo.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sync_channel.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/page/browsing_context_group_info.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/page/page.mojom.h"
#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom.h"
#include "third_party/blink/public/mojom/worker/worklet_global_scope_creation_params.mojom.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/web/web_remote_frame.h"
#include "third_party/blink/public/web/web_shared_storage_worklet_thread.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_view_client.h"
namespace content {
using ::IPC::ChannelMojo;
using ::IPC::Listener;
using ::IPC::SyncChannel;
using ::mojo::AssociatedReceiver;
using ::mojo::AssociatedRemote;
using ::mojo::PendingAssociatedReceiver;
using ::mojo::PendingAssociatedRemote;
using ::mojo::PendingReceiver;
using ::mojo::PendingRemote;
using ::mojo::Receiver;
using ::mojo::Remote;
using PassKey = ::base::PassKey<AgentSchedulingGroup>;
namespace {
RenderThreadImpl& ToImpl(RenderThread& render_thread) {
DCHECK(RenderThreadImpl::current());
return static_cast<RenderThreadImpl&>(render_thread);
}
static features::MBIMode GetMBIMode() {
return base::FeatureList::IsEnabled(features::kMBIMode)
? features::kMBIModeParam.Get()
: features::MBIMode::kLegacy;
}
// Creates a main WebRemoteFrame for `web_view`.
void CreateRemoteMainFrame(
const blink::RemoteFrameToken& frame_token,
mojo::PendingAssociatedRemote<blink::mojom::RemoteFrameHost>
remote_frame_host,
mojo::PendingAssociatedReceiver<blink::mojom::RemoteFrame>
remote_frame_receiver,
mojo::PendingAssociatedRemote<blink::mojom::RemoteMainFrameHost>
remote_main_frame_host,
mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrame>
remote_main_frame_receiver,
base::UnguessableToken& devtools_main_frame_token,
blink::mojom::FrameReplicationStatePtr replication_state,
blink::WebFrame* opener_frame,
blink::WebView* web_view) {
blink::WebRemoteFrame::CreateMainFrame(
web_view, frame_token, /*is_loading=*/false, devtools_main_frame_token,
opener_frame, std::move(remote_frame_host),
std::move(remote_frame_receiver), std::move(replication_state));
// Root frame proxy has no ancestors to point to their RenderWidget.
// The WebRemoteFrame created here was already attached to the Page as its
// main frame, so we can call WebView's DidAttachRemoteMainFrame().
web_view->DidAttachRemoteMainFrame(std::move(remote_main_frame_host),
std::move(remote_main_frame_receiver));
}
// Blink inappropriately makes decisions if there is a WebViewClient set,
// so currently we need to always create a WebViewClient.
class SelfOwnedWebViewClient : public blink::WebViewClient {
public:
void OnDestruct() override { delete this; }
};
} // namespace
AgentSchedulingGroup::ReceiverData::ReceiverData(
const std::string& name,
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface> receiver)
: name(std::move(name)), receiver(std::move(receiver)) {}
AgentSchedulingGroup::ReceiverData::ReceiverData(ReceiverData&& other)
: name(std::move(other.name)), receiver(std::move(other.receiver)) {}
AgentSchedulingGroup::ReceiverData::~ReceiverData() = default;
// AgentSchedulingGroup:
AgentSchedulingGroup::AgentSchedulingGroup(
RenderThread& render_thread,
mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> bootstrap,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker_remote)
: agent_group_scheduler_(
blink::scheduler::WebThreadScheduler::MainThreadScheduler()
.CreateWebAgentGroupScheduler()),
render_thread_(render_thread),
// `receiver_` will be bound by `OnAssociatedInterfaceRequest()`.
receiver_(this) {
DCHECK(agent_group_scheduler_);
DCHECK_NE(GetMBIMode(), features::MBIMode::kLegacy);
agent_group_scheduler_->BindInterfaceBroker(std::move(broker_remote));
channel_ = SyncChannel::Create(
/*listener=*/this, /*ipc_task_runner=*/render_thread_->GetIOTaskRunner(),
/*listener_task_runner=*/agent_group_scheduler_->DefaultTaskRunner(),
render_thread_->GetShutdownEvent());
// TODO(crbug.com/1111231): Add necessary filters.
// Currently, the renderer process has these filters:
// 1. `UnfreezableMessageFilter` - in the process of being removed,
// 2. `PnaclTranslationResourceHost` - NaCl is going away, and
// 3. `AutomationMessageFilter` - needs to be handled somehow.
channel_->Init(
ChannelMojo::CreateClientFactory(
bootstrap.PassPipe(),
/*ipc_task_runner=*/render_thread_->GetIOTaskRunner(),
/*proxy_task_runner=*/agent_group_scheduler_->DefaultTaskRunner()),
/*create_pipe_now=*/true);
}
AgentSchedulingGroup::AgentSchedulingGroup(
RenderThread& render_thread,
PendingAssociatedReceiver<mojom::AgentSchedulingGroup> receiver,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker_remote)
: agent_group_scheduler_(
blink::scheduler::WebThreadScheduler::MainThreadScheduler()
.CreateWebAgentGroupScheduler()),
render_thread_(render_thread),
receiver_(this,
std::move(receiver),
agent_group_scheduler_->DefaultTaskRunner()) {
DCHECK(agent_group_scheduler_);
DCHECK_EQ(GetMBIMode(), features::MBIMode::kLegacy);
agent_group_scheduler_->BindInterfaceBroker(std::move(broker_remote));
}
AgentSchedulingGroup::~AgentSchedulingGroup() = default;
bool AgentSchedulingGroup::OnMessageReceived(const IPC::Message& message) {
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
DCHECK_NE(message.routing_id(), MSG_ROUTING_CONTROL);
auto* listener = GetListener(message.routing_id());
if (!listener)
return false;
return listener->OnMessageReceived(message);
#else
return false;
#endif
}
void AgentSchedulingGroup::OnBadMessageReceived(const IPC::Message& message) {
// Not strictly required, since we don't currently do anything with bad
// messages in the renderer, but if we ever do then this will "just work".
return ToImpl(*render_thread_).OnBadMessageReceived(message);
}
void AgentSchedulingGroup::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
// The ASG's channel should only be used to bootstrap the ASG mojo interface.
DCHECK_EQ(interface_name, mojom::AgentSchedulingGroup::Name_);
DCHECK(!receiver_.is_bound());
PendingAssociatedReceiver<mojom::AgentSchedulingGroup> pending_receiver(
std::move(handle));
receiver_.Bind(std::move(pending_receiver),
agent_group_scheduler_->DefaultTaskRunner());
}
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
bool AgentSchedulingGroup::Send(IPC::Message* message) {
std::unique_ptr<IPC::Message> msg(message);
if (GetMBIMode() == features::MBIMode::kLegacy)
return render_thread_->Send(msg.release());
// This DCHECK is too idealistic for now - messages that are handled by
// filters are sent control messages since they are intercepted before
// routing. It is put here as documentation for now, since this code would not
// be reached until we activate
// `features::MBIMode::kEnabledPerRenderProcessHost` or
// `features::MBIMode::kEnabledPerSiteInstance`.
DCHECK_NE(message->routing_id(), MSG_ROUTING_CONTROL);
DCHECK(channel_);
return channel_->Send(msg.release());
}
#endif
void AgentSchedulingGroup::AddFrameRoute(
const blink::LocalFrameToken& frame_token,
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
int routing_id,
#endif
RenderFrameImpl* render_frame,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(!base::Contains(listener_map_, frame_token));
listener_map_.insert({frame_token, render_frame});
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
DCHECK(!base::Contains(routing_id_map_, routing_id));
routing_id_map_.insert({routing_id, render_frame});
render_thread_->AddRoute(routing_id, render_frame);
#endif
// See warning in `GetAssociatedInterface`.
// Replay any `GetAssociatedInterface` calls for this route.
auto range = pending_receivers_.equal_range(frame_token);
for (auto iter = range.first; iter != range.second; ++iter) {
ReceiverData& data = iter->second;
render_frame->OnAssociatedInterfaceRequest(data.name,
data.receiver.PassHandle());
}
pending_receivers_.erase(range.first, range.second);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
render_thread_->AttachTaskRunnerToRoute(routing_id, std::move(task_runner));
#endif
}
void AgentSchedulingGroup::RemoveFrameRoute(
const blink::LocalFrameToken& frame_token
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
,
int routing_id
#endif
) {
DCHECK(base::Contains(listener_map_, frame_token));
listener_map_.erase(frame_token);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
DCHECK(base::Contains(routing_id_map_, routing_id));
routing_id_map_.erase(routing_id);
render_thread_->RemoveRoute(routing_id);
#endif
}
void AgentSchedulingGroup::DidUnloadRenderFrame(
const blink::LocalFrameToken& frame_token) {
host_remote_->DidUnloadRenderFrame(frame_token);
}
void AgentSchedulingGroup::CreateView(mojom::CreateViewParamsPtr params) {
RenderThreadImpl& renderer = ToImpl(*render_thread_);
renderer.SetScrollAnimatorEnabled(
params->web_preferences.enable_scroll_animator, PassKey());
CreateWebView(std::move(params),
/*was_created_by_renderer=*/false,
/*base_url=*/blink::WebURL());
}
blink::WebView* AgentSchedulingGroup::CreateWebView(
mojom::CreateViewParamsPtr params,
bool was_created_by_renderer,
const blink::WebURL& base_url) {
DCHECK(RenderThread::IsMainThread());
blink::WebFrame* opener_frame = nullptr;
if (params->opener_frame_token)
opener_frame =
blink::WebFrame::FromFrameToken(params->opener_frame_token.value());
blink::WebView* web_view = blink::WebView::Create(
new SelfOwnedWebViewClient(), params->hidden, params->is_prerendering,
/*is_inside_portal=*/false,
params->type == mojom::ViewWidgetType::kFencedFrame
? std::make_optional(params->fenced_frame_mode)
: std::nullopt,
/*compositing_enabled=*/true, params->never_composited,
opener_frame ? opener_frame->View() : nullptr,
std::move(params->blink_page_broadcast), agent_group_scheduler(),
params->session_storage_namespace_id, params->base_background_color,
params->browsing_context_group_info, &params->color_provider_colors);
bool local_main_frame = params->main_frame->is_local_params();
web_view->SetRendererPreferences(params->renderer_preferences);
web_view->SetWebPreferences(params->web_preferences);
web_view->SetPageAttributionSupport(params->attribution_support);
const bool is_for_nested_main_frame =
params->type != mojom::ViewWidgetType::kTopLevel;
if (!local_main_frame) {
// Create a remote main frame.
auto remote_params = std::move(params->main_frame->get_remote_params());
CreateRemoteMainFrame(
remote_params->token,
std::move(remote_params->frame_interfaces->frame_host),
std::move(remote_params->frame_interfaces->frame_receiver),
std::move(remote_params->main_frame_interfaces->main_frame_host),
std::move(remote_params->main_frame_interfaces->main_frame),
params->devtools_main_frame_token, std::move(params->replication_state),
opener_frame, web_view);
} else {
auto local_params = std::move(params->main_frame->get_local_params());
if (!local_params->previous_frame_token) {
// Create a local non-provisional main frame.
RenderFrameImpl::CreateMainFrame(
*this, web_view, opener_frame, is_for_nested_main_frame,
/*is_for_scalable_page=*/params->type !=
mojom::ViewWidgetType::kFencedFrame,
std::move(params->replication_state),
params->devtools_main_frame_token, std::move(local_params), base_url);
} else {
// Create a local provisional main frame and a placeholder RemoteFrame as
// a placeholder main frame for the new WebView. This can only happen for
// provisional frames for main frame navigations that will do a
// LocalFrame <-> LocalFrame swap with the previous main frame, which
// belongs to a different WebView and blink::Page. For other main
// frame navigations, the WebView will be created with a real main
// RemoteFrame, and the provisional frame will be created separately
// through AgentSchedulingGroup::CreateFrame().
//
// The new provisional main frame will use the newly created WebView,
// but will not be attached to the blink::Page associated with the WebView
// yet. Instead, a placeholder main RemoteFrame that is not connected to
// any RenderFrameProxyHost on the browser side will be the placeholder
// main frame for the new WebView's blink::Page. This is needed because
// the WebView needs to have a main frame, but the provisional LocalFrame
// can't be attached to the Page yet (as it is still provisional), so
// the placeholder main RemoteFrame is used instead. We can't create a
// real RemoteFrame, because the navigation is a same-SiteInstanceGroup
// navigation (as the previous Page's LocalFrame is in the same renderer
// process as the new provisional LocalFrame), which means we can't have a
// RenderFrameProxyHost on the browser side for the RemoteFrame to point
// to (because the main frame shouldn't have a proxy for the
// SiteInstanceGroup it's currently on).
//
// The provisional LocalFrame will be appointed as the provisional frame
// for the placeholder RemoteFrame, while also retaining a pointer to the
// previous page's local main frame. When the provisional frame commits,
// both the placeholder main RemoteFrame and the previous page's local
// frame will be swapped out, and the provisional frame will be swapped in
// to become the main frame for the new WebView's blink::Page.
//
// In summary, the steps involved in main frame LocalFrame <-> LocalFrame
// swaps are:
// 1. Create a new WebView with a placeholder main RemoteFrame, and a
// provisional main LocalFrame for the RemoteFrame (see code below).
// 2. Wait for the navigation to either commit or get canceled.
// 2a. If the navigation gets canceled, the provisional main LocalFrame
// will get deleted. Separately, the new WebView will also get deleted,
// which will delete the placeholder main RemoteFrame along with it.
// 2b. If the navigation gets committed:
// - The new WebView will swap out the placeholder main RemoteFrame, and
// swap in the provisional main LocalFrame, and commit the navigation to
// that LocalFrame.
// - The old WebView will swap out its main LocalFrame, and we will swap
// in a newly created placeholder main RemoteFrame, so that the old
// WebView still have a valid main frame.
// Create the placeholder RemoteFrame.
CreateRemoteMainFrame(
blink::RemoteFrameToken(), mojo::NullAssociatedRemote(),
mojo::NullAssociatedReceiver(), mojo::NullAssociatedRemote(),
mojo::NullAssociatedReceiver(), params->devtools_main_frame_token,
params->replication_state.Clone(), opener_frame, web_view);
// Create the provisional main LocalFrame.
RenderFrameImpl::CreateFrame(
*this, local_params->frame_token, local_params->routing_id,
std::move(local_params->frame),
std::move(local_params->interface_broker),
std::move(local_params->associated_interface_provider_remote),
web_view, local_params->previous_frame_token,
params->opener_frame_token,
/*parent_frame_token=*/std::nullopt,
/*previous_sibling_frame_token=*/std::nullopt,
params->devtools_main_frame_token,
blink::mojom::TreeScopeType::kDocument,
std::move(params->replication_state),
std::move(local_params->widget_params),
/*frame_owner_properties=*/nullptr,
local_params->is_on_initial_empty_document,
local_params->document_token,
std::move(local_params->policy_container), is_for_nested_main_frame);
}
}
// TODO(davidben): Move this state from Blink into content.
if (params->window_was_opened_by_another_window)
web_view->SetOpenedByDOM();
GetContentClient()->renderer()->WebViewCreated(
web_view, was_created_by_renderer,
params->outermost_origin ? &params->outermost_origin.value() : nullptr);
return web_view;
}
void AgentSchedulingGroup::CreateFrame(mojom::CreateFrameParamsPtr params) {
RenderFrameImpl::CreateFrame(
*this, params->frame_token, params->routing_id, std::move(params->frame),
std::move(params->interface_broker),
std::move(params->associated_interface_provider_remote),
/*web_view=*/nullptr, params->previous_frame_token,
params->opener_frame_token, params->parent_frame_token,
params->previous_sibling_frame_token, params->devtools_frame_token,
params->tree_scope_type, std::move(params->replication_state),
std::move(params->widget_params),
std::move(params->frame_owner_properties),
params->is_on_initial_empty_document, params->document_token,
std::move(params->policy_container), params->is_for_nested_main_frame);
}
void AgentSchedulingGroup::CreateSharedStorageWorkletService(
mojo::PendingReceiver<blink::mojom::SharedStorageWorkletService> receiver,
blink::mojom::WorkletGlobalScopeCreationParamsPtr
global_scope_creation_params) {
blink::WebSharedStorageWorkletThread::Start(
agent_group_scheduler_->DefaultTaskRunner(), std::move(receiver),
std::move(global_scope_creation_params));
}
void AgentSchedulingGroup::BindAssociatedInterfaces(
mojo::PendingAssociatedRemote<mojom::AgentSchedulingGroupHost> remote_host,
mojo::PendingAssociatedReceiver<mojom::RouteProvider>
route_provider_receiever) {
host_remote_.Bind(std::move(remote_host),
agent_group_scheduler_->DefaultTaskRunner());
route_provider_receiver_.Bind(std::move(route_provider_receiever),
agent_group_scheduler_->DefaultTaskRunner());
}
void AgentSchedulingGroup::GetRoute(
const blink::LocalFrameToken& frame_token,
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
receiver) {
DCHECK(receiver.is_valid());
associated_interface_provider_receivers_.Add(
this, std::move(receiver), frame_token,
agent_group_scheduler_->DefaultTaskRunner());
}
void AgentSchedulingGroup::GetAssociatedInterface(
const std::string& name,
mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface>
receiver) {
const auto& frame_token =
associated_interface_provider_receivers_.current_context();
if (auto* listener = GetListener(frame_token)) {
listener->OnAssociatedInterfaceRequest(name, receiver.PassHandle());
} else {
// THIS IS UNSAFE!
// Associated receivers must be bound immediately or they could drop
// messages. This is needed short term so the browser side Remote isn't
// broken even after the corresponding `AddRoute` happens. Browser should
// avoid calling this before the corresponding `AddRoute`, but this is a
// short term workaround until that happens.
pending_receivers_.emplace(frame_token,
ReceiverData(name, std::move(receiver)));
}
}
RenderFrameImpl* AgentSchedulingGroup::GetListener(
const blink::LocalFrameToken& frame_token) {
return base::FindPtrOrNull(listener_map_, frame_token);
}
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
RenderFrameImpl* AgentSchedulingGroup::GetListener(int32_t routing_id) {
return base::FindPtrOrNull(routing_id_map_, routing_id);
}
#endif
} // namespace content