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 |