OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/renderer_host/render_process_host_impl.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/process_util.h" |
| 9 #include "base/run_loop.h" |
| 10 #include "content/browser/child_process_launcher.h" |
| 11 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| 12 #include "content/browser/site_instance_impl.h" |
| 13 #include "content/browser/storage_partition_impl.h" |
| 14 #include "content/browser/webui/web_ui_controller_factory_registry.h" |
| 15 #include "content/common/child_process_messages.h" |
| 16 #include "content/common/view_messages.h" |
| 17 #include "content/public/browser/render_process_host_factory.h" |
| 18 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 19 #include "content/public/test/test_browser_context.h" |
| 20 #include "content/public/test/test_browser_thread.h" |
| 21 #include "content/test/test_content_browser_client.h" |
| 22 #include "content/test/test_render_view_host_factory.h" |
| 23 #include "content/test/test_web_contents.h" |
| 24 #include "ipc/ipc_switches.h" |
| 25 #include "ipc/ipc_test_sink.h" |
| 26 #include "net/url_request/url_request_test_util.h" |
| 27 #include "testing/gtest/include/gtest/gtest.h" |
| 28 |
| 29 namespace content { |
| 30 namespace { |
| 31 |
| 32 class FakeChildProcessLauncher; |
| 33 |
| 34 struct LaunchedChildProcess { |
| 35 // Becomes NULL after the FakeChildProcessLauncher is destroyed. |
| 36 FakeChildProcessLauncher* launcher_; |
| 37 // Collects messages sent to the child process. |
| 38 IPC::TestSink messages_; |
| 39 }; |
| 40 |
| 41 class FakeChildProcessLauncher : public ChildProcessLauncher { |
| 42 public: |
| 43 FakeChildProcessLauncher(LaunchedChildProcess* info, |
| 44 const IPC::ChannelHandle& channel, |
| 45 Client* client) |
| 46 : info_(info), |
| 47 client_(client), |
| 48 starting_(true), |
| 49 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), |
| 50 channel_(new IPC::ChannelProxy( |
| 51 channel, |
| 52 IPC::Channel::MODE_CLIENT, |
| 53 &info->messages_, |
| 54 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))), |
| 55 weak_this_factory_(this) { |
| 56 info_->launcher_ = this; |
| 57 |
| 58 // Automatically start the child process so the RenderProcessHostImpl sends |
| 59 // messages to it. To mimic real child processes, don't start instantly. |
| 60 base::MessageLoopProxy::current()->PostTask( |
| 61 FROM_HERE, |
| 62 base::Bind(&FakeChildProcessLauncher::Start, |
| 63 weak_this_factory_.GetWeakPtr())); |
| 64 } |
| 65 |
| 66 virtual ~FakeChildProcessLauncher() { info_->launcher_ = NULL; } |
| 67 |
| 68 void Send(IPC::Message* msg) { |
| 69 EXPECT_TRUE(channel_->Send(msg)); |
| 70 } |
| 71 |
| 72 Client* client() const { return client_; } |
| 73 |
| 74 // Implementation of ChildProcessLauncher. |
| 75 virtual bool IsStarting() OVERRIDE { return starting_; } |
| 76 virtual base::ProcessHandle GetHandle() OVERRIDE { |
| 77 return base::Process::Current().handle(); |
| 78 } |
| 79 virtual base::TerminationStatus GetChildTerminationStatus(bool known_dead, |
| 80 int* exit_code) |
| 81 OVERRIDE { |
| 82 return termination_status_; |
| 83 } |
| 84 virtual void SetProcessBackgrounded(bool background) OVERRIDE {} |
| 85 virtual void SetTerminateChildOnShutdown(bool terminate_on_shutdown) |
| 86 OVERRIDE {} |
| 87 |
| 88 private: |
| 89 void Start() { |
| 90 starting_ = false; |
| 91 client_->OnProcessLaunched(); |
| 92 } |
| 93 |
| 94 LaunchedChildProcess* info_; |
| 95 Client* client_; |
| 96 bool starting_; |
| 97 base::TerminationStatus termination_status_; |
| 98 scoped_ptr<IPC::ChannelProxy> channel_; |
| 99 base::WeakPtrFactory<FakeChildProcessLauncher> weak_this_factory_; |
| 100 }; |
| 101 |
| 102 scoped_ptr<ChildProcessLauncher> NewFakeChildProcessLauncher( |
| 103 ScopedVector<LaunchedChildProcess>* child_processes, |
| 104 #if defined(OS_WIN) |
| 105 SandboxedProcessLauncherDelegate* delegate, |
| 106 #elif defined(OS_POSIX) |
| 107 bool use_zygote, |
| 108 const base::EnvironmentVector& environ, |
| 109 int ipcfd, |
| 110 #endif |
| 111 CommandLine* cmd_line, |
| 112 int child_process_id, |
| 113 ChildProcessLauncher::Client* client) { |
| 114 #if defined(OS_WIN) |
| 115 delete delegate; |
| 116 #endif |
| 117 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); |
| 118 child_processes->push_back(new LaunchedChildProcess()); |
| 119 std::string channel_id = |
| 120 cmd_line->GetSwitchValueASCII(switches::kProcessChannelID); |
| 121 return scoped_ptr<ChildProcessLauncher>(new FakeChildProcessLauncher( |
| 122 child_processes->back(), |
| 123 IPC::ChannelHandle(channel_id |
| 124 #if defined(OS_POSIX) |
| 125 , |
| 126 base::FileDescriptor(ipcfd, false) |
| 127 #endif |
| 128 ), |
| 129 client)); |
| 130 } |
| 131 |
| 132 class RPHIBrowserContext : public TestBrowserContext { |
| 133 private: |
| 134 virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess( |
| 135 int renderer_child_id) OVERRIDE { |
| 136 return GetRequestContext(); |
| 137 } |
| 138 }; |
| 139 |
| 140 class RPHIBrowserClient : public TestContentBrowserClient { |
| 141 public: |
| 142 RPHIBrowserClient() |
| 143 : request_context_getter_(new net::TestURLRequestContextGetter( |
| 144 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {} |
| 145 |
| 146 virtual net::URLRequestContextGetter* CreateRequestContext( |
| 147 BrowserContext* browser_context, |
| 148 ProtocolHandlerMap* protocol_handlers) OVERRIDE { |
| 149 return request_context_getter_.get(); |
| 150 } |
| 151 |
| 152 private: |
| 153 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; |
| 154 }; |
| 155 |
| 156 class FakeChildProcessRPHFactory : public RenderProcessHostFactory { |
| 157 public: |
| 158 FakeChildProcessRPHFactory( |
| 159 ScopedVector<LaunchedChildProcess>* child_processes) |
| 160 : child_processes_(child_processes) { |
| 161 SiteInstanceImpl::set_render_process_host_factory(this); |
| 162 } |
| 163 virtual ~FakeChildProcessRPHFactory() { |
| 164 SiteInstanceImpl::set_render_process_host_factory(NULL); |
| 165 } |
| 166 |
| 167 virtual RenderProcessHost* CreateRenderProcessHost( |
| 168 BrowserContext* browser_context, |
| 169 SiteInstance* site_instance) const OVERRIDE { |
| 170 RenderProcessHostImpl* host = |
| 171 new RenderProcessHostImpl(browser_context, |
| 172 static_cast<StoragePartitionImpl*>( |
| 173 TestBrowserContext::GetStoragePartition( |
| 174 browser_context, site_instance)), |
| 175 false, |
| 176 false); |
| 177 host->SetChildProcessLauncherFactory( |
| 178 base::Bind(&NewFakeChildProcessLauncher, child_processes_)); |
| 179 return host; |
| 180 } |
| 181 |
| 182 private: |
| 183 ScopedVector<LaunchedChildProcess>* child_processes_; |
| 184 }; |
| 185 |
| 186 class RenderProcessHostImplTest : public testing::Test { |
| 187 public: |
| 188 RenderProcessHostImplTest() |
| 189 : ui_thread_(BrowserThread::UI, &message_loop_), |
| 190 webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_), |
| 191 file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING, |
| 192 &message_loop_), |
| 193 io_thread_(BrowserThread::IO, &message_loop_), |
| 194 old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) { |
| 195 } |
| 196 |
| 197 virtual ~RenderProcessHostImplTest() { |
| 198 base::RunLoop().RunUntilIdle(); |
| 199 SetBrowserClientForTesting(old_browser_client_); |
| 200 } |
| 201 |
| 202 protected: |
| 203 ScopedVector<LaunchedChildProcess> child_processes_; |
| 204 base::MessageLoopForIO message_loop_; |
| 205 TestBrowserThread ui_thread_; |
| 206 TestBrowserThread webkit_thread_; |
| 207 TestBrowserThread file_user_blocking_thread_; |
| 208 TestBrowserThread io_thread_; |
| 209 |
| 210 RPHIBrowserContext browser_context_; |
| 211 RPHIBrowserClient test_browser_client_; |
| 212 ContentBrowserClient* old_browser_client_; |
| 213 ResourceDispatcherHostImpl resource_dispatcher_host_; |
| 214 |
| 215 DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest); |
| 216 }; |
| 217 |
| 218 // http://crbug.com/87176 |
| 219 TEST_F(RenderProcessHostImplTest, |
| 220 RejectShutdownFromChildProcessWithOneActiveView) { |
| 221 const GURL kUrl("http://example.com"); |
| 222 |
| 223 scoped_refptr<SiteInstance> site( |
| 224 SiteInstance::CreateForURL(&browser_context_, kUrl)); |
| 225 FakeChildProcessRPHFactory rph_factory(&child_processes_); |
| 226 |
| 227 WebContents::CreateParams params(&browser_context_, site.get()); |
| 228 scoped_ptr<WebContentsImpl> tab( |
| 229 WebContentsImpl::CreateWithOpener(params, NULL)); |
| 230 tab->GetController().LoadURL(kUrl, Referrer(), PAGE_TRANSITION_TYPED, ""); |
| 231 base::RunLoop().RunUntilIdle(); |
| 232 ASSERT_EQ(1U, child_processes_.size()); |
| 233 |
| 234 child_processes_[0]->messages_.ClearMessages(); |
| 235 |
| 236 RenderProcessHostImpl* tab_process = |
| 237 static_cast<RenderProcessHostImpl*>(tab->GetSiteInstance()->GetProcess()); |
| 238 EXPECT_EQ(1, tab_process->GetActiveViewCount()); |
| 239 EXPECT_EQ(child_processes_[0]->launcher_->client(), tab_process); |
| 240 |
| 241 // Sometimes the renderer process's ShutdownRequest (corresponding to the |
| 242 // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until |
| 243 // after the browser process decides to re-use the renderer for a new purpose. |
| 244 // This test makes sure the browser doesn't let the renderer die in that case. |
| 245 child_processes_[0]->launcher_-> |
| 246 Send(new ChildProcessHostMsg_ShutdownRequest()); |
| 247 base::RunLoop().RunUntilIdle(); |
| 248 EXPECT_FALSE(child_processes_[0]->messages_.GetFirstMessageMatching( |
| 249 ChildProcessMsg_Shutdown::ID)) |
| 250 << "Host sent a message confirming that the renderer process should shut " |
| 251 << "down even though it was still being used."; |
| 252 } |
| 253 |
| 254 } // namespace |
| 255 } // namespace content |
OLD | NEW |