Chromium Code Reviews| 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 StoragePartitionImpl* storage_partition) | |
| 161 : child_processes_(child_processes), | |
| 162 storage_partition_(storage_partition) { | |
| 163 SiteInstanceImpl::set_render_process_host_factory(this); | |
| 164 } | |
| 165 virtual ~FakeChildProcessRPHFactory() { | |
| 166 SiteInstanceImpl::set_render_process_host_factory(NULL); | |
| 167 } | |
| 168 | |
| 169 virtual RenderProcessHost* CreateRenderProcessHost( | |
| 170 BrowserContext* browser_context) const OVERRIDE { | |
| 171 RenderProcessHostImpl* host = new RenderProcessHostImpl( | |
| 172 browser_context, storage_partition_, false, false); | |
| 173 host->SetChildProcessLauncherFactory( | |
| 174 base::Bind(&NewFakeChildProcessLauncher, child_processes_)); | |
| 175 return host; | |
| 176 } | |
| 177 | |
| 178 private: | |
| 179 ScopedVector<LaunchedChildProcess>* child_processes_; | |
| 180 StoragePartitionImpl* storage_partition_; | |
| 181 }; | |
| 182 | |
| 183 class RenderProcessHostImplTest : public testing::Test { | |
| 184 public: | |
| 185 RenderProcessHostImplTest() | |
| 186 : ui_thread_(BrowserThread::UI, &message_loop_), | |
| 187 webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_), | |
| 188 file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING, | |
| 189 &message_loop_), | |
| 190 io_thread_(BrowserThread::IO, &message_loop_), | |
| 191 old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) { | |
| 192 } | |
| 193 | |
| 194 virtual ~RenderProcessHostImplTest() { | |
| 195 base::RunLoop().RunUntilIdle(); | |
| 196 SetBrowserClientForTesting(old_browser_client_); | |
| 197 } | |
| 198 | |
| 199 protected: | |
| 200 ScopedVector<LaunchedChildProcess> child_processes_; | |
| 201 base::MessageLoopForIO message_loop_; | |
|
Jeffrey Yasskin
2013/06/05 22:53:20
I'll be able to replace this list of threads with
| |
| 202 TestBrowserThread ui_thread_; | |
| 203 TestBrowserThread webkit_thread_; | |
| 204 TestBrowserThread file_user_blocking_thread_; | |
| 205 TestBrowserThread io_thread_; | |
| 206 | |
| 207 RPHIBrowserContext browser_context_; | |
| 208 RPHIBrowserClient test_browser_client_; | |
| 209 ContentBrowserClient* old_browser_client_; | |
| 210 ResourceDispatcherHostImpl resource_dispatcher_host_; | |
| 211 | |
| 212 DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest); | |
| 213 }; | |
| 214 | |
| 215 // http://crbug.com/87176 | |
| 216 TEST_F(RenderProcessHostImplTest, | |
| 217 RejectShutdownFromChildProcessWithOneActiveView) { | |
| 218 const GURL kUrl("http://example.com"); | |
| 219 | |
| 220 scoped_refptr<SiteInstance> site( | |
| 221 SiteInstance::CreateForURL(&browser_context_, kUrl)); | |
| 222 FakeChildProcessRPHFactory rph_factory( | |
| 223 &child_processes_, | |
| 224 static_cast<StoragePartitionImpl*>( | |
| 225 TestBrowserContext::GetStoragePartition(&browser_context_, | |
| 226 site.get()))); | |
| 227 | |
| 228 WebContents::CreateParams params(&browser_context_, site.get()); | |
| 229 scoped_ptr<WebContentsImpl> tab( | |
| 230 WebContentsImpl::CreateWithOpener(params, NULL)); | |
| 231 tab->GetController().LoadURL(kUrl, Referrer(), PAGE_TRANSITION_TYPED, ""); | |
| 232 base::RunLoop().RunUntilIdle(); | |
| 233 ASSERT_EQ(1U, child_processes_.size()); | |
| 234 | |
| 235 child_processes_[0]->messages_.ClearMessages(); | |
| 236 | |
| 237 RenderProcessHostImpl* tab_process = | |
| 238 static_cast<RenderProcessHostImpl*>(tab->GetSiteInstance()->GetProcess()); | |
| 239 EXPECT_EQ(1, tab_process->GetActiveViewCount()); | |
| 240 EXPECT_EQ(child_processes_[0]->launcher_->client(), tab_process); | |
| 241 | |
| 242 // Sometimes the renderer process's ShutdownRequest (corresponding to the | |
| 243 // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until | |
| 244 // after the browser process decides to re-use the renderer for a new purpose. | |
| 245 // This test makes sure the browser doesn't let the renderer die in that case. | |
| 246 child_processes_[0]->launcher_-> | |
| 247 Send(new ChildProcessHostMsg_ShutdownRequest()); | |
| 248 base::RunLoop().RunUntilIdle(); | |
| 249 EXPECT_FALSE(child_processes_[0]->messages_.GetFirstMessageMatching( | |
| 250 ChildProcessMsg_Shutdown::ID)) | |
| 251 << "Host sent a message confirming that the renderer process should shut " | |
| 252 << "down even though it was still being used."; | |
| 253 } | |
| 254 | |
| 255 } // namespace | |
| 256 } // namespace content | |
| OLD | NEW |