blob: 02fe26c947f7dcfa396860e0a481e30f062f14a3 [file] [log] [blame]
// 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)