#include <memory>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_split.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/test_timeouts.h"
#include "base/threading/hang_watcher.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_process_host_internal_observer.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_creation_observer.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pseudonymization_util.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_service.mojom.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_browser_main_parts.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/storage_partition_test_helpers.h"
#include "media/base/media_switches.h"
#include "media/base/test_data_util.h"
#include "media/mojo/buildflags.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#if BUILDFLAG(IS_WIN)
#include "base/features.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "mojo/public/cpp/platform/platform_handle_security_util_win.h"
#include "sandbox/policy/switches.h"
#endif
namespace content {
namespace {
class DelayedHttpResponseWithResolver final
: public net::test_server::BasicHttpResponse {
public:
class Resolver final : public base::RefCountedThreadSafe<Resolver> {
public:
void Resolve() {
base::AutoLock auto_lock(lock_);
DCHECK(!resolved_);
resolved_ = true;
if (!task_runner_) {
return;
}
for (auto& response : response_closures_)
task_runner_->PostTask(FROM_HERE, std::move(response));
response_closures_.clear();
}
void Add(base::OnceClosure response) {
base::AutoLock auto_lock(lock_);
if (resolved_) {
std::move(response).Run();
return;
}
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
base::SingleThreadTaskRunner::GetCurrentDefault();
if (task_runner_) {
DCHECK_EQ(task_runner_, task_runner);
} else {
task_runner_ = std::move(task_runner);
}
response_closures_.push_back(std::move(response));
}
private:
friend class base::RefCountedThreadSafe<Resolver>;
~Resolver() = default;
base::Lock lock_;
std::vector<base::OnceClosure> response_closures_;
bool resolved_ GUARDED_BY(lock_) = false;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_ GUARDED_BY(lock_);
};
explicit DelayedHttpResponseWithResolver(scoped_refptr<Resolver> resolver)
: resolver_(std::move(resolver)) {}
DelayedHttpResponseWithResolver(const DelayedHttpResponseWithResolver&) =
delete;
DelayedHttpResponseWithResolver& operator=(
const DelayedHttpResponseWithResolver&) = delete;
void SendResponse(
base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override {
resolver_->Add(base::BindOnce(
&net::test_server::HttpResponseDelegate::SendHeadersContentAndFinish,
delegate, code(), GetHttpReasonPhrase(code()), BuildHeaders(),
content()));
}
private:
const scoped_refptr<Resolver> resolver_;
};
std::unique_ptr<net::test_server::HttpResponse> HandleBeacon(
const net::test_server::HttpRequest& request) {
if (request.relative_url != "/beacon")
return nullptr;
return std::make_unique<net::test_server::BasicHttpResponse>();
}
std::unique_ptr<net::test_server::HttpResponse> HandleHungBeacon(
const base::RepeatingClosure& on_called,
const net::test_server::HttpRequest& request) {
if (request.relative_url != "/beacon")
return nullptr;
if (on_called) {
on_called.Run();
}
return std::make_unique<net::test_server::HungResponse>();
}
std::unique_ptr<net::test_server::HttpResponse> HandleHungBeaconWithResolver(
scoped_refptr<DelayedHttpResponseWithResolver::Resolver> resolver,
const net::test_server::HttpRequest& request) {
if (request.relative_url != "/beacon")
return nullptr;
return std::make_unique<DelayedHttpResponseWithResolver>(std::move(resolver));
}
}
class RenderProcessHostTestBase : public ContentBrowserTest,
public RenderProcessHostObserver {
public:
RenderProcessHostTestBase() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
switches::kAutoplayPolicy,
switches::autoplay::kNoUserGestureRequiredPolicy);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
}
void SetVisibleClients(RenderProcessHost* process, int32_t visible_clients) {
RenderProcessHostImpl* impl = static_cast<RenderProcessHostImpl*>(process);
impl->visible_clients_ = visible_clients;
}
protected:
void SetProcessExitCallback(RenderProcessHost* rph,
base::OnceClosure callback) {
Observe(rph);
process_exit_callback_ = std::move(callback);
}
void Observe(RenderProcessHost* rph) {
DCHECK(!observation_.IsObserving());
observation_.Observe(rph);
}
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override {
++process_exits_;
if (process_exit_callback_)
std::move(process_exit_callback_).Run();
}
void RenderProcessHostDestroyed(RenderProcessHost* host) override {
++host_destructions_;
observation_.Reset();
}
void WaitUntilProcessExits(int target) {
while (process_exits_ < target)
base::RunLoop().RunUntilIdle();
}
base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
observation_{this};
int process_exits_ = 0;
int host_destructions_ = 0;
base::OnceClosure process_exit_callback_;
};
class SpareRendererContentBrowserClient
: public ContentBrowserTestContentBrowserClient {
public:
bool IsSuitableHost(RenderProcessHost* process_host,
const GURL& site_url) override {
if (RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()) {
return process_host ==
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
}
return true;
}
};
class NonSpareRendererContentBrowserClient
: public ContentBrowserTestContentBrowserClient {
public:
NonSpareRendererContentBrowserClient() = default;
NonSpareRendererContentBrowserClient(
const NonSpareRendererContentBrowserClient&) = delete;
NonSpareRendererContentBrowserClient& operator=(
const NonSpareRendererContentBrowserClient&) = delete;
bool IsSuitableHost(RenderProcessHost* process_host,
const GURL& site_url) override {
return RenderProcessHostImpl::GetSpareRenderProcessHostForTesting() !=
process_host;
}
bool ShouldTryToUseExistingProcessHost(BrowserContext* context,
const GURL& url) override {
return true;
}
bool ShouldUseSpareRenderProcessHost(BrowserContext* browser_context,
const GURL& site_url) override {
return false;
}
};
class KeepAliveHandleContentBrowserClient
: public ContentBrowserTestContentBrowserClient {
public:
explicit KeepAliveHandleContentBrowserClient(base::OnceClosure callback) {
started_callback_ = std::move(callback);
}
void OnKeepaliveRequestStarted(BrowserContext* browser_context) override {
ContentBrowserTestContentBrowserClient::OnKeepaliveRequestStarted(
browser_context);
CHECK(started_callback_);
GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
std::move(started_callback_));
}
private:
base::OnceClosure started_callback_;
};
class RenderProcessHostTest : public RenderProcessHostTestBase,
public ::testing::WithParamInterface<bool> {
public:
RenderProcessHostTest() = default;
void SetUp() override {
if (IsKeepAliveInBrowserMigrationEnabled()) {
feature_list_.InitAndEnableFeature(
blink::features::kKeepAliveInBrowserMigration);
} else {
feature_list_.InitAndDisableFeature(
blink::features::kKeepAliveInBrowserMigration);
}
RenderProcessHostTestBase::SetUp();
}
protected:
bool IsKeepAliveInBrowserMigrationEnabled() { return GetParam(); }
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
RenderProcessHostTest,
testing::Values(false, true),
[](const testing::TestParamInfo<RenderProcessHostTest::ParamType>& info) {
return info.param ? "KeepAliveInBrowserMigration" : "Default";
});
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, GuestsAreNotSuitableHosts) {
RenderProcessHost::SetMaxRendererProcessCount(1);
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderProcessHost* rph =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
static_cast<RenderProcessHostImpl*>(rph)->SetForGuestsOnlyForTesting();
EXPECT_EQ(1, RenderProcessHost::GetCurrentRenderProcessCountForTesting());
GURL::Replacements replace_host;
replace_host.SetHostStr("localhost");
GURL another_url = embedded_test_server()->GetURL("/simple_page.html");
another_url = another_url.ReplaceComponents(replace_host);
EXPECT_TRUE(NavigateToURL(CreateBrowser(), another_url));
EXPECT_EQ(2, RenderProcessHost::GetCurrentRenderProcessCountForTesting());
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostTaken) {
ASSERT_TRUE(embedded_test_server()->Start());
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
EXPECT_NE(nullptr, spare_renderer);
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
Shell* window = CreateBrowser();
EXPECT_TRUE(NavigateToURL(window, test_url));
EXPECT_EQ(spare_renderer,
window->web_contents()->GetPrimaryMainFrame()->GetProcess());
EXPECT_NE(spare_renderer,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
EXPECT_NE(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
} else {
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
}
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostNotTaken) {
ASSERT_TRUE(embedded_test_server()->Start());
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->off_the_record_browser_context());
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
Shell* window = CreateBrowser();
EXPECT_TRUE(NavigateToURL(window, test_url));
EXPECT_NE(spare_renderer,
window->web_contents()->GetPrimaryMainFrame()->GetProcess());
if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
EXPECT_NE(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
} else {
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
}
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostKilled) {
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
mojo::Remote<mojom::TestService> service;
ASSERT_NE(nullptr, spare_renderer);
spare_renderer->BindReceiver(service.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
SetProcessExitCallback(spare_renderer, run_loop.QuitClosure());
{
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(spare_renderer);
service->DoSomething(base::DoNothing());
run_loop.Run();
}
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
SpareRendererSurpressedMaxProcesses) {
ASSERT_TRUE(embedded_test_server()->Start());
SpareRendererContentBrowserClient browser_client;
RenderProcessHost::SetMaxRendererProcessCount(1);
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
RenderProcessHost::SetMaxRendererProcessCount(2);
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
EXPECT_NE(nullptr, spare_renderer);
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
Shell* new_window = CreateBrowser();
EXPECT_TRUE(NavigateToURL(new_window, test_url));
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
EXPECT_EQ(spare_renderer,
new_window->web_contents()->GetPrimaryMainFrame()->GetProcess());
RenderProcessHost::SetMaxRendererProcessCount(0);
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRendererOnProcessReuse) {
ASSERT_TRUE(embedded_test_server()->Start());
NonSpareRendererContentBrowserClient browser_client;
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
EXPECT_NE(nullptr, spare_renderer);
Shell* new_browser = CreateBrowser();
EXPECT_EQ(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(),
new_browser->web_contents()->GetPrimaryMainFrame()->GetProcess());
EXPECT_NE(spare_renderer,
new_browser->web_contents()->GetPrimaryMainFrame()->GetProcess());
if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
EXPECT_NE(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
} else {
EXPECT_EQ(nullptr,
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
}
base::WaitableEvent launcher_thread_done(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
GetProcessLauncherTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce([](base::WaitableEvent* done) { done->Signal(); },
base::Unretained(&launcher_thread_done)));
ASSERT_TRUE(launcher_thread_done.TimedWait(TestTimeouts::action_timeout()));
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
SpareRenderProcessHostDuringShutdown) {
content::RenderProcessHost::WarmupSpareRenderProcessHost(
shell()->web_contents()->GetBrowserContext());
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRendererDuringClosing) {
content::RenderProcessHost::WarmupSpareRenderProcessHost(
shell()->web_contents()->GetBrowserContext());
shell()->web_contents()->Close();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
SpareProcessVsCustomStoragePartition) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("a.com", "/simple_page.html");
CustomStoragePartitionBrowserClient modified_client(GURL("http://a.com/"));
BrowserContext* browser_context =
ShellContentBrowserClient::Get()->browser_context();
scoped_refptr<SiteInstance> test_site_instance =
SiteInstance::CreateForURL(browser_context, test_url);
StoragePartition* default_storage =
browser_context->GetDefaultStoragePartition();
StoragePartition* custom_storage =
browser_context->GetStoragePartition(test_site_instance.get());
EXPECT_NE(default_storage, custom_storage);
Shell* window = CreateBrowser();
RenderProcessHost* old_process =
window->web_contents()->GetPrimaryMainFrame()->GetProcess();
EXPECT_EQ(default_storage, old_process->GetStoragePartition());
RenderProcessHost::WarmupSpareRenderProcessHost(browser_context);
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
ASSERT_TRUE(spare_renderer);
EXPECT_EQ(default_storage, spare_renderer->GetStoragePartition());
EXPECT_TRUE(NavigateToURL(window, test_url));
RenderProcessHost* new_process =
window->web_contents()->GetPrimaryMainFrame()->GetProcess();
EXPECT_NE(new_process, old_process);
EXPECT_EQ(custom_storage, new_process->GetStoragePartition());
EXPECT_NE(spare_renderer, new_process);
}
class RenderProcessHostObserverCounter : public RenderProcessHostObserver {
public:
explicit RenderProcessHostObserverCounter(RenderProcessHost* host) {
host->AddObserver(this);
observing_ = true;
observed_host_ = host;
}
RenderProcessHostObserverCounter(const RenderProcessHostObserverCounter&) =
delete;
RenderProcessHostObserverCounter& operator=(
const RenderProcessHostObserverCounter&) = delete;
~RenderProcessHostObserverCounter() override {
if (observing_)
observed_host_->RemoveObserver(this);
}
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override {
DCHECK(observing_);
DCHECK_EQ(host, observed_host_);
exited_count_++;
}
void RenderProcessHostDestroyed(RenderProcessHost* host) override {
DCHECK(observing_);
DCHECK_EQ(host, observed_host_);
destroyed_count_++;
host->RemoveObserver(this);
observing_ = false;
observed_host_ = nullptr;
}
int exited_count() const { return exited_count_; }
int destroyed_count() const { return destroyed_count_; }
private:
int exited_count_ = 0;
int destroyed_count_ = 0;
bool observing_ = false;
raw_ptr<RenderProcessHost> observed_host_ = nullptr;
};
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareVsDisableKeepAliveRefCount) {
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
base::RunLoop().RunUntilIdle();
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
RenderProcessHostObserverCounter counter(spare_renderer);
RenderProcessHostWatcher process_watcher(
spare_renderer, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
spare_renderer->DisableRefCounts();
process_watcher.Wait();
EXPECT_TRUE(process_watcher.did_exit_normally());
base::RunLoop().RunUntilIdle();
DCHECK_EQ(1, counter.exited_count());
DCHECK_EQ(1, counter.destroyed_count());
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareVsFastShutdown) {
RenderProcessHost::WarmupSpareRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context());
base::RunLoop().RunUntilIdle();
RenderProcessHost* spare_renderer =
RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
RenderProcessHostObserverCounter counter(spare_renderer);
RenderProcessHostWatcher process_watcher(
spare_renderer, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
spare_renderer->FastShutdownIfPossible();
process_watcher.Wait();
EXPECT_FALSE(process_watcher.did_exit_normally());
base::RunLoop().RunUntilIdle();
DCHECK_EQ(1, counter.exited_count());
DCHECK_EQ(1, counter.destroyed_count());
}
class ShellCloser : public RenderProcessHostObserver {
public:
ShellCloser(Shell* shell, std::string* logging_string)
: shell_(shell), logging_string_(logging_string) {}
protected:
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override {
logging_string_->append("ShellCloser::RenderProcessExited ");
shell_->Close();
}
void RenderProcessHostDestroyed(RenderProcessHost* host) override {
logging_string_->append("ShellCloser::RenderProcessHostDestroyed ");
}
raw_ptr<Shell, DanglingUntriaged> shell_;
raw_ptr<std::string> logging_string_;
};
class ObserverLogger : public RenderProcessHostObserver {
public:
explicit ObserverLogger(std::string* logging_string)
: logging_string_(logging_string), host_destroyed_(false) {}
bool host_destroyed() { return host_destroyed_; }
protected:
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override {
logging_string_->append("ObserverLogger::RenderProcessExited ");
}
void RenderProcessHostDestroyed(RenderProcessHost* host) override {
logging_string_->append("ObserverLogger::RenderProcessHostDestroyed ");
host_destroyed_ = true;
}
raw_ptr<std::string> logging_string_;
bool host_destroyed_;
};
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls \
DISABLED_AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#else
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls \
AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
std::string logging_string;
ShellCloser shell_closer(shell(), &logging_string);
ObserverLogger observer_logger(&logging_string);
RenderProcessHost* rph =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
observation_1(&shell_closer);
base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
observation_2(&observer_logger);
observation_1.Observe(rph);
observation_2.Observe(rph);
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(shell());
NavigateToURLBlockUntilNavigationsComplete(shell(),
GURL(blink::kChromeUICrashURL), 1);
EXPECT_EQ(
"ShellCloser::RenderProcessExited "
"ObserverLogger::RenderProcessExited "
"ShellCloser::RenderProcessHostDestroyed "
"ObserverLogger::RenderProcessHostDestroyed ",
logging_string);
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessOnBadMojoMessage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderProcessHost* rph =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
host_destructions_ = 0;
process_exits_ = 0;
mojo::Remote<mojom::TestService> service;
rph->BindReceiver(service.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
SetProcessExitCallback(rph, run_loop.QuitClosure());
{
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
service->DoSomething(base::DoNothing());
run_loop.Run();
}
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
}
class AudioStartObserver : public WebContentsObserver {
public:
AudioStartObserver(WebContents* web_contents,
RenderFrameHost* render_frame_host,
base::OnceClosure audible_closure)
: WebContentsObserver(web_contents),
render_frame_host_(
static_cast<RenderFrameHostImpl*>(render_frame_host)),
contents_audible_(web_contents->IsCurrentlyAudible()),
frame_audible_(render_frame_host_->is_audible()),
audible_closure_(std::move(audible_closure)) {
MaybeFireClosure();
}
~AudioStartObserver() override = default;
void OnAudioStateChanged(bool audible) override {
DCHECK_NE(audible, contents_audible_);
contents_audible_ = audible;
MaybeFireClosure();
}
void OnFrameAudioStateChanged(RenderFrameHost* render_frame_host,
bool audible) override {
if (render_frame_host_ != render_frame_host)
return;
DCHECK_NE(frame_audible_, audible);
frame_audible_ = audible;
MaybeFireClosure();
}
private:
void MaybeFireClosure() {
if (contents_audible_ && frame_audible_)
std::move(audible_closure_).Run();
}
raw_ptr<RenderFrameHostImpl> render_frame_host_ = nullptr;
bool contents_audible_ = false;
bool frame_audible_ = false;
base::OnceClosure audible_closure_;
};
#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(IS_ANDROID)
#define KillProcessZerosAudioStreams DISABLED_KillProcessZerosAudioStreams
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessZerosAudioStreams) {
embedded_test_server()->ServeFilesFromSourceDirectory(
media::GetTestDataPath());
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/webaudio_oscillator.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
{
base::RunLoop run_loop;
AudioStartObserver observer(shell()->web_contents(),
shell()->web_contents()->GetPrimaryMainFrame(),
run_loop.QuitClosure());
std::string result;
EXPECT_EQ("OK", EvalJs(shell(), "StartOscillator();"))
<< "Failed to execute javascript.";
run_loop.Run();
ASSERT_EQ(1, rph->get_media_stream_count_for_testing());
}
host_destructions_ = 0;
process_exits_ = 0;
mojo::Remote<mojom::TestService> service;
rph->BindReceiver(service.BindNewPipeAndPassReceiver());
{
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
base::RunLoop run_loop;
SetProcessExitCallback(rph, run_loop.QuitClosure());
service->DoSomething(base::DoNothing());
run_loop.Run();
}
{
base::RunLoop run_loop;
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
}
class CaptureStreamRenderProcessHostTest : public RenderProcessHostTestBase {
public:
void SetUp() override {
EnablePixelOutput();
RenderProcessHostTestBase::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
RenderProcessHostTestBase::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
GetUserMediaIncrementsVideoCaptureStreams) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
std::string result;
EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndExpectSuccess({video: true});"))
<< "Failed to execute javascript.";
EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
}
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
StopResetsVideoCaptureStreams) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
std::string result;
EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndStop({video: true});"))
<< "Failed to execute javascript.";
EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
}
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
KillProcessZerosVideoCaptureStreams) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
std::string result;
EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndExpectSuccess({video: true});"))
<< "Failed to execute javascript.";
EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
host_destructions_ = 0;
process_exits_ = 0;
mojo::Remote<mojom::TestService> service;
rph->BindReceiver(service.BindNewPipeAndPassReceiver());
{
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
base::RunLoop run_loop;
SetProcessExitCallback(rph, run_loop.QuitClosure());
service->DoSomething(base::DoNothing());
run_loop.Run();
}
{
base::RunLoop run_loop;
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
}
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
GetUserMediaAudioOnlyIncrementsMediaStreams) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
std::string result;
EXPECT_TRUE(ExecJs(
shell(), "getUserMediaAndExpectSuccess({video: false, audio: true});"))
<< "Failed to execute javascript.";
EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
}
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
KillProcessZerosAudioCaptureStreams) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
std::string result;
EXPECT_TRUE(ExecJs(
shell(), "getUserMediaAndExpectSuccess({video: false, audio: true});"))
<< "Failed to execute javascript.";
EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
host_destructions_ = 0;
process_exits_ = 0;
mojo::Remote<mojom::TestService> service;
rph->BindReceiver(service.BindNewPipeAndPassReceiver());
{
ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
base::RunLoop run_loop;
SetProcessExitCallback(rph, run_loop.QuitClosure());
service->DoSomething(base::DoNothing());
run_loop.Run();
}
{
base::RunLoop run_loop;
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KeepAliveRendererProcess) {
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleBeacon));
ASSERT_TRUE(embedded_test_server()->Start());
if (AreDefaultSiteInstancesEnabled()) {
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{"foo.com"});
}
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/send-beacon.html")));
RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderProcessHostImpl* rph =
static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
host_destructions_ = 0;
process_exits_ = 0;
Observe(rph);
rfh->SetKeepAliveTimeoutForTesting(base::Seconds(30));
if (IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
}
base::TimeTicks start = base::TimeTicks::Now();
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
WaitUntilProcessExits(1);
EXPECT_LT(base::TimeTicks::Now() - start, base::Seconds(30));
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
KeepAliveRendererProcessWithServiceWorker) {
if (IsKeepAliveInBrowserMigrationEnabled()) {
return;
}
base::RunLoop run_loop;
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeacon, run_loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(),
embedded_test_server()->GetURL("/workers/service_worker_setup.html")));
EXPECT_EQ("ok", EvalJs(shell(), "setup();"));
RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
EXPECT_EQ(rph->worker_ref_count(), 1);
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("/workers/send-beacon.html")));
run_loop.Run();
ASSERT_EQ(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
if (!IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_EQ(rph->keep_alive_ref_count(), 1);
} else {
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
}
EXPECT_EQ(rph->worker_ref_count(), 1);
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_KeepAliveRendererProcess_Hung \
DISABLED_KeepAliveRendererProcess_Hung
#else
#define MAYBE_KeepAliveRendererProcess_Hung KeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
MAYBE_KeepAliveRendererProcess_Hung) {
base::HangWatcher::StopMonitoringForTesting();
content::DisableBackForwardCacheForTesting(
shell()->web_contents(),
content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
const auto kTestUrl = embedded_test_server()->GetURL("/send-beacon.html");
if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) {
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{kTestUrl.host()});
}
EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));
RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderProcessHostImpl* rph =
static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
host_destructions_ = 0;
process_exits_ = 0;
Observe(rph);
rfh->SetKeepAliveTimeoutForTesting(base::Seconds(1));
if (IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
}
base::TimeTicks start = base::TimeTicks::Now();
EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,<p>hello</p>")));
WaitUntilProcessExits(1);
if (!IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_GE(base::TimeTicks::Now() - start, base::Seconds(1));
}
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_FetchKeepAliveRendererProcess_Hung \
DISABLED_FetchKeepAliveRendererProcess_Hung
#else
#define MAYBE_FetchKeepAliveRendererProcess_Hung \
FetchKeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
MAYBE_FetchKeepAliveRendererProcess_Hung) {
base::HangWatcher::StopMonitoringForTesting();
content::DisableBackForwardCacheForTesting(
shell()->web_contents(),
content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
const auto kTestUrl = embedded_test_server()->GetURL("/fetch-keepalive.html");
if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) {
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{kTestUrl.host()});
}
EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));
RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderProcessHostImpl* rph =
static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
host_destructions_ = 0;
process_exits_ = 0;
Observe(rph);
rfh->SetKeepAliveTimeoutForTesting(base::Seconds(1));
if (IsKeepAliveInBrowserMigrationEnabled()) {
const std::u16string waiting = u"Waiting";
TitleWatcher watcher(shell()->web_contents(), waiting);
ASSERT_EQ(waiting, watcher.WaitAndGetTitle());
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
}
base::TimeTicks start = base::TimeTicks::Now();
EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,<p>hello</p>")));
WaitUntilProcessExits(1);
if (!IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_GE(base::TimeTicks::Now() - start, base::Seconds(1));
}
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ManyKeepaliveRequests) {
auto resolver =
base::MakeRefCounted<DelayedHttpResponseWithResolver::Resolver>();
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeaconWithResolver, resolver));
const std::u16string title = u"Resolved";
const std::u16string waiting = u"Waiting";
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(),
embedded_test_server()->GetURL("/fetch-keepalive.html?requests=256")));
{
TitleWatcher watcher(shell()->web_contents(), waiting);
EXPECT_EQ(waiting, watcher.WaitAndGetTitle());
}
resolver->Resolve();
{
TitleWatcher watcher(shell()->web_contents(), title);
EXPECT_EQ(title, watcher.WaitAndGetTitle());
}
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, TooManyKeepaliveRequests) {
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
const std::u16string title = u"Rejected";
TitleWatcher watcher(shell()->web_contents(), title);
EXPECT_TRUE(NavigateToURL(
shell(),
embedded_test_server()->GetURL("/fetch-keepalive.html?requests=257")));
EXPECT_EQ(title, watcher.WaitAndGetTitle());
}
class IsProcessBackgroundedObserver : public RenderProcessHostInternalObserver {
public:
explicit IsProcessBackgroundedObserver(RenderProcessHostImpl* host) {
host_observation_.Observe(host);
}
void RenderProcessBackgroundedChanged(RenderProcessHostImpl* host) override {
backgrounded_ = host->IsProcessBackgrounded();
}
absl::optional<bool> TakeValue() {
auto value = backgrounded_;
backgrounded_ = absl::nullopt;
return value;
}
private:
absl::optional<bool> backgrounded_;
base::ScopedObservation<RenderProcessHostImpl,
RenderProcessHostInternalObserver>
host_observation_{this};
};
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, PriorityOverride) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderProcessHost* rph =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
RenderProcessHostImpl* process = static_cast<RenderProcessHostImpl*>(rph);
IsProcessBackgroundedObserver observer(process);
EXPECT_FALSE(process->HasPriorityOverride());
EXPECT_FALSE(process->IsProcessBackgrounded());
EXPECT_FALSE(observer.TakeValue().has_value());
process->SetPriorityOverride(false );
EXPECT_TRUE(process->HasPriorityOverride());
EXPECT_TRUE(process->IsProcessBackgrounded());
EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());
process->SetPriorityOverride(true );
EXPECT_TRUE(process->HasPriorityOverride());
EXPECT_FALSE(process->IsProcessBackgrounded());
EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());
process->SetPriorityOverride(false );
EXPECT_TRUE(process->HasPriorityOverride());
EXPECT_TRUE(process->IsProcessBackgrounded());
EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());
process->AddPendingView();
EXPECT_TRUE(process->HasPriorityOverride());
EXPECT_TRUE(process->IsProcessBackgrounded());
EXPECT_FALSE(observer.TakeValue().has_value());
process->ClearPriorityOverride();
EXPECT_FALSE(process->HasPriorityOverride());
EXPECT_FALSE(process->IsProcessBackgrounded());
EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());
process->RemovePendingView();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ConstructedButNotInitializedYet) {
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
EXPECT_EQ(ShellContentBrowserClient::Get()->browser_context(),
process->GetBrowserContext());
EXPECT_FALSE(process->IsForGuestsOnly());
EXPECT_FALSE(process->IsInitializedAndNotDead());
EXPECT_FALSE(process->IsReady());
EXPECT_FALSE(process->GetProcess().IsValid());
EXPECT_EQ(base::kNullProcessHandle, process->GetProcess().Handle());
EXPECT_TRUE(process->GetChannel());
EXPECT_TRUE(process->GetRendererInterface());
process->Cleanup();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, FastShutdownForStartingProcess) {
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
process->Init();
EXPECT_TRUE(process->FastShutdownIfPossible());
process->Cleanup();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
FastShutdownWithKeepAliveRequest) {
base::RunLoop request_sent_loop, request_handled_loop;
KeepAliveHandleContentBrowserClient browser_client(
request_handled_loop.QuitClosure());
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(HandleHungBeacon, request_sent_loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
const auto kTestUrl = embedded_test_server()->GetURL("/send-beacon.html");
ASSERT_TRUE(NavigateToURL(shell(), kTestUrl));
RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame());
RenderProcessHostImpl* rph =
static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
request_sent_loop.Run();
if (IsKeepAliveInBrowserMigrationEnabled()) {
EXPECT_EQ(rph->keep_alive_ref_count(), 0);
EXPECT_TRUE(rph->FastShutdownIfPossible());
} else {
request_handled_loop.Run();
EXPECT_EQ(rph->keep_alive_ref_count(), 1);
EXPECT_FALSE(rph->FastShutdownIfPossible());
}
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ForEachRenderFrameHost) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a = embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a(a,a))");
GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
GURL url_new_window = embedded_test_server()->GetURL(
"c.com", "/cross_site_iframe_factory.html?c(a)");
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHost* rfh_a = shell()->web_contents()->GetPrimaryMainFrame();
std::vector<RenderFrameHost*> process_a_frames =
CollectAllRenderFrameHosts(rfh_a);
Shell* new_window = CreateBrowser();
ASSERT_TRUE(NavigateToURL(new_window, url_new_window));
auto* new_window_main_frame = static_cast<RenderFrameHostImpl*>(
new_window->web_contents()->GetPrimaryMainFrame());
ASSERT_EQ(new_window_main_frame->child_count(), 1u);
RenderFrameHost* new_window_sub_frame =
new_window_main_frame->child_at(0)->current_frame_host();
ASSERT_EQ(rfh_a->GetProcess(), new_window_sub_frame->GetProcess());
process_a_frames.push_back(new_window_sub_frame);
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
TestNavigationManager manager(web_contents, url_b);
shell()->LoadURL(url_b);
ASSERT_TRUE(manager.WaitForRequestStart());
FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
RenderFrameHostImpl* rfh_b = root->render_manager()->speculative_frame_host();
ASSERT_TRUE(rfh_b);
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
rfh_b->lifecycle_state());
auto non_speculative_rfh_collector =
[](std::vector<RenderFrameHost*>& results, RenderFrameHost* rfh) {
auto* rfhi = static_cast<RenderFrameHostImpl*>(rfh);
EXPECT_NE(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
rfhi->lifecycle_state());
results.push_back(rfh);
};
RenderProcessHostImpl* rph_a =
static_cast<RenderProcessHostImpl*>(rfh_a->GetProcess());
std::vector<RenderFrameHost*> same_process_rfhs;
rph_a->ForEachRenderFrameHost(base::BindRepeating(
non_speculative_rfh_collector, std::ref(same_process_rfhs)));
EXPECT_EQ(5, rph_a->GetRenderFrameHostCount());
EXPECT_EQ(5u, same_process_rfhs.size());
EXPECT_THAT(same_process_rfhs,
testing::UnorderedElementsAreArray(process_a_frames.data(),
process_a_frames.size()));
same_process_rfhs.clear();
RenderProcessHostImpl* rph_b =
static_cast<RenderProcessHostImpl*>(rfh_b->GetProcess());
ASSERT_NE(rph_a, rph_b);
rph_b->ForEachRenderFrameHost(base::BindRepeating(
non_speculative_rfh_collector, std::ref(same_process_rfhs)));
EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
EXPECT_EQ(same_process_rfhs.size(), 0u);
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_b->lifecycle_state());
EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
same_process_rfhs.clear();
rph_b->ForEachRenderFrameHost(base::BindRepeating(
non_speculative_rfh_collector, std::ref(same_process_rfhs)));
EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
EXPECT_EQ(1u, same_process_rfhs.size());
EXPECT_THAT(same_process_rfhs, testing::ElementsAre(rfh_b));
}
namespace {
class LeakCleanupObserver : public WebContentsObserver {
public:
explicit LeakCleanupObserver(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
~LeakCleanupObserver() override = default;
void PrimaryMainFrameRenderProcessGone(
base::TerminationStatus status) override {
termination_count_++;
CHECK_EQ(status,
base::TerminationStatus::TERMINATION_STATUS_NORMAL_TERMINATION);
}
int termination_count() { return termination_count_; }
void reset_termination_count() { termination_count_ = 0; }
private:
int termination_count_ = 0;
};
class FastShutdownExitObserver : public RenderProcessHostObserver {
public:
explicit FastShutdownExitObserver(RenderProcessHost* process) {
process->AddObserver(this);
}
~FastShutdownExitObserver() override = default;
void RenderProcessExited(RenderProcessHost* host,
const ChildProcessTerminationInfo& info) override {
if (host->FastShutdownStarted())
fast_shutdown_exit_count_++;
}
void RenderProcessHostDestroyed(RenderProcessHost* host) override {
host->RemoveObserver(this);
}
int fast_shutdown_exit_count() { return fast_shutdown_exit_count_; }
void reset_exit_count() { fast_shutdown_exit_count_ = 0; }
private:
int fast_shutdown_exit_count_ = 0;
};
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, AllowUnusedProcessToExit) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
RenderProcessHost::SetMaxRendererProcessCount(1);
content::DisableBackForwardCacheForTesting(
shell()->web_contents(),
content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* original_rfh = root->current_frame_host();
RenderProcessHost* original_process = original_rfh->GetProcess();
int original_process_id = original_process->GetID();
EXPECT_FALSE(original_process->IsInitializedAndNotDead());
EXPECT_FALSE(original_rfh->IsRenderFrameLive());
process_exits_ = 0;
host_destructions_ = 0;
Observe(original_process);
FastShutdownExitObserver fast_shutdown_observer(original_process);
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
Shell* shell2 =
Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
url_a, nullptr, gfx::Size());
FrameTreeNode* root2 = static_cast<WebContentsImpl*>(shell2->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* rfh2 = root2->current_frame_host();
EXPECT_EQ(original_process, rfh2->GetProcess());
EXPECT_TRUE(original_process->IsInitializedAndNotDead());
EXPECT_TRUE(rfh2->IsRenderFrameLive());
EXPECT_FALSE(original_rfh->IsRenderFrameLive());
LeakCleanupObserver leak_cleanup_observer(shell()->web_contents());
RenderProcessHostWatcher exit_observer(
original_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
RenderFrameDeletedObserver rfh2_deleted_observer(rfh2);
shell2->Close();
rfh2_deleted_observer.WaitUntilDeleted();
exit_observer.Wait();
EXPECT_TRUE(exit_observer.did_exit_normally());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
EXPECT_EQ(1, leak_cleanup_observer.termination_count());
EXPECT_EQ(1, fast_shutdown_observer.fast_shutdown_exit_count());
EXPECT_EQ(original_rfh, root->current_frame_host());
EXPECT_EQ(original_process, original_rfh->GetProcess());
EXPECT_FALSE(original_process->IsInitializedAndNotDead());
EXPECT_FALSE(original_rfh->IsRenderFrameLive());
EXPECT_FALSE(original_rfh->render_view_host()->IsRenderViewLive());
EXPECT_FALSE(
root->current_frame_host()
->browsing_context_state()
->GetRenderFrameProxyHost(original_rfh->GetSiteInstance()->group()));
process_exits_ = 0;
host_destructions_ = 0;
leak_cleanup_observer.reset_termination_count();
fast_shutdown_observer.reset_exit_count();
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* replaced_rfh = root->current_frame_host();
ASSERT_EQ(original_process, replaced_rfh->GetProcess());
EXPECT_EQ(original_process_id, replaced_rfh->GetProcess()->GetID());
EXPECT_TRUE(replaced_rfh->GetProcess()->IsInitializedAndNotDead());
EXPECT_TRUE(replaced_rfh->IsRenderFrameLive());
EXPECT_FALSE(original_process->FastShutdownStarted());
EXPECT_EQ(0, process_exits_);
EXPECT_EQ(0, host_destructions_);
EXPECT_EQ(0, fast_shutdown_observer.fast_shutdown_exit_count());
EXPECT_EQ(0, leak_cleanup_observer.termination_count());
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
RenderProcessHostWatcher cleanup_observer(
original_process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
cleanup_observer.Wait();
RenderFrameHostImpl* rfh_b = root->current_frame_host();
EXPECT_NE(original_process_id, rfh_b->GetProcess()->GetID());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(1, host_destructions_);
EXPECT_EQ(0, fast_shutdown_observer.fast_shutdown_exit_count());
EXPECT_EQ(0, leak_cleanup_observer.termination_count());
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
AllowUnusedProcessToExitAfterCrash) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL initial_url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b,b)"));
EXPECT_TRUE(NavigateToURL(shell(), initial_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* child_rfh0 = root->child_at(0)->current_frame_host();
RenderFrameHostImpl* child_rfh1 = root->child_at(1)->current_frame_host();
RenderViewHostImpl* rvh_b = child_rfh0->render_view_host();
int process_b_id = child_rfh0->GetProcess()->GetID();
EXPECT_EQ(child_rfh0->GetProcess(), child_rfh1->GetProcess());
EXPECT_TRUE(child_rfh0->GetProcess()->IsInitializedAndNotDead());
EXPECT_TRUE(child_rfh0->IsRenderFrameLive());
EXPECT_TRUE(child_rfh1->IsRenderFrameLive());
EXPECT_TRUE(rvh_b->IsRenderViewLive());
process_exits_ = 0;
host_destructions_ = 0;
Observe(child_rfh0->GetProcess());
{
RenderProcessHostWatcher termination_observer(
child_rfh0->GetProcess(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
child_rfh0->GetProcess()->Shutdown(0);
termination_observer.Wait();
}
EXPECT_FALSE(child_rfh0->GetProcess()->IsInitializedAndNotDead());
EXPECT_FALSE(child_rfh0->IsRenderFrameLive());
EXPECT_FALSE(child_rfh1->IsRenderFrameLive());
EXPECT_FALSE(rvh_b->IsRenderViewLive());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
process_exits_ = 0;
host_destructions_ = 0;
{
TestFrameNavigationObserver reload_observer(root->child_at(0));
std::string reload_script(
"var f = document.getElementById('child-0');"
"f.src = f.src;");
EXPECT_TRUE(ExecuteScript(root, reload_script));
reload_observer.Wait();
}
RenderFrameHostImpl* new_child_rfh0 = root->child_at(0)->current_frame_host();
EXPECT_EQ(child_rfh1->GetProcess(), new_child_rfh0->GetProcess());
EXPECT_TRUE(new_child_rfh0->GetProcess()->IsInitializedAndNotDead());
EXPECT_TRUE(new_child_rfh0->IsRenderFrameLive());
EXPECT_FALSE(child_rfh1->IsRenderFrameLive());
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
RenderFrameDeletedObserver rfh_deleted_observer(new_child_rfh0);
TestFrameNavigationObserver navigation_observer(root->child_at(0));
EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url_c));
navigation_observer.Wait();
rfh_deleted_observer.WaitUntilDeleted();
EXPECT_NE(child_rfh1->GetProcess(),
root->child_at(0)->current_frame_host()->GetProcess());
EXPECT_FALSE(child_rfh1->GetProcess()->IsInitializedAndNotDead());
EXPECT_FALSE(child_rfh1->IsRenderFrameLive());
EXPECT_FALSE(rvh_b->IsRenderViewLive());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(0, host_destructions_);
RenderFrameProxyHost* proxy =
root->current_frame_host()
->browsing_context_state()
->GetRenderFrameProxyHost(child_rfh1->GetSiteInstance()->group());
EXPECT_FALSE(proxy->is_render_frame_proxy_live());
process_exits_ = 0;
host_destructions_ = 0;
{
TestFrameNavigationObserver reload_observer(root->child_at(1));
std::string reload_script(
"var f = document.getElementById('child-1');"
"f.src = f.src;");
EXPECT_TRUE(ExecuteScript(root, reload_script));
reload_observer.Wait();
}
RenderFrameHostImpl* new_child_rfh1 = root->child_at(1)->current_frame_host();
EXPECT_EQ(process_b_id, new_child_rfh1->GetProcess()->GetID());
EXPECT_TRUE(new_child_rfh1->GetProcess()->IsInitializedAndNotDead());
EXPECT_TRUE(new_child_rfh1->IsRenderFrameLive());
EXPECT_EQ(0, process_exits_);
EXPECT_EQ(0, host_destructions_);
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, HandleNestedFrameDeletion) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* rfh_a = root->current_frame_host();
RenderProcessHost* process_a = rfh_a->GetProcess();
int process_a_id = process_a->GetID();
Observe(process_a);
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
RenderProcessHostWatcher cleanup_observer(
process_a, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
shell()->web_contents()->GetController().GetBackForwardCache().Flush();
cleanup_observer.Wait();
RenderFrameHostImpl* rfh_b = root->current_frame_host();
EXPECT_NE(process_a_id, rfh_b->GetProcess()->GetID());
EXPECT_EQ(1, process_exits_);
EXPECT_EQ(1, host_destructions_);
}
namespace {
class RenderFrameDeletionObserver : public WebContentsObserver {
public:
explicit RenderFrameDeletionObserver(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
~RenderFrameDeletionObserver() override = default;
void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
render_frame_deleted_count_++;
auto rfh_collector = [](std::vector<RenderFrameHost*>& results,
RenderFrameHost* rfh) { results.push_back(rfh); };
RenderProcessHostImpl* process =
static_cast<RenderProcessHostImpl*>(render_frame_host->GetProcess());
std::vector<RenderFrameHost*> all_rfhs;
process->ForEachRenderFrameHost(
base::BindRepeating(rfh_collector, std::ref(all_rfhs)));
render_frame_host_iterator_count_ += all_rfhs.size();
}
int render_frame_deleted_count() { return render_frame_deleted_count_; }
int render_frame_host_iterator_count() {
return render_frame_host_iterator_count_;
}
private:
int render_frame_deleted_count_ = 0;
int render_frame_host_iterator_count_ = 0;
};
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ForEachFrameNestedFrameDeletion) {
if (!IsBackForwardCacheEnabled())
return;
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* rfh_a = root->current_frame_host();
RenderProcessHost* process_a = rfh_a->GetProcess();
int process_a_id = process_a->GetID();
RenderFrameDeletionObserver rfh_deletion_observer(shell()->web_contents());
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
RenderProcessHostWatcher cleanup_observer(
process_a, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
shell()->web_contents()->GetController().GetBackForwardCache().Flush();
cleanup_observer.Wait();
RenderFrameHostImpl* rfh_b = root->current_frame_host();
EXPECT_NE(process_a_id, rfh_b->GetProcess()->GetID());
EXPECT_EQ(2, rfh_deletion_observer.render_frame_deleted_count());
EXPECT_EQ(0, rfh_deletion_observer.render_frame_host_iterator_count());
}
#if BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ZeroExecutionTimes) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
sandbox::policy::switches::kNoSandbox)) {
return;
}
base::HistogramTester histogram_tester;
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
RenderProcessHostWatcher process_watcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
process->Init();
process_watcher.Wait();
EXPECT_TRUE(process->IsReady());
histogram_tester.ExpectUniqueSample(
"BrowserRenderProcessHost.SuspendedChild.UserExecutionRecorded", false,
1);
histogram_tester.ExpectUniqueSample(
"BrowserRenderProcessHost.SuspendedChild.KernelExecutionRecorded", false,
1);
process->Cleanup();
}
class RenderProcessHostWriteableFileTest
: public RenderProcessHostTestBase,
public ::testing::WithParamInterface<
std::tuple</*enforcement_enabled=*/bool,
/*add_no_execute_flags=*/bool>> {
public:
void SetUp() override {
enforcement_feature_.InitWithFeatureState(
base::features::kEnforceNoExecutableFileHandles,
IsEnforcementEnabled());
RenderProcessHostTestBase::SetUp();
}
protected:
bool IsEnforcementEnabled() { return std::get<0>(GetParam()); }
bool ShouldMarkNoExecute() { return std::get<1>(GetParam()); }
private:
base::test::ScopedFeatureList enforcement_feature_;
};
IN_PROC_BROWSER_TEST_P(RenderProcessHostWriteableFileTest,
PassUnsafeWriteableExecutableFile) {
#if !DCHECK_IS_ON()
GTEST_SKIP();
#else
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
sandbox::policy::switches::kNoSandbox)) {
GTEST_SKIP();
}
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderProcessHost* rph =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
mojo::Remote<mojom::TestService> test_service;
rph->BindReceiver(test_service.BindNewPipeAndPassReceiver());
uint32_t flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
base::File::FLAG_WRITE;
if (ShouldMarkNoExecute()) {
flags = base::File::AddFlagsForPassingToUntrustedProcess(flags);
}
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
base::File temp_file_writeable(file_path, flags);
ASSERT_TRUE(temp_file_writeable.IsValid());
bool error_was_called = false;
mojo::SetUnsafeFileHandleCallbackForTesting(
base::BindLambdaForTesting([&error_was_called]() -> bool {
error_was_called = true;
return true;
}));
base::RunLoop run_loop;
test_service->PassWriteableFile(std::move(temp_file_writeable),
run_loop.QuitClosure());
run_loop.Run();
bool should_violation_occur =
IsEnforcementEnabled() && !ShouldMarkNoExecute();
EXPECT_EQ(should_violation_occur, error_was_called);
#endif
}
INSTANTIATE_TEST_SUITE_P(
All,
RenderProcessHostWriteableFileTest,
testing::Combine(testing::Bool(),
testing::Bool()));
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
SetPseudonymizationSaltSynchronized) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html")));
RenderProcessHost* rph1 =
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
Shell* second_shell = CreateBrowser();
EXPECT_TRUE(NavigateToURL(second_shell, embedded_test_server()->GetURL(
"b.com", "/simple_page.html")));
RenderProcessHost* rph2 =
second_shell->web_contents()->GetPrimaryMainFrame()->GetProcess();
EXPECT_NE(rph1->GetProcess().Pid(), rph2->GetProcess().Pid());
const std::string test_string = "testing123";
uint32_t browser_result =
PseudonymizationUtil::PseudonymizeStringForTesting(test_string);
for (RenderProcessHost* rph : {rph1, rph2}) {
mojo::Remote<mojom::TestService> service;
rph->BindReceiver(service.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
absl::optional<uint32_t> renderer_result = absl::nullopt;
service->PseudonymizeString(
test_string, base::BindLambdaForTesting([&](uint32_t result) {
renderer_result = result;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(renderer_result.has_value());
EXPECT_EQ(*renderer_result, browser_result);
}
}
class CreationObserver : public RenderProcessHostCreationObserver {
public:
explicit CreationObserver(
base::RepeatingClosure closure = base::RepeatingClosure())
: closure_(std::move(closure)) {}
void OnRenderProcessHostCreated(RenderProcessHost* process_host) override {
if (closure_) {
closure_.Run();
}
}
private:
base::RepeatingClosure closure_;
};
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, HostCreationObserved) {
int created_count = 0;
CreationObserver creation_observer(
base::BindLambdaForTesting([&created_count]() { ++created_count; }));
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
RenderProcessHostWatcher process_watcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
process->Init();
process_watcher.Wait();
ASSERT_TRUE(process->IsReady());
EXPECT_EQ(1, created_count);
process->Cleanup();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
HostCreationObserversAddedDuringNotification) {
std::vector<std::unique_ptr<CreationObserver>> added_creation_observers;
const int kObserversToAdd = 1000;
int added_observer_notification_count = 0;
const auto increment_added_observer_notification_count =
base::BindLambdaForTesting([&added_observer_notification_count]() {
++added_observer_notification_count;
});
CreationObserver creation_observer1(base::BindLambdaForTesting(
[&added_creation_observers,
increment_added_observer_notification_count]() {
for (int i = 0; i < kObserversToAdd; ++i) {
added_creation_observers.push_back(std::make_unique<CreationObserver>(
increment_added_observer_notification_count));
}
}));
CreationObserver creation_observer2;
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
RenderProcessHostWatcher process_watcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
process->Init();
process_watcher.Wait();
EXPECT_TRUE(process->IsReady());
EXPECT_EQ(kObserversToAdd, added_observer_notification_count);
process->Cleanup();
}
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
HostCreationObserversDestroyedDuringNotification) {
base::OnceClosure destroy_second_observer;
CreationObserver creation_observer1(
base::BindLambdaForTesting([&destroy_second_observer]() {
std::move(destroy_second_observer).Run();
}));
auto creation_observer2 = std::make_unique<CreationObserver>();
destroy_second_observer = base::BindLambdaForTesting(
[&creation_observer2]() { creation_observer2.reset(); });
RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
ShellContentBrowserClient::Get()->browser_context(), nullptr);
RenderProcessHostWatcher process_watcher(
process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
process->Init();
process_watcher.Wait();
EXPECT_TRUE(process->IsReady());
EXPECT_EQ(nullptr, creation_observer2.get());
process->Cleanup();
}
}