| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/browsing_data/model/tabs_counter.h" |
| |
| #import "base/functional/bind.h" |
| #import "components/browsing_data/core/pref_names.h" |
| #import "ios/chrome/browser/sessions/model/session_restoration_service.h" |
| #import "ios/chrome/browser/sessions/model/session_restoration_service_tmpl.h" |
| #import "ios/chrome/browser/shared/model/browser/browser.h" |
| #import "ios/chrome/browser/shared/model/browser/browser_list.h" |
| #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" |
| #import "net/base/completion_repeating_callback.h" |
| |
| TabsCounter::TabsCounter(BrowserList* browser_list, |
| SessionRestorationService* service) |
| : browser_list_(browser_list), service_(service), weak_ptr_factory_(this) { |
| CHECK(browser_list); |
| CHECK(service); |
| } |
| |
| TabsCounter::~TabsCounter() = default; |
| |
| const char* TabsCounter::GetPrefName() const { |
| return browsing_data::prefs::kCloseTabs; |
| } |
| |
| void TabsCounter::Count() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Cancel existing requests and make sure the counters are reset. |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| ResetCounts(); |
| |
| std::set<Browser*> all_regular_browsers = browser_list_->BrowsersOfType( |
| BrowserList::BrowserType::kRegularAndInactive); |
| |
| // Report result early if browser list is empty. |
| if (all_regular_browsers.empty()) { |
| ReportTabsResult(); |
| return; |
| } |
| |
| for (Browser* browser : all_regular_browsers) { |
| pending_tasks_count_++; |
| service_->LoadDataFromStorage( |
| browser, |
| base::BindRepeating( |
| &tabs_closure_util::GetLastCommittedTimestampFromStorage), |
| base::BindOnce(&TabsCounter::OnLoadDataFromStorageResult, |
| weak_ptr_factory_.GetWeakPtr(), browser->AsWeakPtr())); |
| } |
| } |
| |
| void TabsCounter::OnLoadDataFromStorageResult( |
| base::WeakPtr<Browser> weak_browser, |
| tabs_closure_util::WebStateIDToTime result) { |
| // Ensure that all callbacks are on the same thread, so that we do not need |
| // a mutex for `total_tab_count_`, `total_window_count_` and |
| // `pending_tasks_count_`. |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| Browser* browser = weak_browser.get(); |
| if (!browser) { |
| return; |
| } |
| |
| std::set<web::WebStateID> tabs_to_close = tabs_closure_util::GetTabsToClose( |
| browser->GetWebStateList(), GetPeriodStart(), GetPeriodEnd(), result); |
| int tab_count = tabs_to_close.size(); |
| if (tab_count > 0) { |
| total_tab_count_ += tab_count; |
| |
| // Inactive tabs live in a seperate Browser from their active conterparts. |
| // This avoids overcounting the number of windows that have tabs within the |
| // timeframe (e.g. active and inactive browsers with tabs within the range |
| // in the same window). |
| if (!active_browsers_.contains(browser->GetActiveBrowser())) { |
| total_window_count_++; |
| active_browsers_.insert(browser->GetActiveBrowser()); |
| } |
| |
| // The cached tabs' information should include the timestamp for all tabs |
| // within the time range including pinned tabs. Tabs can be unpined without |
| // being realized, and as such we should make sure we still have their |
| // information cached. |
| cached_tabs_info_.merge(tabs_closure_util::GetTabsInfoForCache( |
| result, GetPeriodStart(), GetPeriodEnd())); |
| } |
| |
| // Check if all tasks have returned. If not, return early. |
| CHECK_GT(pending_tasks_count_, 0); |
| if (--pending_tasks_count_ > 0) { |
| return; |
| } |
| |
| // If all tasks have returned, then report the result and reset counts. |
| ReportTabsResult(); |
| } |
| |
| void TabsCounter::ReportTabsResult() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK_GE(total_tab_count_, 0); |
| CHECK_GE(total_window_count_, 0); |
| |
| auto tabs_result = std::make_unique<TabsCounter::TabsResult>( |
| this, total_tab_count_, total_window_count_, cached_tabs_info_); |
| ReportResult(std::move(tabs_result)); |
| ResetCounts(); |
| } |
| |
| void TabsCounter::ResetCounts() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| total_tab_count_ = 0; |
| total_window_count_ = 0; |
| pending_tasks_count_ = 0; |
| active_browsers_.clear(); |
| cached_tabs_info_.clear(); |
| } |
| |
| // TabsCounter::TabsResult ----------------------------------------- |
| |
| TabsCounter::TabsResult::TabsResult( |
| const TabsCounter* source, |
| ResultInt tab_count, |
| ResultInt window_count, |
| tabs_closure_util::WebStateIDToTime cached_tabs_info) |
| : FinishedResult(source, tab_count), |
| window_count_(window_count), |
| cached_tabs_info_(cached_tabs_info) {} |
| |
| TabsCounter::TabsResult::~TabsResult() = default; |