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 // Implementation of ChildProcessLauncher. | |
73 virtual bool IsStarting() OVERRIDE { return starting_; } | |
74 virtual base::ProcessHandle GetHandle() OVERRIDE { | |
75 return base::Process::Current().handle(); | |
76 } | |
77 virtual base::TerminationStatus GetChildTerminationStatus(bool known_dead, | |
78 int* exit_code) | |
79 OVERRIDE { | |
80 return termination_status_; | |
81 } | |
82 virtual void SetProcessBackgrounded(bool background) OVERRIDE {} | |
83 virtual void SetTerminateChildOnShutdown(bool terminate_on_shutdown) | |
84 OVERRIDE {} | |
85 | |
86 private: | |
87 void Start() { | |
88 starting_ = false; | |
89 client_->OnProcessLaunched(); | |
90 } | |
91 | |
92 LaunchedChildProcess* info_; | |
93 Client* client_; | |
94 bool starting_; | |
95 base::TerminationStatus termination_status_; | |
96 scoped_ptr<IPC::ChannelProxy> channel_; | |
97 base::WeakPtrFactory<FakeChildProcessLauncher> weak_this_factory_; | |
98 }; | |
99 | |
100 scoped_ptr<ChildProcessLauncher> NewFakeChildProcessLauncher( | |
101 ScopedVector<LaunchedChildProcess>* child_processes, | |
102 #if defined(OS_WIN) | |
103 SandboxedProcessLauncherDelegate* delegate, | |
104 #elif defined(OS_POSIX) | |
105 bool use_zygote, | |
106 const base::EnvironmentVector& environ, | |
107 int ipcfd, | |
108 #endif | |
109 CommandLine* cmd_line, | |
110 int child_process_id, | |
111 ChildProcessLauncher::Client* client) { | |
112 #if defined(OS_WIN) | |
113 delete delegate; | |
114 #endif | |
115 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); | |
116 child_processes->push_back(new LaunchedChildProcess()); | |
117 std::string channel_id = | |
118 cmd_line->GetSwitchValueASCII(switches::kProcessChannelID); | |
119 return scoped_ptr<ChildProcessLauncher>(new FakeChildProcessLauncher( | |
120 child_processes->back(), | |
121 IPC::ChannelHandle(channel_id | |
122 #if defined(OS_POSIX) | |
123 , | |
124 base::FileDescriptor(ipcfd, false) | |
125 #endif | |
126 ), | |
127 client)); | |
128 } | |
129 | |
130 class RPHIBrowserContext : public TestBrowserContext { | |
131 private: | |
132 virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess( | |
133 int renderer_child_id) OVERRIDE { | |
134 return GetRequestContext(); | |
135 } | |
136 }; | |
137 | |
138 class RPHIBrowserClient : public TestContentBrowserClient { | |
139 public: | |
140 RPHIBrowserClient() | |
141 : request_context_getter_(new net::TestURLRequestContextGetter( | |
142 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {} | |
143 | |
144 virtual net::URLRequestContextGetter* CreateRequestContext( | |
145 BrowserContext* browser_context, | |
146 ProtocolHandlerMap* protocol_handlers) OVERRIDE { | |
147 return request_context_getter_.get(); | |
148 } | |
149 | |
150 private: | |
151 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; | |
152 }; | |
153 | |
154 class FakeChildProcessRPHFactory : public RenderProcessHostFactory { | |
155 public: | |
156 FakeChildProcessRPHFactory( | |
157 ScopedVector<LaunchedChildProcess>* child_processes, | |
158 SiteInstance* site_instance) | |
159 : child_processes_(child_processes), site_instance_(site_instance) { | |
160 SiteInstanceImpl::set_render_process_host_factory(this); | |
161 } | |
162 virtual ~FakeChildProcessRPHFactory() { | |
163 SiteInstanceImpl::set_render_process_host_factory(NULL); | |
164 } | |
165 | |
166 virtual RenderProcessHost* CreateRenderProcessHost( | |
167 BrowserContext* browser_context) const OVERRIDE { | |
168 RenderProcessHostImpl* host = | |
169 new RenderProcessHostImpl(browser_context, | |
170 static_cast<StoragePartitionImpl*>( | |
171 TestBrowserContext::GetStoragePartition( | |
172 browser_context, site_instance_)), | |
173 false, | |
174 false); | |
175 host->SetChildProcessLauncherFactory( | |
176 base::Bind(&NewFakeChildProcessLauncher, child_processes_)); | |
177 return host; | |
178 } | |
179 | |
180 private: | |
181 ScopedVector<LaunchedChildProcess>* child_processes_; | |
182 SiteInstance* site_instance_; | |
Charlie Reis
2013/06/05 19:07:10
It's not clear why this needs a SiteInstance, sinc
Jeffrey Yasskin
2013/06/05 21:23:27
Sure, done.
Another possible refactoring would be
Charlie Reis
2013/06/05 21:54:09
Good point! That seems like a better solution, so
Jeffrey Yasskin
2013/06/05 22:53:20
I've put that in https://codereview.chromium.org/1
| |
183 }; | |
184 | |
185 class RenderProcessHostImplTest : public testing::Test { | |
186 public: | |
187 RenderProcessHostImplTest() | |
188 : ui_thread_(BrowserThread::UI, &message_loop_), | |
189 webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_), | |
190 file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING, | |
191 &message_loop_), | |
192 io_thread_(BrowserThread::IO, &message_loop_), | |
193 old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) { | |
194 } | |
195 | |
196 virtual ~RenderProcessHostImplTest() { | |
197 base::RunLoop().RunUntilIdle(); | |
198 SetBrowserClientForTesting(old_browser_client_); | |
199 } | |
200 | |
201 protected: | |
202 ScopedVector<LaunchedChildProcess> child_processes_; | |
203 base::MessageLoopForIO message_loop_; | |
204 TestBrowserThread ui_thread_; | |
205 TestBrowserThread webkit_thread_; | |
206 TestBrowserThread file_user_blocking_thread_; | |
207 TestBrowserThread io_thread_; | |
208 | |
209 RPHIBrowserContext browser_context_; | |
210 RPHIBrowserClient test_browser_client_; | |
211 ContentBrowserClient* old_browser_client_; | |
212 ResourceDispatcherHostImpl resource_dispatcher_host_; | |
213 | |
214 DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest); | |
215 }; | |
216 | |
217 // http://crbug.com/87176 | |
218 TEST_F(RenderProcessHostImplTest, | |
219 RejectShutdownFromChildProcessWithOneActiveView) { | |
220 const GURL kUrl("http://example.com"); | |
221 | |
222 scoped_refptr<SiteInstance> site( | |
223 SiteInstance::CreateForURL(&browser_context_, kUrl)); | |
224 FakeChildProcessRPHFactory rph_factory(&child_processes_, site.get()); | |
225 | |
226 WebContents::CreateParams params(&browser_context_, site.get()); | |
227 scoped_ptr<WebContentsImpl> tab( | |
228 WebContentsImpl::CreateWithOpener(params, NULL)); | |
229 tab->GetController().LoadURL(kUrl, Referrer(), PAGE_TRANSITION_TYPED, ""); | |
Charlie Reis
2013/06/05 19:07:10
How does example.com commit? Am I missing a part
Jeffrey Yasskin
2013/06/05 21:23:27
No, AFAIK, there's no hard-coded response for it,
Charlie Reis
2013/06/05 21:54:09
Well, we usually use a TestWebContents and call Te
Jeffrey Yasskin
2013/06/05 22:53:20
I started this test with a TestWebContents, but Te
| |
230 base::RunLoop().RunUntilIdle(); | |
231 ASSERT_EQ(1U, child_processes_.size()); | |
232 | |
233 child_processes_[0]->messages_.ClearMessages(); | |
234 | |
235 // Double-check that the loading tab isn't pending. | |
236 EXPECT_EQ(NULL, | |
Charlie Reis
2013/06/05 19:07:10
Any reason not to use EXPECT_FALSE? That seems to
Jeffrey Yasskin
2013/06/05 21:23:27
No particular reason. Done.
| |
237 tab->GetRenderManagerForTesting()->pending_render_view_host()); | |
238 EXPECT_EQ(1, | |
239 static_cast<RenderProcessHostImpl*>( | |
240 tab->GetSiteInstance()->GetProcess())->GetActiveViewCount()); | |
241 | |
242 // Sometimes the renderer process's ShutdownRequest (corresponding to the | |
243 // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until | |
Charlie Reis
2013/06/05 19:07:10
Interesting. I like that we don't have to simulat
| |
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()); | |
Charlie Reis
2013/06/05 19:07:10
nit: -> on previous line
Jeffrey Yasskin
2013/06/05 21:23:27
Done. Since clang-format puts the -> at the beginn
Charlie Reis
2013/06/05 21:54:09
Thanks. Not sure if there's an official rule on t
| |
248 base::RunLoop().RunUntilIdle(); | |
249 EXPECT_EQ(NULL, | |
Charlie Reis
2013/06/05 19:07:10
EXPECT_FALSE?
Jeffrey Yasskin
2013/06/05 21:23:27
Done.
| |
250 child_processes_[0]->messages_.GetFirstMessageMatching( | |
251 ChildProcessMsg_Shutdown::ID)) | |
252 << "Host sent a message confirming that the renderer process should shut " | |
253 << "down even though it was still being used."; | |
254 } | |
255 | |
256 } // namespace | |
257 } // namespace content | |
OLD | NEW |