blob: 165fe6f89936c18410a447bb6269c320a31e39ab [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resource_coordinator/tab_manager.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/check.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/notreached.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resource_coordinator/tab_helper.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/resource_coordinator/tab_manager_features.h"
#include "chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h"
#include "chrome/browser/resource_coordinator/tab_manager_stats_collector.h"
#include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
#include "chrome/browser/resource_coordinator/time.h"
#include "chrome/browser/resource_coordinator/utils.h"
#include "chrome/browser/sessions/tab_loader.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tab_ui_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
#include "components/variations/variations_associated_data.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/mock_navigation_handle.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/web_contents_tester.h"
#include "net/base/network_change_notifier.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using content::MockNavigationHandle;
using content::NavigationThrottle;
using content::WebContents;
using content::WebContentsTester;
namespace resource_coordinator {
class TabManagerTest : public ChromeRenderViewHostTestHarness {
public:
TabManagerTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
// Start with a non-zero time.
task_environment()->FastForwardBy(base::Seconds(42));
}
std::unique_ptr<WebContents> CreateWebContents() {
std::unique_ptr<WebContents> web_contents = CreateTestWebContents();
ResourceCoordinatorTabHelper::CreateForWebContents(web_contents.get());
// Commit an URL to allow discarding.
content::WebContentsTester::For(web_contents.get())
->NavigateAndCommit(GURL("https://www.example.com"));
return web_contents;
}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
tab_manager_ = g_browser_process->GetTabManager();
}
bool IsTabDiscarded(content::WebContents* content) {
return TabLifecycleUnitExternal::FromWebContents(content)->IsDiscarded();
}
protected:
TabManager* tab_manager_ = nullptr;
};
// TODO(georgesak): Add tests for protection to tabs with form input and
// playing audio;
TEST_F(TabManagerTest, IsInternalPage) {
EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDownloadsURL)));
EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIHistoryURL)));
EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUINewTabURL)));
EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUISettingsURL)));
// Debugging URLs are not included.
#if BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_FALSE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDiscardsURL)));
#endif
EXPECT_FALSE(
TabManager::IsInternalPage(GURL(chrome::kChromeUINetInternalsURL)));
// Prefix matches are included.
GURL::Replacements replace_fake_path;
replace_fake_path.SetPathStr("fakeSetting");
EXPECT_TRUE(TabManager::IsInternalPage(
GURL(chrome::kChromeUISettingsURL).ReplaceComponents(replace_fake_path)));
}
// Data race on Linux. http://crbug.com/787842
// Flaky on Mac and Windows: https://crbug.com/995682
#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
defined(OS_WIN)
#define MAYBE_DiscardTabWithNonVisibleTabs DISABLED_DiscardTabWithNonVisibleTabs
#else
#define MAYBE_DiscardTabWithNonVisibleTabs DiscardTabWithNonVisibleTabs
#endif
// Verify that:
// - On ChromeOS, DiscardTab can discard every non-visible tab, but cannot
// discard a visible tab.
// - On other platforms, DiscardTab can discard every tab that is not active in
// its tab strip.
TEST_F(TabManagerTest, MAYBE_DiscardTabWithNonVisibleTabs) {
// Create 2 tab strips. Simulate the second tab strip being hidden by hiding
// its active tab.
auto window1 = std::make_unique<TestBrowserWindow>();
Browser::CreateParams params1(profile(), true);
params1.type = Browser::TYPE_NORMAL;
params1.window = window1.get();
auto browser1 = std::unique_ptr<Browser>(Browser::Create(params1));
TabStripModel* tab_strip1 = browser1->tab_strip_model();
tab_strip1->AppendWebContents(CreateWebContents(), true);
tab_strip1->AppendWebContents(CreateWebContents(), false);
tab_strip1->GetWebContentsAt(0)->WasShown();
tab_strip1->GetWebContentsAt(1)->WasHidden();
auto window2 = std::make_unique<TestBrowserWindow>();
Browser::CreateParams params2(profile(), true);
params2.type = Browser::TYPE_NORMAL;
params2.window = window2.get();
auto browser2 = std::unique_ptr<Browser>(Browser::Create(params1));
TabStripModel* tab_strip2 = browser2->tab_strip_model();
tab_strip2->AppendWebContents(CreateWebContents(), true);
tab_strip2->AppendWebContents(CreateWebContents(), false);
tab_strip2->GetWebContentsAt(0)->WasHidden();
tab_strip2->GetWebContentsAt(1)->WasHidden();
// Advance time enough that the tabs are urgent discardable.
task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
for (int i = 0; i < 4; ++i)
tab_manager_->DiscardTab(LifecycleUnitDiscardReason::URGENT);
// Active tab in a visible window should not be discarded.
EXPECT_FALSE(IsTabDiscarded(tab_strip1->GetWebContentsAt(0)));
// Non-active tabs should be discarded.
EXPECT_TRUE(IsTabDiscarded(tab_strip1->GetWebContentsAt(1)));
EXPECT_TRUE(IsTabDiscarded(tab_strip2->GetWebContentsAt(1)));
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On ChromeOS, a non-visible tab should be discarded even if it's active in
// its tab strip.
EXPECT_TRUE(IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
#else
// On other platforms, an active tab is never discarded, even if it's not
// visible.
EXPECT_FALSE(IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
// Tabs with a committed URL must be closed explicitly to avoid DCHECK errors.
tab_strip1->CloseAllTabs();
tab_strip2->CloseAllTabs();
}
TEST_F(TabManagerTest, IsTabRestoredInForeground) {
std::unique_ptr<WebContents> contents = CreateWebContents();
contents->WasShown();
tab_manager_->OnWillRestoreTab(contents.get());
EXPECT_TRUE(tab_manager_->IsTabRestoredInForeground(contents.get()));
contents = CreateWebContents();
contents->WasHidden();
tab_manager_->OnWillRestoreTab(contents.get());
EXPECT_FALSE(tab_manager_->IsTabRestoredInForeground(contents.get()));
}
TEST_F(TabManagerTest, GetSortedLifecycleUnits) {
auto window = std::make_unique<TestBrowserWindow>();
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_NORMAL;
params.window = window.get();
auto browser = std::unique_ptr<Browser>(Browser::Create(params));
TabStripModel* tab_strip = browser->tab_strip_model();
const int num_of_tabs_to_test = 20;
for (int i = 0; i < num_of_tabs_to_test; ++i) {
task_environment()->FastForwardBy(base::Seconds(10));
tab_strip->AppendWebContents(CreateWebContents(), /*foreground=*/true);
}
LifecycleUnitVector lifecycle_units = tab_manager_->GetSortedLifecycleUnits();
EXPECT_EQ(lifecycle_units.size(), static_cast<size_t>(num_of_tabs_to_test));
// Check that the lifecycle_units are sorted with ascending importance.
for (int i = 0; i < num_of_tabs_to_test - 1; ++i) {
EXPECT_TRUE(lifecycle_units[i]->GetSortKey() <
lifecycle_units[i + 1]->GetSortKey());
}
tab_strip->CloseAllTabs();
}
} // namespace resource_coordinator