 Chromium Code Reviews
 Chromium Code Reviews Issue 16267002:
  Re-fix http://crbug.com/87176, and add a test.  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 16267002:
  Re-fix http://crbug.com/87176, and add a test.  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| 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 |