// 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/visited_url_ranking/model/ios_tab_model_url_visit_data_fetcher.h"
#import "components/sync_device_info/device_info.h"
#import "components/url_deduplication/url_deduplication_helper.h"
#import "components/visited_url_ranking/public/fetcher_config.h"
#import "components/visited_url_ranking/public/url_visit.h"
#import "components/visited_url_ranking/public/url_visit_util.h"
#import "ios/chrome/browser/sessions/model/ios_chrome_session_tab_helper.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/browser/browser_list_factory.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/components/webui/web_ui_url_constants.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/web_state.h"
#import "ui/base/device_form_factor.h"
namespace visited_url_ranking {
namespace {
base::Time GetWebStateLastModificationTime(web::WebState* web_state) {
base::Time last_modification = web_state->GetLastActiveTime();
if (web_state->IsRealized()) {
web::NavigationItem* item =
web_state->GetNavigationManager()->GetLastCommittedItem();
if (item) {
last_modification = item->GetTimestamp();
}
}
return last_modification;
}
URLVisitAggregate::Tab MakeAggregateTabFromWebState(
web::WebState* web_state,
base::Time last_modification) {
syncer::DeviceInfo::FormFactor form_factor =
ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
? syncer::DeviceInfo::FormFactor::kTablet
: syncer::DeviceInfo::FormFactor::kPhone;
const GURL& url = web_state->GetLastCommittedURL();
visited_url_ranking::URLVisit visit(
url, web_state->GetTitle(), last_modification, form_factor,
visited_url_ranking::URLVisit::Source::kLocal);
int32_t tab_id =
IOSChromeSessionTabHelper::FromWebState(web_state)->session_id().id();
URLVisitAggregate::Tab tab(tab_id, visit);
return tab;
}
} // namespace
IOSTabModelURLVisitDataFetcher::IOSTabModelURLVisitDataFetcher(
ChromeBrowserState* browser_state)
: browser_state_(browser_state) {}
IOSTabModelURLVisitDataFetcher::~IOSTabModelURLVisitDataFetcher() {}
void IOSTabModelURLVisitDataFetcher::FetchURLVisitData(
const FetchOptions& options,
const FetcherConfig& config,
FetchResultCallback callback) {
// OTR URL should never be processed.
CHECK(!browser_state_->IsOffTheRecord());
std::map<URLMergeKey, URLVisitAggregate::TabData> url_visit_tab_data_map;
const BrowserList* browser_list =
BrowserListFactory::GetForBrowserState(browser_state_);
for (Browser* browser : browser_list->BrowsersOfType(
BrowserList::BrowserType::kRegularAndInactive)) {
WebStateList* web_state_list = browser->GetWebStateList();
for (int i = 0; i < web_state_list->count(); i++) {
web::WebState* web_state = web_state_list->GetWebStateAt(i);
const GURL& url = web_state->GetLastCommittedURL();
if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) {
continue;
}
base::Time last_modification = GetWebStateLastModificationTime(web_state);
if (last_modification < options.begin_time) {
continue;
}
auto url_key = ComputeURLMergeKey(url, web_state->GetTitle(),
config.deduplication_helper);
auto it = url_visit_tab_data_map.find(url_key);
bool tab_data_map_already_has_url_entry =
(it != url_visit_tab_data_map.end());
if (!tab_data_map_already_has_url_entry) {
url_visit_tab_data_map.emplace(
url_key,
MakeAggregateTabFromWebState(web_state, last_modification));
}
auto& tab_data = url_visit_tab_data_map.at(url_key);
if (tab_data_map_already_has_url_entry) {
if (tab_data.last_active_tab.visit.last_modified < last_modification) {
tab_data.last_active_tab =
MakeAggregateTabFromWebState(web_state, last_modification);
}
tab_data.tab_count += 1;
}
tab_data.last_active =
std::max(tab_data.last_active, web_state->GetLastActiveTime());
tab_data.pinned =
tab_data.pinned || web_state_list->IsWebStatePinnedAt(i);
tab_data.in_group =
tab_data.in_group || web_state_list->GetGroupOfWebStateAt(i);
}
}
std::map<URLMergeKey, URLVisitAggregate::URLVisitVariant>
url_visit_variant_map;
std::transform(
std::make_move_iterator(url_visit_tab_data_map.begin()),
std::make_move_iterator(url_visit_tab_data_map.end()),
std::inserter(url_visit_variant_map, url_visit_variant_map.end()),
[](auto kv) { return std::make_pair(kv.first, std::move(kv.second)); });
std::move(callback).Run(
{FetchResult::Status::kSuccess, std::move(url_visit_variant_map)});
}
} // namespace visited_url_ranking