// Copyright 2017 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/tabs/model/tab_helper_util.h"
#import "base/feature_list.h"
#import "components/breadcrumbs/core/breadcrumbs_status.h"
#import "components/commerce/ios/browser/commerce_tab_helper.h"
#import "components/favicon/core/favicon_service.h"
#import "components/favicon/ios/web_favicon_driver.h"
#import "components/history/core/browser/top_sites.h"
#import "components/history/ios/browser/web_state_top_sites_observer.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/language/ios/browser/ios_language_detection_tab_helper.h"
#import "components/omnibox/common/omnibox_features.h"
#import "components/safe_browsing/core/common/features.h"
#import "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h"
#import "components/supervised_user/core/common/features.h"
#import "components/ukm/ios/ukm_url_recorder.h"
#import "ios/chrome/browser/app_launcher/model/app_launcher_abuse_detector.h"
#import "ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.h"
#import "ios/chrome/browser/autofill/model/autofill_tab_helper.h"
#import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
#import "ios/chrome/browser/autofill/model/form_suggestion_tab_helper.h"
#import "ios/chrome/browser/commerce/model/price_alert_util.h"
#import "ios/chrome/browser/commerce/model/price_notifications/price_notifications_tab_helper.h"
#import "ios/chrome/browser/commerce/model/push_notification/push_notification_feature.h"
#import "ios/chrome/browser/commerce/model/shopping_persisted_data_tab_helper.h"
#import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
#import "ios/chrome/browser/complex_tasks/model/ios_task_tab_helper.h"
#import "ios/chrome/browser/contextual_panel/model/contextual_panel_model_service.h"
#import "ios/chrome/browser/contextual_panel/model/contextual_panel_model_service_factory.h"
#import "ios/chrome/browser/contextual_panel/model/contextual_panel_tab_helper.h"
#import "ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_tab_helper.h"
#import "ios/chrome/browser/download/model/ar_quick_look_tab_helper.h"
#import "ios/chrome/browser/download/model/document_download_tab_helper.h"
#import "ios/chrome/browser/download/model/download_manager_tab_helper.h"
#import "ios/chrome/browser/download/model/pass_kit_tab_helper.h"
#import "ios/chrome/browser/download/model/safari_download_tab_helper.h"
#import "ios/chrome/browser/download/model/vcard_tab_helper.h"
#import "ios/chrome/browser/drive/model/drive_tab_helper.h"
#import "ios/chrome/browser/favicon/model/favicon_service_factory.h"
#import "ios/chrome/browser/find_in_page/model/find_tab_helper.h"
#import "ios/chrome/browser/find_in_page/model/java_script_find_tab_helper.h"
#import "ios/chrome/browser/find_in_page/model/util.h"
#import "ios/chrome/browser/follow/model/follow_tab_helper.h"
#import "ios/chrome/browser/history/model/history_service_factory.h"
#import "ios/chrome/browser/history/model/history_tab_helper.h"
#import "ios/chrome/browser/history/model/top_sites_factory.h"
#import "ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h"
#import "ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h"
#import "ios/chrome/browser/https_upgrades/model/typed_navigation_upgrade_tab_helper.h"
#import "ios/chrome/browser/infobars/model/infobar_badge_tab_helper.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/infobars/model/overlays/default_infobar_overlay_request_factory.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_request_inserter.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_tab_helper.h"
#import "ios/chrome/browser/infobars/model/overlays/translate_overlay_tab_helper.h"
#import "ios/chrome/browser/itunes_urls/model/itunes_urls_handler_tab_helper.h"
#import "ios/chrome/browser/lens/model/lens_tab_helper.h"
#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
#import "ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h"
#import "ios/chrome/browser/link_to_text/model/link_to_text_tab_helper.h"
#import "ios/chrome/browser/metrics/model/pageload_foreground_duration_tab_helper.h"
#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_tab_helper.h"
#import "ios/chrome/browser/optimization_guide/model/optimization_guide_validation_tab_helper.h"
#import "ios/chrome/browser/overlays/model/public/overlay_request_queue.h"
#import "ios/chrome/browser/overscroll_actions/model/overscroll_actions_tab_helper.h"
#import "ios/chrome/browser/page_info/about_this_site_tab_helper.h"
#import "ios/chrome/browser/passwords/model/password_controller.h"
#import "ios/chrome/browser/passwords/model/password_tab_helper.h"
#import "ios/chrome/browser/passwords/model/well_known_change_password_tab_helper.h"
#import "ios/chrome/browser/permissions/model/permissions_tab_helper.h"
#import "ios/chrome/browser/policy_url_blocking/model/policy_url_blocking_tab_helper.h"
#import "ios/chrome/browser/prerender/model/prerender_service_factory.h"
#import "ios/chrome/browser/reading_list/model/offline_page_tab_helper.h"
#import "ios/chrome/browser/reading_list/model/reading_list_model_factory.h"
#import "ios/chrome/browser/reading_list/model/reading_list_web_state_observer.h"
#import "ios/chrome/browser/safe_browsing/model/safe_browsing_client_factory.h"
#import "ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_factory.h"
#import "ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_tab_helper.h"
#import "ios/chrome/browser/search_engines/model/search_engine_tab_helper.h"
#import "ios/chrome/browser/sessions/model/ios_chrome_session_tab_helper.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/sharing/model/share_file_download_tab_helper.h"
#import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h"
#import "ios/chrome/browser/ssl/model/captive_portal_tab_helper.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_error_container.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_url_filter_tab_helper.h"
#import "ios/chrome/browser/tabs/model/ios_chrome_synced_tab_delegate.h"
#import "ios/chrome/browser/translate/model/chrome_ios_translate_client.h"
#import "ios/chrome/browser/ui/page_info/features.h"
#import "ios/chrome/browser/voice/model/voice_search_navigations_tab_helper.h"
#import "ios/chrome/browser/web/model/annotations/annotations_tab_helper.h"
#import "ios/chrome/browser/web/model/blocked_popup_tab_helper.h"
#import "ios/chrome/browser/web/model/font_size/font_size_tab_helper.h"
#import "ios/chrome/browser/web/model/image_fetch/image_fetch_tab_helper.h"
#import "ios/chrome/browser/web/model/invalid_url_tab_helper.h"
#import "ios/chrome/browser/web/model/load_timing_tab_helper.h"
#import "ios/chrome/browser/web/model/page_placeholder_tab_helper.h"
#import "ios/chrome/browser/web/model/print/print_tab_helper.h"
#import "ios/chrome/browser/web/model/repost_form_tab_helper.h"
#import "ios/chrome/browser/web/model/sad_tab_tab_helper.h"
#import "ios/chrome/browser/web/model/web_performance_metrics/web_performance_metrics_tab_helper.h"
#import "ios/chrome/browser/web_selection/model/web_selection_tab_helper.h"
#import "ios/chrome/browser/webui/model/net_export_tab_helper.h"
#import "ios/components/security_interstitials/https_only_mode/feature.h"
#import "ios/components/security_interstitials/https_only_mode/https_only_mode_container.h"
#import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
#import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h"
#import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h"
#import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_client.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_tab_helper.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_unsafe_resource_container.h"
#import "ios/public/provider/chrome/browser/text_zoom/text_zoom_api.h"
#import "ios/web/common/annotations_utils.h"
#import "ios/web/public/find_in_page/java_script_find_in_page_manager.h"
#import "ios/web/public/web_state.h"
namespace {
// Returns whether the `flag` is set in `mask`.
constexpr bool IsTabHelperFilterMaskSet(TabHelperFilter mask,
TabHelperFilter flag) {
return (mask & flag) == flag;
}
} // namespace
void AttachTabHelpers(web::WebState* web_state, TabHelperFilter filter_flags) {
ChromeBrowserState* const browser_state =
ChromeBrowserState::FromBrowserState(web_state->GetBrowserState());
const bool is_off_the_record = browser_state->IsOffTheRecord();
const bool for_prerender =
IsTabHelperFilterMaskSet(filter_flags, TabHelperFilter::kPrerender);
const bool for_bottom_sheet =
IsTabHelperFilterMaskSet(filter_flags, TabHelperFilter::kBottomSheet);
// When adding a new tab helper, please consider whether it should be filtered
// out when the web_state is presented in the following context:
// - kPrerender: Tab helpers that are not required or not used for navigation
// should be filtered out.
// - kBottomSheet: The bottom sheet is overlayed on the BVC, tab helpers that
// rely on BVC's toolbar entry points should be filtered out.
//
// When a web state is presented by the BVC, AttachTabHelpers is called to
// attach all tab helpers. (the method is idempotent, so it is okay to call it
// multiple times for the same WebState).
// IOSChromeSessionTabHelper sets up the session ID used by other helpers,
// so it needs to be created before them.
IOSChromeSessionTabHelper::CreateForWebState(web_state);
OverlayRequestQueue::CreateForWebState(web_state);
VoiceSearchNavigationTabHelper::CreateForWebState(web_state);
IOSChromeSyncedTabDelegate::CreateForWebState(web_state);
InfoBarManagerImpl::CreateForWebState(web_state);
if (IsNativeFindInPageAvailable()) {
FindTabHelper::CreateForWebState(web_state);
} else {
web::JavaScriptFindInPageManager::CreateForWebState(web_state);
JavaScriptFindTabHelper::CreateForWebState(web_state);
}
HistoryTabHelper::CreateForWebState(web_state);
LoadTimingTabHelper::CreateForWebState(web_state);
OverscrollActionsTabHelper::CreateForWebState(web_state);
IOSTaskTabHelper::CreateForWebState(web_state);
if (!for_bottom_sheet &&
IsPriceAlertsEligible(web_state->GetBrowserState())) {
ShoppingPersistedDataTabHelper::CreateForWebState(web_state);
}
commerce::CommerceTabHelper::CreateForWebState(
web_state, is_off_the_record,
commerce::ShoppingServiceFactory::GetForBrowserState(browser_state));
if (!for_bottom_sheet && !for_prerender) {
// Since LensTabHelper listens for a custom scheme, it needs to be
// created before AppLauncherTabHelper, which will filter out
// unhandled schemes.
LensTabHelper::CreateForWebState(web_state);
if (IsLensOverlayAvailable()) {
LensOverlayTabHelper::CreateForWebState(web_state);
}
AppLauncherTabHelper::CreateForWebState(
web_state, [[AppLauncherAbuseDetector alloc] init], is_off_the_record);
}
security_interstitials::IOSBlockingPageTabHelper::CreateForWebState(
web_state);
password_manager::WellKnownChangePasswordTabHelper::CreateForWebState(
web_state);
InvalidUrlTabHelper::CreateForWebState(web_state);
if (!for_bottom_sheet) {
InfobarOverlayRequestInserter::CreateForWebState(
web_state, &DefaultInfobarOverlayRequestFactory);
InfobarOverlayTabHelper::CreateForWebState(web_state);
TranslateOverlayTabHelper::CreateForWebState(web_state);
}
if (ios::provider::IsTextZoomEnabled()) {
FontSizeTabHelper::CreateForWebState(web_state);
}
if (breadcrumbs::IsEnabled(GetApplicationContext()->GetLocalState())) {
BreadcrumbManagerTabHelper::CreateForWebState(web_state);
}
AnnotationsTabHelper::CreateForWebState(web_state);
SafeBrowsingClient* client =
SafeBrowsingClientFactory::GetForBrowserState(browser_state);
SafeBrowsingQueryManager::CreateForWebState(web_state, client);
SafeBrowsingTabHelper::CreateForWebState(web_state, client);
SafeBrowsingUrlAllowList::CreateForWebState(web_state);
SafeBrowsingUnsafeResourceContainer::CreateForWebState(web_state);
TailoredSecurityTabHelper::CreateForWebState(
web_state,
TailoredSecurityServiceFactory::GetForBrowserState(browser_state));
PolicyUrlBlockingTabHelper::CreateForWebState(web_state);
// Supervised user services are not supported for off-the-record browser
// state.
if (!is_off_the_record) {
SupervisedUserURLFilterTabHelper::CreateForWebState(web_state);
SupervisedUserErrorContainer::CreateForWebState(web_state);
}
ImageFetchTabHelper::CreateForWebState(web_state);
NewTabPageTabHelper::CreateForWebState(web_state);
ShareFileDownloadTabHelper::CreateForWebState(web_state);
OptimizationGuideTabHelper::CreateForWebState(web_state);
OptimizationGuideValidationTabHelper::CreateForWebState(web_state);
ChromeBrowserState* original_browser_state =
browser_state->GetOriginalChromeBrowserState();
favicon::WebFaviconDriver::CreateForWebState(
web_state,
ios::FaviconServiceFactory::GetForBrowserState(
original_browser_state, ServiceAccessType::IMPLICIT_ACCESS));
history::WebStateTopSitesObserver::CreateForWebState(
web_state,
ios::TopSitesFactory::GetForBrowserState(original_browser_state).get());
// Depends on favicon::WebFaviconDriver, must be created after it.
SearchEngineTabHelper::CreateForWebState(web_state);
ukm::InitializeSourceUrlRecorderForWebState(web_state);
// Download tab helpers.
DownloadManagerTabHelper::CreateForWebState(web_state);
SafariDownloadTabHelper::CreateForWebState(web_state);
VcardTabHelper::CreateForWebState(web_state);
// Drive tab helper.
if (base::FeatureList::IsEnabled(kIOSSaveToDrive)) {
DocumentDownloadTabHelper::CreateForWebState(web_state);
}
PageloadForegroundDurationTabHelper::CreateForWebState(web_state);
LookalikeUrlTabHelper::CreateForWebState(web_state);
LookalikeUrlTabAllowList::CreateForWebState(web_state);
LookalikeUrlContainer::CreateForWebState(web_state);
// TODO(crbug.com/41360476): pre-rendered WebState have lots of unnecessary
// tab helpers for historical reasons. For the moment, AttachTabHelpers
// allows to inhibit the creation of some of them. Once PreloadController
// has been refactored to only create the necessary tab helpers, this
// condition can be removed.
if (!for_bottom_sheet && !for_prerender) {
SadTabTabHelper::CreateForWebState(
web_state, SadTabTabHelper::kDefaultRepeatFailureInterval);
SnapshotTabHelper::CreateForWebState(web_state);
PagePlaceholderTabHelper::CreateForWebState(web_state);
ChromeIOSTranslateClient::CreateForWebState(web_state);
PasswordTabHelper::CreateForWebState(web_state);
AutofillBottomSheetTabHelper::CreateForWebState(web_state);
AutofillTabHelper::CreateForWebState(web_state);
FormSuggestionTabHelper::CreateForWebState(web_state, @[
PasswordTabHelper::FromWebState(web_state)->GetSuggestionProvider(),
AutofillTabHelper::FromWebState(web_state)->GetSuggestionProvider(),
]);
}
if (!for_bottom_sheet) {
InfobarBadgeTabHelper::GetOrCreateForWebState(web_state);
}
if (base::FeatureList::IsEnabled(kSharedHighlightingIOS)) {
LinkToTextTabHelper::CreateForWebState(web_state);
}
if (IsPartialTranslateEnabled() || IsSearchWithEnabled()) {
WebSelectionTabHelper::CreateForWebState(web_state);
}
WebPerformanceMetricsTabHelper::CreateForWebState(web_state);
OfflinePageTabHelper::CreateForWebState(
web_state, ReadingListModelFactory::GetForBrowserState(browser_state));
PermissionsTabHelper::CreateForWebState(web_state);
RepostFormTabHelper::CreateForWebState(web_state);
if (base::FeatureList::IsEnabled(
security_interstitials::features::kHttpsOnlyMode) ||
base::FeatureList::IsEnabled(
security_interstitials::features::kHttpsUpgrades)) {
HttpsOnlyModeUpgradeTabHelper::CreateForWebState(
web_state, browser_state->GetPrefs(),
PrerenderServiceFactory::GetForBrowserState(browser_state),
HttpsUpgradeServiceFactory::GetForBrowserState(browser_state));
HttpsOnlyModeContainer::CreateForWebState(web_state);
}
if (base::FeatureList::IsEnabled(omnibox::kDefaultTypedNavigationsToHttps)) {
TypedNavigationUpgradeTabHelper::CreateForWebState(
web_state, PrerenderServiceFactory::GetForBrowserState(browser_state),
HttpsUpgradeServiceFactory::GetForBrowserState(browser_state));
}
if (!is_off_the_record) {
FollowTabHelper::CreateForWebState(web_state);
}
if (!for_bottom_sheet && !is_off_the_record) {
PriceNotificationsTabHelper::CreateForWebState(web_state);
}
if (!for_bottom_sheet && !is_off_the_record && IsContextualPanelEnabled()) {
ContextualPanelModelService* model_service =
ContextualPanelModelServiceFactory::GetForBrowserState(
ChromeBrowserState::FromBrowserState(browser_state));
ContextualPanelTabHelper::CreateForWebState(web_state,
model_service->models());
}
if (!for_bottom_sheet && !is_off_the_record &&
IsAboutThisSiteFeatureEnabled()) {
if (auto* optimization_guide_decider =
OptimizationGuideServiceFactory::GetForBrowserState(
browser_state)) {
AboutThisSiteTabHelper::CreateForWebState(web_state,
optimization_guide_decider);
}
}
}