Index: content/browser/site_per_process_browsertest.cc |
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc |
index e00123815bc6da00b34ab33fa3ce1dd3fd9fb28e..f61bdb707805bfc763abc03b11f40ba3a592ce70 100644 |
--- a/content/browser/site_per_process_browsertest.cc |
+++ b/content/browser/site_per_process_browsertest.cc |
@@ -31,8 +31,10 @@ |
#include "content/browser/renderer_host/render_view_host_impl.h" |
#include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
#include "content/browser/renderer_host/render_widget_host_view_aura.h" |
+#include "content/common/child_process_messages.h" |
#include "content/common/frame_messages.h" |
#include "content/common/input/synthetic_tap_gesture_params.h" |
+#include "content/common/input_messages.h" |
#include "content/common/view_messages.h" |
#include "content/public/browser/cert_store.h" |
#include "content/public/browser/notification_observer.h" |
@@ -6413,4 +6415,83 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
crash_renderer_and_wait_for_input_state_none(host_process); |
} |
+// Helper class to wait for a ChildProcessHostMsg_ShutdownRequest message to |
+// arrive. |
+class ShutdownRequestMessageFilter : public BrowserMessageFilter { |
+ public: |
+ ShutdownRequestMessageFilter() |
+ : BrowserMessageFilter(ChildProcessMsgStart), |
+ message_loop_runner_(new MessageLoopRunner) {} |
+ |
+ bool OnMessageReceived(const IPC::Message& message) override { |
+ if (message.type() == ChildProcessHostMsg_ShutdownRequest::ID) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ShutdownRequestMessageFilter::OnShutdownRequest, this)); |
+ } |
+ return false; |
+ } |
+ |
+ void OnShutdownRequest() { message_loop_runner_->Quit(); } |
+ |
+ void Wait() { message_loop_runner_->Run(); } |
+ |
+ private: |
+ ~ShutdownRequestMessageFilter() override {} |
+ |
+ scoped_refptr<MessageLoopRunner> message_loop_runner_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ShutdownRequestMessageFilter); |
+}; |
+ |
+// Test for https://crbug.com/568836. From an A-embed-B page, navigate the |
+// subframe from B to A. This cleans up the process for B, but the test delays |
+// the browser side from killing the B process right away. This allows the |
+// B process to process two ViewMsg_Close messages sent to the subframe's |
+// RenderWidget and RenderView, in that order. In the bug, the latter crashed |
nasko
2016/04/18 21:24:26
nit: "and the RenderView".
alexmos
2016/04/22 05:09:34
Done. (I used "and to the RenderView".)
|
+// while detaching the subframe's LocalFrame (triggered as part of closing the |
+// RenderView), because this tried to access the subframe's WebFrameWidget |
+// (from RenderFrameImpl::didChangeSelection), which had already been cleared |
+// by the former. |
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, |
+ CloseSubframeWidgetAndViewOnProcessExit) { |
+ GURL main_url(embedded_test_server()->GetURL( |
+ "a.com", "/cross_site_iframe_factory.html?a(b)")); |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
+ |
+ FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
+ ->GetFrameTree() |
+ ->root(); |
+ |
+ // "Select all" in the subframe. The bug only happens if there's a selection |
+ // change, which triggers the path through didChangeSelection. |
+ root->child_at(0)->current_frame_host()->Send(new InputMsg_SelectAll( |
+ root->child_at(0)->current_frame_host()->GetRoutingID())); |
+ |
+ // Prevent b.com process from terminating right away once the subframe |
+ // navigates away from b.com below. This is necessary so that the renderer |
+ // process has time to process the closings of RenderWidget and RenderView, |
+ // which is where the original bug was triggered. Incrementing worker |
+ // RefCount will cause RenderProcessHostImpl::Cleanup to forego process |
+ // termination. |
+ RenderProcessHost* subframe_process = |
+ root->child_at(0)->current_frame_host()->GetProcess(); |
+ subframe_process->IncrementWorkerRefCount(); |
+ |
+ // Navigate the subframe away from b.com. Since this is the last active |
+ // frame in the b.com process, this causes the RenderWidget and RenderView to |
+ // be closed. If this succeeds without crashing, the renderer will release |
+ // the process and send a ChildProcessHostMsg_ShutdownRequest to the browser |
+ // process to ask whether it's ok to terminate. Thus, wait for this message |
+ // to ensure that the RenderView and widget were closed without crashing. |
+ scoped_refptr<ShutdownRequestMessageFilter> filter = |
+ new ShutdownRequestMessageFilter(); |
+ subframe_process->AddFilter(filter.get()); |
+ NavigateFrameToURL(root->child_at(0), |
+ embedded_test_server()->GetURL("a.com", "/title1.html")); |
+ filter->Wait(); |
+ |
+ subframe_process->DecrementWorkerRefCount(); |
nasko
2016/04/18 21:24:26
Do we care to wait for process termination at all?
alexmos
2016/04/22 05:09:34
Correct, we don't need to wait for process termina
|
+} |
+ |
} // namespace content |