| Index: content/browser/renderer_host/render_process_host_impl_unittest.cc
|
| diff --git a/content/browser/renderer_host/render_process_host_impl_unittest.cc b/content/browser/renderer_host/render_process_host_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b6c8a7152ea1e35300bca512731ca93866796767
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/render_process_host_impl_unittest.cc
|
| @@ -0,0 +1,255 @@
|
| +// 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/renderer_host/render_process_host_impl.h"
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/process_util.h"
|
| +#include "base/run_loop.h"
|
| +#include "content/browser/child_process_launcher.h"
|
| +#include "content/browser/loader/resource_dispatcher_host_impl.h"
|
| +#include "content/browser/site_instance_impl.h"
|
| +#include "content/browser/storage_partition_impl.h"
|
| +#include "content/browser/webui/web_ui_controller_factory_registry.h"
|
| +#include "content/common/child_process_messages.h"
|
| +#include "content/common/view_messages.h"
|
| +#include "content/public/browser/render_process_host_factory.h"
|
| +#include "content/public/common/sandboxed_process_launcher_delegate.h"
|
| +#include "content/public/test/test_browser_context.h"
|
| +#include "content/public/test/test_browser_thread.h"
|
| +#include "content/test/test_content_browser_client.h"
|
| +#include "content/test/test_render_view_host_factory.h"
|
| +#include "content/test/test_web_contents.h"
|
| +#include "ipc/ipc_switches.h"
|
| +#include "ipc/ipc_test_sink.h"
|
| +#include "net/url_request/url_request_test_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace content {
|
| +namespace {
|
| +
|
| +class FakeChildProcessLauncher;
|
| +
|
| +struct LaunchedChildProcess {
|
| + // Becomes NULL after the FakeChildProcessLauncher is destroyed.
|
| + FakeChildProcessLauncher* launcher_;
|
| + // Collects messages sent to the child process.
|
| + IPC::TestSink messages_;
|
| +};
|
| +
|
| +class FakeChildProcessLauncher : public ChildProcessLauncher {
|
| + public:
|
| + FakeChildProcessLauncher(LaunchedChildProcess* info,
|
| + const IPC::ChannelHandle& channel,
|
| + Client* client)
|
| + : info_(info),
|
| + client_(client),
|
| + starting_(true),
|
| + termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
|
| + channel_(new IPC::ChannelProxy(
|
| + channel,
|
| + IPC::Channel::MODE_CLIENT,
|
| + &info->messages_,
|
| + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
|
| + weak_this_factory_(this) {
|
| + info_->launcher_ = this;
|
| +
|
| + // Automatically start the child process so the RenderProcessHostImpl sends
|
| + // messages to it. To mimic real child processes, don't start instantly.
|
| + base::MessageLoopProxy::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&FakeChildProcessLauncher::Start,
|
| + weak_this_factory_.GetWeakPtr()));
|
| + }
|
| +
|
| + virtual ~FakeChildProcessLauncher() { info_->launcher_ = NULL; }
|
| +
|
| + void Send(IPC::Message* msg) {
|
| + EXPECT_TRUE(channel_->Send(msg));
|
| + }
|
| +
|
| + Client* client() const { return client_; }
|
| +
|
| + // Implementation of ChildProcessLauncher.
|
| + virtual bool IsStarting() OVERRIDE { return starting_; }
|
| + virtual base::ProcessHandle GetHandle() OVERRIDE {
|
| + return base::Process::Current().handle();
|
| + }
|
| + virtual base::TerminationStatus GetChildTerminationStatus(bool known_dead,
|
| + int* exit_code)
|
| + OVERRIDE {
|
| + return termination_status_;
|
| + }
|
| + virtual void SetProcessBackgrounded(bool background) OVERRIDE {}
|
| + virtual void SetTerminateChildOnShutdown(bool terminate_on_shutdown)
|
| + OVERRIDE {}
|
| +
|
| + private:
|
| + void Start() {
|
| + starting_ = false;
|
| + client_->OnProcessLaunched();
|
| + }
|
| +
|
| + LaunchedChildProcess* info_;
|
| + Client* client_;
|
| + bool starting_;
|
| + base::TerminationStatus termination_status_;
|
| + scoped_ptr<IPC::ChannelProxy> channel_;
|
| + base::WeakPtrFactory<FakeChildProcessLauncher> weak_this_factory_;
|
| +};
|
| +
|
| +scoped_ptr<ChildProcessLauncher> NewFakeChildProcessLauncher(
|
| + ScopedVector<LaunchedChildProcess>* child_processes,
|
| +#if defined(OS_WIN)
|
| + SandboxedProcessLauncherDelegate* delegate,
|
| +#elif defined(OS_POSIX)
|
| + bool use_zygote,
|
| + const base::EnvironmentVector& environ,
|
| + int ipcfd,
|
| +#endif
|
| + CommandLine* cmd_line,
|
| + int child_process_id,
|
| + ChildProcessLauncher::Client* client) {
|
| +#if defined(OS_WIN)
|
| + delete delegate;
|
| +#endif
|
| + scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
|
| + child_processes->push_back(new LaunchedChildProcess());
|
| + std::string channel_id =
|
| + cmd_line->GetSwitchValueASCII(switches::kProcessChannelID);
|
| + return scoped_ptr<ChildProcessLauncher>(new FakeChildProcessLauncher(
|
| + child_processes->back(),
|
| + IPC::ChannelHandle(channel_id
|
| +#if defined(OS_POSIX)
|
| + ,
|
| + base::FileDescriptor(ipcfd, false)
|
| +#endif
|
| + ),
|
| + client));
|
| +}
|
| +
|
| +class RPHIBrowserContext : public TestBrowserContext {
|
| + private:
|
| + virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess(
|
| + int renderer_child_id) OVERRIDE {
|
| + return GetRequestContext();
|
| + }
|
| +};
|
| +
|
| +class RPHIBrowserClient : public TestContentBrowserClient {
|
| + public:
|
| + RPHIBrowserClient()
|
| + : request_context_getter_(new net::TestURLRequestContextGetter(
|
| + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {}
|
| +
|
| + virtual net::URLRequestContextGetter* CreateRequestContext(
|
| + BrowserContext* browser_context,
|
| + ProtocolHandlerMap* protocol_handlers) OVERRIDE {
|
| + return request_context_getter_.get();
|
| + }
|
| +
|
| + private:
|
| + scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
|
| +};
|
| +
|
| +class FakeChildProcessRPHFactory : public RenderProcessHostFactory {
|
| + public:
|
| + FakeChildProcessRPHFactory(
|
| + ScopedVector<LaunchedChildProcess>* child_processes)
|
| + : child_processes_(child_processes) {
|
| + SiteInstanceImpl::set_render_process_host_factory(this);
|
| + }
|
| + virtual ~FakeChildProcessRPHFactory() {
|
| + SiteInstanceImpl::set_render_process_host_factory(NULL);
|
| + }
|
| +
|
| + virtual RenderProcessHost* CreateRenderProcessHost(
|
| + BrowserContext* browser_context,
|
| + SiteInstance* site_instance) const OVERRIDE {
|
| + RenderProcessHostImpl* host =
|
| + new RenderProcessHostImpl(browser_context,
|
| + static_cast<StoragePartitionImpl*>(
|
| + TestBrowserContext::GetStoragePartition(
|
| + browser_context, site_instance)),
|
| + false,
|
| + false);
|
| + host->SetChildProcessLauncherFactory(
|
| + base::Bind(&NewFakeChildProcessLauncher, child_processes_));
|
| + return host;
|
| + }
|
| +
|
| + private:
|
| + ScopedVector<LaunchedChildProcess>* child_processes_;
|
| +};
|
| +
|
| +class RenderProcessHostImplTest : public testing::Test {
|
| + public:
|
| + RenderProcessHostImplTest()
|
| + : ui_thread_(BrowserThread::UI, &message_loop_),
|
| + webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_),
|
| + file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING,
|
| + &message_loop_),
|
| + io_thread_(BrowserThread::IO, &message_loop_),
|
| + old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) {
|
| + }
|
| +
|
| + virtual ~RenderProcessHostImplTest() {
|
| + base::RunLoop().RunUntilIdle();
|
| + SetBrowserClientForTesting(old_browser_client_);
|
| + }
|
| +
|
| + protected:
|
| + ScopedVector<LaunchedChildProcess> child_processes_;
|
| + base::MessageLoopForIO message_loop_;
|
| + TestBrowserThread ui_thread_;
|
| + TestBrowserThread webkit_thread_;
|
| + TestBrowserThread file_user_blocking_thread_;
|
| + TestBrowserThread io_thread_;
|
| +
|
| + RPHIBrowserContext browser_context_;
|
| + RPHIBrowserClient test_browser_client_;
|
| + ContentBrowserClient* old_browser_client_;
|
| + ResourceDispatcherHostImpl resource_dispatcher_host_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest);
|
| +};
|
| +
|
| +// http://crbug.com/87176
|
| +TEST_F(RenderProcessHostImplTest,
|
| + RejectShutdownFromChildProcessWithOneActiveView) {
|
| + const GURL kUrl("http://example.com");
|
| +
|
| + scoped_refptr<SiteInstance> site(
|
| + SiteInstance::CreateForURL(&browser_context_, kUrl));
|
| + FakeChildProcessRPHFactory rph_factory(&child_processes_);
|
| +
|
| + WebContents::CreateParams params(&browser_context_, site.get());
|
| + scoped_ptr<WebContentsImpl> tab(
|
| + WebContentsImpl::CreateWithOpener(params, NULL));
|
| + tab->GetController().LoadURL(kUrl, Referrer(), PAGE_TRANSITION_TYPED, "");
|
| + base::RunLoop().RunUntilIdle();
|
| + ASSERT_EQ(1U, child_processes_.size());
|
| +
|
| + child_processes_[0]->messages_.ClearMessages();
|
| +
|
| + RenderProcessHostImpl* tab_process =
|
| + static_cast<RenderProcessHostImpl*>(tab->GetSiteInstance()->GetProcess());
|
| + EXPECT_EQ(1, tab_process->GetActiveViewCount());
|
| + EXPECT_EQ(child_processes_[0]->launcher_->client(), tab_process);
|
| +
|
| + // Sometimes the renderer process's ShutdownRequest (corresponding to the
|
| + // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until
|
| + // after the browser process decides to re-use the renderer for a new purpose.
|
| + // This test makes sure the browser doesn't let the renderer die in that case.
|
| + child_processes_[0]->launcher_->
|
| + Send(new ChildProcessHostMsg_ShutdownRequest());
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_FALSE(child_processes_[0]->messages_.GetFirstMessageMatching(
|
| + ChildProcessMsg_Shutdown::ID))
|
| + << "Host sent a message confirming that the renderer process should shut "
|
| + << "down even though it was still being used.";
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace content
|
|
|