Don't show hung-renderer-dialog for background tabs.
Bug: 881812
Change-Id: I7e9eddb4386baac05e9292463f9f597132c5c14e
Reviewed-on: https://chromium-review.googlesource.com/1222684
Commit-Queue: Ćukasz Anforowicz <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Cr-Commit-Position: refs/heads/master@{#593270}
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index a7b5746a..8f1dd731 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -38,6 +38,7 @@
#include "base/test/test_timeouts.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/timer.h"
#include "build/build_config.h"
#include "cc/input/touch_action.h"
#include "components/network_session_configurator/common/network_switches.h"
@@ -13093,9 +13094,13 @@
~UnresponsiveRendererObserver() override {}
- RenderProcessHost* Wait() {
- if (!captured_render_process_host_)
+ RenderProcessHost* Wait(base::TimeDelta timeout = base::TimeDelta::Max()) {
+ if (!captured_render_process_host_) {
+ base::OneShotTimer timer;
+ timer.Start(FROM_HERE, timeout, run_loop_.QuitClosure());
run_loop_.Run();
+ timer.Stop();
+ }
return captured_render_process_host_;
}
@@ -13114,26 +13119,20 @@
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
CommitTimeoutForHungRenderer) {
// Navigate first tab to a.com.
- GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
- EXPECT_TRUE(NavigateToURL(shell(), url));
+ GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), a_url));
RenderProcessHost* a_process =
shell()->web_contents()->GetMainFrame()->GetProcess();
- // Use window.open to open b.com in a second tab. Using a renderer-initiated
- // navigation is important to leave a.com and b.com SiteInstances in the same
+ // Open b.com in a second tab. Using a renderer-initiated navigation is
+ // important to leave a.com and b.com SiteInstances in the same
// BrowsingInstance (so the b.com -> a.com navigation in the next test step
// will reuse the process associated with the first a.com tab).
- const char* kWindowOpenScript = R"(
- var anchor = document.createElement("a");
- anchor.href = "/cross-site/b.com/title2.html";
- anchor.target = "_blank";
- document.body.appendChild(anchor);
- anchor.click(); )";
- WebContentsAddedObserver new_window_observer;
- EXPECT_TRUE(ExecuteScript(shell()->web_contents(), kWindowOpenScript));
- WebContents* new_window = new_window_observer.GetWebContents();
- EXPECT_TRUE(WaitForLoadStop(new_window));
- RenderProcessHost* b_process = new_window->GetMainFrame()->GetProcess();
+ GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
+ Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
+ WebContents* new_contents = new_shell->web_contents();
+ EXPECT_TRUE(WaitForLoadStop(new_contents));
+ RenderProcessHost* b_process = new_contents->GetMainFrame()->GetProcess();
EXPECT_NE(a_process, b_process);
// Hang the first tab's renderer.
@@ -13144,13 +13143,10 @@
// the hung process.
NavigationHandleImpl::SetCommitTimeoutForTesting(
base::TimeDelta::FromMilliseconds(100));
- const char* kNavigationScript = R"(
- var anchor = document.createElement("a");
- anchor.href = "/cross-site/a.com/title3.html";
- document.body.appendChild(anchor);
- anchor.click(); )";
- UnresponsiveRendererObserver unresponsive_renderer_observer(new_window);
- EXPECT_TRUE(ExecuteScript(new_window, kNavigationScript));
+ GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
+ UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
+ EXPECT_TRUE(
+ ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
// Verify that we will be notified about the unresponsive renderer. Before
// changes in https://crrev.com/c/1089797, the test would hang here forever.
@@ -13161,6 +13157,58 @@
NavigationHandleImpl::SetCommitTimeoutForTesting(base::TimeDelta());
}
+// This is a regression test for https://crbug.com/881812 which complained that
+// the hung renderer dialog used to undesirably show up for background tabs
+// (typically during session restore when many navigations would be happening in
+// backgrounded processes).
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+ CommitTimeoutForInvisibleWebContents) {
+ // Navigate first tab to a.com.
+ GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), a_url));
+ RenderProcessHost* a_process =
+ shell()->web_contents()->GetMainFrame()->GetProcess();
+
+ // Open b.com in a second tab. Using a renderer-initiated navigation is
+ // important to leave a.com and b.com SiteInstances in the same
+ // BrowsingInstance (so the b.com -> a.com navigation in the next test step
+ // will reuse the process associated with the first a.com tab).
+ GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
+ Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
+ WebContents* new_contents = new_shell->web_contents();
+ EXPECT_TRUE(WaitForLoadStop(new_contents));
+ RenderProcessHost* b_process = new_contents->GetMainFrame()->GetProcess();
+ EXPECT_NE(a_process, b_process);
+
+ // Hang the first tab's renderer.
+ const char* kHungScript = "setTimeout(function() { for (;;) {}; }, 0);";
+ EXPECT_TRUE(ExecuteScript(shell()->web_contents(), kHungScript));
+
+ // Hide the second tab. This should prevent reporting of hangs in this tab
+ // (see https://crbug.com/881812).
+ new_contents->WasHidden();
+ EXPECT_EQ(Visibility::HIDDEN, new_contents->GetVisibility());
+
+ // Attempt to navigate the second tab to a.com. This will attempt to reuse
+ // the hung process.
+ base::TimeDelta kTimeout = base::TimeDelta::FromMilliseconds(100);
+ NavigationHandleImpl::SetCommitTimeoutForTesting(kTimeout);
+ GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
+ UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
+ EXPECT_TRUE(
+ ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
+
+ // Verify that we will not be notified about the unresponsive renderer.
+ // Before changes in https://crrev.com/c/1089797, the test would get notified
+ // and therefore |hung_process| would be non-null.
+ RenderProcessHost* hung_process =
+ unresponsive_renderer_observer.Wait(kTimeout * 10);
+ EXPECT_FALSE(hung_process);
+
+ // Reset the timeout.
+ NavigationHandleImpl::SetCommitTimeoutForTesting(base::TimeDelta());
+}
+
// Tests that an inner WebContents will reattach to its outer WebContents after
// a navigation that causes a process swap.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProcessSwapOnInnerContents) {
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index acf0ba9..09f197b 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5966,15 +5966,22 @@
void WebContentsImpl::RendererUnresponsive(
RenderWidgetHostImpl* render_widget_host,
base::RepeatingClosure hang_monitor_restarter) {
- for (auto& observer : observers_)
- observer.OnRendererUnresponsive(render_widget_host->GetProcess());
-
if (ShouldIgnoreUnresponsiveRenderer())
return;
+ // Do not report hangs (to task manager, to hang renderer dialog, etc.) for
+ // invisible tabs (like extension background page, background tabs). See
+ // https://crbug.com/881812 for rationale and for choosing the visibility
+ // (rather than process priority) as the signal here.
+ if (GetVisibility() != Visibility::VISIBLE)
+ return;
+
if (!render_widget_host->renderer_initialized())
return;
+ for (auto& observer : observers_)
+ observer.OnRendererUnresponsive(render_widget_host->GetProcess());
+
if (delegate_)
delegate_->RendererUnresponsive(this, render_widget_host,
std::move(hang_monitor_restarter));