| // 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 "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "chrome/browser/defaults.h" |
| #include "chrome/browser/lifetime/browser_shutdown.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sessions/exit_type_service.h" |
| #include "chrome/browser/sessions/session_data_deleter.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "storage/browser/quota/special_storage_policy.h" |
| |
| namespace { |
| // Pref name for Status preference. |
| extern const char kSessionDataStatusPref[] = "sessions.session_data_status"; |
| } // namespace |
| |
| // static |
| void SessionDataService::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterIntegerPref(kSessionDataStatusPref, |
| static_cast<int>(Status::kUninitialized)); |
| } |
| |
| SessionDataService::SessionDataService( |
| Profile* profile, |
| std::unique_ptr<SessionDataDeleter> deleter) |
| : profile_(profile), deleter_(std::move(deleter)) { |
| DCHECK(profile_); |
| DCHECK(!profile_->IsOffTheRecord()); |
| DCHECK(deleter_); |
| |
| Status last_status = static_cast<Status>( |
| profile_->GetPrefs()->GetInteger(kSessionDataStatusPref)); |
| |
| int int_status = static_cast<int>(last_status); |
| if (int_status < 0 || int_status > static_cast<int>(Status::kMaxValue)) |
| last_status = Status::kUninitialized; |
| |
| SetStatusPref(Status::kInitialized); |
| auto* policy = profile_->GetSpecialStoragePolicy(); |
| if (policy && policy->HasSessionOnlyOrigins()) { |
| MaybeContinueDeletionFromLastSesssion(last_status); |
| } |
| |
| for (Browser* browser : *BrowserList::GetInstance()) |
| OnBrowserAdded(browser); |
| |
| BrowserList::AddObserver(this); |
| } |
| |
| SessionDataService::~SessionDataService() { |
| BrowserList::RemoveObserver(this); |
| } |
| |
| void SessionDataService::MaybeContinueDeletionFromLastSesssion( |
| Status last_status) { |
| switch (last_status) { |
| case Status::kUninitialized: |
| case Status::kDeletionFinished: |
| case Status::kNoDeletionDueToForceKeepSessionData: |
| return; // No deletion needed. |
| case Status::kInitialized: |
| // Deletion did not happen on shutdown and we didn't even update the |
| // status preference. Check profile status: |
| switch (ExitTypeService::GetLastSessionExitType(profile_)) { |
| case ExitType::kCrashed: |
| // To allow the user to continue a session after a crash, we will not |
| // delete cookies. |
| return; |
| case ExitType::kClean: |
| case ExitType::kForcedShutdown: |
| // In case of a regular shutdown that skipped deletion, we should |
| // delete cookies. |
| break; |
| } |
| break; |
| case Status::kDeletionStarted: |
| case Status::kNoDeletionDueToShutdown: |
| break; // Deletion needed. |
| } |
| |
| // Skip session cookie deletion as they already get cleared on startup. |
| // (SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnStartup) |
| deleter_->DeleteSessionOnlyData( |
| /*skip_session_cookies=*/true, |
| base::BindOnce(&SessionDataService::OnCleanupAtStartupFinished, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SessionDataService::OnCleanupAtStartupFinished() {} |
| |
| void SessionDataService::SetStatusPref(Status status) { |
| profile_->GetPrefs()->SetInteger(kSessionDataStatusPref, |
| static_cast<int>(status)); |
| } |
| |
| void SessionDataService::OnBrowserAdded(Browser* browser) { |
| if (browser->profile() != profile_) |
| return; |
| |
| // A window was opened. Ensure that we run another cleanup the next time |
| // all windows are closed. |
| SetStatusPref(Status::kInitialized); |
| cleanup_started_ = false; |
| } |
| |
| void SessionDataService::OnBrowserRemoved(Browser* browser) { |
| if (browser->profile() != profile_) |
| return; |
| |
| // Check for any open windows for the current profile. |
| for (Browser* open_browser : *BrowserList::GetInstance()) { |
| if (open_browser->profile() == profile_) |
| return; |
| } |
| |
| // Session cookies should stay alive on platforms where the browser stays |
| // alive without windows. |
| bool skip_session_cookies = browser_defaults::kBrowserAliveWithNoWindows; |
| |
| // Clear session data if the last window for a profile has been closed. |
| StartCleanupInternal(skip_session_cookies); |
| } |
| |
| void SessionDataService::StartCleanup() { |
| StartCleanupInternal(false); |
| } |
| |
| void SessionDataService::StartCleanupInternal(bool skip_session_cookies) { |
| if (cleanup_started_) |
| return; |
| |
| if (force_keep_session_state_) |
| return; |
| |
| if (browser_shutdown::IsTryingToQuit()) { |
| SetStatusPref(Status::kNoDeletionDueToShutdown); |
| return; |
| } |
| |
| cleanup_started_ = true; |
| SetStatusPref(Status::kDeletionStarted); |
| |
| deleter_->DeleteSessionOnlyData( |
| skip_session_cookies, |
| base::BindOnce(&SessionDataService::OnCleanupAtSessionEndFinished, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SessionDataService::OnCleanupAtSessionEndFinished() { |
| SetStatusPref(Status::kDeletionFinished); |
| } |
| |
| void SessionDataService::SetForceKeepSessionState() { |
| SetStatusPref(Status::kNoDeletionDueToForceKeepSessionData); |
| force_keep_session_state_ = true; |
| } |