1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
// Copyright 2021 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/sessions/session_data_service.h"
#include <memory>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/sessions/session_data_deleter.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Invoke;
using testing::Mock;
using testing::StrictMock;
namespace {
// A test version of SessionDataDeleter that doesn't actually do any deletion.
class TestSessionDataDeleter : public SessionDataDeleter {
public:
TestSessionDataDeleter() : SessionDataDeleter(nullptr) {}
MOCK_METHOD2(DeleteSessionOnlyData, void(bool, base::OnceClosure));
};
// Helper to run the callback received by DeleteSessionOnlyData.
void RunCallback(bool skip_session_cookies, base::OnceClosure callback) {
std::move(callback).Run();
}
} // namespace
class SessionDataServiceTest : public BrowserWithTestWindowTest {
public:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
auto cookie_settings = CookieSettingsFactory::GetForProfile(profile());
cookie_settings->SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY);
profile()->SetExtensionSpecialStoragePolicy(
base::MakeRefCounted<ExtensionSpecialStoragePolicy>(
cookie_settings.get()));
RestartService(CreateDeleter());
}
void TearDown() override {
session_data_service_.reset();
browser_shutdown::SetTryingToQuit(false);
BrowserWithTestWindowTest::TearDown();
}
std::unique_ptr<StrictMock<TestSessionDataDeleter>> CreateDeleter() {
return std::make_unique<StrictMock<TestSessionDataDeleter>>();
}
// Simulates Chrome being restarted from the SessionDataService's perspective.
void RestartService(
std::unique_ptr<StrictMock<TestSessionDataDeleter>> deleter) {
session_data_deleter_ = deleter.get();
session_data_service_ =
std::make_unique<SessionDataService>(profile(), std::move(deleter));
}
StrictMock<TestSessionDataDeleter>* deleter() {
return session_data_deleter_;
}
SessionDataService* service() { return session_data_service_.get(); }
private:
raw_ptr<StrictMock<TestSessionDataDeleter>, DanglingUntriaged>
session_data_deleter_;
std::unique_ptr<SessionDataService> session_data_service_;
};
TEST_F(SessionDataServiceTest, StartCleanup) {
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(false, _));
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, CleanupOnWindowClosed) {
const BrowserList* browser_list = BrowserList::GetInstance();
EXPECT_EQ(1U, browser_list->size());
auto new_window = CreateBrowserWindow();
auto new_browser =
CreateBrowser(profile(), Browser::TYPE_NORMAL, false, new_window.get());
EXPECT_EQ(2U, browser_list->size());
new_browser.reset();
EXPECT_EQ(1U, browser_list->size());
Mock::VerifyAndClearExpectations(deleter());
bool skip_session_cookies = browser_defaults::kBrowserAliveWithNoWindows;
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(skip_session_cookies, _));
set_browser(nullptr);
EXPECT_EQ(0U, browser_list->size());
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, CleanupOnWindowClosedWithOtherProfileOpen) {
const BrowserList* browser_list = BrowserList::GetInstance();
EXPECT_EQ(1U, browser_list->size());
auto* new_profile = profile_manager()->CreateTestingProfile("second_profile");
auto new_window = CreateBrowserWindow();
auto new_browser =
CreateBrowser(new_profile, Browser::TYPE_NORMAL, false, new_window.get());
EXPECT_EQ(2U, browser_list->size());
bool skip_session_cookies = browser_defaults::kBrowserAliveWithNoWindows;
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(skip_session_cookies, _));
set_browser(nullptr);
EXPECT_EQ(1U, browser_list->size());
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, RepeatCleanupAfterNewWindowOpened) {
// Close browser and expect cleanup.
bool skip_session_cookies = browser_defaults::kBrowserAliveWithNoWindows;
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(skip_session_cookies, _));
set_browser(nullptr);
Mock::VerifyAndClearExpectations(deleter());
// Additional requests for cleanup will be ignored.
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// Unless a new browser is opened.
auto new_window = CreateBrowserWindow();
auto new_browser =
CreateBrowser(profile(), Browser::TYPE_NORMAL, false, new_window.get());
Mock::VerifyAndClearExpectations(deleter());
// And another cleanup is started.
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(false, _));
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, SkipOnShutdown) {
browser_shutdown::SetTryingToQuit(true);
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// No deletion during shutdown but the deletion will continue on startup.
browser_shutdown::SetTryingToQuit(false);
auto new_deleter = CreateDeleter();
EXPECT_CALL(*new_deleter, DeleteSessionOnlyData(true, _));
RestartService(std::move(new_deleter));
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, NoContinuedDeletionWithoutSettings) {
browser_shutdown::SetTryingToQuit(true);
CookieSettingsFactory::GetForProfile(profile())->SetDefaultCookieSetting(
CONTENT_SETTING_ALLOW);
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// No deletion during shutdown and no deletion on startup without SESSION_ONLY
// setting.
browser_shutdown::SetTryingToQuit(false);
RestartService(CreateDeleter());
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, ContinueUnfinishedDeletions) {
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(false, _));
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// Deletion is not marked as finished, so it will continue on restart.
auto new_deleter = CreateDeleter();
EXPECT_CALL(*new_deleter, DeleteSessionOnlyData(true, _))
.WillOnce(Invoke(&RunCallback));
RestartService(std::move(new_deleter));
Mock::VerifyAndClearExpectations(deleter());
// At shutdown, another deletion is started. This time it finishes.
EXPECT_CALL(*deleter(), DeleteSessionOnlyData(false, _))
.WillOnce(Invoke(&RunCallback));
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// A finished deletion does not continue after restart.
RestartService(CreateDeleter());
Mock::VerifyAndClearExpectations(deleter());
}
TEST_F(SessionDataServiceTest, SkipOnForceSessionState) {
// No deletion when state should be kept.
service()->SetForceKeepSessionState();
service()->StartCleanup();
Mock::VerifyAndClearExpectations(deleter());
// Also deletion on restart after state was kept.
RestartService(CreateDeleter());
Mock::VerifyAndClearExpectations(deleter());
}
|