blob: 1202e49da23f071ea405bca724cdc4de31c2b321 [file] [log] [blame]
// Copyright 2013 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/render_frame_message_filter.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/syslog_logging.h"
#include "base/task/post_task.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/download/public/common/download_url_parameters.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/ipc_utils.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/content_constants_internal.h"
#include "content/common/frame_messages.h"
#include "content/common/frame_owner_properties.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_features.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
#if !defined(OS_MACOSX)
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#endif
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/browser/plugin_service_impl.h"
#include "content/browser/ppapi_plugin_process_host.h"
#include "content/public/browser/plugin_service_filter.h"
#endif
namespace content {
namespace {
void CreateChildFrameOnUI(
int process_id,
int parent_routing_id,
blink::WebTreeScopeType scope,
const std::string& frame_name,
const std::string& frame_unique_name,
bool is_created_by_script,
const base::UnguessableToken& devtools_frame_token,
const blink::FramePolicy& frame_policy,
const FrameOwnerProperties& frame_owner_properties,
blink::FrameOwnerElementType owner_type,
int new_routing_id,
mojo::ScopedMessagePipeHandle interface_provider_request_handle,
mojo::ScopedMessagePipeHandle document_interface_broker_content_handle,
mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle,
mojo::ScopedMessagePipeHandle browser_interface_broker_handle) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderFrameHostImpl* render_frame_host =
RenderFrameHostImpl::FromID(process_id, parent_routing_id);
// Handles the RenderFrameHost being deleted on the UI thread while
// processing a subframe creation message.
if (render_frame_host) {
render_frame_host->OnCreateChildFrame(
new_routing_id,
service_manager::mojom::InterfaceProviderRequest(
std::move(interface_provider_request_handle)),
mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
std::move(document_interface_broker_content_handle)),
mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
std::move(document_interface_broker_blink_handle)),
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>(
std::move(browser_interface_broker_handle)),
scope, frame_name, frame_unique_name, is_created_by_script,
devtools_frame_token, frame_policy, frame_owner_properties, owner_type);
}
}
// |blob_data_handle| is only here for the legacy code path. With network
// service enabled |blob_url_token| should be provided and will be used instead
// to download the correct blob.
void DownloadUrlOnUIThread(
std::unique_ptr<download::DownloadUrlParameters> parameters,
std::unique_ptr<storage::BlobDataHandle> blob_data_handle,
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* render_process_host =
RenderProcessHost::FromID(parameters->render_process_host_id());
if (!render_process_host)
return;
BrowserContext* browser_context = render_process_host->GetBrowserContext();
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
if (blob_url_token) {
blob_url_loader_factory =
ChromeBlobStorageContext::URLLoaderFactoryForToken(
browser_context, std::move(blob_url_token));
}
DownloadManager* download_manager =
BrowserContext::GetDownloadManager(browser_context);
parameters->set_download_source(download::DownloadSource::FROM_RENDERER);
download_manager->DownloadUrl(std::move(parameters),
std::move(blob_data_handle),
std::move(blob_url_loader_factory));
}
// Common functionality for converting a sync renderer message to a callback
// function in the browser. Derive from this, create it on the heap when
// issuing your callback. When done, write your reply parameters into
// reply_msg(), and then call SendReplyAndDeleteThis().
class RenderMessageCompletionCallback {
public:
RenderMessageCompletionCallback(RenderFrameMessageFilter* filter,
IPC::Message* reply_msg)
: filter_(filter),
reply_msg_(reply_msg) {
}
virtual ~RenderMessageCompletionCallback() {
if (reply_msg_) {
// If the owner of this class failed to call SendReplyAndDeleteThis(),
// send an error reply to prevent the renderer from being hung.
reply_msg_->set_reply_error();
filter_->Send(reply_msg_);
}
}
RenderFrameMessageFilter* filter() { return filter_.get(); }
IPC::Message* reply_msg() { return reply_msg_; }
void SendReplyAndDeleteThis() {
filter_->Send(reply_msg_);
reply_msg_ = nullptr;
delete this;
}
private:
scoped_refptr<RenderFrameMessageFilter> filter_;
IPC::Message* reply_msg_;
};
} // namespace
#if BUILDFLAG(ENABLE_PLUGINS)
class RenderFrameMessageFilter::OpenChannelToPpapiBrokerCallback
: public PpapiPluginProcessHost::BrokerClient {
public:
OpenChannelToPpapiBrokerCallback(RenderFrameMessageFilter* filter,
int routing_id)
: filter_(filter),
routing_id_(routing_id) {
}
~OpenChannelToPpapiBrokerCallback() override {}
void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
int* renderer_id) override {
// base::kNullProcessHandle indicates that the channel will be used by the
// browser itself. Make sure we never output that value here.
CHECK_NE(base::kNullProcessHandle, filter_->PeerHandle());
*renderer_handle = filter_->PeerHandle();
*renderer_id = filter_->render_process_id_;
}
void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
base::ProcessId plugin_pid,
int /* plugin_child_id */) override {
filter_->Send(new ViewMsg_PpapiBrokerChannelCreated(routing_id_,
plugin_pid,
channel_handle));
delete this;
}
bool Incognito() override { return filter_->incognito_; }
private:
scoped_refptr<RenderFrameMessageFilter> filter_;
int routing_id_;
};
class RenderFrameMessageFilter::OpenChannelToPpapiPluginCallback
: public RenderMessageCompletionCallback,
public PpapiPluginProcessHost::PluginClient {
public:
OpenChannelToPpapiPluginCallback(RenderFrameMessageFilter* filter,
IPC::Message* reply_msg)
: RenderMessageCompletionCallback(filter, reply_msg) {}
void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
int* renderer_id) override {
// base::kNullProcessHandle indicates that the channel will be used by the
// browser itself. Make sure we never output that value here.
CHECK_NE(base::kNullProcessHandle, filter()->PeerHandle());
*renderer_handle = filter()->PeerHandle();
*renderer_id = filter()->render_process_id_;
}
void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
base::ProcessId plugin_pid,
int plugin_child_id) override {
FrameHostMsg_OpenChannelToPepperPlugin::WriteReplyParams(
reply_msg(), channel_handle, plugin_pid, plugin_child_id);
SendReplyAndDeleteThis();
}
bool Incognito() override { return filter()->incognito_; }
};
#endif // ENABLE_PLUGINS
RenderFrameMessageFilter::RenderFrameMessageFilter(
int render_process_id,
PluginServiceImpl* plugin_service,
BrowserContext* browser_context,
StoragePartition* storage_partition,
RenderWidgetHelper* render_widget_helper)
: BrowserMessageFilter(FrameMsgStart),
#if BUILDFLAG(ENABLE_PLUGINS)
plugin_service_(plugin_service),
profile_data_directory_(storage_partition->GetPath()),
#endif // ENABLE_PLUGINS
resource_context_(browser_context->GetResourceContext()),
render_widget_helper_(render_widget_helper),
incognito_(browser_context->IsOffTheRecord()),
render_process_id_(render_process_id) {
}
RenderFrameMessageFilter::~RenderFrameMessageFilter() {
// This function should be called on the IO thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void RenderFrameMessageFilter::ClearResourceContext() {
resource_context_ = nullptr;
}
bool RenderFrameMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderFrameMessageFilter, message)
IPC_MESSAGE_HANDLER(FrameHostMsg_CreateChildFrame, OnCreateChildFrame)
IPC_MESSAGE_HANDLER(FrameHostMsg_DownloadUrl, OnDownloadUrl)
IPC_MESSAGE_HANDLER(FrameHostMsg_SaveImageFromDataURL,
OnSaveImageFromDataURL)
IPC_MESSAGE_HANDLER(FrameHostMsg_Are3DAPIsBlocked, OnAre3DAPIsBlocked)
#if BUILDFLAG(ENABLE_PLUGINS)
IPC_MESSAGE_HANDLER(FrameHostMsg_GetPluginInfo, OnGetPluginInfo)
IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_OpenChannelToPepperPlugin,
OnOpenChannelToPepperPlugin)
IPC_MESSAGE_HANDLER(FrameHostMsg_DidCreateOutOfProcessPepperInstance,
OnDidCreateOutOfProcessPepperInstance)
IPC_MESSAGE_HANDLER(FrameHostMsg_DidDeleteOutOfProcessPepperInstance,
OnDidDeleteOutOfProcessPepperInstance)
IPC_MESSAGE_HANDLER(FrameHostMsg_OpenChannelToPpapiBroker,
OnOpenChannelToPpapiBroker)
IPC_MESSAGE_HANDLER(FrameHostMsg_PluginInstanceThrottleStateChange,
OnPluginInstanceThrottleStateChange)
#endif // ENABLE_PLUGINS
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void RenderFrameMessageFilter::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
void RenderFrameMessageFilter::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
#if BUILDFLAG(ENABLE_PLUGINS)
if (message.type() == FrameHostMsg_GetPluginInfo::ID)
*thread = BrowserThread::UI;
#endif // ENABLE_PLUGINS
}
void RenderFrameMessageFilter::DownloadUrl(
int render_view_id,
int render_frame_id,
const GURL& url,
const Referrer& referrer,
const url::Origin& initiator,
const base::string16& suggested_name,
const bool use_prompt,
const bool follow_cross_origin_redirects,
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token) const {
if (!resource_context_)
return;
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("renderer_initiated_download", R"(
semantics {
sender: "Download from Renderer"
description:
"The frame has either navigated to a URL that was determined to be "
"a download via one of the renderer's classification mechanisms, "
"or WebView has requested a <canvas> or <img> element at a "
"specific location be to downloaded."
trigger:
"The user navigated to a destination that was categorized as a "
"download, or WebView triggered saving a <canvas> or <img> tag."
data: "Only the URL we are attempting to download."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting: "This feature cannot be disabled by settings."
chrome_policy {
DownloadRestrictions {
DownloadRestrictions: 3
}
}
})");
std::unique_ptr<download::DownloadUrlParameters> parameters(
new download::DownloadUrlParameters(url, render_process_id_,
render_view_id, render_frame_id,
traffic_annotation));
parameters->set_content_initiated(true);
parameters->set_suggested_name(suggested_name);
parameters->set_prompt(use_prompt);
parameters->set_follow_cross_origin_redirects(follow_cross_origin_redirects);
parameters->set_referrer(referrer.url);
parameters->set_referrer_policy(
Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
parameters->set_initiator(initiator);
// If network service is enabled we should always have a |blob_url_token|,
// which will be used to download the correct blob. But in the legacy
// non-network service code path we still need to look up the BlobDataHandle
// for the URL here, to make sure the correct blob ends up getting downloaded.
std::unique_ptr<storage::BlobDataHandle> blob_data_handle;
if (url.SchemeIsBlob()) {
ChromeBlobStorageContext* blob_context =
GetChromeBlobStorageContextForResourceContext(resource_context_);
blob_data_handle = blob_context->context()->GetBlobDataFromPublicURL(url);
// Don't care if the above fails. We are going to let the download go
// through and allow it to be interrupted so that the embedder can deal.
}
base::PostTask(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&DownloadUrlOnUIThread, std::move(parameters),
std::move(blob_data_handle), std::move(blob_url_token)));
}
void RenderFrameMessageFilter::OnCreateChildFrame(
const FrameHostMsg_CreateChildFrame_Params& params,
FrameHostMsg_CreateChildFrame_Params_Reply* params_reply) {
params_reply->child_routing_id = render_widget_helper_->GetNextRoutingID();
service_manager::mojom::InterfaceProviderPtr interface_provider;
auto interface_provider_request(mojo::MakeRequest(&interface_provider));
params_reply->new_interface_provider =
interface_provider.PassInterface().PassHandle().release();
mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
document_interface_broker_content;
auto document_interface_broker_receiver_content =
document_interface_broker_content.InitWithNewPipeAndPassReceiver();
params_reply->document_interface_broker_content_handle =
document_interface_broker_content.PassPipe().release();
mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
document_interface_broker_blink;
auto document_interface_broker_receiver_blink =
document_interface_broker_blink.InitWithNewPipeAndPassReceiver();
params_reply->document_interface_broker_blink_handle =
document_interface_broker_blink.PassPipe().release();
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker;
auto browser_interface_broker_receiver =
browser_interface_broker.InitWithNewPipeAndPassReceiver();
params_reply->browser_interface_broker_handle =
browser_interface_broker.PassPipe().release();
params_reply->devtools_frame_token = base::UnguessableToken::Create();
base::PostTask(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(
&CreateChildFrameOnUI, render_process_id_, params.parent_routing_id,
params.scope, params.frame_name, params.frame_unique_name,
params.is_created_by_script, params_reply->devtools_frame_token,
params.frame_policy, params.frame_owner_properties,
params.frame_owner_element_type, params_reply->child_routing_id,
interface_provider_request.PassMessagePipe(),
document_interface_broker_receiver_content.PassPipe(),
document_interface_broker_receiver_blink.PassPipe(),
browser_interface_broker_receiver.PassPipe()));
}
void RenderFrameMessageFilter::OnDownloadUrl(
const FrameHostMsg_DownloadUrl_Params& params) {
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token;
if (!VerifyDownloadUrlParams(render_process_id_, params, &blob_url_token))
return;
DownloadUrl(params.render_view_id, params.render_frame_id, params.url,
params.referrer, params.initiator_origin, params.suggested_name,
false, params.follow_cross_origin_redirects,
std::move(blob_url_token));
}
void RenderFrameMessageFilter::OnSaveImageFromDataURL(
int render_view_id,
int render_frame_id,
const std::string& url_str) {
// Please refer to RenderFrameImpl::saveImageFromDataURL().
if (url_str.length() >= kMaxLengthOfDataURLString)
return;
GURL data_url(url_str);
if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme))
return;
DownloadUrl(render_view_id, render_frame_id, data_url, Referrer(),
url::Origin(), base::string16(), true, true, mojo::NullRemote());
}
void RenderFrameMessageFilter::OnAre3DAPIsBlocked(int render_frame_id,
const GURL& top_origin_url,
ThreeDAPIType requester,
bool* blocked) {
*blocked = GpuDataManagerImpl::GetInstance()->Are3DAPIsBlocked(
top_origin_url, render_process_id_, render_frame_id, requester);
}
#if BUILDFLAG(ENABLE_PLUGINS)
void RenderFrameMessageFilter::OnGetPluginInfo(
int render_frame_id,
const GURL& url,
const url::Origin& main_frame_origin,
const std::string& mime_type,
bool* found,
WebPluginInfo* info,
std::string* actual_mime_type) {
bool allow_wildcard = true;
*found = plugin_service_->GetPluginInfo(
render_process_id_, render_frame_id, url, main_frame_origin, mime_type,
allow_wildcard, nullptr, info, actual_mime_type);
}
void RenderFrameMessageFilter::OnOpenChannelToPepperPlugin(
const base::FilePath& path,
const base::Optional<url::Origin>& origin_lock,
IPC::Message* reply_msg) {
plugin_service_->OpenChannelToPpapiPlugin(
render_process_id_, path, profile_data_directory_, origin_lock,
new OpenChannelToPpapiPluginCallback(this, reply_msg));
}
void RenderFrameMessageFilter::OnDidCreateOutOfProcessPepperInstance(
int plugin_child_id,
int32_t pp_instance,
PepperRendererInstanceData instance_data,
bool is_external) {
// It's important that we supply the render process ID ourselves based on the
// channel the message arrived on. We use the
// PP_Instance -> (process id, frame id)
// mapping to decide how to handle messages received from the (untrusted)
// plugin, so an exploited renderer must not be able to insert fake mappings
// that may allow it access to other render processes.
DCHECK_EQ(0, instance_data.render_process_id);
instance_data.render_process_id = render_process_id_;
if (is_external) {
// We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
GetContentClient()->browser()->GetExternalBrowserPpapiHost(
plugin_child_id));
if (host)
host->AddInstance(pp_instance, instance_data);
} else {
PpapiPluginProcessHost::DidCreateOutOfProcessInstance(
plugin_child_id, pp_instance, instance_data);
}
}
void RenderFrameMessageFilter::OnDidDeleteOutOfProcessPepperInstance(
int plugin_child_id,
int32_t pp_instance,
bool is_external) {
if (is_external) {
// We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
GetContentClient()->browser()->GetExternalBrowserPpapiHost(
plugin_child_id));
if (host)
host->DeleteInstance(pp_instance);
} else {
PpapiPluginProcessHost::DidDeleteOutOfProcessInstance(
plugin_child_id, pp_instance);
}
}
void RenderFrameMessageFilter::OnOpenChannelToPpapiBroker(
int routing_id,
const base::FilePath& path) {
plugin_service_->OpenChannelToPpapiBroker(
render_process_id_, routing_id, path,
new OpenChannelToPpapiBrokerCallback(this, routing_id));
}
void RenderFrameMessageFilter::OnPluginInstanceThrottleStateChange(
int plugin_child_id,
int32_t pp_instance,
bool is_throttled) {
// Feature is only implemented for non-external Plugins.
PpapiPluginProcessHost::OnPluginInstanceThrottleStateChange(
plugin_child_id, pp_instance, is_throttled);
}
#endif // ENABLE_PLUGINS
} // namespace content