| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/sad_tab_helper.h" |
| |
| #include "base/features.h" |
| #include "base/test/run_until.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/sad_tab.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/no_renderer_crashes_assertion.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/process/process.h" |
| #include "chrome/common/chrome_result_codes.h" |
| #endif |
| |
| class SadTabHelperBrowserTest : public InProcessBrowserTest { |
| public: |
| SadTabHelperBrowserTest() = default; |
| SadTabHelperBrowserTest(const SadTabHelperBrowserTest&) = delete; |
| SadTabHelperBrowserTest& operator=(const SadTabHelperBrowserTest&) = delete; |
| |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature( |
| base::features::kUseTerminationStatusMemoryExhaustion); |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // This test is Windows-only because the mechanism it verifies—proactively |
| // terminating a renderer process to prevent a commit failure—is a |
| // Windows-specific memory-saving intervention within PartitionAlloc. The test |
| // simulates this termination using the specific exit code generated by that |
| // mechanism, which is only meaningful on Windows. |
| #if BUILDFLAG(IS_WIN) |
| IN_PROC_BROWSER_TEST_F( |
| SadTabHelperBrowserTest, |
| ProcessTerminatedWithEvictionExitCodeIsDiscarded_IfHidden) { |
| content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes; |
| const GURL url(embedded_test_server()->GetURL("/title1.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Terminate the first tab (at index 0). |
| const int target_tab_index = 0; |
| content::WebContents* web_contents_to_kill = |
| browser()->tab_strip_model()->GetWebContentsAt(target_tab_index); |
| ASSERT_TRUE(web_contents_to_kill); |
| |
| // Open a new tab to make the first one hidden. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL(url.spec() + "#2"), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| ASSERT_EQ(web_contents_to_kill->GetVisibility(), content::Visibility::HIDDEN); |
| |
| web_contents_to_kill->GetPrimaryMainFrame() |
| ->GetProcess() |
| ->GetProcess() |
| .Terminate( |
| CHROME_RESULT_CODE_TERMINATED_BY_OTHER_PROCESS_ON_COMMIT_FAILURE, |
| false); |
| |
| // Poll the TabStripModel to get the current WebContents at the target |
| // index and check its state. |
| EXPECT_TRUE(base::test::RunUntil([&]() { |
| content::WebContents* current_web_contents = |
| browser()->tab_strip_model()->GetWebContentsAt(target_tab_index); |
| // It's possible for the WebContents to be briefly null during the swap. |
| return current_web_contents && current_web_contents->WasDiscarded(); |
| })); |
| } |
| #endif // BUILDFLAG(IS_WIN) |