blob: 528345d5af4c1805f0a816fca38366244844ef02 [file] [log] [blame]
// Copyright (c) 2012 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/public/test/mock_render_process_host.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/process/process_handle.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/token.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/frame_messages.h"
#include "content/common/input_messages.h"
#include "content/common/renderer.mojom.h"
#include "content/public/browser/android/child_process_importance.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/service_manager_connection.h"
#include "content/public/common/service_names.mojom.h"
#include "content/test/not_implemented_network_url_loader_factory.h"
#include "media/media_buildflags.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace content {
MockRenderProcessHost::MockRenderProcessHost(BrowserContext* browser_context)
: bad_msg_count_(0),
factory_(nullptr),
id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
has_connection_(false),
browser_context_(browser_context),
prev_routing_id_(0),
fast_shutdown_started_(false),
deletion_callback_called_(false),
is_for_guests_only_(false),
is_never_suitable_for_reuse_(false),
is_process_backgrounded_(false),
is_unused_(true),
keep_alive_ref_count_(0),
child_identity_(
mojom::kRendererServiceName,
BrowserContext::GetServiceInstanceGroupFor(browser_context),
base::Token::CreateRandom(),
base::Token::CreateRandom()),
url_loader_factory_(nullptr),
weak_ptr_factory_(this) {
// Child process security operations can't be unit tested unless we add
// ourselves as an existing child process.
ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID());
RenderProcessHostImpl::RegisterHost(GetID(), this);
}
MockRenderProcessHost::~MockRenderProcessHost() {
ChildProcessSecurityPolicyImpl::GetInstance()->Remove(GetID());
if (factory_)
factory_->Remove(this);
// In unit tests, Cleanup() might not have been called.
if (!deletion_callback_called_) {
for (auto& observer : observers_)
observer.RenderProcessHostDestroyed(this);
RenderProcessHostImpl::UnregisterHost(GetID());
}
}
void MockRenderProcessHost::SimulateCrash() {
SimulateRenderProcessExit(base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
}
void MockRenderProcessHost::SimulateRenderProcessExit(
base::TerminationStatus status,
int exit_code) {
has_connection_ = false;
ChildProcessTerminationInfo termination_info{status, exit_code};
NotificationService::current()->Notify(
NOTIFICATION_RENDERER_PROCESS_CLOSED, Source<RenderProcessHost>(this),
Details<ChildProcessTerminationInfo>(&termination_info));
for (auto& observer : observers_)
observer.RenderProcessExited(this, termination_info);
// Send every routing ID a FrameHostMsg_RenderProcessGone message. To ensure a
// predictable order for unittests which may assert against the order, we sort
// the listeners by descending routing ID, instead of using the arbitrary
// hash-map order like RenderProcessHostImpl.
std::vector<std::pair<int32_t, IPC::Listener*>> sorted_listeners_;
base::IDMap<IPC::Listener*>::iterator iter(&listeners_);
while (!iter.IsAtEnd()) {
sorted_listeners_.push_back(
std::make_pair(iter.GetCurrentKey(), iter.GetCurrentValue()));
iter.Advance();
}
std::sort(sorted_listeners_.rbegin(), sorted_listeners_.rend());
for (auto& entry_pair : sorted_listeners_) {
entry_pair.second->OnMessageReceived(FrameHostMsg_RenderProcessGone(
entry_pair.first, static_cast<int>(termination_info.status),
termination_info.exit_code));
}
}
bool MockRenderProcessHost::Init() {
has_connection_ = true;
return true;
}
void MockRenderProcessHost::EnableSendQueue() {}
int MockRenderProcessHost::GetNextRoutingID() {
return ++prev_routing_id_;
}
void MockRenderProcessHost::AddRoute(int32_t routing_id,
IPC::Listener* listener) {
listeners_.AddWithID(listener, routing_id);
}
void MockRenderProcessHost::RemoveRoute(int32_t routing_id) {
DCHECK(listeners_.Lookup(routing_id) != nullptr);
listeners_.Remove(routing_id);
Cleanup();
}
void MockRenderProcessHost::AddObserver(RenderProcessHostObserver* observer) {
observers_.AddObserver(observer);
}
void MockRenderProcessHost::RemoveObserver(
RenderProcessHostObserver* observer) {
observers_.RemoveObserver(observer);
}
void MockRenderProcessHost::ShutdownForBadMessage(
CrashReportMode crash_report_mode) {
++bad_msg_count_;
}
void MockRenderProcessHost::UpdateClientPriority(PriorityClient* client) {}
int MockRenderProcessHost::VisibleClientCount() {
int count = 0;
for (auto* client : priority_clients_) {
const Priority priority = client->GetPriority();
if (!priority.is_hidden) {
count++;
}
}
return count;
}
unsigned int MockRenderProcessHost::GetFrameDepth() {
NOTIMPLEMENTED();
return 0u;
}
bool MockRenderProcessHost::GetIntersectsViewport() {
NOTIMPLEMENTED();
return true;
}
bool MockRenderProcessHost::IsForGuestsOnly() {
return is_for_guests_only_;
}
RendererAudioOutputStreamFactoryContext*
MockRenderProcessHost::GetRendererAudioOutputStreamFactoryContext() {
return nullptr;
}
void MockRenderProcessHost::OnMediaStreamAdded() {}
void MockRenderProcessHost::OnMediaStreamRemoved() {}
StoragePartition* MockRenderProcessHost::GetStoragePartition() {
return BrowserContext::GetDefaultStoragePartition(browser_context_);
}
void MockRenderProcessHost::AddWord(const base::string16& word) {
}
bool MockRenderProcessHost::Shutdown(int exit_code) {
return true;
}
bool MockRenderProcessHost::FastShutdownIfPossible(size_t page_count,
bool skip_unload_handlers) {
if (GetActiveViewCount() != page_count)
return false;
// We aren't actually going to do anything, but set |fast_shutdown_started_|
// to true so that tests know we've been called.
fast_shutdown_started_ = true;
return true;
}
bool MockRenderProcessHost::FastShutdownStarted() {
return fast_shutdown_started_;
}
const base::Process& MockRenderProcessHost::GetProcess() {
// Return the current-process handle for the IPC::GetPlatformFileForTransit
// function.
if (process.IsValid())
return process;
static const base::Process current_process(base::Process::Current());
return current_process;
}
bool MockRenderProcessHost::IsReady() {
return false;
}
bool MockRenderProcessHost::Send(IPC::Message* msg) {
// Save the message in the sink.
sink_.OnMessageReceived(*msg);
delete msg;
return true;
}
int MockRenderProcessHost::GetID() {
return id_;
}
bool MockRenderProcessHost::IsInitializedAndNotDead() {
return has_connection_;
}
void MockRenderProcessHost::SetBlocked(bool blocked) {}
bool MockRenderProcessHost::IsBlocked() {
return false;
}
std::unique_ptr<base::CallbackList<void(bool)>::Subscription>
MockRenderProcessHost::RegisterBlockStateChangedCallback(
const base::RepeatingCallback<void(bool)>& cb) {
return nullptr;
}
static void DeleteIt(base::WeakPtr<MockRenderProcessHost> h) {
if (h)
delete h.get();
}
void MockRenderProcessHost::Cleanup() {
if (listeners_.IsEmpty()) {
for (auto& observer : observers_)
observer.RenderProcessHostDestroyed(this);
// Post the delete of |this| as a WeakPtr so that if |this| is deleted by a
// test directly, we don't double free.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&DeleteIt, weak_ptr_factory_.GetWeakPtr()));
RenderProcessHostImpl::UnregisterHost(GetID());
deletion_callback_called_ = true;
}
}
void MockRenderProcessHost::AddPendingView() {
}
void MockRenderProcessHost::RemovePendingView() {
}
void MockRenderProcessHost::AddPriorityClient(PriorityClient* priority_client) {
priority_clients_.insert(priority_client);
}
void MockRenderProcessHost::RemovePriorityClient(
PriorityClient* priority_client) {
priority_clients_.erase(priority_client);
}
#if defined(OS_ANDROID)
ChildProcessImportance MockRenderProcessHost::GetEffectiveImportance() {
NOTIMPLEMENTED();
return ChildProcessImportance::NORMAL;
}
#endif
void MockRenderProcessHost::SetSuddenTerminationAllowed(bool allowed) {
}
bool MockRenderProcessHost::SuddenTerminationAllowed() {
return true;
}
BrowserContext* MockRenderProcessHost::GetBrowserContext() {
return browser_context_;
}
bool MockRenderProcessHost::InSameStoragePartition(
StoragePartition* partition) {
// Mock RPHs only have one partition.
return true;
}
IPC::ChannelProxy* MockRenderProcessHost::GetChannel() {
return nullptr;
}
void MockRenderProcessHost::AddFilter(BrowserMessageFilter* filter) {
}
base::TimeDelta MockRenderProcessHost::GetChildProcessIdleTime() {
return base::TimeDelta::FromMilliseconds(0);
}
void MockRenderProcessHost::BindInterface(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
if (binder_overrides_.count(interface_name) > 0)
binder_overrides_[interface_name].Run(std::move(interface_pipe));
}
const service_manager::Identity& MockRenderProcessHost::GetChildIdentity() {
return child_identity_;
}
std::unique_ptr<base::SharedPersistentMemoryAllocator>
MockRenderProcessHost::TakeMetricsAllocator() {
return nullptr;
}
const base::TimeTicks&
MockRenderProcessHost::GetInitTimeForNavigationMetrics() {
static base::TimeTicks dummy_time = base::TimeTicks::Now();
return dummy_time;
}
bool MockRenderProcessHost::IsProcessBackgrounded() {
return is_process_backgrounded_;
}
size_t MockRenderProcessHost::GetKeepAliveRefCount() const {
return keep_alive_ref_count_;
}
void MockRenderProcessHost::IncrementKeepAliveRefCount(
KeepAliveClientType client) {
++keep_alive_ref_count_;
}
void MockRenderProcessHost::DecrementKeepAliveRefCount(
KeepAliveClientType client) {
--keep_alive_ref_count_;
}
void MockRenderProcessHost::DisableKeepAliveRefCount() {
keep_alive_ref_count_ = 0;
// RenderProcessHost::DisableKeepAliveRefCount() virtual method gets called as
// part of BrowserContext::NotifyWillBeDestroyed(...). Normally
// MockRenderProcessHost::DisableKeepAliveRefCount doesn't call Cleanup,
// because the MockRenderProcessHost might be owned by a test. However, when
// the MockRenderProcessHost is the spare RenderProcessHost, we know that it
// is owned by the SpareRenderProcessHostManager and we need to delete the
// spare to avoid reports/DCHECKs about memory leaks.
if (this == RenderProcessHostImpl::GetSpareRenderProcessHostForTesting())
Cleanup();
}
bool MockRenderProcessHost::IsKeepAliveRefCountDisabled() {
return false;
}
void MockRenderProcessHost::PurgeAndSuspend() {}
void MockRenderProcessHost::Resume() {}
mojom::Renderer* MockRenderProcessHost::GetRendererInterface() {
if (!renderer_interface_) {
renderer_interface_.reset(new mojom::RendererAssociatedPtr);
mojo::MakeRequestAssociatedWithDedicatedPipe(renderer_interface_.get());
}
return renderer_interface_->get();
}
resource_coordinator::ProcessResourceCoordinator*
MockRenderProcessHost::GetProcessResourceCoordinator() {
if (!process_resource_coordinator_) {
content::ServiceManagerConnection* connection =
content::ServiceManagerConnection::GetForProcess();
// Tests may not set up a connection.
service_manager::Connector* connector =
connection ? connection->GetConnector() : nullptr;
process_resource_coordinator_ =
std::make_unique<resource_coordinator::ProcessResourceCoordinator>(
connector);
}
return process_resource_coordinator_.get();
}
void MockRenderProcessHost::CreateURLLoaderFactory(
const base::Optional<url::Origin>& origin,
network::mojom::TrustedURLLoaderHeaderClientPtrInfo header_client,
network::mojom::URLLoaderFactoryRequest request) {
url_loader_factory_->Clone(std::move(request));
}
void MockRenderProcessHost::SetIsNeverSuitableForReuse() {
is_never_suitable_for_reuse_ = true;
}
bool MockRenderProcessHost::MayReuseHost() {
return !is_never_suitable_for_reuse_;
}
bool MockRenderProcessHost::IsUnused() {
return is_unused_;
}
void MockRenderProcessHost::SetIsUsed() {
is_unused_ = false;
}
bool MockRenderProcessHost::HostHasNotBeenUsed() {
return IsUnused() && listeners_.IsEmpty() && GetKeepAliveRefCount() == 0;
}
void MockRenderProcessHost::LockToOrigin(const GURL& lock_url) {
ChildProcessSecurityPolicyImpl::GetInstance()->LockToOrigin(GetID(),
lock_url);
if (SiteInstanceImpl::IsOriginLockASite(lock_url))
is_renderer_locked_to_site_ = true;
}
void MockRenderProcessHost::BindCacheStorage(
blink::mojom::CacheStorageRequest request,
const url::Origin& origin) {
cache_storage_request_ = std::move(request);
}
void MockRenderProcessHost::BindIndexedDB(
blink::mojom::IDBFactoryRequest request,
const url::Origin& origin) {
idb_factory_request_ = std::move(request);
}
void MockRenderProcessHost::CleanupCorbExceptionForPluginUponDestruction() {}
void MockRenderProcessHost::FilterURL(bool empty_allowed, GURL* url) {
RenderProcessHostImpl::FilterURL(this, empty_allowed, url);
}
void MockRenderProcessHost::EnableAudioDebugRecordings(
const base::FilePath& file) {
}
void MockRenderProcessHost::DisableAudioDebugRecordings() {}
void MockRenderProcessHost::SetEchoCanceller3(
bool enable,
base::OnceCallback<void(bool, const std::string&)> callback) {}
RenderProcessHost::WebRtcStopRtpDumpCallback
MockRenderProcessHost::StartRtpDump(
bool incoming,
bool outgoing,
const WebRtcRtpPacketCallback& packet_callback) {
return WebRtcStopRtpDumpCallback();
}
void MockRenderProcessHost::EnableWebRtcEventLogOutput(int lid,
int output_period_ms) {}
void MockRenderProcessHost::DisableWebRtcEventLogOutput(int lid) {}
bool MockRenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
IPC::Listener* listener = listeners_.Lookup(msg.routing_id());
if (listener)
return listener->OnMessageReceived(msg);
return false;
}
void MockRenderProcessHost::OnChannelConnected(int32_t peer_pid) {}
void MockRenderProcessHost::OverrideBinderForTesting(
const std::string& interface_name,
const InterfaceBinder& binder) {
binder_overrides_[interface_name] = binder;
}
void MockRenderProcessHost::OverrideRendererInterfaceForTesting(
std::unique_ptr<mojo::AssociatedInterfacePtr<mojom::Renderer>>
renderer_interface) {
renderer_interface_ = std::move(renderer_interface);
}
void MockRenderProcessHost::OverrideURLLoaderFactory(
network::mojom::URLLoaderFactory* factory) {
url_loader_factory_ = factory;
}
MockRenderProcessHostFactory::MockRenderProcessHostFactory()
: default_mock_url_loader_factory_(
std::make_unique<NotImplementedNetworkURLLoaderFactory>()) {}
MockRenderProcessHostFactory::~MockRenderProcessHostFactory() {
// Detach this object from MockRenderProcesses to prevent them from calling
// MockRenderProcessHostFactory::Remove() when destroyed.
for (const auto& process : processes_)
process->SetFactory(nullptr);
}
RenderProcessHost* MockRenderProcessHostFactory::CreateRenderProcessHost(
BrowserContext* browser_context,
SiteInstance* site_instance) const {
std::unique_ptr<MockRenderProcessHost> host =
std::make_unique<MockRenderProcessHost>(browser_context);
host->OverrideURLLoaderFactory(default_mock_url_loader_factory_.get());
processes_.push_back(std::move(host));
processes_.back()->SetFactory(this);
return processes_.back().get();
}
void MockRenderProcessHostFactory::Remove(MockRenderProcessHost* host) const {
for (auto it = processes_.begin(); it != processes_.end(); ++it) {
if (it->get() == host) {
it->release();
processes_.erase(it);
break;
}
}
}
} // namespace content