blob: 347138abf1a92d028674362374aad832af092d48 [file] [log] [blame]
// Copyright 2018 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/browser_view/ui_bundled/browser_coordinator.h"
#import <StoreKit/StoreKit.h>
#import <memory>
#import <optional>
#import "base/check_deref.h"
#import "base/functional/callback_helpers.h"
#import "base/memory/raw_ptr.h"
#import "base/memory/weak_ptr.h"
#import "base/metrics/histogram_functions.h"
#import "base/scoped_observation.h"
#import "base/strings/sys_string_conversions.h"
#import "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
#import "components/commerce/core/commerce_feature_list.h"
#import "components/commerce/core/feature_utils.h"
#import "components/commerce/core/shopping_service.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/infobars/core/infobar.h"
#import "components/infobars/core/infobar_manager.h"
#import "components/password_manager/core/browser/ui/credential_ui_entry.h"
#import "components/password_manager/core/common/password_manager_features.h"
#import "components/plus_addresses/features.h"
#import "components/prefs/pref_service.h"
#import "components/profile_metrics/browser_profile_type.h"
#import "components/safe_browsing/core/common/features.h"
#import "components/segmentation_platform/embedder/home_modules/tips_manager/signal_constants.h"
#import "components/supervised_user/core/browser/supervised_user_utils.h"
#import "components/supervised_user/core/common/features.h"
#import "components/translate/core/browser/translate_manager.h"
#import "components/trusted_vault/trusted_vault_server_constants.h"
#import "ios/chrome/browser/app_launcher/model/app_launcher_tab_helper_browser_presentation_provider.h"
#import "ios/chrome/browser/app_store_rating/ui_bundled/features.h"
#import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_prompt/enterprise_prompt_coordinator.h"
#import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_prompt/enterprise_prompt_type.h"
#import "ios/chrome/browser/authentication/ui_bundled/signin_presenter.h"
#import "ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.h"
#import "ios/chrome/browser/autofill/ui_bundled/authentication/card_unmask_authentication_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/bottom_sheet/autofill_edit_profile_bottom_sheet_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/bottom_sheet/virtual_card_enrollment_bottom_sheet_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/error_dialog/autofill_error_dialog_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_coordinator.h"
#import "ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_coordinator.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_coordinator.h"
#import "ios/chrome/browser/browser_container/model/edit_menu_builder.h"
#import "ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.h"
#import "ios/chrome/browser/browser_container/ui_bundled/browser_container_view_controller.h"
#import "ios/chrome/browser/browser_view/ui_bundled/browser_coordinator+Testing.h"
#import "ios/chrome/browser/browser_view/ui_bundled/browser_view_controller+private.h"
#import "ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.h"
#import "ios/chrome/browser/browser_view/ui_bundled/browser_view_visibility_consumer.h"
#import "ios/chrome/browser/browser_view/ui_bundled/key_commands_provider.h"
#import "ios/chrome/browser/browser_view/ui_bundled/safe_area_provider.h"
#import "ios/chrome/browser/browser_view/ui_bundled/tab_events_mediator.h"
#import "ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.h"
#import "ios/chrome/browser/bubble/ui_bundled/bubble_presenter_coordinator.h"
#import "ios/chrome/browser/bubble/ui_bundled/bubble_presenter_delegate.h"
#import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.h"
#import "ios/chrome/browser/commerce/model/push_notification/push_notification_feature.h"
#import "ios/chrome/browser/commerce/model/shopping_service_factory.h"
#import "ios/chrome/browser/content_settings/model/host_content_settings_map_factory.h"
#import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h"
#import "ios/chrome/browser/contextual_panel/coordinator/contextual_sheet_coordinator.h"
#import "ios/chrome/browser/contextual_panel/model/contextual_panel_tab_helper.h"
#import "ios/chrome/browser/contextual_panel/utils/contextual_panel_metrics.h"
#import "ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/default_promo/ui_bundled/default_browser_promo_non_modal_commands.h"
#import "ios/chrome/browser/default_promo/ui_bundled/default_browser_promo_non_modal_coordinator.h"
#import "ios/chrome/browser/default_promo/ui_bundled/default_promo_non_modal_presentation_delegate.h"
#import "ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_commands.h"
#import "ios/chrome/browser/default_promo/ui_bundled/generic/default_browser_generic_promo_coordinator.h"
#import "ios/chrome/browser/docking_promo/coordinator/docking_promo_coordinator.h"
#import "ios/chrome/browser/download/model/download_directory_util.h"
#import "ios/chrome/browser/download/model/external_app_util.h"
#import "ios/chrome/browser/download/model/pass_kit_tab_helper.h"
#import "ios/chrome/browser/download/ui_bundled/ar_quick_look_coordinator.h"
#import "ios/chrome/browser/download/ui_bundled/auto_deletion/auto_deletion_coordinator.h"
#import "ios/chrome/browser/download/ui_bundled/download_manager_coordinator.h"
#import "ios/chrome/browser/download/ui_bundled/features.h"
#import "ios/chrome/browser/download/ui_bundled/pass_kit_coordinator.h"
#import "ios/chrome/browser/download/ui_bundled/safari_download_coordinator.h"
#import "ios/chrome/browser/download/ui_bundled/vcard_coordinator.h"
#import "ios/chrome/browser/drive_file_picker/coordinator/root_drive_file_picker_coordinator.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_util.h"
#import "ios/chrome/browser/find_bar/ui_bundled/find_bar_controller_ios.h"
#import "ios/chrome/browser/find_bar/ui_bundled/find_bar_coordinator.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/first_run/ui_bundled/omnibox_position/omnibox_position_choice_coordinator.h"
#import "ios/chrome/browser/follow/model/follow_browser_agent.h"
#import "ios/chrome/browser/follow/model/followed_web_site.h"
#import "ios/chrome/browser/follow/ui_bundled/first_follow_coordinator.h"
#import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller.h"
#import "ios/chrome/browser/google_one/coordinator/google_one_coordinator.h"
#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_mediator.h"
#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/infobars/model/infobar_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/intents/intents_donation_helper.h"
#import "ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.h"
#import "ios/chrome/browser/lens/ui_bundled/lens_coordinator.h"
#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h"
#import "ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h"
#import "ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h"
#import "ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h"
#import "ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.h"
#import "ios/chrome/browser/ntp/model/new_tab_page_state.h"
#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_component_factory.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.h"
#import "ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.h"
#import "ios/chrome/browser/overlays/model/public/overlay_presenter.h"
#import "ios/chrome/browser/overlays/ui_bundled/overlay_container_coordinator.h"
#import "ios/chrome/browser/overscroll_actions/model/overscroll_actions_tab_helper.h"
#import "ios/chrome/browser/overscroll_actions/ui_bundled/overscroll_actions_controller.h"
#import "ios/chrome/browser/page_info/ui_bundled/page_info_coordinator.h"
#import "ios/chrome/browser/page_info/ui_bundled/requirements/page_info_presentation.h"
#import "ios/chrome/browser/parcel_tracking/parcel_tracking_infobar_delegate.h"
#import "ios/chrome/browser/parcel_tracking/parcel_tracking_opt_in_status.h"
#import "ios/chrome/browser/parcel_tracking/parcel_tracking_step.h"
#import "ios/chrome/browser/parcel_tracking/parcel_tracking_util.h"
#import "ios/chrome/browser/parcel_tracking/tracking_source.h"
#import "ios/chrome/browser/parcel_tracking/ui_bundled/parcel_tracking_opt_in_coordinator.h"
#import "ios/chrome/browser/passwords/model/password_controller_delegate.h"
#import "ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_coordinator.h"
#import "ios/chrome/browser/passwords/ui_bundled/password_breach_coordinator.h"
#import "ios/chrome/browser/passwords/ui_bundled/password_protection_coordinator.h"
#import "ios/chrome/browser/passwords/ui_bundled/password_protection_coordinator_delegate.h"
#import "ios/chrome/browser/passwords/ui_bundled/password_suggestion_coordinator.h"
#import "ios/chrome/browser/phone_number/ui_bundled/add_contacts_coordinator.h"
#import "ios/chrome/browser/phone_number/ui_bundled/country_code_picker_coordinator.h"
#import "ios/chrome/browser/plus_addresses/coordinator/plus_address_bottom_sheet_coordinator.h"
#import "ios/chrome/browser/prerender/model/preload_controller_delegate.h"
#import "ios/chrome/browser/prerender/model/prerender_service.h"
#import "ios/chrome/browser/prerender/model/prerender_service_factory.h"
#import "ios/chrome/browser/presenters/ui_bundled/vertical_animation_container.h"
#import "ios/chrome/browser/price_notifications/ui_bundled/price_notifications_view_coordinator.h"
#import "ios/chrome/browser/print/coordinator/print_coordinator.h"
#import "ios/chrome/browser/promos_manager/model/features.h"
#import "ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.h"
#import "ios/chrome/browser/qr_scanner/ui_bundled/qr_scanner_legacy_coordinator.h"
#import "ios/chrome/browser/reading_list/model/reading_list_browser_agent.h"
#import "ios/chrome/browser/reading_list/ui_bundled/reading_list_coordinator.h"
#import "ios/chrome/browser/reading_list/ui_bundled/reading_list_coordinator_delegate.h"
#import "ios/chrome/browser/recent_tabs/ui_bundled/recent_tabs_coordinator.h"
#import "ios/chrome/browser/recent_tabs/ui_bundled/recent_tabs_coordinator_delegate.h"
#import "ios/chrome/browser/sad_tab/ui_bundled/sad_tab_coordinator.h"
#import "ios/chrome/browser/safe_browsing/ui_bundled/safe_browsing_coordinator.h"
#import "ios/chrome/browser/save_to_drive/ui_bundled/save_to_drive_coordinator.h"
#import "ios/chrome/browser/save_to_photos/ui_bundled/save_to_photos_coordinator.h"
#import "ios/chrome/browser/send_tab_to_self/ui_bundled/send_tab_to_self_coordinator.h"
#import "ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_credit_card_coordinator.h"
#import "ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_credit_card_coordinator_delegate.h"
#import "ios/chrome/browser/settings/ui_bundled/clear_browsing_data/features.h"
#import "ios/chrome/browser/settings/ui_bundled/clear_browsing_data/quick_delete_coordinator.h"
#import "ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator.h"
#import "ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_coordinator_delegate.h"
#import "ios/chrome/browser/shared/coordinator/alert/repost_form_coordinator.h"
#import "ios/chrome/browser/shared/coordinator/alert/repost_form_coordinator_delegate.h"
#import "ios/chrome/browser/shared/coordinator/default_browser_promo/non_modal_default_browser_promo_scheduler_scene_agent.h"
#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/public/commands/activity_service_commands.h"
#import "ios/chrome/browser/shared/public/commands/activity_service_share_url_command.h"
#import "ios/chrome/browser/shared/public/commands/add_contacts_commands.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/auto_deletion_commands.h"
#import "ios/chrome/browser/shared/public/commands/autofill_commands.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/contextual_panel_entrypoint_commands.h"
#import "ios/chrome/browser/shared/public/commands/contextual_panel_entrypoint_iph_commands.h"
#import "ios/chrome/browser/shared/public/commands/contextual_sheet_commands.h"
#import "ios/chrome/browser/shared/public/commands/country_code_picker_commands.h"
#import "ios/chrome/browser/shared/public/commands/drive_file_picker_commands.h"
#import "ios/chrome/browser/shared/public/commands/feed_commands.h"
#import "ios/chrome/browser/shared/public/commands/find_in_page_commands.h"
#import "ios/chrome/browser/shared/public/commands/google_one_commands.h"
#import "ios/chrome/browser/shared/public/commands/help_commands.h"
#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
#import "ios/chrome/browser/shared/public/commands/load_query_commands.h"
#import "ios/chrome/browser/shared/public/commands/mini_map_commands.h"
#import "ios/chrome/browser/shared/public/commands/new_tab_page_commands.h"
#import "ios/chrome/browser/shared/public/commands/omnibox_commands.h"
#import "ios/chrome/browser/shared/public/commands/page_info_commands.h"
#import "ios/chrome/browser/shared/public/commands/parcel_tracking_opt_in_commands.h"
#import "ios/chrome/browser/shared/public/commands/parent_access_commands.h"
#import "ios/chrome/browser/shared/public/commands/password_breach_commands.h"
#import "ios/chrome/browser/shared/public/commands/password_protection_commands.h"
#import "ios/chrome/browser/shared/public/commands/password_suggestion_commands.h"
#import "ios/chrome/browser/shared/public/commands/policy_change_commands.h"
#import "ios/chrome/browser/shared/public/commands/popup_menu_commands.h"
#import "ios/chrome/browser/shared/public/commands/price_notifications_commands.h"
#import "ios/chrome/browser/shared/public/commands/promos_manager_commands.h"
#import "ios/chrome/browser/shared/public/commands/qr_generation_commands.h"
#import "ios/chrome/browser/shared/public/commands/quick_delete_commands.h"
#import "ios/chrome/browser/shared/public/commands/save_image_to_photos_command.h"
#import "ios/chrome/browser/shared/public/commands/save_to_drive_commands.h"
#import "ios/chrome/browser/shared/public/commands/save_to_photos_commands.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/commands/share_highlight_command.h"
#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
#import "ios/chrome/browser/shared/public/commands/text_zoom_commands.h"
#import "ios/chrome/browser/shared/public/commands/toolbar_commands.h"
#import "ios/chrome/browser/shared/public/commands/unit_conversion_commands.h"
#import "ios/chrome/browser/shared/public/commands/web_content_commands.h"
#import "ios/chrome/browser/shared/public/commands/whats_new_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/public/features/features_utils.h"
#import "ios/chrome/browser/shared/ui/elements/activity_overlay_coordinator.h"
#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
#import "ios/chrome/browser/shared/ui/util/page_animation_util.h"
#import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
#import "ios/chrome/browser/shared/ui/util/top_view_controller.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/shared/ui/util/util_swift.h"
#import "ios/chrome/browser/sharing/ui_bundled/sharing_coordinator.h"
#import "ios/chrome/browser/sharing/ui_bundled/sharing_params.h"
#import "ios/chrome/browser/sharing/ui_bundled/sharing_positioner.h"
#import "ios/chrome/browser/side_swipe/ui_bundled/side_swipe_mediator.h"
#import "ios/chrome/browser/signin/model/account_consistency_browser_agent.h"
#import "ios/chrome/browser/signin/model/account_consistency_service_factory.h"
#import "ios/chrome/browser/snapshots/model/model_swift.h"
#import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h"
#import "ios/chrome/browser/snapshots/model/web_state_snapshot_info.h"
#import "ios/chrome/browser/spotlight_debugger/ui_bundled/spotlight_debugger_coordinator.h"
#import "ios/chrome/browser/store_kit/model/store_kit_coordinator.h"
#import "ios/chrome/browser/store_kit/model/store_kit_coordinator_delegate.h"
#import "ios/chrome/browser/supervised_user/coordinator/parent_access_coordinator.h"
#import "ios/chrome/browser/sync/model/sync_error_browser_agent.h"
#import "ios/chrome/browser/tab_insertion/model/tab_insertion_browser_agent.h"
#import "ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/coordinator/tab_strip_coordinator.h"
#import "ios/chrome/browser/tabs/model/tab_title_util.h"
#import "ios/chrome/browser/tabs/ui_bundled/tab_strip_legacy_coordinator.h"
#import "ios/chrome/browser/text_zoom/ui_bundled/text_zoom_coordinator.h"
#import "ios/chrome/browser/tips_manager/model/tips_manager_ios.h"
#import "ios/chrome/browser/tips_manager/model/tips_manager_ios_factory.h"
#import "ios/chrome/browser/tips_notifications/coordinator/enhanced_safe_browsing_promo_coordinator.h"
#import "ios/chrome/browser/tips_notifications/coordinator/lens_promo_coordinator.h"
#import "ios/chrome/browser/toolbar/ui_bundled/accessory/toolbar_accessory_coordinator_delegate.h"
#import "ios/chrome/browser/toolbar/ui_bundled/accessory/toolbar_accessory_presenter.h"
#import "ios/chrome/browser/toolbar/ui_bundled/toolbar_coordinator.h"
#import "ios/chrome/browser/translate/model/chrome_ios_translate_client.h"
#import "ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h"
#import "ios/chrome/browser/ui/whats_new/whats_new_coordinator.h"
#import "ios/chrome/browser/unit_conversion/ui_bundled/unit_conversion_coordinator.h"
#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
#import "ios/chrome/browser/url_loading/model/url_loading_notifier_browser_agent.h"
#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
#import "ios/chrome/browser/view_source/model/view_source_browser_agent.h"
#import "ios/chrome/browser/voice/ui_bundled/text_to_speech_playback_controller.h"
#import "ios/chrome/browser/voice/ui_bundled/text_to_speech_playback_controller_factory.h"
#import "ios/chrome/browser/web/model/choose_file/choose_file_tab_helper.h"
#import "ios/chrome/browser/web/model/font_size/font_size_tab_helper.h"
#import "ios/chrome/browser/web/model/page_placeholder_browser_agent.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/repost_form_tab_helper_delegate.h"
#import "ios/chrome/browser/web/model/web_navigation_browser_agent.h"
#import "ios/chrome/browser/web/model/web_navigation_ntp_delegate.h"
#import "ios/chrome/browser/web/model/web_state_delegate_browser_agent.h"
#import "ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.h"
#import "ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent_observer_bridge.h"
#import "ios/chrome/browser/webui/model/net_export_tab_helper_delegate.h"
#import "ios/chrome/browser/webui/ui_bundled/net_export_coordinator.h"
#import "ios/chrome/common/ui/util/ui_util.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/public/provider/chrome/browser/fullscreen/fullscreen_api.h"
#import "ios/public/provider/chrome/browser/signin/choice_api.h"
#import "ios/public/provider/chrome/browser/text_zoom/text_zoom_api.h"
#import "ios/public/provider/chrome/browser/voice_search/voice_search_api.h"
#import "ios/public/provider/chrome/browser/voice_search/voice_search_controller.h"
#import "ios/web/public/web_state.h"
#import "ui/base/device_form_factor.h"
#import "ui/base/l10n/l10n_util.h"
namespace {
// Duration of the toolbar animation.
constexpr base::TimeDelta kLegacyFullscreenControllerToolbarAnimationDuration =
base::Milliseconds(300);
// URL to share when user selects "Share Chrome"
const char kChromeAppStoreUrl[] =
"https://apps.apple.com/app/id535886823?pt=9008&ct=iosChromeShare&mt=8";
// Enum for toolbar to present.
enum class ToolbarKind {
kTextZoom,
kFindInPage,
};
} // anonymous namespace
@interface BrowserCoordinator () <
ActivityServiceCommands,
AddContactsCommands,
AppLauncherTabHelperBrowserPresentationProvider,
AutoDeletionCommands,
AutofillAddCreditCardCoordinatorDelegate,
BrowserCoordinatorCommands,
BrowserViewVisibilityConsumer,
BubblePresenterDelegate,
ContextualPanelEntrypointIPHCommands,
ContextualSheetCommands,
CountryCodePickerCommands,
DefaultBrowserGenericPromoCommands,
DefaultPromoNonModalPresentationDelegate,
DriveFilePickerCommands,
EditMenuBuilder,
EnterprisePromptCoordinatorDelegate,
FormInputAccessoryCoordinatorNavigator,
GoogleOneCommands,
MiniMapCommands,
NetExportTabHelperDelegate,
NewTabPageCommands,
OverscrollActionsControllerDelegate,
PageInfoCommands,
PageInfoPresentation,
ParcelTrackingOptInCommands,
ParentAccessCommands,
PasswordBreachCommands,
PasswordControllerDelegate,
PasswordProtectionCommands,
PasswordProtectionCoordinatorDelegate,
PasswordSettingsCoordinatorDelegate,
PasswordSuggestionCommands,
PasswordSuggestionCoordinatorDelegate,
PriceNotificationsCommands,
PromosManagerCommands,
PolicyChangeCommands,
PreloadControllerDelegate,
QuickDeleteCommands,
ReadingListCoordinatorDelegate,
RecentTabsCoordinatorDelegate,
RepostFormCoordinatorDelegate,
RepostFormTabHelperDelegate,
SaveToDriveCommands,
SaveToPhotosCommands,
SigninPresenter,
SnapshotGeneratorDelegate,
StoreKitCoordinatorDelegate,
ToolbarAccessoryCoordinatorDelegate,
UnitConversionCommands,
URLLoadingDelegate,
WebContentCommands,
WebNavigationNTPDelegate,
WebUsageEnablerBrowserAgentObserving,
WhatsNewCommands>
// Whether the coordinator is started.
@property(nonatomic, assign, getter=isStarted) BOOL started;
// Activates/deactivates the object. This will enable/disable the ability for
// this object to browse, and to have live UIWebViews associated with it. While
// not active, the UI will not react to changes in the tab model, so generally
// an inactive BVC should not be visible.
@property(nonatomic, assign, getter=isActive) BOOL active;
// Whether web usage is enabled for the WebStates in `self.browser`.
@property(nonatomic, assign, getter=isWebUsageEnabled) BOOL webUsageEnabled;
// Handles command dispatching, provided by the Browser instance.
@property(nonatomic, weak) CommandDispatcher* dispatcher;
// The coordinator managing the container view controller.
@property(nonatomic, strong)
BrowserContainerCoordinator* browserContainerCoordinator;
// Mediator for incognito reauth.
@property(nonatomic, strong) IncognitoReauthMediator* incognitoAuthMediator;
// Mediator for tab lifecylce.
@property(nonatomic, strong) TabLifecycleMediator* tabLifecycleMediator;
// Mediator for tab events.
@property(nonatomic, strong) TabEventsMediator* tabEventsMediator;
// =================================================
// Child Coordinators, listed in alphabetical order.
// =================================================
// Coordinator for displaying a modal overlay with activity indicator to prevent
// the user from interacting with the browser view.
@property(nonatomic, strong)
ActivityOverlayCoordinator* activityOverlayCoordinator;
// Coordinator to add new credit card.
@property(nonatomic, strong)
AutofillAddCreditCardCoordinator* addCreditCardCoordinator;
// Coordinator to show the Autofill progress dialog.
@property(nonatomic, strong)
AutofillProgressDialogCoordinator* autofillProgressDialogCoordinator;
// Presents a QLPreviewController in order to display USDZ format 3D models.
@property(nonatomic, strong) ARQuickLookCoordinator* ARQuickLookCoordinator;
// Coordinator in charge of the presenting autofill options in a bottom sheet.
@property(nonatomic, strong) PasswordSuggestionBottomSheetCoordinator*
passwordSuggestionBottomSheetCoordinator;
// Coordinator in charge of the presenting autofill options in a bottom sheet.
@property(nonatomic, strong) PaymentsSuggestionBottomSheetCoordinator*
paymentsSuggestionBottomSheetCoordinator;
// Coordinator for the authentication when unmasking card during autofill.
@property(nonatomic, strong)
CardUnmaskAuthenticationCoordinator* cardUnmaskAuthenticationCoordinator;
@property(nonatomic, strong)
PlusAddressBottomSheetCoordinator* plusAddressBottomSheetCoordinator;
@property(nonatomic, strong) AutofillEditProfileBottomSheetCoordinator*
autofillEditProfileBottomSheetCoordinator;
@property(nonatomic, strong) VirtualCardEnrollmentBottomSheetCoordinator*
virtualCardEnrollmentBottomSheetCoordinator;
// Coordinator for the choice screen.
@property(nonatomic, strong) ChromeCoordinator* choiceCoordinator;
// Coordinator-ish provider for context menus.
@property(nonatomic, strong)
ContextMenuConfigurationProvider* contextMenuProvider;
// Coordinator that manages the presentation of Download Manager UI.
@property(nonatomic, strong)
DownloadManagerCoordinator* downloadManagerCoordinator;
// The coordinator that manages enterprise prompts.
@property(nonatomic, strong)
EnterprisePromptCoordinator* enterprisePromptCoordinator;
// Coordinator to show the Autofill error dialog.
@property(nonatomic, strong)
AutofillErrorDialogCoordinator* autofillErrorDialogCoordinator;
// Coordinator for the find bar.
@property(nonatomic, strong) FindBarCoordinator* findBarCoordinator;
// Coordinator for the First Follow modal.
@property(nonatomic, strong) FirstFollowCoordinator* firstFollowCoordinator;
// Coordinator in charge of the presenting autofill options above the
// keyboard.
@property(nonatomic, strong)
FormInputAccessoryCoordinator* formInputAccessoryCoordinator;
// The container coordinators for the infobar modalities.
@property(nonatomic, strong)
OverlayContainerCoordinator* infobarBannerOverlayContainerCoordinator;
@property(nonatomic, strong)
OverlayContainerCoordinator* infobarModalOverlayContainerCoordinator;
// Coordinator in charge of presenting a mini map.
@property(nonatomic, strong) MiniMapCoordinator* miniMapCoordinator;
// The coordinator that manages net export.
@property(nonatomic, strong) NetExportCoordinator* netExportCoordinator;
// Coordinator for the non-modal default promo.
@property(nonatomic, strong)
DefaultBrowserPromoNonModalCoordinator* nonModalPromoCoordinator;
// Coordinator for new tab pages.
@property(nonatomic, strong) NewTabPageCoordinator* NTPCoordinator;
// Coordinator for Page Info UI.
@property(nonatomic, strong) ChromeCoordinator* pageInfoCoordinator;
// Coordinator for parcel tracking opt-in UI presentation.
@property(nonatomic, strong)
ParcelTrackingOptInCoordinator* parcelTrackingOptInCoordinator;
// Coordinator to display local web approvals parent access UI in a bottom
// sheet.
@property(nonatomic, strong) ParentAccessCoordinator* parentAccessCoordinator;
// Coordinator for the PassKit UI presentation.
@property(nonatomic, strong) PassKitCoordinator* passKitCoordinator;
// Coordinator for the password breach UI presentation.
@property(nonatomic, strong)
PasswordBreachCoordinator* passwordBreachCoordinator;
// Coordinator for the password protection UI presentation.
@property(nonatomic, strong)
PasswordProtectionCoordinator* passwordProtectionCoordinator;
// Coordinator for the password settings UI presentation.
@property(nonatomic, strong)
PasswordSettingsCoordinator* passwordSettingsCoordinator;
// Coordinator for the password suggestion UI presentation.
@property(nonatomic, strong)
PasswordSuggestionCoordinator* passwordSuggestionCoordinator;
// Coordinator for the popup menu.
@property(nonatomic, strong) PopupMenuCoordinator* popupMenuCoordinator;
// Coordinator for the price notifications UI presentation.
@property(nonatomic, strong)
PriceNotificationsViewCoordinator* priceNotificationsViewCoordiantor;
// Used to display the Print UI. Nil if not visible.
@property(nonatomic, strong) PrintCoordinator* printCoordinator;
// Coordinator for app-wide promos.
@property(nonatomic, strong) PromosManagerCoordinator* promosManagerCoordinator;
// Coordinator for the QR scanner.
@property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
// Coordinator for displaying the Reading List.
@property(nonatomic, strong) ReadingListCoordinator* readingListCoordinator;
// Coordinator for Recent Tabs.
@property(nonatomic, strong) RecentTabsCoordinator* recentTabsCoordinator;
// Coordinator for displaying Repost Form dialog.
@property(nonatomic, strong) RepostFormCoordinator* repostFormCoordinator;
// Coordinator for displaying Sad Tab.
@property(nonatomic, strong) SadTabCoordinator* sadTabCoordinator;
// Presents a SFSafariViewController in order to download .mobileconfig file.
@property(nonatomic, strong)
SafariDownloadCoordinator* SafariDownloadCoordinator;
// Coordinator for Safe Browsing.
@property(nonatomic, strong) SafeBrowsingCoordinator* safeBrowsingCoordinator;
// Coordinator for displaying the Save to Drive UI.
@property(nonatomic, strong) SaveToDriveCoordinator* saveToDriveCoordinator;
// Coordinator for displaying the Save to Photos UI.
@property(nonatomic, strong) SaveToPhotosCoordinator* saveToPhotosCoordinator;
// Coordinator for sharing scenarios.
@property(nonatomic, strong) SharingCoordinator* sharingCoordinator;
// The coordinator used for Spotlight Debugger.
@property(nonatomic, strong)
SpotlightDebuggerCoordinator* spotlightDebuggerCoordinator;
// Coordinator for presenting SKStoreProductViewController.
@property(nonatomic, strong) StoreKitCoordinator* storeKitCoordinator;
// Coordinator for Text Zoom.
@property(nonatomic, strong) TextZoomCoordinator* textZoomCoordinator;
// Coordinator in charge of presenting a unit converter.
@property(nonatomic, strong)
UnitConversionCoordinator* unitConversionCoordinator;
// Opens downloaded Vcard.
@property(nonatomic, strong) VcardCoordinator* vcardCoordinator;
// The coordinator used for What's New feature.
@property(nonatomic, strong) WhatsNewCoordinator* whatsNewCoordinator;
// The manager used to display a default browser promo.
@property(nonatomic, strong) DefaultBrowserGenericPromoCoordinator*
defaultBrowserGenericPromoCoordinator;
// The webState of the active tab.
@property(nonatomic, readonly) web::WebState* activeWebState;
@end
@implementation BrowserCoordinator {
BrowserViewControllerDependencies _viewControllerDependencies;
KeyCommandsProvider* _keyCommandsProvider;
BubblePresenterCoordinator* _bubblePresenterCoordinator;
BubbleViewControllerPresenter* _contextualPanelEntrypointHelpPresenter;
ToolbarAccessoryPresenter* _toolbarAccessoryPresenter;
LensCoordinator* _lensCoordinator;
LensViewFinderCoordinator* _lensViewFinderCoordinator;
LensOverlayCoordinator* _lensOverlayCoordinator;
ToolbarCoordinator* _toolbarCoordinator;
TabStripCoordinator* _tabStripCoordinator;
TabStripLegacyCoordinator* _legacyTabStripCoordinator;
SideSwipeMediator* _sideSwipeMediator;
raw_ptr<FullscreenController> _fullscreenController;
// The coordinator that shows the Send Tab To Self UI.
SendTabToSelfCoordinator* _sendTabToSelfCoordinator;
BookmarksCoordinator* _bookmarksCoordinator;
std::optional<ToolbarKind> _nextToolbarToPresent;
CredentialProviderPromoCoordinator* _credentialProviderPromoCoordinator;
DockingPromoCoordinator* _dockingPromoCoordinator;
// Used to display the Voice Search UI. Nil if not visible.
id<VoiceSearchController> _voiceSearchController;
raw_ptr<UrlLoadingNotifierBrowserAgent> _urlLoadingNotifierBrowserAgent;
id<LoadQueryCommands> _loadQueryCommandsHandler;
id<OmniboxCommands> _omniboxCommandsHandler;
LayoutGuideCenter* _layoutGuideCenter;
raw_ptr<WebNavigationBrowserAgent> _webNavigationBrowserAgent;
raw_ptr<UrlLoadingBrowserAgent> _urlLoadingBrowserAgent;
AddContactsCoordinator* _addContactsCoordinator;
CountryCodePickerCoordinator* _countryCodePickerCoordinator;
OmniboxPositionChoiceCoordinator* _omniboxPositionChoiceCoordinator;
std::unique_ptr<WebUsageEnablerBrowserAgentObserverBridge>
_webUsageEnablerObserver;
ContextualSheetCoordinator* _contextualSheetCoordinator;
RootDriveFilePickerCoordinator* _driveFilePickerCoordinator;
GoogleOneCoordinator* _googleOneCoordinator;
SafeAreaProvider* _safeAreaProvider;
// Number of time `showActivityOverlay` was called and its callback not
// called.
int _numberOfActivityOverly;
// Callback to remove the activity overlay started by the browser coordinator
// itself.
base::ScopedClosureRunner _activityOverlayCallback;
// The coordinator for the new Delete Browsing Data screen, also called Quick
// Delete.
QuickDeleteCoordinator* _quickDeleteCoordinator;
LensPromoCoordinator* _lensPromoCoordinator;
EnhancedSafeBrowsingPromoCoordinator* _enhancedSafeBrowsingPromoCoordinator;
AutoDeletionCoordinator* _autoDeletionCoordiantor;
}
#pragma mark - ChromeCoordinator
- (void)start {
if (self.started) {
return;
}
DCHECK(!self.viewController);
_webUsageEnablerObserver =
std::make_unique<WebUsageEnablerBrowserAgentObserverBridge>(
WebUsageEnablerBrowserAgent::FromBrowser(self.browser), self);
// TabLifeCycleMediator should start before createViewController because it
// needs to register itself as a WebStateListObserver before the rest of the
// UI in order to be able to install the tab helper delegate before the UI is
// notified of WebStateList events.
[self startTabLifeCycleMediator];
[self createViewControllerDependencies];
[self createViewController];
[self updateViewControllerDependencies];
// Force the view load at a specific time.
// TODO(crbug.com/40263730): This should ideally go in createViewController,
// but part of creating the view controller involves setting up a dispatch to
// a command that isn't handled until updateViewControllerDependencies
// (OmniboxCommands).
BOOL created = [self ensureViewIsCreated];
CHECK(created);
// Independent mediators should start before coordinators so model state is
// accurate for any UI that starts up.
[self startIndependentMediators];
[self startChildCoordinators];
// Browser delegates can have dependencies on coordinators.
[self installDelegatesForBrowser];
[self installDelegatesForBrowserState];
[super start];
self.started = YES;
}
- (void)stop {
if (!self.started) {
return;
}
self.started = NO;
[super stop];
self.active = NO;
[self uninstallDelegatesForBrowserState];
[self uninstallDelegatesForBrowser];
[self.tabEventsMediator disconnect];
[self.tabLifecycleMediator disconnect];
[self.dispatcher stopDispatchingToTarget:self];
[self stopChildCoordinators];
[self destroyViewController];
[self destroyViewControllerDependencies];
_webUsageEnablerObserver.reset();
_activityOverlayCallback.RunAndReset();
}
- (void)dealloc {
DCHECK(!_bookmarksCoordinator);
}
#pragma mark - Public
- (BOOL)isPlayingTTS {
return _voiceSearchController.audioPlaying;
}
- (void)setActive:(BOOL)active {
DCHECK_EQ(_active, self.viewController.active);
if (_active == active) {
return;
}
_active = active;
// If not active, display an activity indicator overlay over the view to
// prevent interaction with the web page.
if (active) {
_activityOverlayCallback.RunAndReset();
} else if (!_activityOverlayCallback) {
_activityOverlayCallback = [self showActivityOverlay];
}
ProfileIOS* profile = self.browser->GetProfile();
if (profile) {
TextToSpeechPlaybackControllerFactory::GetInstance()
->GetForProfile(profile)
->SetEnabled(active);
}
self.webUsageEnabled = active;
if (active) {
// If the NTP was stopped because of a -setActive:NO call, then the NTP
// needs to be restarted when -setActive:YES is called subsequently (i.e.
// delete browsing data). This should not be needed for any other use case,
// but on initial startup this is inevitably called after restoring tabs, so
// cannot assert that it has not been started.
web::WebState* webState =
self.browser->GetWebStateList()->GetActiveWebState();
if (webState && NewTabPageTabHelper::FromWebState(webState)->IsActive() &&
!self.NTPCoordinator.started) {
// Avoid Voiceover focus to be stollen if the BrowserViewController is not
// the top view.
BOOL ntpIsTopView = !self.viewController.presentedViewController;
self.NTPCoordinator.canfocusAccessibilityOmniboxWhenViewAppears =
ntpIsTopView;
[self.NTPCoordinator start];
}
} else {
[self.NTPCoordinator stop];
}
self.viewController.active = active;
}
- (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
dismissOmnibox:(BOOL)dismissOmnibox {
[self stopSaveToPhotos];
[self hideSaveToDrive];
[self hideDriveFilePicker];
[self.passKitCoordinator stop];
self.passKitCoordinator = nil;
[self.printCoordinator dismissAnimated:YES];
[self.readingListCoordinator stop];
self.readingListCoordinator.delegate = nil;
self.readingListCoordinator = nil;
[self.sharingCoordinator stop];
self.sharingCoordinator = nil;
[self.passwordBreachCoordinator stop];
self.passwordBreachCoordinator = nil;
[self stopPasswordProtectionCoordinator];
[self.passwordSuggestionBottomSheetCoordinator stop];
self.passwordSuggestionBottomSheetCoordinator = nil;
[self.passwordSuggestionCoordinator stop];
self.passwordSuggestionCoordinator = nil;
[self hidePageInfo];
[self.paymentsSuggestionBottomSheetCoordinator stop];
self.paymentsSuggestionBottomSheetCoordinator = nil;
[self.plusAddressBottomSheetCoordinator stop];
self.plusAddressBottomSheetCoordinator = nil;
[self.virtualCardEnrollmentBottomSheetCoordinator stop];
self.virtualCardEnrollmentBottomSheetCoordinator = nil;
[self dismissAutofillErrorDialog];
[self dismissAutofillProgressDialog];
[_sendTabToSelfCoordinator stop];
_sendTabToSelfCoordinator = nil;
[self.passwordSettingsCoordinator stop];
self.passwordSettingsCoordinator.delegate = nil;
self.passwordSettingsCoordinator = nil;
[self hidePriceNotifications];
[self.unitConversionCoordinator stop];
self.unitConversionCoordinator = nil;
[self stopRepostFormCoordinator];
[_formInputAccessoryCoordinator clearPresentedState];
[_quickDeleteCoordinator stop];
_quickDeleteCoordinator = nil;
[_addContactsCoordinator stop];
_addContactsCoordinator = nil;
[_countryCodePickerCoordinator stop];
_countryCodePickerCoordinator = nil;
[_googleOneCoordinator stop];
_googleOneCoordinator = nil;
[self dismissLensPromo];
[self dismissEnhancedSafeBrowsingPromo];
[self dismissAccountMenu];
[self dismissAutoDeletionActionSheet];
[self.viewController clearPresentedStateWithCompletion:completion
dismissOmnibox:dismissOmnibox];
}
#pragma mark - Private
// Returns whether overscroll actions should be allowed. When screeen size is
// not regular, they should be enabled.
- (BOOL)shouldAllowOverscrollActions {
return !_toolbarAccessoryPresenter.presenting &&
!IsRegularXRegularSizeClass(self.viewController);
}
// Stops the password protection coordinator.
- (void)stopPasswordProtectionCoordinator {
[self.passwordProtectionCoordinator stop];
self.passwordProtectionCoordinator.delegate = nil;
self.passwordProtectionCoordinator = nil;
}
- (void)stopAutofillAddCreditCardCoordinator {
[self.addCreditCardCoordinator stop];
self.addCreditCardCoordinator.delegate = nil;
self.addCreditCardCoordinator = nil;
}
- (void)stopRepostFormCoordinator {
[self.repostFormCoordinator stop];
self.repostFormCoordinator.delegate = nil;
self.repostFormCoordinator = nil;
}
// Stops the recent tabs coordinator
- (void)stopRecentTabsCoordinator {
[self.recentTabsCoordinator stop];
self.recentTabsCoordinator.delegate = nil;
self.recentTabsCoordinator = nil;
}
// Stop the store kit coordinator.
- (void)stopStoreKitCoordinator {
[self.storeKitCoordinator stop];
self.storeKitCoordinator.delegate = nil;
self.storeKitCoordinator = nil;
}
// Stops the coordinator for password manager settings.
- (void)stopPasswordSettingsCoordinator {
[self.passwordSettingsCoordinator stop];
self.passwordSettingsCoordinator.delegate = nil;
self.passwordSettingsCoordinator = nil;
}
// Dismisses the account menu.
- (void)dismissAccountMenu {
if (!_NTPCoordinator) {
return;
}
}
- (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
if (!self.browser->GetProfile() || !self.started) {
return;
}
_webUsageEnabled = webUsageEnabled;
self.viewController.webUsageEnabled = webUsageEnabled;
}
// Displays activity overlay.
- (base::ScopedClosureRunner)showActivityOverlay {
_numberOfActivityOverly++;
self.activityOverlayCoordinator = [[ActivityOverlayCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.activityOverlayCoordinator start];
return base::ScopedClosureRunner(base::BindOnce(
[](BrowserCoordinator* strongSelf) {
[strongSelf decreaseActivityOverlay];
},
self));
}
// Hides activity overlay number. Remove it if the number becomes 0..
- (void)decreaseActivityOverlay {
_numberOfActivityOverly--;
if (_numberOfActivityOverly == 0) {
[self.activityOverlayCoordinator stop];
self.activityOverlayCoordinator = nil;
}
}
// Instantiates a BrowserViewController.
- (void)createViewController {
DCHECK(self.browserContainerCoordinator.viewController);
_viewController = [[BrowserViewController alloc]
initWithBrowserContainerViewController:self.browserContainerCoordinator
.viewController
keyCommandsProvider:_keyCommandsProvider
dependencies:_viewControllerDependencies];
_viewController.browserViewVisibilityConsumer = self;
self.tabLifecycleMediator.baseViewController = self.viewController;
self.tabLifecycleMediator.passwordControllerDelegate = self;
_webNavigationBrowserAgent->SetDelegate(self);
self.contextMenuProvider = [[ContextMenuConfigurationProvider alloc]
initWithBrowser:self.browser
baseViewController:_viewController];
}
// Shuts down the BrowserViewController.
- (void)destroyViewController {
self.viewController.active = NO;
self.viewController.webUsageEnabled = NO;
self.viewController.browserViewVisibilityConsumer = nil;
[self.contextMenuProvider stop];
self.contextMenuProvider = nil;
raw_ptr<TabBasedIPHBrowserAgent> tabBasedIPHBrowserAgent =
TabBasedIPHBrowserAgent::FromBrowser(self.browser);
if (tabBasedIPHBrowserAgent) {
tabBasedIPHBrowserAgent->RootViewForInProductHelpWillDisappear();
}
// TODO(crbug.com/40256480): Remove when BVC will no longer handle commands.
[self.dispatcher stopDispatchingToTarget:self.viewController];
[self.viewController shutdown];
_viewController = nil;
}
// Ensure BrowserViewController's view is created
- (BOOL)ensureViewIsCreated {
// Call `-view` for the side effect of creating the view.
UIView* view = self.viewController.view;
return view != nil;
}
// Creates the browser view controller dependencies.
- (void)createViewControllerDependencies {
_dispatcher = self.browser->GetCommandDispatcher();
// Add commands protocols handled by this class in this array to let the
// dispatcher know where to dispatch such commands. This must be done before
// starting any child coordinator, otherwise they won't be able to resolve
// handlers.
NSArray<Protocol*>* protocols = @[
@protocol(ActivityServiceCommands),
@protocol(AutoDeletionCommands),
@protocol(AutofillCommands),
@protocol(BrowserCoordinatorCommands),
@protocol(ContextualPanelEntrypointIPHCommands),
@protocol(ContextualSheetCommands),
@protocol(DefaultBrowserPromoNonModalCommands),
@protocol(DriveFilePickerCommands),
@protocol(FeedCommands),
@protocol(PromosManagerCommands),
@protocol(FindInPageCommands),
@protocol(NewTabPageCommands),
@protocol(PageInfoCommands),
@protocol(PasswordBreachCommands),
@protocol(PasswordProtectionCommands),
@protocol(PasswordSuggestionCommands),
@protocol(PolicyChangeCommands),
@protocol(PriceNotificationsCommands),
@protocol(QuickDeleteCommands),
@protocol(SaveToDriveCommands),
@protocol(SaveToPhotosCommands),
@protocol(TextZoomCommands),
@protocol(WebContentCommands),
@protocol(DefaultBrowserGenericPromoCommands),
@protocol(MiniMapCommands),
@protocol(ParcelTrackingOptInCommands),
@protocol(ParentAccessCommands),
@protocol(UnitConversionCommands),
@protocol(AddContactsCommands),
@protocol(CountryCodePickerCommands),
@protocol(WhatsNewCommands),
@protocol(GoogleOneCommands),
];
for (Protocol* protocol in protocols) {
[_dispatcher startDispatchingToTarget:self forProtocol:protocol];
}
ProfileIOS* profile = self.browser->GetProfile();
_keyCommandsProvider =
[[KeyCommandsProvider alloc] initWithBrowser:self.browser];
_keyCommandsProvider.applicationHandler =
HandlerForProtocol(_dispatcher, ApplicationCommands);
_keyCommandsProvider.settingsHandler =
HandlerForProtocol(_dispatcher, SettingsCommands);
_keyCommandsProvider.findInPageHandler =
HandlerForProtocol(_dispatcher, FindInPageCommands);
_keyCommandsProvider.browserCoordinatorHandler =
HandlerForProtocol(_dispatcher, BrowserCoordinatorCommands);
_keyCommandsProvider.quickDeleteHandler =
HandlerForProtocol(_dispatcher, QuickDeleteCommands);
// TODO(crbug.com/40937114): This can't use HandlerForProtocol because
// dispatch for BookmarksCommands is set up when the tab grid coordinator
// starts, which is after this is called, so for now use static_cast until
// that can be untangled.
_keyCommandsProvider.bookmarksHandler =
static_cast<id<BookmarksCommands>>(_dispatcher);
PrerenderService* prerenderService =
PrerenderServiceFactory::GetForProfile(profile);
if (!profile->IsOffTheRecord()) {
DCHECK(prerenderService);
prerenderService->SetDelegate(self);
}
_fullscreenController = FullscreenController::FromBrowser(self.browser);
_layoutGuideCenter = LayoutGuideCenterForBrowser(self.browser);
_webNavigationBrowserAgent =
WebNavigationBrowserAgent::FromBrowser(self.browser);
_urlLoadingBrowserAgent = UrlLoadingBrowserAgent::FromBrowser(self.browser);
_urlLoadingNotifierBrowserAgent =
UrlLoadingNotifierBrowserAgent::FromBrowser(self.browser);
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(profile);
if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
if (IsModernTabStripOrRaccoonEnabled()) {
_tabStripCoordinator =
[[TabStripCoordinator alloc] initWithBrowser:self.browser];
} else {
_legacyTabStripCoordinator =
[[TabStripLegacyCoordinator alloc] initWithBrowser:self.browser];
_legacyTabStripCoordinator.animationWaitDuration =
kLegacyFullscreenControllerToolbarAnimationDuration.InSecondsF();
}
}
_bubblePresenterCoordinator =
[[BubblePresenterCoordinator alloc] initWithBrowser:self.browser];
_bubblePresenterCoordinator.bubblePresenterDelegate = self;
[_bubblePresenterCoordinator start];
_toolbarCoordinator =
[[ToolbarCoordinator alloc] initWithBrowser:self.browser];
OmniboxPositionBrowserAgent* omniboxPositionBrowserAgent =
OmniboxPositionBrowserAgent::FromBrowser(self.browser);
_toolbarAccessoryPresenter = [[ToolbarAccessoryPresenter alloc]
initWithIsIncognito:profile->IsOffTheRecord()
omniboxPositionBrowserAgent:omniboxPositionBrowserAgent];
_toolbarAccessoryPresenter.topToolbarLayoutGuide =
[_layoutGuideCenter makeLayoutGuideNamed:kPrimaryToolbarGuide];
_toolbarAccessoryPresenter.bottomToolbarLayoutGuide =
[_layoutGuideCenter makeLayoutGuideNamed:kSecondaryToolbarGuide];
_sideSwipeMediator = [[SideSwipeMediator alloc]
initWithFullscreenController:_fullscreenController
webStateList:self.browser->GetWebStateList()];
_sideSwipeMediator.layoutGuideCenter =
LayoutGuideCenterForBrowser(self.browser);
_sideSwipeMediator.toolbarInteractionHandler = _toolbarCoordinator;
_sideSwipeMediator.toolbarSnapshotProvider = _toolbarCoordinator;
_sideSwipeMediator.engagementTracker = engagementTracker;
_sideSwipeMediator.helpHandler =
HandlerForProtocol(_dispatcher, HelpCommands);
if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET &&
!IsModernTabStripOrRaccoonEnabled()) {
[_sideSwipeMediator setTabStripDelegate:_legacyTabStripCoordinator];
}
_bookmarksCoordinator =
[[BookmarksCoordinator alloc] initWithBrowser:self.browser];
self.browserContainerCoordinator = [[BrowserContainerCoordinator alloc]
initWithBaseViewController:nil
browser:self.browser];
[self.browserContainerCoordinator start];
self.downloadManagerCoordinator = [[DownloadManagerCoordinator alloc]
initWithBaseViewController:self.browserContainerCoordinator.viewController
browser:self.browser];
self.downloadManagerCoordinator.presenter =
[[VerticalAnimationContainer alloc] init];
self.tabLifecycleMediator.downloadManagerTabHelperDelegate =
self.downloadManagerCoordinator;
self.qrScannerCoordinator =
[[QRScannerLegacyCoordinator alloc] initWithBrowser:self.browser];
self.popupMenuCoordinator =
[[PopupMenuCoordinator alloc] initWithBrowser:self.browser];
self.popupMenuCoordinator.UIUpdater = _toolbarCoordinator;
// Coordinator `start` is executed before setting it's `baseViewController`.
// It is done intentionally, since this does not affecting the coordinator's
// behavior but helps command handler setup below.
[self.popupMenuCoordinator start];
_NTPCoordinator = [[NewTabPageCoordinator alloc]
initWithBrowser:self.browser
componentFactory:[[NewTabPageComponentFactory alloc] init]];
_NTPCoordinator.toolbarDelegate = _toolbarCoordinator;
if (IsLVFUnifiedExperienceEnabled()) {
_lensViewFinderCoordinator =
[[LensViewFinderCoordinator alloc] initWithBrowser:self.browser];
} else {
_lensCoordinator = [[LensCoordinator alloc] initWithBrowser:self.browser];
}
_safeAreaProvider = [[SafeAreaProvider alloc] initWithBrowser:self.browser];
_voiceSearchController =
ios::provider::CreateVoiceSearchController(self.browser);
_viewControllerDependencies.toolbarAccessoryPresenter =
_toolbarAccessoryPresenter;
_viewControllerDependencies.popupMenuCoordinator = self.popupMenuCoordinator;
_viewControllerDependencies.ntpCoordinator = _NTPCoordinator;
_viewControllerDependencies.toolbarCoordinator = _toolbarCoordinator;
_viewControllerDependencies.tabStripCoordinator = _tabStripCoordinator;
_viewControllerDependencies.legacyTabStripCoordinator =
_legacyTabStripCoordinator;
_viewControllerDependencies.sideSwipeMediator = _sideSwipeMediator;
_viewControllerDependencies.bookmarksCoordinator = _bookmarksCoordinator;
_viewControllerDependencies.fullscreenController = _fullscreenController;
_viewControllerDependencies.textZoomHandler =
HandlerForProtocol(_dispatcher, TextZoomCommands);
_viewControllerDependencies.helpHandler =
HandlerForProtocol(_dispatcher, HelpCommands);
_viewControllerDependencies.popupMenuCommandsHandler =
HandlerForProtocol(_dispatcher, PopupMenuCommands);
_viewControllerDependencies.applicationCommandsHandler =
HandlerForProtocol(_dispatcher, ApplicationCommands);
_viewControllerDependencies.findInPageCommandsHandler =
HandlerForProtocol(_dispatcher, FindInPageCommands);
_viewControllerDependencies.isOffTheRecord = profile->IsOffTheRecord();
_viewControllerDependencies.urlLoadingBrowserAgent = _urlLoadingBrowserAgent;
_viewControllerDependencies.tabUsageRecorderBrowserAgent =
TabUsageRecorderBrowserAgent::FromBrowser(self.browser);
_viewControllerDependencies.layoutGuideCenter = _layoutGuideCenter;
_viewControllerDependencies.webStateList =
self.browser->GetWebStateList()->AsWeakPtr();
_viewControllerDependencies.voiceSearchController = _voiceSearchController;
_viewControllerDependencies.safeAreaProvider = _safeAreaProvider;
_viewControllerDependencies.pagePlaceholderBrowserAgent =
PagePlaceholderBrowserAgent::FromBrowser(self.browser);
}
- (void)updateViewControllerDependencies {
BrowserViewController* viewController = self.viewController;
_bookmarksCoordinator.baseViewController = viewController;
_toolbarAccessoryPresenter.baseViewController = viewController;
self.qrScannerCoordinator.baseViewController = viewController;
[self.qrScannerCoordinator start];
self.popupMenuCoordinator.baseViewController = viewController;
// The Lens coordinator needs to be started before the primary toolbar
// coordinator so that the LensCommands dispatcher is correctly registered in
// time.
if (IsLVFUnifiedExperienceEnabled()) {
_lensViewFinderCoordinator.baseViewController = viewController;
[_lensViewFinderCoordinator start];
} else {
_lensCoordinator.baseViewController = viewController;
_lensCoordinator.delegate = viewController;
[_lensCoordinator start];
}
_toolbarCoordinator.baseViewController = viewController;
_toolbarCoordinator.omniboxFocusDelegate = viewController;
_toolbarCoordinator.popupPresenterDelegate = viewController;
_toolbarCoordinator.toolbarHeightDelegate = viewController;
[_toolbarCoordinator start];
_loadQueryCommandsHandler =
HandlerForProtocol(_dispatcher, LoadQueryCommands);
_viewController.loadQueryCommandsHandler = _loadQueryCommandsHandler;
_voiceSearchController.dispatcher = _loadQueryCommandsHandler;
_omniboxCommandsHandler = HandlerForProtocol(_dispatcher, OmniboxCommands);
_keyCommandsProvider.omniboxHandler = _omniboxCommandsHandler;
_viewController.omniboxCommandsHandler = _omniboxCommandsHandler;
_legacyTabStripCoordinator.baseViewController = viewController;
_tabStripCoordinator.baseViewController = viewController;
_NTPCoordinator.baseViewController = viewController;
_bubblePresenterCoordinator.baseViewController = viewController;
[_dispatcher startDispatchingToTarget:viewController
forProtocol:@protocol(BrowserCommands)];
}
// Destroys the browser view controller dependencies.
- (void)destroyViewControllerDependencies {
_viewControllerDependencies.toolbarAccessoryPresenter = nil;
_viewControllerDependencies.popupMenuCoordinator = nil;
_viewControllerDependencies.ntpCoordinator = nil;
_viewControllerDependencies.toolbarCoordinator = nil;
_viewControllerDependencies.tabStripCoordinator = nil;
_viewControllerDependencies.legacyTabStripCoordinator = nil;
_viewControllerDependencies.sideSwipeMediator = nil;
_viewControllerDependencies.bookmarksCoordinator = nil;
_viewControllerDependencies.fullscreenController = nil;
_viewControllerDependencies.textZoomHandler = nil;
_viewControllerDependencies.helpHandler = nil;
_viewControllerDependencies.popupMenuCommandsHandler = nil;
_viewControllerDependencies.applicationCommandsHandler = nil;
_viewControllerDependencies.findInPageCommandsHandler = nil;
_viewControllerDependencies.urlLoadingBrowserAgent = nil;
_viewControllerDependencies.tabUsageRecorderBrowserAgent = nil;
_viewControllerDependencies.layoutGuideCenter = nil;
_viewControllerDependencies.voiceSearchController = nil;
_viewControllerDependencies.safeAreaProvider = nil;
_viewControllerDependencies.pagePlaceholderBrowserAgent = nil;
[_voiceSearchController dismissMicPermissionHelp];
[_voiceSearchController disconnect];
_voiceSearchController.dispatcher = nil;
_voiceSearchController = nil;
[_bookmarksCoordinator stop];
_bookmarksCoordinator = nil;
[_bubblePresenterCoordinator stop];
_bubblePresenterCoordinator = nil;
_legacyTabStripCoordinator = nil;
_tabStripCoordinator = nil;
[_sideSwipeMediator disconnect];
_sideSwipeMediator = nil;
_toolbarCoordinator = nil;
_loadQueryCommandsHandler = nil;
_omniboxCommandsHandler = nil;
[_toolbarAccessoryPresenter disconnect];
_toolbarAccessoryPresenter = nil;
[_contextualPanelEntrypointHelpPresenter dismissAnimated:NO];
_contextualPanelEntrypointHelpPresenter = nil;
_fullscreenController = nullptr;
[self.popupMenuCoordinator stop];
self.popupMenuCoordinator = nil;
[self.qrScannerCoordinator stop];
self.qrScannerCoordinator = nil;
[_lensCoordinator stop];
_lensCoordinator = nil;
if (IsLVFUnifiedExperienceEnabled()) {
[_lensViewFinderCoordinator stop];
_lensViewFinderCoordinator = nil;
} else {
[_lensOverlayCoordinator stop];
_lensOverlayCoordinator = nil;
}
[self.downloadManagerCoordinator stop];
self.downloadManagerCoordinator = nil;
[self.browserContainerCoordinator stop];
self.browserContainerCoordinator = nil;
[_NTPCoordinator stop];
_NTPCoordinator = nil;
_keyCommandsProvider = nil;
_dispatcher = nil;
_layoutGuideCenter = nil;
_webNavigationBrowserAgent = nil;
_urlLoadingBrowserAgent = nil;
}
// Starts child coordinators.
- (void)startChildCoordinators {
// Dispatcher should be instantiated so that it can be passed to child
// coordinators.
DCHECK(self.dispatcher);
self.ARQuickLookCoordinator = [[ARQuickLookCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.ARQuickLookCoordinator start];
self.SafariDownloadCoordinator = [[SafariDownloadCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.SafariDownloadCoordinator start];
self.vcardCoordinator =
[[VcardCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser];
[self.vcardCoordinator start];
self.printCoordinator =
[[PrintCoordinator alloc] initWithBaseViewController:self.viewController];
// Updates the printControllar value inside tabLifecycleMediator.
self.tabLifecycleMediator.printCoordinator = self.printCoordinator;
// Help should only show in regular, non-incognito.
if (!self.browser->GetProfile()->IsOffTheRecord()) {
[self.popupMenuCoordinator startPopupMenuHelpCoordinator];
}
/* choiceCoordinator is created and started by a BrowserCommand */
/* NetExportCoordinator is created and started by a delegate method */
/* passwordBreachCoordinator is created and started by a BrowserCommand */
/* passwordProtectionCoordinator is created and started by a BrowserCommand */
/* passwordSettingsCoordinator is created and started by a delegate method */
/* passwordSuggestionBottomSheetCoordinator is created and started by a
* BrowserCommand */
/* passwordSuggestionCoordinator is created and started by a BrowserCommand */
/* paymentsSuggestionBottomSheetCoordinator is created and started by a
* BrowserCommand */
/* virtualCardEnrollmentBottomSheetCoordinator is created and started by a
* BrowserCommand */
/* autofillErrorDialogCoordinator is created and started by a BrowserCommand
*/
/* autofillProgressDialogCoordinator is created and started by a
* BrowserCommand */
/* PriceNotificationsViewCoordinator is created and started by a
* BrowserCommand */
/* ReadingListCoordinator is created and started by a BrowserCommand */
/* RecentTabsCoordinator is created and started by a BrowserCommand */
/* RepostFormCoordinator is created and started by a delegate method */
/* WhatsNewCoordinator is created and started by a BrowserCommand */
// TODO(crbug.com/40823248): Should start when the Sad Tab UI appears.
self.sadTabCoordinator =
[[SadTabCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser];
[self.sadTabCoordinator setOverscrollDelegate:self];
/* SharingCoordinator is created and started by an ActivityServiceCommand */
self.safeBrowsingCoordinator = [[SafeBrowsingCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.safeBrowsingCoordinator start];
// TODO(crbug.com/40228065): Refactor this coordinator so it doesn't directly
// access the BVC's view.
self.formInputAccessoryCoordinator = [[FormInputAccessoryCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.formInputAccessoryCoordinator.navigator = self;
[self.formInputAccessoryCoordinator start];
// TODO(crbug.com/40228065): Refactor this coordinator so it doesn't dirctly
// access the BVC's view.
self.infobarModalOverlayContainerCoordinator =
[[OverlayContainerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
modality:OverlayModality::kInfobarModal];
[self.infobarModalOverlayContainerCoordinator start];
self.viewController.infobarModalOverlayContainerViewController =
self.infobarModalOverlayContainerCoordinator.viewController;
// TODO(crbug.com/40228065): Refactor this coordinator so it doesn't directly
// access the BVC's view.
self.infobarBannerOverlayContainerCoordinator =
[[OverlayContainerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
modality:OverlayModality::kInfobarBanner];
[self.infobarBannerOverlayContainerCoordinator start];
self.viewController.infobarBannerOverlayContainerViewController =
self.infobarBannerOverlayContainerCoordinator.viewController;
_credentialProviderPromoCoordinator =
[[CredentialProviderPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
_credentialProviderPromoCoordinator.promosUIHandler =
_promosManagerCoordinator;
[_credentialProviderPromoCoordinator start];
_dockingPromoCoordinator = [[DockingPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
_dockingPromoCoordinator.promosUIHandler = _promosManagerCoordinator;
[_dockingPromoCoordinator start];
if (IsLensOverlayAvailable()) {
_lensOverlayCoordinator = [[LensOverlayCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[_lensOverlayCoordinator start];
}
}
// Stops child coordinators.
- (void)stopChildCoordinators {
[self.ARQuickLookCoordinator stop];
self.ARQuickLookCoordinator = nil;
[self.findBarCoordinator stop];
self.findBarCoordinator = nil;
[self.firstFollowCoordinator stop];
self.firstFollowCoordinator = nil;
[self.formInputAccessoryCoordinator stop];
self.formInputAccessoryCoordinator = nil;
[self.SafariDownloadCoordinator stop];
self.SafariDownloadCoordinator = nil;
[self.vcardCoordinator stop];
self.vcardCoordinator = nil;
[self hidePageInfo];
[self.passKitCoordinator stop];
self.passKitCoordinator = nil;
[self.passwordBreachCoordinator stop];
self.passwordBreachCoordinator = nil;
[self stopPasswordProtectionCoordinator];
[self.passwordSuggestionBottomSheetCoordinator stop];
self.passwordSuggestionBottomSheetCoordinator = nil;
[self.passwordSuggestionCoordinator stop];
self.passwordSuggestionCoordinator = nil;
[self.paymentsSuggestionBottomSheetCoordinator stop];
self.paymentsSuggestionBottomSheetCoordinator = nil;
[self.cardUnmaskAuthenticationCoordinator stop];
self.cardUnmaskAuthenticationCoordinator = nil;
[self.plusAddressBottomSheetCoordinator stop];
self.plusAddressBottomSheetCoordinator = nil;
[self.virtualCardEnrollmentBottomSheetCoordinator stop];
self.virtualCardEnrollmentBottomSheetCoordinator = nil;
[self dismissAutofillErrorDialog];
[self dismissAutofillProgressDialog];
[self.printCoordinator stop];
self.printCoordinator = nil;
[self hidePriceNotifications];
[self.promosManagerCoordinator stop];
self.promosManagerCoordinator = nil;
[self.readingListCoordinator stop];
self.readingListCoordinator.delegate = nil;
self.readingListCoordinator = nil;
[self stopRecentTabsCoordinator];
[self stopRepostFormCoordinator];
// TODO(crbug.com/40823248): Should stop when the Sad Tab UI appears.
[self.sadTabCoordinator stop];
[self.sadTabCoordinator disconnect];
self.sadTabCoordinator = nil;
[self.safeBrowsingCoordinator stop];
self.safeBrowsingCoordinator = nil;
[self.sharingCoordinator stop];
self.sharingCoordinator = nil;
[self stopStoreKitCoordinator];
[self hideTextZoomUI];
[self stopAutofillAddCreditCardCoordinator];
[self.infobarBannerOverlayContainerCoordinator stop];
self.infobarBannerOverlayContainerCoordinator = nil;
[self.infobarModalOverlayContainerCoordinator stop];
self.infobarModalOverlayContainerCoordinator = nil;
[self.nonModalPromoCoordinator stop];
self.nonModalPromoCoordinator = nil;
[self.netExportCoordinator stop];
self.netExportCoordinator = nil;
[_sendTabToSelfCoordinator stop];
_sendTabToSelfCoordinator = nil;
[self.whatsNewCoordinator stop];
self.whatsNewCoordinator = nil;
[self.passwordSettingsCoordinator stop];
self.passwordSettingsCoordinator.delegate = nil;
self.passwordSettingsCoordinator = nil;
[_credentialProviderPromoCoordinator stop];
_credentialProviderPromoCoordinator = nil;
[_dockingPromoCoordinator stop];
_dockingPromoCoordinator = nil;
[self.defaultBrowserGenericPromoCoordinator stop];
self.defaultBrowserGenericPromoCoordinator = nil;
[self.choiceCoordinator stop];
self.choiceCoordinator = nil;
[self.miniMapCoordinator stop];
self.miniMapCoordinator = nil;
[self.saveToDriveCoordinator stop];
self.saveToDriveCoordinator = nil;
[self.saveToPhotosCoordinator stop];
self.saveToPhotosCoordinator = nil;
[self.parcelTrackingOptInCoordinator stop];
self.parcelTrackingOptInCoordinator = nil;
[self.unitConversionCoordinator stop];
self.unitConversionCoordinator = nil;
[_addContactsCoordinator stop];
_addContactsCoordinator = nil;
[_quickDeleteCoordinator stop];
_quickDeleteCoordinator = nil;
[_googleOneCoordinator stop];
_googleOneCoordinator = nil;
[self hideDriveFilePicker];
[self hideContextualSheet];
[self dismissEditAddressBottomSheet];
[self dismissLensPromo];
[self dismissEnhancedSafeBrowsingPromo];
[self dismissAutoDeletionActionSheet];
}
// Starts independent mediators owned by this coordinator.
- (void)startIndependentMediators {
// Cache frequently repeated property values to curb generated code bloat.
ProfileIOS* profile = self.browser->GetProfile();
BrowserViewController* browserViewController = self.viewController;
DCHECK(self.browserContainerCoordinator.viewController);
self.tabEventsMediator = [[TabEventsMediator alloc]
initWithWebStateList:self.browser->GetWebStateList()
ntpCoordinator:_NTPCoordinator
profile:profile
loadingNotifier:_urlLoadingNotifierBrowserAgent];
self.tabEventsMediator.toolbarSnapshotProvider = _toolbarCoordinator;
self.tabEventsMediator.consumer = browserViewController;
CHECK(self.tabLifecycleMediator);
self.tabLifecycleMediator.NTPTabHelperDelegate = self.tabEventsMediator;
browserViewController.reauthHandler =
HandlerForProtocol(self.dispatcher, IncognitoReauthCommands);
browserViewController.nonModalPromoPresentationDelegate = self;
if (profile->IsOffTheRecord()) {
SceneState* sceneState = self.browser->GetSceneState();
IncognitoReauthSceneAgent* reauthAgent =
[IncognitoReauthSceneAgent agentFromScene:sceneState];
self.incognitoAuthMediator =
[[IncognitoReauthMediator alloc] initWithReauthAgent:reauthAgent];
self.incognitoAuthMediator.consumer = browserViewController;
}
}
- (void)startTabLifeCycleMediator {
Browser* browser = self.browser;
TabLifecycleMediator* tabLifecycleMediator = [[TabLifecycleMediator alloc]
initWithWebStateList:browser->GetWebStateList()];
// Set properties that are already valid.
tabLifecycleMediator.prerenderService =
PrerenderServiceFactory::GetForProfile(browser->GetProfile());
tabLifecycleMediator.commandDispatcher = browser->GetCommandDispatcher();
tabLifecycleMediator.tabHelperDelegate = self;
tabLifecycleMediator.repostFormDelegate = self;
tabLifecycleMediator.tabInsertionBrowserAgent =
TabInsertionBrowserAgent::FromBrowser(browser);
tabLifecycleMediator.snapshotGeneratorDelegate = self;
tabLifecycleMediator.overscrollActionsDelegate = self;
tabLifecycleMediator.appLauncherBrowserPresentationProvider = self;
tabLifecycleMediator.editMenuBuilder = self;
self.tabLifecycleMediator = tabLifecycleMediator;
}
- (web::WebState*)activeWebState {
WebStateList* webStateList = self.browser->GetWebStateList();
return webStateList ? webStateList->GetActiveWebState() : nullptr;
}
- (void)contextualPanelEntrypointIPHDidDismissWithConfig:
(base::WeakPtr<ContextualPanelItemConfiguration>)config
dismissalReason:
(IPHDismissalReasonType)
IPHDismissalReasonType {
ContextualPanelItemConfiguration* config_ptr = config.get();
if (!config_ptr) {
return;
}
[HandlerForProtocol(self.dispatcher, ContextualPanelEntrypointCommands)
notifyContextualPanelEntrypointIPHDismissed];
ProfileIOS* profile = self.browser->GetProfile();
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(profile);
if (!engagementTracker || !_contextualPanelEntrypointHelpPresenter) {
return;
}
engagementTracker->Dismissed(*config_ptr->iph_feature);
_contextualPanelEntrypointHelpPresenter = nil;
if (IPHDismissalReasonType == IPHDismissalReasonType::kTappedAnchorView ||
IPHDismissalReasonType == IPHDismissalReasonType::kTappedIPH) {
[self openContextualSheet];
[self recordContextualPanelEntrypointIPHDismissed:
ContextualPanelIPHDismissedReason::UserInteracted];
return;
}
if (IPHDismissalReasonType ==
IPHDismissalReasonType::kTappedOutsideIPHAndAnchorView ||
IPHDismissalReasonType == IPHDismissalReasonType::kTappedClose) {
engagementTracker->NotifyEvent(
config_ptr->iph_entrypoint_explicitly_dismissed);
[self recordContextualPanelEntrypointIPHDismissed:
ContextualPanelIPHDismissedReason::UserDismissed];
return;
}
if (IPHDismissalReasonType == IPHDismissalReasonType::kTimedOut) {
[self recordContextualPanelEntrypointIPHDismissed:
ContextualPanelIPHDismissedReason::TimedOut];
return;
}
[self recordContextualPanelEntrypointIPHDismissed:
ContextualPanelIPHDismissedReason::Other];
}
- (void)recordContextualPanelEntrypointIPHDismissed:
(ContextualPanelIPHDismissedReason)dismissalReason {
base::UmaHistogramEnumeration("IOS.ContextualPanel.IPH.DismissedReason",
dismissalReason);
}
#pragma mark - ActivityServiceCommands
- (void)stopAndStartSharingCoordinator {
SharingParams* params =
[[SharingParams alloc] initWithScenario:SharingScenario::TabShareButton];
// Exit fullscreen if needed to make sure that share button is visible.
_fullscreenController->ExitFullscreen();
id<SharingPositioner> positioner = _toolbarCoordinator.sharingPositioner;
UIBarButtonItem* anchor = nil;
if ([positioner respondsToSelector:@selector(barButtonItem)]) {
anchor = positioner.barButtonItem;
}
[self.sharingCoordinator stop];
self.sharingCoordinator = nil;
self.sharingCoordinator = [[SharingCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
params:params
originView:positioner.sourceView
originRect:positioner.sourceRect
anchor:anchor];
[self.sharingCoordinator start];
}
- (void)showShareSheet {
// Defocus Find-In-Page before opening the share sheet. This will result in
// closing the Find-In-Page for some OS versions.
[self defocusFindInPage];
if (!self.sharingCoordinator) {
[self stopAndStartSharingCoordinator];
} else {
[self.sharingCoordinator cancelIfNecessaryAndCreateNewCoordinator];
}
}
- (void)showShareSheetForChromeApp {
GURL URL = GURL(kChromeAppStoreUrl);
NSString* title =
l10n_util::GetNSString(IDS_IOS_OVERFLOW_MENU_SHARE_CHROME_TITLE);
NSString* additionalText =
l10n_util::GetNSString(IDS_IOS_OVERFLOW_MENU_SHARE_CHROME_DESC);
SharingParams* params =
[[SharingParams alloc] initWithURL:URL
title:title
additionalText:additionalText
scenario:SharingScenario::ShareChrome];
// Exit fullscreen if needed to make sure that share button is visible.
_fullscreenController->ExitFullscreen();
UIView* originView =
[_layoutGuideCenter referencedViewUnderName:kToolsMenuGuide];
self.sharingCoordinator =
[[SharingCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser
params:params
originView:originView];
[self.sharingCoordinator start];
}
- (void)showShareSheetForHighlight:(ShareHighlightCommand*)command {
SharingParams* params =
[[SharingParams alloc] initWithURL:command.URL
title:command.title
additionalText:command.selectedText
scenario:SharingScenario::SharedHighlight];
self.sharingCoordinator =
[[SharingCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser
params:params
originView:command.sourceView
originRect:command.sourceRect
anchor:nil];
[self.sharingCoordinator start];
}
- (void)showShareSheetForURL:(ActivityServiceShareURLCommand*)command {
SharingParams* params = [[SharingParams alloc]
initWithURL:command.URL
title:command.title
scenario:SharingScenario::ShareInWebContextMenu];
self.sharingCoordinator =
[[SharingCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser
params:params
originView:command.sourceView
originRect:command.sourceRect
anchor:nil];
[self.sharingCoordinator start];
}
#pragma mark - AutoDeletionCommands
- (void)presentAutoDeletionActionSheetWithDownloadTask:
(web::DownloadTask*)task {
// Do not present the action sheet if it is already being presented.
if (_autoDeletionCoordiantor) {
return;
}
_autoDeletionCoordiantor = [[AutoDeletionCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
downloadTask:task];
[_autoDeletionCoordiantor start];
}
- (void)dismissAutoDeletionActionSheet {
[_autoDeletionCoordiantor stop];
_autoDeletionCoordiantor = nil;
}
#pragma mark - AutofillBottomSheetCommands
- (void)showPasswordBottomSheet:(const autofill::FormActivityParams&)params {
// Do not present the bottom sheet if it is already being presented.
if (self.passwordSuggestionBottomSheetCoordinator) {
return;
}
self.passwordSuggestionBottomSheetCoordinator =
[[PasswordSuggestionBottomSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
params:params
delegate:self];
self.passwordSuggestionBottomSheetCoordinator.settingsHandler =
HandlerForProtocol(self.dispatcher, SettingsCommands);
self.passwordSuggestionBottomSheetCoordinator
.browserCoordinatorCommandsHandler =
HandlerForProtocol(self.dispatcher, BrowserCoordinatorCommands);
[self.passwordSuggestionBottomSheetCoordinator start];
}
- (void)showPaymentsBottomSheet:(const autofill::FormActivityParams&)params {
// Do not present the bottom sheet if it is already being presented.
if (self.paymentsSuggestionBottomSheetCoordinator) {
return;
}
self.paymentsSuggestionBottomSheetCoordinator =
[[PaymentsSuggestionBottomSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
params:params];
self.paymentsSuggestionBottomSheetCoordinator.settingsHandler =
HandlerForProtocol(self.dispatcher, SettingsCommands);
self.paymentsSuggestionBottomSheetCoordinator
.browserCoordinatorCommandsHandler =
HandlerForProtocol(self.dispatcher, BrowserCoordinatorCommands);
[self.paymentsSuggestionBottomSheetCoordinator start];
}
- (void)showCardUnmaskAuthentication {
self.cardUnmaskAuthenticationCoordinator =
[[CardUnmaskAuthenticationCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.cardUnmaskAuthenticationCoordinator.shouldStartWithCvcAuth = NO;
[self.cardUnmaskAuthenticationCoordinator start];
}
- (void)continueCardUnmaskWithOtpAuth {
// This assumes the card unmask authentication coordinator is already created
// by the showCardUnmaskAuthentication function above. Otherwise do nothing.
[self.cardUnmaskAuthenticationCoordinator continueWithOtpAuth];
}
- (void)continueCardUnmaskWithCvcAuth {
if (self.cardUnmaskAuthenticationCoordinator) {
// If the coordinator exists, it means that multiple authentication options
// are provided and we have already presented the authentication selection
// dialog, and the navigation controller is already created. Upon user
// selection, we should show the CVC input dialog by pushing the view to the
// navigation stack.
[self.cardUnmaskAuthenticationCoordinator continueWithCvcAuth];
} else {
// If the coordinator does not exists, it means there is only one
// authentication option (CVC auth) provided, and the navigation controller
// is not yet created, so we skip the authentication selection step and
// start directly with the CVC input dialog.
self.cardUnmaskAuthenticationCoordinator =
[[CardUnmaskAuthenticationCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.cardUnmaskAuthenticationCoordinator.shouldStartWithCvcAuth = YES;
[self.cardUnmaskAuthenticationCoordinator start];
}
}
- (void)showPlusAddressesBottomSheet {
self.plusAddressBottomSheetCoordinator =
[[PlusAddressBottomSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.plusAddressBottomSheetCoordinator start];
}
- (void)showVirtualCardEnrollmentBottomSheet:
(std::unique_ptr<autofill::VirtualCardEnrollUiModel>)model
originWebState:(web::WebState*)originWebState {
if (self.virtualCardEnrollmentBottomSheetCoordinator) {
[self.virtualCardEnrollmentBottomSheetCoordinator stop];
}
if (self.activeWebState != originWebState) {
// Do not show the sheet if the current tab is not the one where the credit
// card was originally saved.
return;
}
self.virtualCardEnrollmentBottomSheetCoordinator =
[[VirtualCardEnrollmentBottomSheetCoordinator alloc]
initWithUIModel:std::move(model)
baseViewController:self.viewController
browser:self.browser];
[self.virtualCardEnrollmentBottomSheetCoordinator start];
}
- (void)showEditAddressBottomSheet {
self.autofillEditProfileBottomSheetCoordinator =
[[AutofillEditProfileBottomSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.autofillEditProfileBottomSheetCoordinator start];
}
- (void)dismissEditAddressBottomSheet {
if (self.autofillEditProfileBottomSheetCoordinator) {
[self.autofillEditProfileBottomSheetCoordinator stop];
}
self.autofillEditProfileBottomSheetCoordinator = nil;
}
- (void)showAutofillErrorDialog:
(autofill::AutofillErrorDialogContext)errorContext {
if (self.autofillErrorDialogCoordinator) {
[self.autofillErrorDialogCoordinator stop];
}
self.autofillErrorDialogCoordinator = [[AutofillErrorDialogCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
errorContext:std::move(errorContext)];
self.autofillErrorDialogCoordinator.autofillCommandsHandler =
HandlerForProtocol(self.dispatcher, AutofillCommands);
[self.autofillErrorDialogCoordinator start];
}
- (void)dismissAutofillErrorDialog {
[self.autofillErrorDialogCoordinator stop];
self.autofillErrorDialogCoordinator = nil;
}
- (void)showAutofillProgressDialog {
if (self.autofillProgressDialogCoordinator) {
[self.autofillProgressDialogCoordinator stop];
}
self.autofillProgressDialogCoordinator =
[[AutofillProgressDialogCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.autofillProgressDialogCoordinator start];
}
- (void)dismissAutofillProgressDialog {
[self.autofillProgressDialogCoordinator stop];
self.autofillProgressDialogCoordinator = nil;
}
#pragma mark - BrowserCoordinatorCommands
- (void)printTabWithBaseViewController:(UIViewController*)baseViewController {
DCHECK(self.printCoordinator);
web::WebState* webState =
self.browser->GetWebStateList()->GetActiveWebState();
[self.printCoordinator printWebState:webState
baseViewController:baseViewController];
}
- (void)printImage:(UIImage*)image
title:(NSString*)title
baseViewController:(UIViewController*)baseViewController {
DCHECK(self.printCoordinator);
[self.printCoordinator printImage:image
title:title
baseViewController:baseViewController];
}
- (void)showReadingList {
if (self.readingListCoordinator) {
[self closeReadingList];
}
self.readingListCoordinator = [[ReadingListCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.readingListCoordinator.delegate = self;
[self.readingListCoordinator start];
}
- (void)showBookmarksManager {
[IntentDonationHelper donateIntent:IntentType::kOpenBookmarks];
[_bookmarksCoordinator presentBookmarks];
}
- (void)showDownloadsFolder {
NSURL* URL = GetFilesAppUrl();
if (!URL) {
return;
}
[[UIApplication sharedApplication] openURL:URL
options:@{}
completionHandler:nil];
base::UmaHistogramEnumeration(
"Download.OpenDownloads.PerProfileType",
profile_metrics::GetBrowserProfileType(self.browser->GetProfile()));
}
- (void)showRecentTabs {
[IntentDonationHelper donateIntent:IntentType::kOpenRecentTabs];
// TODO(crbug.com/40568563): If BVC's clearPresentedState is ever called (such
// as in tearDown after a failed egtest), then this coordinator is left in a
// started state even though its corresponding VC is no longer on screen.
// That causes issues when the coordinator is started again and we destroy the
// old mediator without disconnecting it first. Temporarily work around these
// issues by not having a long lived coordinator. A longer-term solution will
// require finding a way to stop this coordinator so that the mediator is
// properly disconnected and destroyed and does not live longer than its
// associated VC.
[self.recentTabsCoordinator stop];
self.recentTabsCoordinator = nil;
self.recentTabsCoordinator = [[RecentTabsCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.recentTabsCoordinator.loadStrategy = UrlLoadStrategy::NORMAL;
self.recentTabsCoordinator.delegate = self;
[self.recentTabsCoordinator start];
}
- (void)showTranslate {
ProfileIOS* profile = self.browser->GetProfile();
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(profile);
engagementTracker->NotifyEvent(
feature_engagement::events::kTriggeredTranslateInfobar);
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
ChromeIOSTranslateClient* translateClient =
ChromeIOSTranslateClient::FromWebState(activeWebState);
if (translateClient) {
translate::TranslateManager* translateManager =
translateClient->GetTranslateManager();
DCHECK(translateManager);
translateManager->ShowTranslateUI(/*auto_translate=*/true,
/*triggered_from_menu=*/true);
}
// Records the usage of Google Translate. This notifies the Tips Manager,
// which may trigger tips or guidance related to translation features.
if (IsSegmentationTipsManagerEnabled()) {
TipsManagerIOS* tipsManager = TipsManagerIOSFactory::GetForProfile(profile);
tipsManager->NotifySignal(
segmentation_platform::tips_manager::signals::kUsedGoogleTranslation);
}
}
- (void)showHelpPage {
GURL helpUrl(l10n_util::GetStringUTF16(IDS_IOS_TOOLS_MENU_HELP_URL));
UrlLoadParams params = UrlLoadParams::InNewTab(helpUrl);
params.append_to = OpenPosition::kCurrentTab;
params.user_initiated = NO;
params.in_incognito = self.browser->GetProfile()->IsOffTheRecord();
_urlLoadingBrowserAgent->Load(params);
}
- (void)showAddCreditCard {
self.addCreditCardCoordinator = [[AutofillAddCreditCardCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.addCreditCardCoordinator.delegate = self;
[self.addCreditCardCoordinator start];
}
- (void)showSendTabToSelfUI:(const GURL&)url title:(NSString*)title {
_sendTabToSelfCoordinator = [[SendTabToSelfCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
signinPresenter:self
url:url
title:title];
[_sendTabToSelfCoordinator start];
}
- (void)hideSendTabToSelfUI {
DCHECK(_sendTabToSelfCoordinator);
[_sendTabToSelfCoordinator stop];
_sendTabToSelfCoordinator = nil;
}
#if !defined(NDEBUG)
- (void)viewSource {
ViewSourceBrowserAgent* viewSourceAgent =
ViewSourceBrowserAgent::FromBrowser(self.browser);
viewSourceAgent->ViewSourceForActiveWebState();
}
#endif // !defined(NDEBUG)
- (void)focusFakebox {
if ([self isNTPActiveForCurrentWebState]) {
[_NTPCoordinator focusFakebox];
}
}
// TODO(crbug.com/40806293): Refactor this command away, and add a mediator to
// observe the active web state closing and push updates into the BVC for UI
// work.
- (void)closeCurrentTab {
WebStateList* webStateList = self.browser->GetWebStateList();
int active_index = webStateList->active_index();
if (active_index == WebStateList::kInvalidIndex) {
return;
}
BOOL canShowTabStrip = IsRegularXRegularSizeClass(self.viewController);
UIView* contentArea = self.browserContainerCoordinator.viewController.view;
UIView* snapshotView = nil;
if (!canShowTabStrip) {
snapshotView = [contentArea snapshotViewAfterScreenUpdates:NO];
snapshotView.frame = contentArea.frame;
}
webStateList->CloseWebStateAt(active_index, WebStateList::CLOSE_USER_ACTION);
if (!canShowTabStrip) {
[contentArea addSubview:snapshotView];
page_animation_util::AnimateOutWithCompletion(snapshotView, ^{
[snapshotView removeFromSuperview];
});
}
}
- (void)showSpotlightDebugger {
[self.spotlightDebuggerCoordinator stop];
self.spotlightDebuggerCoordinator = [[SpotlightDebuggerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.spotlightDebuggerCoordinator start];
}
- (void)preloadVoiceSearch {
// Preload VoiceSearchController and views and view controllers needed
// for voice search.
[_voiceSearchController prepareToAppear];
}
- (void)dismissPasswordSuggestions {
[self.passwordSuggestionBottomSheetCoordinator stop];
self.passwordSuggestionBottomSheetCoordinator = nil;
}
- (void)dismissPaymentSuggestions {
[self.paymentsSuggestionBottomSheetCoordinator stop];
self.paymentsSuggestionBottomSheetCoordinator = nil;
}
- (void)dismissCardUnmaskAuthentication {
[self.cardUnmaskAuthenticationCoordinator stop];
self.cardUnmaskAuthenticationCoordinator = nil;
}
- (void)dismissPlusAddressBottomSheet {
[self.plusAddressBottomSheetCoordinator stop];
self.plusAddressBottomSheetCoordinator = nil;
}
- (void)dismissVirtualCardEnrollmentBottomSheet {
[self.virtualCardEnrollmentBottomSheetCoordinator stop];
self.virtualCardEnrollmentBottomSheetCoordinator = nil;
}
- (void)showOmniboxPositionChoice {
if (!_omniboxPositionChoiceCoordinator) {
_omniboxPositionChoiceCoordinator =
[[OmniboxPositionChoiceCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
} else {
[_omniboxPositionChoiceCoordinator stop];
}
[_omniboxPositionChoiceCoordinator start];
}
- (void)dismissOmniboxPositionChoice {
[_omniboxPositionChoiceCoordinator stop];
_omniboxPositionChoiceCoordinator = nil;
}
- (void)showLensPromo {
[_lensPromoCoordinator stop];
_lensPromoCoordinator = [[LensPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[_lensPromoCoordinator start];
}
- (void)dismissLensPromo {
[_lensPromoCoordinator stop];
_lensPromoCoordinator = nil;
}
- (void)showEnhancedSafeBrowsingPromo {
[_enhancedSafeBrowsingPromoCoordinator stop];
_enhancedSafeBrowsingPromoCoordinator =
[[EnhancedSafeBrowsingPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[_enhancedSafeBrowsingPromoCoordinator start];
}
- (void)dismissEnhancedSafeBrowsingPromo {
[_enhancedSafeBrowsingPromoCoordinator stop];
_enhancedSafeBrowsingPromoCoordinator = nil;
}
- (BOOL)navigateBackWithAnimationIfNeeded {
if (!IsLensOverlaySameTabNavigationEnabled() ||
IsCompactHeight(self.viewController)) {
return NO;
}
LensOverlayTabHelper* lensOverlayTabHelper =
LensOverlayTabHelper::FromWebState(self.activeWebState);
if (lensOverlayTabHelper &&
lensOverlayTabHelper->IsLensOverlayInvokedOnMostRecentBackItem()) {
[_sideSwipeMediator
animateSwipe:SwipeType::CHANGE_PAGE
inDirection:UseRTLLayout() ? UISwipeGestureRecognizerDirectionLeft
: UISwipeGestureRecognizerDirectionRight];
return YES;
}
return NO;
}
- (void)animateLensOverlayNavigationToURL:(GURL)URL {
[_sideSwipeMediator
prepareForSlideInDirection:UseRTLLayout()
? UISwipeGestureRecognizerDirectionRight
: UISwipeGestureRecognizerDirectionLeft];
__weak SideSwipeMediator* weakSideSwipeMediator = _sideSwipeMediator;
[HandlerForProtocol(_dispatcher, LensOverlayCommands)
hideLensUI:NO
completion:^{
[weakSideSwipeMediator slideToCenterAnimated];
}];
[_loadQueryCommandsHandler loadQuery:base::SysUTF8ToNSString(URL.spec())
immediately:YES];
}
#pragma mark - BrowserViewVisibilityConsumer
- (void)browserViewDidChangeVisibility {
// TODO(crbug.com/377763682): This is a temporary fix to avoid a crash.
if (!self.browser) {
NOTREACHED(base::NotFatalUntil::M133);
return;
}
raw_ptr<TabBasedIPHBrowserAgent> tabBasedIPHBrowserAgent =
TabBasedIPHBrowserAgent::FromBrowser(self.browser);
if (!tabBasedIPHBrowserAgent) {
return;
}
if (self.viewController.viewVisible) {
tabBasedIPHBrowserAgent->RootViewForInProductHelpDidAppear();
} else {
tabBasedIPHBrowserAgent->RootViewForInProductHelpWillDisappear();
}
}
#pragma mark - ContextualPanelEntrypointIPHCommands
- (BOOL)showContextualPanelEntrypointIPHWithConfig:
(ContextualPanelItemConfiguration*)config
anchorPoint:(CGPoint)anchorPoint
isBottomOmnibox:(BOOL)isBottomOmnibox {
ContextualPanelItemConfiguration& config_ref = CHECK_DEREF(config);
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(
self.browser->GetProfile());
if (!engagementTracker) {
return NO;
}
__weak __typeof(self) weakSelf = self;
base::WeakPtr<ContextualPanelItemConfiguration> config_weak_ptr =
config_ref.weak_ptr_factory.GetWeakPtr();
CallbackWithIPHDismissalReasonType dismissalCallback = ^(
IPHDismissalReasonType IPHDismissalReasonType,
feature_engagement::Tracker::SnoozeAction snoozeAction) {
[weakSelf contextualPanelEntrypointIPHDidDismissWithConfig:config_weak_ptr
dismissalReason:
IPHDismissalReasonType];
};
UIImage* image =
[UIImage imageNamed:base::SysUTF8ToNSString(config_ref.iph_image_name)];
_contextualPanelEntrypointHelpPresenter =
[[BubbleViewControllerPresenter alloc]
initWithText:ShouldShowRichContextualPanelEntrypointIPH()
? base::SysUTF8ToNSString(config_ref.iph_text)
: base::SysUTF8ToNSString(config_ref.iph_title)
title:base::SysUTF8ToNSString(config_ref.iph_title)
image:image
arrowDirection:isBottomOmnibox ? BubbleArrowDirectionDown
: BubbleArrowDirectionUp
alignment:BubbleAlignmentTopOrLeading
bubbleType:ShouldShowRichContextualPanelEntrypointIPH()
? BubbleViewTypeRich
: BubbleViewTypeDefault
dismissalCallback:dismissalCallback];
_contextualPanelEntrypointHelpPresenter.voiceOverAnnouncement =
base::SysUTF8ToNSString(config_ref.iph_text);
_contextualPanelEntrypointHelpPresenter.ignoreWebContentAreaInteractions =
YES;
_contextualPanelEntrypointHelpPresenter.customBubbleVisibilityDuration =
LargeContextualPanelEntrypointDisplayedInSeconds();
// Early return if the bubble wouldn't fit in its parent view.
if (![_contextualPanelEntrypointHelpPresenter
canPresentInView:self.viewController.view
anchorPoint:anchorPoint]) {
_contextualPanelEntrypointHelpPresenter = nil;
return NO;
}
// Do this check last as the FET needs to know the IPH can be shown.
if (!engagementTracker->ShouldTriggerHelpUI(*config_ref.iph_feature)) {
_contextualPanelEntrypointHelpPresenter = nil;
return NO;
}
[_contextualPanelEntrypointHelpPresenter
presentInViewController:self.viewController
anchorPoint:anchorPoint];
return YES;
}
- (void)dismissContextualPanelEntrypointIPH:(BOOL)animated {
[_contextualPanelEntrypointHelpPresenter dismissAnimated:animated];
_contextualPanelEntrypointHelpPresenter = nil;
}
#pragma mark - ContextualSheetCommands
- (void)openContextualSheet {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return;
}
// Close the keyboard before opening the sheet.
UIView* view = activeWebState->GetView();
if (view) {
[view endEditing:YES];
}
ContextualPanelTabHelper* contextualPanelTabHelper =
ContextualPanelTabHelper::FromWebState(activeWebState);
contextualPanelTabHelper->OpenContextualPanel();
[self showContextualSheetUIIfActive];
}
- (void)closeContextualSheet {
web::WebState* activeWebState = self.activeWebState;
if (activeWebState) {
ContextualPanelTabHelper* contextualPanelTabHelper =
ContextualPanelTabHelper::FromWebState(activeWebState);
contextualPanelTabHelper->CloseContextualPanel();
}
[self hideContextualSheet];
}
- (void)showContextualSheetUIIfActive {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
ContextualPanelTabHelper* contextualPanelTabHelper =
ContextualPanelTabHelper::FromWebState(activeWebState);
if (!contextualPanelTabHelper->IsContextualPanelCurrentlyOpened()) {
return;
}
_contextualSheetCoordinator = [[ContextualSheetCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
_contextualSheetCoordinator.presenter = self.viewController;
[_contextualSheetCoordinator start];
}
- (void)hideContextualSheet {
[_contextualSheetCoordinator stop];
_contextualSheetCoordinator = nil;
}
#pragma mark - DefaultBrowserPromoCommands
- (void)hidePromo {
[self.defaultBrowserGenericPromoCoordinator stop];
self.defaultBrowserGenericPromoCoordinator = nil;
}
#pragma mark - DriveFilePickerCommands
- (void)showDriveFilePicker {
if (!base::FeatureList::IsEnabled(kIOSChooseFromDrive)) {
return;
}
// If there is a coordinator, stop it before showing it again.
[self hideDriveFilePicker];
// Return early if the current WebState is not choosing files.
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState || activeWebState->IsBeingDestroyed()) {
// If there is no active WebState or it is being destroyed, do nothing.
return;
}
ChooseFileTabHelper* tab_helper =
ChooseFileTabHelper::GetOrCreateForWebState(activeWebState);
if (!tab_helper->IsChoosingFiles()) {
return;
}
// Start the coordinator.
_driveFilePickerCoordinator = [[RootDriveFilePickerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
webState:activeWebState];
[_driveFilePickerCoordinator start];
}
- (void)hideDriveFilePicker {
[_driveFilePickerCoordinator stop];
_driveFilePickerCoordinator = nil;
}
- (void)setDriveFilePickerSelectedIdentity:
(id<SystemIdentity>)selectedIdentity {
[_driveFilePickerCoordinator setSelectedIdentity:selectedIdentity];
}
#pragma mark - FeedCommands
- (void)showFirstFollowUIForWebSite:(FollowedWebSite*)followedWebSite {
self.firstFollowCoordinator = [[FirstFollowCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
followedWebSite:followedWebSite];
[self.firstFollowCoordinator start];
}
#pragma mark - FindInPageCommands
- (void)openFindInPage {
if (_toolbarAccessoryPresenter.isPresenting) {
_nextToolbarToPresent = ToolbarKind::kFindInPage;
[self closeTextZoom];
return;
}
if (IsNativeFindInPageAvailable()) {
[self showSystemFindPanel];
} else {
[self showFindBar];
}
}
- (void)closeFindInPage {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return;
}
AbstractFindTabHelper* helper =
GetConcreteFindTabHelperFromWebState(activeWebState);
DCHECK(helper);
if (helper->IsFindUIActive()) {
helper->StopFinding();
} else {
[self.findBarCoordinator stop];
self.findBarCoordinator = nil;
}
}
- (void)showFindUIIfActive {
auto* findHelper = GetConcreteFindTabHelperFromWebState(self.activeWebState);
if (!findHelper || !findHelper->IsFindUIActive()) {
return;
}
if (IsNativeFindInPageAvailable()) {
[self showSystemFindPanel];
} else if (!_toolbarAccessoryPresenter.isPresenting) {
DCHECK(!self.findBarCoordinator);
self.findBarCoordinator = [self newFindBarCoordinator];
[self.findBarCoordinator start];
}
}
- (void)hideFindUI {
if (IsNativeFindInPageAvailable()) {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
auto* helper = FindTabHelper::FromWebState(activeWebState);
helper->DismissFindNavigator();
} else {
[self.findBarCoordinator stop];
self.findBarCoordinator = nil;
}
}
- (void)defocusFindInPage {
if (IsNativeFindInPageAvailable()) {
// The System Find Panel UI cannot be "defocused" so closing Find in Page
// altogether instead.
[self closeFindInPage];
} else {
[self.findBarCoordinator defocusFindBar];
}
}
- (void)searchFindInPage {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
auto* helper = GetConcreteFindTabHelperFromWebState(activeWebState);
helper->StartFinding([self.findBarCoordinator.findBarController searchTerm]);
if (!self.browser->GetProfile()->IsOffTheRecord()) {
helper->PersistSearchTerm();
}
}
- (void)findNextStringInPage {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
// TODO(crbug.com/40465124): Reshow find bar if necessary.
GetConcreteFindTabHelperFromWebState(activeWebState)
->ContinueFinding(JavaScriptFindTabHelper::FORWARD);
}
- (void)findPreviousStringInPage {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
// TODO(crbug.com/40465124): Reshow find bar if necessary.
GetConcreteFindTabHelperFromWebState(activeWebState)
->ContinueFinding(JavaScriptFindTabHelper::REVERSE);
}
#pragma mark - FindInPageCommands Helpers
- (void)showSystemFindPanel {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
auto* helper = FindTabHelper::FromWebState(activeWebState);
if (!helper->IsFindUIActive()) {
// Hide the Omnibox to avoid user's confusion about which text field is
// currently focused. The mode is force to avoid the bottom Omnibox
// appearing above the find in page collapsed toolbar when scrolling.
_fullscreenController->EnterForceFullscreenMode(
/* insets_update_enabled */ true);
helper->SetFindUIActive(true);
}
// If the Native Find in Page variant does not use the Chrome Find bar, it
// is sufficient to call `StartFinding()` directly on the Find tab helper of
// the current web state.
helper->StartFinding(@"");
}
- (void)showFindBar {
if (!self.canShowFindBar) {
return;
}
[self.findBarCoordinator stop];
self.findBarCoordinator = [self newFindBarCoordinator];
[self.findBarCoordinator start];
}
- (BOOL)canShowFindBar {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return NO;
}
auto* helper = GetConcreteFindTabHelperFromWebState(activeWebState);
return (helper && helper->CurrentPageSupportsFindInPage() &&
!helper->IsFindUIActive());
}
- (FindBarCoordinator*)newFindBarCoordinator {
FindBarCoordinator* findBarCoordinator =
[[FindBarCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser];
findBarCoordinator.presenter = _toolbarAccessoryPresenter;
findBarCoordinator.delegate = self;
findBarCoordinator.presentationDelegate = self.viewController;
return findBarCoordinator;
}
#pragma mark - AddContactsCommands
- (void)presentAddContactsForPhoneNumber:(NSString*)phoneNumber {
_addContactsCoordinator = [[AddContactsCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
phoneNumber:phoneNumber];
[_addContactsCoordinator start];
}
- (void)hideAddContacts {
[_addContactsCoordinator stop];
_addContactsCoordinator = nil;
}
#pragma mark - CountryCodePickerCommands
- (void)presentCountryCodePickerForPhoneNumber:(NSString*)phoneNumber {
_countryCodePickerCoordinator = [[CountryCodePickerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
_countryCodePickerCoordinator.phoneNumber = phoneNumber;
[_countryCodePickerCoordinator start];
}
- (void)hideCountryCodePicker {
[_countryCodePickerCoordinator stop];
_countryCodePickerCoordinator = nil;
}
#pragma mark - PromosManagerCommands
- (void)showPromo {
if (!self.promosManagerCoordinator) {
id<CredentialProviderPromoCommands> credentialProviderPromoHandler =
HandlerForProtocol(self.browser->GetCommandDispatcher(),
CredentialProviderPromoCommands);
id<DockingPromoCommands> dockingPromoHandler = HandlerForProtocol(
self.browser->GetCommandDispatcher(), DockingPromoCommands);
self.promosManagerCoordinator = [[PromosManagerCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
credentialProviderPromoHandler:credentialProviderPromoHandler
dockingPromoHandler:dockingPromoHandler];
// CredentialProviderPromoCoordinator is initialized earlier than this, so
// make sure to set its UI handler.
_credentialProviderPromoCoordinator.promosUIHandler =
self.promosManagerCoordinator;
// _dockingPromoCoordinator is initialized earlier than this, so
// make sure to set its UI handler.
_dockingPromoCoordinator.promosUIHandler = self.promosManagerCoordinator;
[self.promosManagerCoordinator start];
} else {
[self.promosManagerCoordinator displayPromoIfAvailable];
}
}
- (void)showAppStoreReviewPrompt {
if (IsAppStoreRatingEnabled()) {
UIWindowScene* scene = [self.browser->GetSceneState() scene];
[SKStoreReviewController requestReviewInScene:scene];
// Apple doesn't tell whether the app store review window will show or
// provide a callback for when it is dismissed, so alert the coordinator
// here so it can do any necessary cleanup.
[self.promosManagerCoordinator promoWasDismissed];
}
}
- (void)showWhatsNewPromo {
[self showWhatsNew];
self.whatsNewCoordinator.promosUIHandler = self.promosManagerCoordinator;
self.whatsNewCoordinator.shouldShowBubblePromoOnDismiss = YES;
}
- (void)showDefaultBrowserPromo {
if (self.defaultBrowserGenericPromoCoordinator) {
// The default browser promo manager is already being displayed. Early
// return as this is expected if a default browser promo was open and the
// app was backgrounded.
return;
}
self.defaultBrowserGenericPromoCoordinator =
[[DefaultBrowserGenericPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.defaultBrowserGenericPromoCoordinator.promosUIHandler =
self.promosManagerCoordinator;
self.defaultBrowserGenericPromoCoordinator.handler = self;
[self.defaultBrowserGenericPromoCoordinator start];
}
- (void)showDefaultBrowserPromoAfterRemindMeLater {
self.defaultBrowserGenericPromoCoordinator =
[[DefaultBrowserGenericPromoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.defaultBrowserGenericPromoCoordinator.promosUIHandler =
self.promosManagerCoordinator;
self.defaultBrowserGenericPromoCoordinator.handler = self;
self.defaultBrowserGenericPromoCoordinator.promoWasFromRemindMeLater = YES;
[self.defaultBrowserGenericPromoCoordinator start];
}
#pragma mark - PageInfoCommands
- (void)showPageInfo {
PageInfoCoordinator* pageInfoCoordinator = [[PageInfoCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
pageInfoCoordinator.presentationProvider = self;
[self.pageInfoCoordinator stop];
self.pageInfoCoordinator = pageInfoCoordinator;
[self.pageInfoCoordinator start];
}
- (void)hidePageInfo {
[self.pageInfoCoordinator stop];
self.pageInfoCoordinator = nil;
}
#pragma mark - FormInputAccessoryCoordinatorNavigator
- (void)openPasswordManager {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showSavedPasswordsSettingsFromViewController:self.viewController
showCancelButton:YES];
}
- (void)openPasswordSettings {
// TODO(crbug.com/40067451): Switch back to DCHECK if the number of reports is
// low.
DUMP_WILL_BE_CHECK(!self.passwordSettingsCoordinator);
// Use main browser to open the password settings.
SceneState* sceneState = self.browser->GetSceneState();
self.passwordSettingsCoordinator = [[PasswordSettingsCoordinator alloc]
initWithBaseViewController:self.viewController
browser:sceneState.browserProviderInterface
.mainBrowserProvider.browser];
self.passwordSettingsCoordinator.delegate = self;
[self.passwordSettingsCoordinator start];
}
- (void)openAddressSettings {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showProfileSettingsFromViewController:self.viewController];
}
- (void)openCreditCardSettings {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showCreditCardSettings];
}
#pragma mark - RepostFormTabHelperDelegate
- (void)repostFormTabHelper:(RepostFormTabHelper*)helper
presentRepostFormDialogForWebState:(web::WebState*)webState
dialogAtPoint:(CGPoint)location
completionHandler:(void (^)(BOOL))completion {
self.repostFormCoordinator = [[RepostFormCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
dialogLocation:location
webState:webState
completionHandler:completion];
self.repostFormCoordinator.delegate = self;
[self.repostFormCoordinator start];
}
- (void)repostFormTabHelperDismissRepostFormDialog:
(RepostFormTabHelper*)helper {
[self stopRepostFormCoordinator];
}
#pragma mark - ToolbarAccessoryCoordinatorDelegate
- (void)toolbarAccessoryCoordinatorDidDismissUI:
(ChromeCoordinator*)coordinator {
[self.findBarCoordinator stop];
self.findBarCoordinator = nil;
[self hideTextZoomUI];
if (!_nextToolbarToPresent.has_value()) {
return;
}
const ToolbarKind nextToolbarToPresent = *_nextToolbarToPresent;
_nextToolbarToPresent = std::nullopt;
switch (nextToolbarToPresent) {
case ToolbarKind::kTextZoom:
[self openTextZoom];
break;
case ToolbarKind::kFindInPage:
[self openFindInPage];
break;
}
}
#pragma mark - TextZoomCommands
- (void)openTextZoom {
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
AbstractFindTabHelper* findTabHelper =
GetConcreteFindTabHelperFromWebState(activeWebState);
DCHECK(findTabHelper);
if (findTabHelper->IsFindUIActive()) {
// If Find UI is active, close Find in Page.
[self closeFindInPage];
if (_toolbarAccessoryPresenter.isPresenting) {
// If the Chrome Find Bar is presented (as opposed to the System Find
// Panel UI) then open Text Zoom asynchronously once the Find Bar is
// dismissed.
_nextToolbarToPresent = ToolbarKind::kTextZoom;
return;
}
}
[self.textZoomCoordinator stop];
self.textZoomCoordinator = [self newTextZoomCoordinator];
[self.textZoomCoordinator start];
}
- (void)closeTextZoom {
web::WebState* activeWebState = self.activeWebState;
if (activeWebState) {
if (ios::provider::IsTextZoomEnabled()) {
FontSizeTabHelper* fontSizeTabHelper =
FontSizeTabHelper::FromWebState(activeWebState);
fontSizeTabHelper->SetTextZoomUIActive(false);
}
}
[self hideTextZoomUI];
}
- (void)showTextZoomUIIfActive {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return;
}
FontSizeTabHelper* fontSizeTabHelper =
FontSizeTabHelper::FromWebState(activeWebState);
if (fontSizeTabHelper && fontSizeTabHelper->IsTextZoomUIActive() &&
!_toolbarAccessoryPresenter.isPresenting) {
DCHECK(!self.textZoomCoordinator);
self.textZoomCoordinator = [self newTextZoomCoordinator];
[self.textZoomCoordinator start];
}
}
- (void)hideTextZoomUI {
[self.textZoomCoordinator stop];
self.textZoomCoordinator = nil;
}
- (TextZoomCoordinator*)newTextZoomCoordinator {
TextZoomCoordinator* textZoomCoordinator = [[TextZoomCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
textZoomCoordinator.presenter = _toolbarAccessoryPresenter;
textZoomCoordinator.delegate = self;
return textZoomCoordinator;
}
#pragma mark - UnitConversionCommands
- (void)presentUnitConversionForSourceUnit:(NSUnit*)sourceUnit
sourceUnitValue:(double)sourceUnitValue
location:(CGPoint)location {
self.unitConversionCoordinator = [[UnitConversionCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
sourceUnit:sourceUnit
sourceUnitValue:sourceUnitValue
location:location];
[self.unitConversionCoordinator start];
}
- (void)hideUnitConversion {
[self.unitConversionCoordinator stop];
self.unitConversionCoordinator = nil;
}
#pragma mark - URLLoadingDelegate
- (void)animateOpenBackgroundTabFromParams:(const UrlLoadParams&)params
completion:(void (^)())completion {
[self.viewController
animateOpenBackgroundTabFromOriginPoint:params.origin_point
completion:completion];
}
#pragma mark - Private WebState management methods
// Installs delegates for self.browser.
- (void)installDelegatesForBrowser {
// The view controller should have been created.
DCHECK(self.viewController);
SyncErrorBrowserAgent::FromBrowser(self.browser)->SetUIProviders(self, self);
WebStateDelegateBrowserAgent::FromBrowser(self.browser)
->SetUIProviders(self.contextMenuProvider,
self.formInputAccessoryCoordinator, self.viewController);
if (_urlLoadingBrowserAgent) {
_urlLoadingBrowserAgent->SetDelegate(self);
}
AccountConsistencyBrowserAgent::CreateForBrowser(self.browser,
self.viewController);
if (FollowBrowserAgent::FromBrowser(self.browser)) {
CommandDispatcher* commandDispatcher = self.browser->GetCommandDispatcher();
FollowBrowserAgent::FromBrowser(self.browser)
->SetUIProviders(
HandlerForProtocol(commandDispatcher, NewTabPageCommands),
static_cast<id<SnackbarCommands>>(commandDispatcher),
HandlerForProtocol(commandDispatcher, FeedCommands));
}
}
// Installs delegates for self.browser->GetProfile()
- (void)installDelegatesForBrowserState {
ProfileIOS* profile = self.browser->GetProfile();
if (profile) {
TextToSpeechPlaybackControllerFactory::GetInstance()
->GetForProfile(profile)
->SetWebStateList(self.browser->GetWebStateList());
}
}
// Uninstalls delegates for self.browser->GetProfile()
- (void)uninstallDelegatesForBrowserState {
ProfileIOS* profile = self.browser->GetProfile();
if (profile) {
TextToSpeechPlaybackControllerFactory::GetInstance()
->GetForProfile(profile)
->SetWebStateList(nullptr);
}
}
// Uninstalls delegates for self.browser.
- (void)uninstallDelegatesForBrowser {
if (_urlLoadingBrowserAgent) {
_urlLoadingBrowserAgent->SetDelegate(nil);
}
WebStateDelegateBrowserAgent::FromBrowser(self.browser)->ClearUIProviders();
SyncErrorBrowserAgent::FromBrowser(self.browser)->ClearUIProviders();
if (FollowBrowserAgent::FromBrowser(self.browser)) {
FollowBrowserAgent::FromBrowser(self.browser)->ClearUIProviders();
}
}
#pragma mark - ParcelTrackingOptInCommands
- (void)showTrackingForParcels:(NSArray<CustomTextCheckingResult*>*)parcels {
commerce::ShoppingService* shoppingService =
commerce::ShoppingServiceFactory::GetForProfile(
self.browser->GetProfile());
if (!shoppingService) {
return;
}
// Filter out parcels that are already being tracked and post
// `showParcelTrackingUIWithNewParcels` command for the new parcel list.
FilterParcelsAndShowParcelTrackingUI(
shoppingService, parcels,
HandlerForProtocol(self.dispatcher, ParcelTrackingOptInCommands));
}
- (void)showTrackingForFilteredParcels:
(NSArray<CustomTextCheckingResult*>*)parcels {
commerce::ShoppingService* shoppingService =
commerce::ShoppingServiceFactory::GetForProfile(
self.browser->GetProfile());
if (!shoppingService) {
return;
}
if (IsUserEligibleParcelTrackingOptInPrompt(
self.browser->GetProfile()->GetPrefs(), shoppingService)) {
[self showParcelTrackingOptInPromptWithParcels:parcels];
} else {
[self maybeShowParcelTrackingInfobarWithParcels:parcels];
}
}
- (void)showParcelTrackingInfobarWithParcels:
(NSArray<CustomTextCheckingResult*>*)parcels
forStep:(ParcelTrackingStep)step {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return;
}
ProfileIOS* profile = self.browser->GetProfile();
if (!commerce::IsParcelTrackingEligible(
commerce::ShoppingServiceFactory::GetForProfile(profile)
->GetAccountChecker())) {
return;
}
if (step == ParcelTrackingStep::kNewPackageTracked) {
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(profile);
engagementTracker->NotifyEvent(feature_engagement::events::kParcelTracked);
}
std::unique_ptr<ParcelTrackingInfobarDelegate> delegate =
std::make_unique<ParcelTrackingInfobarDelegate>(
activeWebState, step, parcels,
HandlerForProtocol(self.dispatcher, ApplicationCommands),
HandlerForProtocol(self.dispatcher, ParcelTrackingOptInCommands));
infobars::InfoBarManager* infobar_manager =
InfoBarManagerImpl::FromWebState(activeWebState);
std::unique_ptr<infobars::InfoBar> infobar = std::make_unique<InfoBarIOS>(
InfobarType::kInfobarTypeParcelTracking, std::move(delegate));
infobar_manager->AddInfoBar(std::move(infobar),
/*replace_existing=*/true);
}
- (void)showParcelTrackingIPH {
[HandlerForProtocol(_dispatcher, HelpCommands)
presentInProductHelpWithType:InProductHelpType::kParcelTracking];
}
#pragma mark - ParcelTrackingOptInCommands helpers
- (void)maybeShowParcelTrackingInfobarWithParcels:
(NSArray<CustomTextCheckingResult*>*)parcels {
IOSParcelTrackingOptInStatus optInStatus =
static_cast<IOSParcelTrackingOptInStatus>(
self.browser->GetProfile()->GetPrefs()->GetInteger(
prefs::kIosParcelTrackingOptInStatus));
switch (optInStatus) {
case IOSParcelTrackingOptInStatus::kAlwaysTrack: {
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return;
}
commerce::ShoppingService* shoppingService =
commerce::ShoppingServiceFactory::GetForProfile(
ProfileIOS::FromBrowserState(activeWebState->GetBrowserState()));
// Track parcels and display infobar if successful.
TrackParcels(
shoppingService, parcels, std::string(),
HandlerForProtocol(self.dispatcher, ParcelTrackingOptInCommands),
/*display_infobar=*/true, TrackingSource::kAutoTrack);
break;
}
case IOSParcelTrackingOptInStatus::kAskToTrack:
[self showParcelTrackingInfobarWithParcels:parcels
forStep:ParcelTrackingStep::
kAskedToTrackPackage];
break;
case IOSParcelTrackingOptInStatus::kNeverTrack:
case IOSParcelTrackingOptInStatus::kStatusNotSet:
// Do not display infobar.
break;
}
}
- (void)showParcelTrackingOptInPromptWithParcels:
(NSArray<CustomTextCheckingResult*>*)parcels {
[self dismissParcelTrackingOptInPrompt];
self.parcelTrackingOptInCoordinator = [[ParcelTrackingOptInCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
parcels:parcels];
[self.parcelTrackingOptInCoordinator start];
}
- (void)dismissParcelTrackingOptInPrompt {
if (self.parcelTrackingOptInCoordinator) {
[self.parcelTrackingOptInCoordinator stop];
self.parcelTrackingOptInCoordinator = nil;
}
}
#pragma mark - ParentAccessCommands
- (void)
showParentAccessBottomSheetForWebState:(web::WebState*)webState
targetURL:(const GURL&)targetURL
filteringBehaviorReason:
(supervised_user::FilteringBehaviorReason)
filteringBehaviorReason
completion:
(void (^)(
supervised_user::LocalApprovalResult))
completion {
if (!supervised_user::IsLocalWebApprovalsEnabled()) {
return;
}
if (self.activeWebState != webState) {
// Do not show the sheet if the current tab is not the one where the
// user initiated parent local web approvals.
return;
}
// Close parent access local web approval if it was already opened for another
// URL.
if (self.parentAccessCoordinator) {
[self.parentAccessCoordinator stop];
}
self.parentAccessCoordinator = [[ParentAccessCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
targetURL:targetURL
filteringBehaviorReason:filteringBehaviorReason
completion:completion];
[self.parentAccessCoordinator start];
}
- (void)hideParentAccessBottomSheet {
[self.parentAccessCoordinator stop];
self.parentAccessCoordinator = nil;
}
#pragma mark - PasswordBreachCommands
- (void)showPasswordBreachForLeakType:(CredentialLeakType)leakType {
self.passwordBreachCoordinator = [[PasswordBreachCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
leakType:leakType];
[self.passwordBreachCoordinator start];
}
#pragma mark - PasswordProtectionCommands
- (void)showPasswordProtectionWarning:(NSString*)warningText
completion:(void (^)(safe_browsing::WarningAction))
completion {
self.passwordProtectionCoordinator = [[PasswordProtectionCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
warningText:warningText];
self.passwordProtectionCoordinator.delegate = self;
[self.passwordProtectionCoordinator startWithCompletion:completion];
}
#pragma mark - PasswordSuggestionCommands
- (void)showPasswordSuggestion:(NSString*)passwordSuggestion
proactive:(BOOL)proactive
webState:(web::WebState*)webState
frame:(base::WeakPtr<web::WebFrame>)frame
decisionHandler:(void (^)(BOOL accept))decisionHandler {
// Do not present the bottom sheet if the calling web state does not match the
// active web state in order to stop the bottom sheet from showing in a tab
// different than the one that triggered it.
if (webState != self.activeWebState) {
return;
}
// Do not present the bottom sheet if it is already being presented.
if (self.passwordSuggestionCoordinator) {
return;
}
self.passwordSuggestionCoordinator = [[PasswordSuggestionCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
passwordSuggestion:passwordSuggestion
frame:frame
decisionHandler:decisionHandler
proactive:proactive];
self.passwordSuggestionCoordinator.delegate = self;
[self.passwordSuggestionCoordinator start];
}
#pragma mark - PriceNotificationsCommands
- (void)showPriceNotifications {
self.priceNotificationsViewCoordiantor =
[[PriceNotificationsViewCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.priceNotificationsViewCoordiantor start];
}
- (void)hidePriceNotifications {
[self.priceNotificationsViewCoordiantor stop];
self.priceNotificationsViewCoordiantor = nil;
}
- (void)presentPriceNotificationsWhileBrowsingIPH {
[HandlerForProtocol(_dispatcher, HelpCommands)
presentInProductHelpWithType:InProductHelpType::
kPriceNotificationsWhileBrowsing];
}
#pragma mark - PolicyChangeCommands
- (void)showForceSignedOutPrompt {
if (!self.enterprisePromptCoordinator) {
self.enterprisePromptCoordinator = [[EnterprisePromptCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
promptType:EnterprisePromptTypeForceSignOut];
self.enterprisePromptCoordinator.delegate = self;
}
[self.enterprisePromptCoordinator start];
}
- (void)showSyncDisabledPrompt {
if (!self.enterprisePromptCoordinator) {
self.enterprisePromptCoordinator = [[EnterprisePromptCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
promptType:EnterprisePromptTypeSyncDisabled];
self.enterprisePromptCoordinator.delegate = self;
}
[self.enterprisePromptCoordinator start];
}
- (void)showRestrictAccountSignedOutPrompt {
SceneState* sceneState = self.browser->GetSceneState();
if (sceneState.activationLevel >= SceneActivationLevelForegroundActive) {
if (!self.enterprisePromptCoordinator) {
self.enterprisePromptCoordinator = [[EnterprisePromptCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
promptType:
EnterprisePromptTypeRestrictAccountSignedOut];
self.enterprisePromptCoordinator.delegate = self;
}
[self.enterprisePromptCoordinator start];
} else {
__weak BrowserCoordinator* weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
static_cast<int64_t>(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[weakSelf showRestrictAccountSignedOutPrompt];
});
}
}
#pragma mark - SaveToDriveCommands
- (void)showSaveToDriveForDownload:(web::DownloadTask*)downloadTask {
// If the Save to Drive coordinator is not nil, stop it.
[self hideSaveToDrive];
_saveToDriveCoordinator = [[SaveToDriveCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
downloadTask:downloadTask];
[_saveToDriveCoordinator start];
}
- (void)hideSaveToDrive {
[_saveToDriveCoordinator stop];
_saveToDriveCoordinator = nil;
}
#pragma mark - SaveToPhotosCommands
- (void)saveImageToPhotos:(SaveImageToPhotosCommand*)command {
if (!command.webState) {
// If the web state does not exist anymore, don't do anything.
return;
}
// If the Save to Photos coordinator is not nil, stop it.
[self stopSaveToPhotos];
self.saveToPhotosCoordinator = [[SaveToPhotosCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
imageURL:command.imageURL
referrer:command.referrer
webState:command.webState.get()];
[self.saveToPhotosCoordinator start];
}
- (void)stopSaveToPhotos {
[self.saveToPhotosCoordinator stop];
self.saveToPhotosCoordinator = nil;
}
#pragma mark - WebContentCommands
- (void)showAppStoreWithParameters:(NSDictionary*)productParameters {
self.storeKitCoordinator = [[StoreKitCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
self.storeKitCoordinator.delegate = self;
self.storeKitCoordinator.iTunesProductParameters = productParameters;
[self.storeKitCoordinator start];
}
- (void)showDialogForPassKitPasses:(NSArray<PKPass*>*)passes {
if (self.passKitCoordinator.passes) {
// Another pass is being displayed -- early return (this is unexpected).
return;
}
self.passKitCoordinator =
[[PassKitCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser];
self.passKitCoordinator.passes = passes;
[self.passKitCoordinator start];
}
#pragma mark - DefaultBrowserPromoNonModalCommands
- (void)showDefaultBrowserNonModalPromoWithReason:
(NonModalDefaultBrowserPromoReason)promoReason {
self.nonModalPromoCoordinator =
[[DefaultBrowserPromoNonModalCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
promoReason:promoReason];
[self.nonModalPromoCoordinator start];
self.nonModalPromoCoordinator.browser = self.browser;
self.nonModalPromoCoordinator.baseViewController = self.viewController;
[self.nonModalPromoCoordinator presentInfobarBannerAnimated:YES
completion:nil];
}
- (void)dismissDefaultBrowserNonModalPromoAnimated:(BOOL)animated {
[self.nonModalPromoCoordinator dismissInfobarBannerAnimated:animated
completion:nil];
}
- (void)defaultBrowserNonModalPromoWasDismissed {
SceneState* sceneState = self.browser->GetSceneState();
[[NonModalDefaultBrowserPromoSchedulerSceneAgent agentFromScene:sceneState]
logPromoWasDismissed];
[self.nonModalPromoCoordinator stop];
self.nonModalPromoCoordinator = nil;
}
#pragma mark - DefaultPromoNonModalPresentationDelegate
- (BOOL)defaultNonModalPromoIsShowing {
return self.nonModalPromoCoordinator != nil;
}
- (void)dismissDefaultNonModalPromoAnimated:(BOOL)animated
completion:(void (^)())completion {
[self.nonModalPromoCoordinator dismissInfobarBannerAnimated:animated
completion:completion];
}
#pragma mark - EditMenuBuilder
- (void)buildEditMenuWithBuilder:(id<UIMenuBuilder>)builder {
return [self.browserContainerCoordinator.editMenuBuilder
buildEditMenuWithBuilder:builder];
}
#pragma mark - EnterprisePromptCoordinatorDelegate
- (void)hideEnterprisePrompForLearnMore:(BOOL)learnMore {
[self.enterprisePromptCoordinator stop];
self.enterprisePromptCoordinator = nil;
}
#pragma mark - NetExportTabHelperDelegate
- (void)netExportTabHelper:(NetExportTabHelper*)tabHelper
showMailComposerWithContext:(ShowMailComposerContext*)context {
self.netExportCoordinator = [[NetExportCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
mailComposerContext:context];
[self.netExportCoordinator start];
}
#pragma mark - PasswordSuggestionCoordinatorDelegate
- (void)closePasswordSuggestion {
[self.passwordSuggestionCoordinator stop];
self.passwordSuggestionCoordinator = nil;
}
#pragma mark - PreloadControllerDelegate methods
- (web::WebState*)webStateToReplace {
return self.browser ? self.browser->GetWebStateList()->GetActiveWebState()
: nullptr;
}
- (UIView*)webViewContainer {
return self.browserContainerCoordinator.viewController.view;
}
#pragma mark - SyncPresenter (Public)
- (void)showPrimaryAccountReauth {
[HandlerForProtocol(self.dispatcher, ApplicationCommands)
showSignin:[[ShowSigninCommand alloc]
initWithOperation:AuthenticationOperation::
kPrimaryAccountReauth
accessPoint:signin_metrics::AccessPoint::
kReauthInfoBar]
baseViewController:self.viewController];
}
- (void)showSyncPassphraseSettings {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showSyncPassphraseSettingsFromViewController:self.viewController];
}
- (void)showGoogleServicesSettings {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showGoogleServicesSettingsFromViewController:self.viewController];
}
- (void)showAccountSettings {
[HandlerForProtocol(self.dispatcher, SettingsCommands)
showAccountsSettingsFromViewController:self.viewController
skipIfUINotAvailable:NO];
}
- (void)showTrustedVaultReauthForFetchKeysWithTrigger:
(syncer::TrustedVaultUserActionTriggerForUMA)trigger {
[HandlerForProtocol(self.dispatcher, ApplicationCommands)
showTrustedVaultReauthForFetchKeysFromViewController:self.viewController
securityDomainID:
trusted_vault::SecurityDomainId::
kChromeSync
trigger:trigger
accessPoint:signin_metrics::
AccessPoint::
kSettings];
}
- (void)showTrustedVaultReauthForDegradedRecoverabilityWithTrigger:
(syncer::TrustedVaultUserActionTriggerForUMA)trigger {
[HandlerForProtocol(self.dispatcher, ApplicationCommands)
showTrustedVaultReauthForDegradedRecoverabilityFromViewController:
self.viewController
securityDomainID:
trusted_vault::
SecurityDomainId::
kChromeSync
trigger:trigger
accessPoint:
signin_metrics::
AccessPoint::
kSettings];
}
#pragma mark - SigninPresenter
- (void)showSignin:(ShowSigninCommand*)command {
[HandlerForProtocol(self.dispatcher, ApplicationCommands)
showSignin:command
baseViewController:self.viewController];
}
#pragma mark - SnapshotGeneratorDelegate methods
// TODO(crbug.com/40206055): Refactor SnapshotGenerator into (probably) a
// mediator with a narrowly-defined API to get UI-layer information from the
// BVC.
- (BOOL)canTakeSnapshotWithWebStateInfo:(WebStateSnapshotInfo*)webStateInfo {
DCHECK(webStateInfo);
web::WebState* webState = webStateInfo.webState;
if (!webState) {
return NO;
}
PagePlaceholderTabHelper* pagePlaceholderTabHelper =
PagePlaceholderTabHelper::FromWebState(webState);
return !pagePlaceholderTabHelper->displaying_placeholder() &&
!pagePlaceholderTabHelper->will_add_placeholder_for_next_navigation();
}
- (void)willUpdateSnapshotWithWebStateInfo:(WebStateSnapshotInfo*)webStateInfo {
DCHECK(webStateInfo);
web::WebState* webState = webStateInfo.webState;
if (!webState) {
return;
}
if ([self isNTPActiveForCurrentWebState]) {
[_NTPCoordinator willUpdateSnapshot];
}
OverscrollActionsTabHelper::FromWebState(webState)->Clear();
}
- (UIEdgeInsets)snapshotEdgeInsetsWithWebStateInfo:
(WebStateSnapshotInfo*)webStateInfo {
DCHECK(webStateInfo);
web::WebState* webState = webStateInfo.webState;
if (!webState) {
return UIEdgeInsetsZero;
}
LensOverlayTabHelper* lensOverlayTabHelper =
LensOverlayTabHelper::FromWebState(webState);
bool isLensOverlayAvailable =
IsLensOverlayAvailable() && lensOverlayTabHelper;
bool isBuildingLensOverlay =
isLensOverlayAvailable &&
lensOverlayTabHelper->IsCapturingLensOverlaySnapshot();
bool isUpdatingLensOverlayTabSwitcherSnapshot =
isLensOverlayAvailable &&
lensOverlayTabHelper->IsUpdatingTabSwitcherSnapshot();
if (isUpdatingLensOverlayTabSwitcherSnapshot && _safeAreaProvider) {
return _safeAreaProvider.safeArea;
} else if (isBuildingLensOverlay) {
return lensOverlayTabHelper->GetSnapshotInsets();
}
UIEdgeInsets maxViewportInsets =
_fullscreenController->GetMaxViewportInsets();
NewTabPageTabHelper* NTPHelper = NewTabPageTabHelper::FromWebState(webState);
if (NTPHelper && NTPHelper->IsActive()) {
const BOOL canShowTabStrip =
IsRegularXRegularSizeClass(self.viewController);
const BOOL isSplitToolbarMode = IsSplitToolbarMode(self.viewController);
// If the NTP is active, then it's used as the base view for snapshotting.
// When the tab strip is visible, the toolbars are not splitted or for the
// incognito NTP, the NTP is already laid out between the toolbars, so it
// should not be inset while snapshotting.
if (canShowTabStrip || !isSplitToolbarMode ||
self.browser->GetProfile()->IsOffTheRecord()) {
return UIEdgeInsetsZero;
}
// For the regular NTP without tab strip, it sits above the bottom toolbar
// but, since it is displayed as full-screen at the top, it requires maximum
// viewport insets.
maxViewportInsets.bottom = 0;
return maxViewportInsets;
} else {
// If the NTP is inactive, the WebState's view is used as the base view for
// snapshotting. If fullscreen is implemented by resizing the scroll view,
// then the WebState view is already laid out within the visible viewport
// and doesn't need to be inset. If fullscreen uses the content inset, then
// the WebState view is laid out fullscreen and should be inset by the
// viewport insets.
return _fullscreenController->ResizesScrollView() ? UIEdgeInsetsZero
: maxViewportInsets;
}
}
- (NSArray<UIView*>*)snapshotOverlaysWithWebStateInfo:
(WebStateSnapshotInfo*)webStateInfo {
DCHECK(webStateInfo);
web::WebState* webState = webStateInfo.webState;
if (!webState) {
return @[];
}
WebStateList* webStateList = self.browser->GetWebStateList();
if (webStateList->GetIndexOfWebState(webState) ==
WebStateList::kInvalidIndex) {
return @[];
}
NSMutableArray<UIView*>* overlays = [NSMutableArray array];
if (IsLensOverlayAvailable()) {
LensOverlayTabHelper* lensOverlayTabHelper =
LensOverlayTabHelper::FromWebState(webState);
if (lensOverlayTabHelper) {
BOOL isLensOverlayCurrentlyInvoked;
if (IsLensOverlaySameTabNavigationEnabled()) {
isLensOverlayCurrentlyInvoked =
lensOverlayTabHelper->IsLensOverlayInvokedOnCurrentNavigationItem();
} else {
isLensOverlayCurrentlyInvoked =
lensOverlayTabHelper->IsLensOverlayUIAttachedAndAlive();
}
// A lens overlay is invoked in the given web state.
if (isLensOverlayCurrentlyInvoked) {
UIView* lensOverlayView = _lensOverlayCoordinator.viewController.view;
if (lensOverlayView) {
[overlays addObject:lensOverlayView];
}
}
}
}
// If the given web state is inactive or web usage is disabled, refrain from
// adding any additional overlays. For inactive web states, only the lens
// overlay is permitted to be added.
if (!self.webUsageEnabled || webState != webStateList->GetActiveWebState()) {
return overlays;
}
UIView* downloadManagerView = _downloadManagerCoordinator.viewController.view;
if (downloadManagerView) {
[overlays addObject:downloadManagerView];
}
UIView* sadTabView = self.sadTabCoordinator.viewController.view;
if (sadTabView) {
[overlays addObject:sadTabView];
}
BrowserContainerViewController* browserContainerViewController =
self.browserContainerCoordinator.viewController;
// The overlay container view controller is presenting something if it has
// a `presentedViewController` AND that view controller's
// `presentingViewController` is the overlay container. Otherwise, some other
// view controller higher up in the hierarchy is doing the presenting. E.g.
// for the overflow menu, the BVC (and eventually the tab grid view
// controller) are presenting the overflow menu, but because those view
// controllers are also above tthe `overlayContainerViewController` in the
// view hierarchy, the overflow menu view controller is also the
// `overlayContainerViewController`'s presentedViewController.
UIViewController* overlayContainerViewController =
browserContainerViewController.webContentsOverlayContainerViewController;
UIViewController* presentedOverlayViewController =
overlayContainerViewController.presentedViewController;
if (presentedOverlayViewController &&
presentedOverlayViewController.presentingViewController ==
overlayContainerViewController) {
[overlays addObject:presentedOverlayViewController.view];
}
UIView* screenTimeView =
browserContainerViewController.screenTimeViewController.view;
if (screenTimeView) {
[overlays addObject:screenTimeView];
}
UIView* childOverlayView =
overlayContainerViewController.childViewControllers.firstObject.view;
if (childOverlayView) {
DCHECK_EQ(1U, overlayContainerViewController.childViewControllers.count);
[overlays addObject:childOverlayView];
}
return overlays;
}
- (UIView*)baseViewWithWebStateInfo:(WebStateSnapshotInfo*)webStateInfo {
DCHECK(webStateInfo);
web::WebState* webState = webStateInfo.webState;
if (!webState) {
return nil;
}
NewTabPageTabHelper* NTPHelper = NewTabPageTabHelper::FromWebState(webState);
if (NTPHelper && NTPHelper->IsActive()) {
// If NTPCoordinator is not started yet, fall back to using the
// webState's view. `_NTPCoordinator.started` should be true in most cases
// but it can be false when the app will be terminated or the browser data
// is removed. In particular, it can be false when this method is called as
// a delayed task while the app is being terminated.
if (_NTPCoordinator.started) {
return _NTPCoordinator.viewController.view;
}
}
return webState->GetView();
}
#pragma mark - NewTabPageCommands
- (void)openNTPScrolledIntoFeedType:(FeedType)feedType {
// Dismiss any presenting modal. Ex. Follow management page.
__weak __typeof(self) weakSelf = self;
[self.viewController
clearPresentedStateWithCompletion:^{
[weakSelf scrollToNTPAfterPresentedStateCleared:feedType];
}
dismissOmnibox:YES];
}
- (void)updateFollowingFeedHasUnseenContent:(BOOL)hasUnseenContent {
[_NTPCoordinator updateFollowingFeedHasUnseenContent:hasUnseenContent];
}
- (void)handleFeedModelOfType:(FeedType)feedType
didEndUpdates:(FeedLayoutUpdateType)updateType {
[_NTPCoordinator handleFeedModelOfType:feedType didEndUpdates:updateType];
}
- (void)scrollToNTPAfterPresentedStateCleared:(FeedType)feedType {
web::WebState* activeWebState = self.activeWebState;
// Configure next NTP to be scrolled into `feedType`.
NewTabPageTabHelper* NTPHelper =
NewTabPageTabHelper::FromWebState(activeWebState);
if (NTPHelper) {
NewTabPageState* ntpState = NTPHelper->GetNTPState();
ntpState.selectedFeed = feedType;
ntpState.shouldScrollToTopOfFeed = YES;
NTPHelper->SetNTPState(ntpState);
}
// Navigate to NTP in same tab.
UrlLoadParams urlLoadParams =
UrlLoadParams::InCurrentTab(GURL(kChromeUINewTabURL));
_urlLoadingBrowserAgent->Load(urlLoadParams);
}
- (void)presentLensIconBubble {
__weak NewTabPageCoordinator* weakNTPCoordinator = _NTPCoordinator;
[HandlerForProtocol(self.dispatcher, ApplicationCommands)
prepareToPresentModal:^{
[weakNTPCoordinator presentLensIconBubble];
}];
}
#pragma mark - WebNavigationNTPDelegate
- (BOOL)isNTPActiveForCurrentWebState {
return [_NTPCoordinator isNTPActiveForCurrentWebState];
}
- (void)reloadNTPForWebState:(web::WebState*)webState {
[_NTPCoordinator reload];
}
#pragma mark - PageInfoPresentation
- (void)presentPageInfoView:(UIView*)pageInfoView {
[pageInfoView setFrame:self.viewController.view.bounds];
[self.viewController.view addSubview:pageInfoView];
}
- (void)prepareForPageInfoPresentation {
// Dismiss the omnibox (if open).
[_omniboxCommandsHandler cancelOmniboxEdit];
}
- (CGPoint)convertToPresentationCoordinatesForOrigin:(CGPoint)origin {
return [self.viewController.view convertPoint:origin fromView:nil];
}
#pragma mark - PasswordSettingsCoordinatorDelegate
- (void)passwordSettingsCoordinatorDidRemove:
(PasswordSettingsCoordinator*)coordinator {
DCHECK_EQ(self.passwordSettingsCoordinator, coordinator);
[self stopPasswordSettingsCoordinator];
}
#pragma mark - PasswordManagerReauthenticationDelegate
- (void)dismissPasswordManagerAfterFailedReauthentication {
[self stopPasswordSettingsCoordinator];
}
#pragma mark - ReadingListCoordinatorDelegate
- (void)closeReadingList {
[self.readingListCoordinator stop];
self.readingListCoordinator.delegate = nil;
self.readingListCoordinator = nil;
}
#pragma mark - BubblePresenterDelegate
- (BOOL)rootViewVisibleForBubblePresenter:(BubblePresenter*)bubblePresenter {
return self.viewController.viewVisible;
}
- (BOOL)isNTPActiveForBubblePresenter:(BubblePresenter*)bubblePresenter {
return self.NTPCoordinator.isNTPActiveForCurrentWebState;
}
- (BOOL)isNTPScrolledToTopForBubblePresenter:(BubblePresenter*)bubblePresenter {
return [self.NTPCoordinator isScrolledToTop];
}
- (BOOL)isOverscrollActionsSupportedForBubblePresenter:
(BubblePresenter*)bubblePresenter {
return [self shouldAllowOverscrollActions];
}
- (void)bubblePresenterDidPerformPullToRefreshGesture:
(BubblePresenter*)bubblePresenter {
if (!self.activeWebState) {
return;
}
OverscrollActionsTabHelper* tabHelper =
OverscrollActionsTabHelper::FromWebState(self.activeWebState);
OverscrollActionsController* controller =
tabHelper->GetOverscrollActionsController();
[controller forceAnimatedScrollRefresh];
}
- (void)bubblePresenter:(BubblePresenter*)bubblePresenter
didPerformSwipeToNavigateInDirection:
(UISwipeGestureRecognizerDirection)direction {
[_sideSwipeMediator animateSwipe:SwipeType::CHANGE_PAGE
inDirection:direction];
}
#pragma mark - OverscrollActionsControllerDelegate methods.
- (void)overscrollActionNewTab:(OverscrollActionsController*)controller {
id<ApplicationCommands> applicationCommandsHandler =
HandlerForProtocol(_dispatcher, ApplicationCommands);
[applicationCommandsHandler
openURLInNewTab:[OpenNewTabCommand
commandWithIncognito:self.browser->GetProfile()
->IsOffTheRecord()]];
}
- (void)overscrollActionCloseTab:(OverscrollActionsController*)controller {
[self closeCurrentTab];
}
- (void)overscrollActionRefresh:(OverscrollActionsController*)controller {
// Instruct the SnapshotTabHelper to ignore the next load event.
// Attempting to snapshot while the overscroll "bounce back" animation is
// occurring will cut the animation short.
web::WebState* activeWebState = self.activeWebState;
DCHECK(activeWebState);
ProfileIOS* profile = self.browser->GetProfile();
feature_engagement::Tracker* engagementTracker =
feature_engagement::TrackerFactory::GetForProfile(profile);
if (engagementTracker) {
engagementTracker->NotifyEvent(
feature_engagement::events::kIOSPullToRefreshUsed);
}
SnapshotTabHelper::FromWebState(activeWebState)->IgnoreNextLoad();
_webNavigationBrowserAgent->Reload();
}
- (BOOL)shouldAllowOverscrollActionsForOverscrollActionsController:
(OverscrollActionsController*)controller {
return [self shouldAllowOverscrollActions];
}
- (UIView*)headerViewForOverscrollActionsController:
(OverscrollActionsController*)controller {
return _toolbarCoordinator.primaryToolbarViewController.view;
}
- (UIView*)toolbarSnapshotViewForOverscrollActionsController:
(OverscrollActionsController*)controller {
return [_toolbarCoordinator.primaryToolbarViewController.view
snapshotViewAfterScreenUpdates:NO];
}
- (CGFloat)headerInsetForOverscrollActionsController:
(OverscrollActionsController*)controller {
// The current WebState can be nil if the Browser's WebStateList is empty
// (e.g. after closing the last tab, etc).
web::WebState* activeWebState = self.activeWebState;
if (!activeWebState) {
return 0.0;
}
OverscrollActionsTabHelper* activeTabHelper =
OverscrollActionsTabHelper::FromWebState(activeWebState);
if (controller == activeTabHelper->GetOverscrollActionsController()) {
return self.viewController.headerHeight;
} else {
return 0.0;
}
}
- (CGFloat)headerHeightForOverscrollActionsController:
(OverscrollActionsController*)controller {
return self.viewController.headerHeight;
}
- (CGFloat)initialContentOffsetForOverscrollActionsController:
(OverscrollActionsController*)controller {
return ios::provider::IsFullscreenSmoothScrollingSupported()
? -[self headerInsetForOverscrollActionsController:controller]
: 0.0;
}
- (FullscreenController*)fullscreenControllerForOverscrollActionsController:
(OverscrollActionsController*)controller {
return _fullscreenController;
}
#pragma mark - PasswordControllerDelegate methods
- (BOOL)displaySignInNotification:(UIViewController*)viewController
fromTabId:(NSString*)tabId {
NSString* visibleTabId = self.activeWebState->GetStableIdentifier();
// Ignore unless the call comes from currently visible tab.
if (![tabId isEqualToString:visibleTabId]) {
return NO;
}
[self.viewController addChildViewController:viewController];
[self.viewController.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self.viewController];
return YES;
}
- (void)displaySavedPasswordList {
id<SettingsCommands> settingsHandler =
HandlerForProtocol(_dispatcher, SettingsCommands);
[settingsHandler
showSavedPasswordsSettingsFromViewController:self.viewController
showCancelButton:YES];
}
- (void)showPasswordDetailsForCredential:
(password_manager::CredentialUIEntry)credential {
id<SettingsCommands> settingsHandler =
HandlerForProtocol(_dispatcher, SettingsCommands);
[settingsHandler showPasswordDetailsForCredential:credential inEditMode:NO];
}
#pragma mark - MiniMapCommands
- (void)presentConsentThenMiniMapForText:(NSString*)text
inWebState:(web::WebState*)webState {
self.miniMapCoordinator =
[[MiniMapCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser
webState:webState
text:text
consentRequired:YES
mode:MiniMapMode::kMap];
[self.miniMapCoordinator start];
}
- (void)presentMiniMapForText:(NSString*)text
inWebState:(web::WebState*)webState {
self.miniMapCoordinator =
[[MiniMapCoordinator alloc] initWithBaseViewController:self.viewController
browser:self.browser
webState:webState
text:text
consentRequired:NO
mode:MiniMapMode::kMap];
[self.miniMapCoordinator start];
}
- (void)presentMiniMapDirectionsForText:(NSString*)text
inWebState:(web::WebState*)webState {
self.miniMapCoordinator = [[MiniMapCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser
webState:webState
text:text
consentRequired:NO
mode:MiniMapMode::kDirections];
[self.miniMapCoordinator start];
}
- (void)hideMiniMap {
[self.miniMapCoordinator stop];
self.miniMapCoordinator = nil;
}
#pragma mark - PasswordProtectionCoordinator
- (void)passwordProtectionCoordinatorWantsToBeStopped:
(PasswordProtectionCoordinator*)coordinator {
CHECK_EQ(self.passwordProtectionCoordinator, coordinator);
[self stopPasswordProtectionCoordinator];
}
#pragma mark - RepostFormCoordinatorDelegate
- (void)repostFormCoordinatorWantsToBeDismissed:
(RepostFormCoordinator*)coordinator {
CHECK_EQ(self.repostFormCoordinator, coordinator);
[self stopRepostFormCoordinator];
}
#pragma mark - RecentTabsCoordinatorDelegate
- (void)recentTabsCoordinatorWantsToBeDismissed:
(RecentTabsCoordinator*)coordinator {
CHECK_EQ(coordinator, self.recentTabsCoordinator);
[self stopRecentTabsCoordinator];
}
#pragma mark - StoreKitCoordinatorDelegate
- (void)storeKitCoordinatorWantsToStop:(StoreKitCoordinator*)coordinator {
CHECK_EQ(coordinator, self.storeKitCoordinator);
[self stopStoreKitCoordinator];
}
#pragma mark - AutofillAddCreditCardCoordinatorDelegate
- (void)autofillAddCreditCardCoordinatorWantsToBeStopped:
(AutofillAddCreditCardCoordinator*)coordinator {
CHECK_EQ(coordinator, self.addCreditCardCoordinator);
[self stopAutofillAddCreditCardCoordinator];
}
#pragma mark - AppLauncherTabHelperBrowserPresentationProvider
- (BOOL)isBrowserPresentingUI {
return self.viewController.presentedViewController != nil;
}
#pragma mark - WebUsageEnablerBrowserAgentObserving
- (void)webUsageEnablerValueChanged:
(WebUsageEnablerBrowserAgent*)webUsageEnabler {
self.active = WebUsageEnablerBrowserAgent::FromBrowser(self.browser)
->IsWebUsageEnabled();
}
#pragma mark - QuickDeleteCommands
- (void)showQuickDeleteAndCanPerformTabsClosureAnimation:
(BOOL)canPerformTabsClosureAnimation {
CHECK(IsIosQuickDeleteEnabled());
CHECK(!self.browser->GetProfile()->IsOffTheRecord());
[_quickDeleteCoordinator stop];
SceneState* sceneState = self.browser->GetSceneState();
_quickDeleteCoordinator = [[QuickDeleteCoordinator alloc]
initWithBaseViewController:top_view_controller::
TopPresentedViewControllerFrom(
sceneState.rootViewController)
browser:self.browser
canPerformTabsClosureAnimation:canPerformTabsClosureAnimation];
[_quickDeleteCoordinator start];
}
- (void)stopQuickDelete {
CHECK(IsIosQuickDeleteEnabled());
[_quickDeleteCoordinator stop];
_quickDeleteCoordinator = nil;
}
- (void)stopQuickDeleteForAnimationWithCompletion:(ProceduralBlock)completion {
CHECK(IsIosQuickDeleteEnabled());
// TODO(crbug.com/335387869): Remove NotFatalUntil and the if below when we're
// sure this code path is infeasible. The BrowserViewController should always
// have at least the QuickDeleteViewController on top of it.
CHECK(self.viewController.presentedViewController, base::NotFatalUntil::M134);
// If BrowserViewController has not presented any view controller, then
// trigger `completion` immediately.
if (!self.viewController.presentedViewController) {
if (completion) {
completion();
}
[self stopQuickDelete];
return;
}
// If BrowserViewController has presented a view controller, then dismiss
// every VC on top of it.
__weak __typeof(self) weakSelf = self;
__weak __typeof(self.dispatcher) weakDispatcher = self.dispatcher;
ProceduralBlock dismissalCompletion = ^{
if (completion) {
completion();
}
// Properly shutdown all coordinators started either by this coordinator or
// by the scene controller. This should include Quick Delete, History and
// the Privacy Settings.
[weakSelf clearPresentedStateWithCompletion:nil dismissOmnibox:YES];
// The protocol might not have a valid target when the shutdown of Quick
// Delete is happening at the same time the UI is being shutdown.
if ([weakDispatcher
dispatchingForProtocol:@protocol(ApplicationCommands)]) {
id<ApplicationCommands> applicationCommandsHandler =
HandlerForProtocol(weakDispatcher, ApplicationCommands);
[applicationCommandsHandler dismissModalDialogsWithCompletion:nil];
}
};
[self.viewController dismissViewControllerAnimated:YES
completion:dismissalCompletion];
}
#pragma mark - WhatsNewCommands
- (void)showWhatsNew {
self.whatsNewCoordinator = [[WhatsNewCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.whatsNewCoordinator start];
}
- (void)dismissWhatsNew {
if (self.whatsNewCoordinator) {
[self.whatsNewCoordinator stop];
self.whatsNewCoordinator = nil;
}
}
- (void)showWhatsNewIPH {
[HandlerForProtocol(_dispatcher, HelpCommands)
presentInProductHelpWithType:InProductHelpType::kWhatsNew];
}
#pragma mark - GoogleOneCommands
- (void)showGoogleOneForIdentity:(id<SystemIdentity>)identity
entryPoint:(GoogleOneEntryPoint)entryPoint
baseViewController:(UIViewController*)baseViewController {
UIViewController* viewController = baseViewController ?: self.viewController;
_googleOneCoordinator =
[[GoogleOneCoordinator alloc] initWithBaseViewController:viewController
browser:self.browser
entryPoint:entryPoint
identity:identity];
[_googleOneCoordinator start];
}
@end