// Copyright 2012 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/app/main_controller.h"
#import <memory>
#import "base/apple/bundle_locations.h"
#import "base/apple/foundation_util.h"
#import "base/barrier_closure.h"
#import "base/feature_list.h"
#import "base/functional/callback.h"
#import "base/functional/concurrent_closures.h"
#import "base/ios/ios_util.h"
#import "base/memory/raw_ptr.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/histogram_macros.h"
#import "base/path_service.h"
#import "base/strings/sys_string_conversions.h"
#import "base/task/sequenced_task_runner.h"
#import "components/component_updater/component_updater_service.h"
#import "components/component_updater/installer_policies/autofill_states_component_installer.h"
#import "components/component_updater/installer_policies/on_device_head_suggest_component_installer.h"
#import "components/component_updater/installer_policies/optimization_hints_component_installer.h"
#import "components/component_updater/installer_policies/plus_address_blocklist_component_installer.h"
#import "components/component_updater/installer_policies/safety_tips_component_installer.h"
#import "components/component_updater/url_param_filter_remover.h"
#import "components/content_settings/core/browser/host_content_settings_map.h"
#import "components/feature_engagement/public/event_constants.h"
#import "components/feature_engagement/public/tracker.h"
#import "components/metrics/metrics_pref_names.h"
#import "components/metrics/metrics_service.h"
#import "components/password_manager/core/common/password_manager_features.h"
#import "components/password_manager/core/common/passwords_directory_util_ios.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#import "components/prefs/pref_change_registrar.h"
#import "components/previous_session_info/previous_session_info.h"
#import "components/sync/service/sync_service.h"
#import "components/web_resource/web_resource_pref_names.h"
#import "ios/chrome/app/app_metrics_app_state_agent.h"
#import "ios/chrome/app/application_delegate/metrics_mediator.h"
#import "ios/chrome/app/application_storage_metrics.h"
#import "ios/chrome/app/background_refresh/background_refresh_app_agent.h"
#import "ios/chrome/app/background_refresh/test_refresher.h"
#import "ios/chrome/app/blocking_scene_commands.h"
#import "ios/chrome/app/deferred_initialization_runner.h"
#import "ios/chrome/app/docking_promo_app_agent.h"
#import "ios/chrome/app/enterprise_app_agent.h"
#import "ios/chrome/app/fast_app_terminate_buildflags.h"
#import "ios/chrome/app/features.h"
#import "ios/chrome/app/feed_app_agent.h"
#import "ios/chrome/app/first_run_app_state_agent.h"
#import "ios/chrome/app/identity_confirmation_app_agent.h"
#import "ios/chrome/app/launch_screen_view_controller.h"
#import "ios/chrome/app/memory_monitor.h"
#import "ios/chrome/app/post_restore_app_agent.h"
#import "ios/chrome/app/profile/profile_controller.h"
#import "ios/chrome/app/profile/profile_state.h"
#import "ios/chrome/app/safe_mode_app_state_agent.h"
#import "ios/chrome/app/search_engine_choice_app_agent.h"
#import "ios/chrome/app/spotlight/spotlight_manager.h"
#import "ios/chrome/app/startup/chrome_app_startup_parameters.h"
#import "ios/chrome/app/startup/chrome_main_starter.h"
#import "ios/chrome/app/startup/client_registration.h"
#import "ios/chrome/app/startup/ios_chrome_main.h"
#import "ios/chrome/app/startup/ios_enable_sandbox_dump_buildflags.h"
#import "ios/chrome/app/startup/provider_registration.h"
#import "ios/chrome/app/startup/register_experimental_settings.h"
#import "ios/chrome/app/startup/setup_debugging.h"
#import "ios/chrome/app/startup_tasks.h"
#import "ios/chrome/app/tests_hook.h"
#import "ios/chrome/app/variations_app_state_agent.h"
#import "ios/chrome/browser/accessibility/model/window_accessibility_change_notifier_app_agent.h"
#import "ios/chrome/browser/appearance/ui_bundled/appearance_customization.h"
#import "ios/chrome/browser/browser_state_metrics/model/browser_state_activity_app_agent.h"
#import "ios/chrome/browser/browsing_data/model/browsing_data_remover.h"
#import "ios/chrome/browser/browsing_data/model/browsing_data_remover_factory.h"
#import "ios/chrome/browser/browsing_data/model/sessions_storage_util.h"
#import "ios/chrome/browser/content_settings/model/host_content_settings_map_factory.h"
#import "ios/chrome/browser/crash_report/model/crash_helper.h"
#import "ios/chrome/browser/crash_report/model/crash_keys_helper.h"
#import "ios/chrome/browser/crash_report/model/crash_loop_detection_util.h"
#import "ios/chrome/browser/crash_report/model/crash_report_helper.h"
#import "ios/chrome/browser/credential_provider/model/credential_provider_buildflags.h"
#import "ios/chrome/browser/default_browser/model/utils.h"
#import "ios/chrome/browser/download/model/download_directory_util.h"
#import "ios/chrome/browser/enterprise/model/idle/idle_service_factory.h"
#import "ios/chrome/browser/external_files/model/external_file_remover_factory.h"
#import "ios/chrome/browser/external_files/model/external_file_remover_impl.h"
#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
#import "ios/chrome/browser/first_run/model/first_run.h"
#import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h"
#import "ios/chrome/browser/mailto_handler/model/mailto_handler_service.h"
#import "ios/chrome/browser/mailto_handler/model/mailto_handler_service_factory.h"
#import "ios/chrome/browser/memory/model/memory_debugger_manager.h"
#import "ios/chrome/browser/metrics/model/first_user_action_recorder.h"
#import "ios/chrome/browser/metrics/model/incognito_usage_app_state_agent.h"
#import "ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h"
#import "ios/chrome/browser/metrics/model/window_configuration_recorder.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
#import "ios/chrome/browser/omaha/model/omaha_service.h"
#import "ios/chrome/browser/passwords/model/password_manager_util_ios.h"
#import "ios/chrome/browser/promos_manager/model/promos_manager_factory.h"
#import "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h"
#import "ios/chrome/browser/screenshot/model/screenshot_metrics_recorder.h"
#import "ios/chrome/browser/search_engines/model/extension_search_engine_data_updater.h"
#import "ios/chrome/browser/search_engines/model/search_engines_util.h"
#import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
#import "ios/chrome/browser/sessions/model/features.h"
#import "ios/chrome/browser/sessions/model/session_restoration_service.h"
#import "ios/chrome/browser/sessions/model/session_restoration_service_factory.h"
#import "ios/chrome/browser/sessions/model/session_util.h"
#import "ios/chrome/browser/share_extension/model/share_extension_service.h"
#import "ios/chrome/browser/share_extension/model/share_extension_service_factory.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_delegate.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_list.h"
#import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/paths/paths.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/profile/profile_manager_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.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/features/features.h"
#import "ios/chrome/browser/shared/public/features/system_flags.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/signin/model/authentication_service_delegate.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/snapshots/model/snapshot_browser_agent.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
#import "ios/chrome/browser/url_loading/model/url_loading_params.h"
#import "ios/chrome/browser/web/model/certificate_policy_app_agent.h"
#import "ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.h"
#import "ios/chrome/browser/webui/ui_bundled/chrome_web_ui_ios_controller_factory.h"
#import "ios/chrome/common/app_group/app_group_constants.h"
#import "ios/chrome/common/app_group/app_group_field_trial_version.h"
#import "ios/chrome/common/app_group/app_group_utils.h"
#import "ios/components/cookie_util/cookie_util.h"
#import "ios/net/cookies/cookie_store_ios.h"
#import "ios/net/empty_nsurlcache.h"
#import "ios/public/provider/chrome/browser/app_distribution/app_distribution_api.h"
#import "ios/public/provider/chrome/browser/overrides/overrides_api.h"
#import "ios/public/provider/chrome/browser/raccoon/raccoon_api.h"
#import "ios/public/provider/chrome/browser/ui_utils/ui_utils_api.h"
#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_api.h"
#import "ios/web/public/webui/web_ui_ios_controller_factory.h"
#import "net/base/apple/url_conversions.h"
#import "services/network/public/cpp/shared_url_loader_factory.h"
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
#import "ios/chrome/app/credential_provider_migrator_app_agent.h"
#import "ios/chrome/browser/credential_provider/model/credential_provider_service_factory.h"
#import "ios/chrome/browser/credential_provider/model/credential_provider_support.h"
#import "ios/chrome/browser/credential_provider/model/credential_provider_util.h"
#endif
#if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
#import "ios/chrome/app/dump_documents_statistics.h"
#endif // BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
namespace {
#if BUILDFLAG(FAST_APP_TERMINATE_ENABLED)
// Skip chromeMain.reset() on shutdown, see crbug.com/1328891 for details.
BASE_FEATURE(kFastApplicationWillTerminate,
"FastApplicationWillTerminate",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif // BUILDFLAG(FAST_APP_TERMINATE_ENABLED)
// Constants for deferring resetting the startup attempt count (to give the app
// a little while to make sure it says alive).
NSString* const kStartupAttemptReset = @"StartupAttemptReset";
// Constants for deferring memory debugging tools startup.
NSString* const kMemoryDebuggingToolsStartup = @"MemoryDebuggingToolsStartup";
// Constant for deferring the cleanup of discarded sessions on disk.
NSString* const kCleanupDiscardedSessions = @"CleanupDiscardedSessions";
// Constants for deferring mailto handling initialization.
NSString* const kMailtoHandlingInitialization = @"MailtoHandlingInitialization";
// Constants for deferring saving field trial values
NSString* const kSaveFieldTrialValues = @"SaveFieldTrialValues";
// Constants for refreshing the WidgetKit after five minutes
NSString* const kWidgetKitRefreshFiveMinutes = @"WidgetKitRefreshFiveMinutes";
// Constants for deferred check if it is necessary to send pings to
// Chrome distribution related services.
NSString* const kSendInstallPingIfNecessary = @"SendInstallPingIfNecessary";
// Constants for deferred deletion of leftover user downloaded files.
NSString* const kDeleteDownloads = @"DeleteDownloads";
// Constants for deferred deletion of leftover temporary passwords files.
NSString* const kDeleteTempPasswords = @"DeleteTempPasswords";
// Constants for deferred UMA logging of existing Siri User shortcuts.
NSString* const kLogSiriShortcuts = @"LogSiriShortcuts";
// Constants for deferred sending of queued feedback.
NSString* const kSendQueuedFeedback = @"SendQueuedFeedback";
// Constants for deferring the upload of crash reports.
NSString* const kUploadCrashReports = @"UploadCrashReports";
// Constants for deferring the cleanup of snapshots on disk.
NSString* const kCleanupSnapshots = @"CleanupSnapshots";
// Constants for deferring startup Spotlight bookmark indexing.
NSString* const kStartSpotlightBookmarksIndexing =
@"StartSpotlightBookmarksIndexing";
// Constants for deferring the enterprise managed device check.
NSString* const kEnterpriseManagedDeviceCheck = @"EnterpriseManagedDeviceCheck";
// Constants for deferred deletion of leftover session state files.
NSString* const kPurgeWebSessionStates = @"PurgeWebSessionStates";
// Constants for deferred favicons clean up.
NSString* const kFaviconsCleanup = @"FaviconsCleanup";
// The minimum amount of time (2 weeks) between calculating and
// logging metrics about the amount of device storage space used by Chrome.
const base::TimeDelta kMinimumTimeBetweenDocumentsSizeLogging = base::Days(14);
// Adapted from chrome/browser/ui/browser_init.cc.
void RegisterComponentsForUpdate() {
component_updater::ComponentUpdateService* cus =
GetApplicationContext()->GetComponentUpdateService();
DCHECK(cus);
base::FilePath path;
const bool success = base::PathService::Get(ios::DIR_USER_DATA, &path);
DCHECK(success);
component_updater::DeleteUrlParamFilter(path);
RegisterOnDeviceHeadSuggestComponent(
cus, GetApplicationContext()->GetApplicationLocale());
RegisterSafetyTipsComponent(cus);
RegisterAutofillStatesComponent(cus,
GetApplicationContext()->GetLocalState());
RegisterOptimizationHintsComponent(cus);
RegisterPlusAddressBlocklistComponent(cus);
}
// The delay, in seconds, for cleaning external files.
const int kExternalFilesCleanupDelaySeconds = 60;
// Delegate for the AuthenticationService.
// TODO(crbug.com/325612973): When browsing data removal is factored into a
// keyed service, make that service an AuthenticationServiceDelegate and just
// assign it directly to the authentication service. That eliminates the need
// for this class.
class MainControllerAuthenticationServiceDelegate
: public AuthenticationServiceDelegate {
public:
explicit MainControllerAuthenticationServiceDelegate(
ChromeBrowserState* browser_state);
MainControllerAuthenticationServiceDelegate(
const MainControllerAuthenticationServiceDelegate&) = delete;
MainControllerAuthenticationServiceDelegate& operator=(
const MainControllerAuthenticationServiceDelegate&) = delete;
~MainControllerAuthenticationServiceDelegate() override;
// AuthenticationServiceDelegate implementation.
void ClearBrowsingData(base::OnceClosure completion) override;
void ClearBrowsingDataForSignedinPeriod(
base::OnceClosure completion) override;
private:
const raw_ptr<ChromeBrowserState> browser_state_ = nullptr;
};
MainControllerAuthenticationServiceDelegate::
MainControllerAuthenticationServiceDelegate(
ChromeBrowserState* browser_state)
: browser_state_(browser_state) {}
MainControllerAuthenticationServiceDelegate::
~MainControllerAuthenticationServiceDelegate() = default;
void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
base::OnceClosure completion) {
BrowsingDataRemover* browsingDataRemover =
BrowsingDataRemoverFactory::GetForBrowserState(browser_state_);
browsingDataRemover->Remove(browsing_data::TimePeriod::ALL_TIME,
BrowsingDataRemoveMask::REMOVE_ALL,
(std::move(completion)));
}
void MainControllerAuthenticationServiceDelegate::
ClearBrowsingDataForSignedinPeriod(base::OnceClosure completion) {
base::Time last_signin_timestamp =
browser_state_->GetPrefs()->GetTime(prefs::kLastSigninTimestamp);
BrowsingDataRemoveMask remove_mask =
BrowsingDataRemoveMask::REMOVE_ALL_FOR_TIME_PERIOD;
if (base::FeatureList::IsEnabled(kIdentityDiscAccountMenu)) {
// If fast account switching via the account particle disk on the NTP is
// enabled, then also close any tabs that were used since the signin. This
// requires separately querying the tab-usage timestamps first.
remove_mask |= BrowsingDataRemoveMask::CLOSE_TABS;
}
// If `kLastSigninTimestamp` has the default base::Time() value, data will be
// cleared for all time, which is intended to happen in this case.
BrowsingDataRemover* browsingDataRemover =
BrowsingDataRemoverFactory::GetForBrowserState(browser_state_);
browsingDataRemover->RemoveInRange(last_signin_timestamp, base::Time::Now(),
remove_mask, std::move(completion));
}
} // namespace
@interface MainController () <PrefObserverDelegate,
BlockingSceneCommands,
SceneStateObserver> {
IBOutlet UIWindow* _window;
// The object that drives the Chrome startup/shutdown logic.
std::unique_ptr<IOSChromeMain> _chromeMain;
// True if the current session began from a cold start. False if the app has
// entered the background at least once since start up.
BOOL _isColdStart;
// An object to record metrics related to the user's first action.
std::unique_ptr<FirstUserActionRecorder> _firstUserActionRecorder;
// Bridge to listen to pref changes.
std::unique_ptr<PrefObserverBridge> _localStatePrefObserverBridge;
// Registrar for pref changes notifications to the local state.
PrefChangeRegistrar _localStatePrefChangeRegistrar;
// Vector updating search engine data (to be accessed in extensions)
// for all loaded browserStates.
std::vector<std::unique_ptr<ExtensionSearchEngineDataUpdater>>
_extensionSearchEngineDataUpdaters;
// The class in charge of showing/hiding the memory debugger when the
// appropriate pref changes.
MemoryDebuggerManager* _memoryDebuggerManager;
// Responsible for indexing chrome links (such as bookmarks, most likely...)
// in system Spotlight index for all loaded browser states.
NSMutableArray<SpotlightManager*>* _spotlightManagers;
// Variable backing metricsMediator property.
__weak MetricsMediator* _metricsMediator;
// Holds the ProfileController for all loaded profiles.
std::map<std::string, ProfileController*> _profileControllers;
WindowConfigurationRecorder* _windowConfigurationRecorder;
// Handler for the startup tasks, deferred or not.
StartupTasks* _startupTasks;
}
// Handles collecting metrics on user triggered screenshots
@property(nonatomic, strong)
ScreenshotMetricsRecorder* screenshotMetricsRecorder;
// Cleanup any persisted data for the session restration on disk.
- (void)cleanupSessionStateCache;
// Cleanup snapshots on disk.
- (void)cleanupSnapshots;
// Cleanup discarded sessions on disk.
- (void)cleanupDiscardedSessions;
// Pings distribution services.
- (void)pingDistributionServices;
// Sends any feedback that happens to still be on local storage.
- (void)sendQueuedFeedback;
// Called whenever an orientation change is received.
- (void)orientationDidChange:(NSNotification*)notification;
// Register to receive orientation change notification to update crash keys.
- (void)registerForOrientationChangeNotifications;
// Asynchronously creates the pref observers.
- (void)schedulePrefObserverInitialization;
// Asynchronously schedules pings to distribution services.
- (void)scheduleAppDistributionPings;
// Asynchronously schedule the init of the memoryDebuggerManager.
- (void)scheduleMemoryDebuggingTools;
// Creates the MailtoHandlerService for all loaded browserStates.
- (void)createMailtoHandlerServices;
// Asynchronously kick off regular free memory checks.
- (void)startFreeMemoryMonitoring;
// Asynchronously schedules the reset of the failed startup attempt counter.
- (void)scheduleStartupAttemptReset;
// Asynchronously schedules the upload of crash reports.
- (void)scheduleCrashReportUpload;
// Asynchronously schedules the cleanup of discarded session files on disk.
- (void)scheduleDiscardedSessionsCleanup;
// Asynchronously schedules the cleanup of snapshots on disk.
- (void)scheduleSnapshotsCleanup;
// Schedules various cleanup tasks that are performed after launch.
- (void)scheduleStartupCleanupTasks;
// Schedules various tasks to be performed after the application becomes active.
- (void)scheduleLowPriorityStartupTasks;
// Schedules external file removal.
- (void)scheduleExternalFileClenup;
// Schedules the deletion of user downloaded files that might be leftover
// from the last time Chrome was run.
- (void)scheduleDeleteTempDownloadsDirectory;
// Schedule the deletion of the temporary passwords files that might
// be left over from incomplete export operations.
- (void)scheduleDeleteTempPasswordsDirectory;
// Crashes the application if requested.
- (void)crashIfRequested;
// Performs synchronous browser state initialization steps.
- (void)initializeBrowserState:(ChromeBrowserState*)browserState;
// Initializes the application to the minimum initialization needed in all
// cases.
- (void)startUpBrowserBasicInitialization;
// Initializes the browser objects for the background handlers, perform any
// background initilisation that are required, and then transition to the
// next stage.
- (void)startUpBrowserBackgroundInitialization;
// Initializes the browser objects for the browser UI (e.g., the browser
// state).
- (void)startUpBrowserForegroundInitialization;
// Performs any background initialisation that are required before the app
// can progress. If there are no initialisation, `completion` must be called
// synchronously.
- (void)performBrowserBackgroundInitialisation:(ProceduralBlock)completion;
@end
@implementation MainController
// Defined by public protocols.
// - StartupInformation
@synthesize isColdStart = _isColdStart;
@synthesize appLaunchTime = _appLaunchTime;
@synthesize isFirstRun = _isFirstRun;
@synthesize didFinishLaunchingTime = _didFinishLaunchingTime;
@synthesize firstSceneConnectionTime = _firstSceneConnectionTime;
SEQUENCE_CHECKER(_sequenceChecker);
#pragma mark - Application lifecycle
- (instancetype)init {
if ((self = [super init])) {
_isFirstRun = ShouldPresentFirstRunExperience();
_startupTasks = [[StartupTasks alloc] init];
_spotlightManagers = [NSMutableArray array];
}
return self;
}
- (void)dealloc {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)startUpBrowserBasicInitialization {
_appLaunchTime = IOSChromeMain::StartTime();
_isColdStart = YES;
UMA_HISTOGRAM_BOOLEAN("IOS.Process.ActivePrewarm",
base::ios::IsApplicationPreWarmed());
[SetupDebugging setUpDebuggingOptions];
// Register all providers before calling any Chromium code.
[ProviderRegistration registerProviders];
// Start dispatching for blocking UI commands.
[self.appState.appCommandDispatcher
startDispatchingToTarget:self
forProtocol:@protocol(BlockingSceneCommands)];
}
- (void)startUpBrowserBackgroundInitialization {
DCHECK(self.appState.initStage > InitStageSafeMode);
NSBundle* baseBundle = base::apple::OuterBundle();
base::apple::SetBaseBundleID(
base::SysNSStringToUTF8([baseBundle bundleIdentifier]).c_str());
// Register default values for experimental settings (Application Preferences)
// and set the "Version" key in the UserDefaults.
[RegisterExperimentalSettings
registerExperimentalSettingsWithUserDefaults:[NSUserDefaults
standardUserDefaults]
bundle:base::apple::
FrameworkBundle()];
// Register all clients before calling any web code.
[ClientRegistration registerClients];
_chromeMain = [ChromeMainStarter startChromeMain];
// Start recording field trial info.
[[PreviousSessionInfo sharedInstance] beginRecordingFieldTrials];
const std::vector<ChromeBrowserState*> loadedProfiles =
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles();
CHECK(!loadedProfiles.empty());
for (ChromeBrowserState* chromeBrowserState : loadedProfiles) {
[self initializeBrowserState:chromeBrowserState];
}
// TODO(crbug.com/343166723): Support having multiple profiles.
ChromeBrowserState* browserState =
GetApplicationContext()
->GetProfileManager()
->GetLastUsedProfileDeprecatedDoNotUse();
auto iterator = _profileControllers.find(browserState->GetBrowserStateName());
DCHECK(iterator != _profileControllers.end());
self.appState.mainProfile = iterator->second.state;
// Give tests a chance to prepare for testing.
tests_hook::SetUpTestsIfPresent();
// Initialize the provider UI global state.
ios::provider::InitializeUI();
// If the user has interacted with the app, then start (or continue) watching
// for crashes. Otherwise, do not watch for crashes.
//
// Depending on the client's ExtendedVariationsSafeMode experiment group (see
// MaybeExtendVariationsSafeMode() in variations_field_trial_creator.cc for
// more info), the signal to start watching for crashes may have occurred
// earlier.
//
// TODO(b/184937096): Remove the below call to OnAppEnterForeground() if this
// call is moved earlier for all clients. It is is being kept here for the
// time being for the control group of the extended Variations Safe Mode
// experiment.
//
// TODO(crbug.com/40190949): Stop watching for a crash if this is a background
// fetch.
if (_appState.userInteracted)
GetApplicationContext()->GetMetricsService()->OnAppEnterForeground();
web::WebUIIOSControllerFactory::RegisterFactory(
ChromeWebUIIOSControllerFactory::GetInstance());
[NSURLCache setSharedURLCache:[EmptyNSURLCache emptyNSURLCache]];
// TODO(crbug.com/325616341): Update PostRestoreAppAgent for multi-identity.
ChromeBrowserState* chromeBrowserState =
self.appState.mainProfile.browserState;
[self.appState
addAgent:
[[PostRestoreAppAgent alloc]
initWithPromosManager:PromosManagerFactory::GetForBrowserState(
chromeBrowserState)
authenticationService:AuthenticationServiceFactory::
GetForBrowserState(chromeBrowserState)
identityManager:IdentityManagerFactory::GetForBrowserState(
chromeBrowserState)]];
if (IsDockingPromoEnabled()) {
switch (DockingPromoExperimentTypeEnabled()) {
case DockingPromoDisplayTriggerArm::kDuringFRE:
break;
case DockingPromoDisplayTriggerArm::kAfterFRE:
case DockingPromoDisplayTriggerArm::kAppLaunch:
[self.appState addAgent:[[DockingPromoAppAgent alloc] init]];
}
}
// Request background refresh.
[[BackgroundRefreshAppAgent agentFromApp:self.appState]
requestAppRefreshWithDelay:30 * 60.0]; // 30 minutes.
// Perform any background initialisation that is required and then
// migrate to the next stage.
__weak __typeof(self) weakSelf = self;
[self performBrowserBackgroundInitialisation:^{
[weakSelf.appState queueTransitionToNextInitStage];
}];
}
- (void)performBrowserBackgroundInitialisation:(ProceduralBlock)completion {
// Migrate the session storage based on the feature.
const SessionRestorationServiceFactory::StorageFormat requested_format =
session::features::UseSessionSerializationOptimizations()
? SessionRestorationServiceFactory::kOptimized
: SessionRestorationServiceFactory::kLegacy;
const std::vector<ChromeBrowserState*> loadedProfiles =
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles();
// `completion` should be called only once all BrowserStates have been
// migrated.
base::RepeatingClosure closure =
base::BarrierClosure(loadedProfiles.size(), base::BindOnce(completion));
// MigrateSessionStorageFormat is synchronous if the storage is already in
// the requested format, so this is safe to call and won't block the app
// startup.
for (ChromeBrowserState* browserState : loadedProfiles) {
SessionRestorationServiceFactory::GetInstance()
->MigrateSessionStorageFormat(browserState, requested_format, closure);
}
}
// This initialization must happen before any windows are created.
- (void)startUpBeforeFirstWindowCreated {
GetApplicationContext()->OnAppEnterForeground();
// Although this duplicates some metrics_service startup logic also in
// IOSChromeMain(), this call does additional work, checking for wifi-only
// and setting up the required support structures.
[_metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:NO];
// Crash the app during startup if requested but only after we have enabled
// uploading crash reports.
[self crashIfRequested];
if (experimental_flags::MustClearApplicationGroupSandbox()) {
// Clear the Application group sandbox if requested. This operation take
// some time and will access the file system synchronously as the rest of
// the startup sequence requires it to be completed before continuing.
app_group::ClearAppGroupSandbox();
}
RegisterComponentsForUpdate();
[[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs];
for (ChromeBrowserState* chromeBrowserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
feature_engagement::Tracker* tracker =
feature_engagement::TrackerFactory::GetForBrowserState(
chromeBrowserState);
// Send "Chrome Opened" event to the feature_engagement::Tracker on cold
// start.
tracker->NotifyEvent(feature_engagement::events::kChromeOpened);
[_metricsMediator notifyCredentialProviderWasUsed:tracker];
[_spotlightManagers
addObject:[SpotlightManager
spotlightManagerWithBrowserState:chromeBrowserState]];
ShareExtensionService* service =
ShareExtensionServiceFactory::GetForBrowserState(chromeBrowserState);
service->Initialize();
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
if (IsCredentialProviderExtensionSupported()) {
CredentialProviderServiceFactory::GetForBrowserState(chromeBrowserState);
}
#endif
}
_windowConfigurationRecorder = [[WindowConfigurationRecorder alloc] init];
}
// This initialization must only happen once there's at least one Chrome window
// open.
- (void)startUpAfterFirstWindowCreated {
// "Low priority" tasks
[_startupTasks registerForApplicationWillResignActiveNotification];
[self registerForOrientationChangeNotifications];
[self scheduleExternalFileClenup];
CustomizeUIAppearance();
[self scheduleStartupCleanupTasks];
ios::provider::InstallOverrides();
[self scheduleLowPriorityStartupTasks];
// Run after UI created to avoid trying to update UI before it is available.
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
enterprise_idle::IdleServiceFactory::GetForBrowserState(browserState)
->OnApplicationWillEnterForeground();
}
// Now that everything is properly set up, run the tests.
tests_hook::RunTestsIfPresent();
self.screenshotMetricsRecorder = [[ScreenshotMetricsRecorder alloc] init];
[self.screenshotMetricsRecorder startRecordingMetrics];
}
- (PostCrashAction)postCrashAction {
if (self.appState.resumingFromSafeMode)
return PostCrashAction::kShowSafeMode;
if (GetApplicationContext()->WasLastShutdownClean())
return PostCrashAction::kRestoreTabsCleanShutdown;
if (crash_util::GetFailedStartupAttemptCount() >= 2) {
return PostCrashAction::kShowNTPWithReturnToTab;
}
return PostCrashAction::kRestoreTabsUncleanShutdown;
}
- (void)startUpBrowserForegroundInitialization {
// TODO(crbug.com/40190949): Determine whether Chrome needs to resume watching
// for crashes.
self.appState.postCrashAction = [self postCrashAction];
[self startUpBeforeFirstWindowCreated];
base::UmaHistogramEnumeration("Stability.IOS.PostCrashAction",
self.appState.postCrashAction);
}
- (void)initializeBrowserState:(ChromeBrowserState*)browserState {
DCHECK(!browserState->IsOffTheRecord());
ProfileController* controller = [[ProfileController alloc] init];
controller.state.browserState = browserState;
auto insertion_result = _profileControllers.insert(
std::make_pair(browserState->GetBrowserStateName(), controller));
DCHECK(insertion_result.second);
search_engines::UpdateSearchEngineCountryCodeIfNeeded(
browserState->GetPrefs());
// Force an obvious initialization of the AuthenticationService. This must
// be done before creation of the UI to ensure the service is initialised
// before use (it is a security issue, so accessing the service CHECKs if
// this is not the case). It is important to do this during background
// initialization when the app is cold started directly into the background
// because it is used by the DiscoverFeedService, which is started in the
// background to perform background refresh. There is no downside to doing
// this during background initialization when the app is launched into the
// foreground.
AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
browserState,
std::make_unique<MainControllerAuthenticationServiceDelegate>(
browserState));
// Force desktop mode when raccoon is enabled.
if (ios::provider::IsRaccoonEnabled()) {
if (!browserState->GetPrefs()->GetBoolean(prefs::kUserAgentWasChanged)) {
HostContentSettingsMap* settingsMap =
ios::HostContentSettingsMapFactory::GetForBrowserState(browserState);
settingsMap->SetDefaultContentSetting(
ContentSettingsType::REQUEST_DESKTOP_SITE, CONTENT_SETTING_ALLOW);
browserState->GetPrefs()->SetBoolean(prefs::kUserAgentWasChanged, true);
}
}
if (IsTabGroupSyncEnabled()) {
// Ensure that the tab group sync services are created to observe updates.
tab_groups::TabGroupSyncServiceFactory::GetForBrowserState(browserState);
}
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
[sceneState addObserver:self];
// If the application is not yet ready to present the UI, install
// a LaunchScreenViewController as the root view of the connected
// SceneState. This ensures that there is no "blank" window.
if (self.appState.initStage < InitStageBrowserObjectsForUI) {
LaunchScreenViewController* launchScreen =
[[LaunchScreenViewController alloc] init];
[sceneState.window setRootViewController:launchScreen];
[sceneState.window makeKeyAndVisible];
}
}
// Called when the first scene becomes active.
- (void)appState:(AppState*)appState
firstSceneHasInitializedUI:(SceneState*)sceneState {
DCHECK(self.appState.initStage > InitStageSafeMode);
if (self.appState.initStage <= InitStageNormalUI) {
return;
}
// TODO(crbug.com/40769058): Pass the scene to this method to make sure that
// the chosen scene is initialized.
[self startUpAfterFirstWindowCreated];
}
- (void)appState:(AppState*)appState
didTransitionFromInitStage:(InitStage)previousInitStage {
// TODO(crbug.com/40769058): Remove this once the bug fixed.
if (previousInitStage == InitStageNormalUI &&
appState.firstSceneHasInitializedUI) {
[self startUpAfterFirstWindowCreated];
}
switch (appState.initStage) {
case InitStageStart:
[appState queueTransitionToNextInitStage];
break;
case InitStageBrowserBasic:
[self startUpBrowserBasicInitialization];
break;
case InitStageSafeMode:
[self addPostSafeModeAgents];
break;
case InitStageVariationsSeed:
break;
case InitStageBrowserObjectsForBackgroundHandlers:
[self startUpBrowserBackgroundInitialization];
break;
case InitStageEnterprise:
break;
case InitStageBrowserObjectsForUI:
[self maybeContinueForegroundInitialization];
break;
case InitStageNormalUI:
// Scene controllers use this stage to create the normal UI if needed.
// There is no specific agent (other than SceneController) handling
// this stage.
[appState queueTransitionToNextInitStage];
break;
case InitStageFirstRun:
break;
case InitStageChoiceScreen:
break;
case InitStageFinal:
// In a multi-window environment we have the correct number of
// connectedScenes only at this stage.
[MetricsMediator
logLaunchMetricsWithStartupInformation:self
connectedScenes:self.appState.connectedScenes];
break;
}
}
- (void)addPostSafeModeAgents {
[self.appState addAgent:[[EnterpriseAppAgent alloc] init]];
[self.appState addAgent:[[IncognitoUsageAppStateAgent alloc] init]];
[self.appState addAgent:[[FirstRunAppAgent alloc] init]];
[self.appState addAgent:[[CertificatePolicyAppAgent alloc] init]];
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
[self.appState addAgent:[[CredentialProviderMigratorAppAgent alloc] init]];
#endif
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
if (level <= SceneActivationLevelDisconnected) {
[sceneState removeObserver:self];
} else if (level > SceneActivationLevelBackground) {
// Stop observing all scenes since we only needed to know when the app
// (first scene) is about to go to the foreground.
for (SceneState* scene in _appState.connectedScenes) {
[scene removeObserver:self];
}
[self maybeContinueForegroundInitialization];
}
}
#pragma mark - Property implementation.
- (void)setAppState:(AppState*)appState {
DCHECK(!_appState);
_appState = appState;
[appState addObserver:self];
// Create app state agents.
[appState addAgent:[[AppMetricsAppStateAgent alloc] init]];
[appState addAgent:[[SafeModeAppAgent alloc] init]];
[appState addAgent:[[SearchEngineChoiceAppAgent alloc] init]];
[appState addAgent:[[VariationsAppStateAgent alloc] init]];
[appState addAgent:[[IdentityConfirmationAppAgent alloc] init]];
BackgroundRefreshAppAgent* refreshAgent =
[[BackgroundRefreshAppAgent alloc] init];
[_appState addAgent:refreshAgent];
// Register background refresh providers.
[refreshAgent addAppRefreshProvider:[[TestRefresher alloc]
initWithAppState:self.appState]];
// TODO(crbug.com/355142171): Remove the FeedAppAgent.
[appState addAgent:[[FeedAppAgent alloc] init]];
// Create the window accessibility agent only when multiple windows are
// possible.
if (base::ios::IsMultipleScenesSupported()) {
[appState addAgent:[[WindowAccessibilityChangeNotifierAppAgent alloc] init]];
}
[appState addAgent:[[BrowserStateActivityAppAgent alloc] init]];
}
// TODO(crbug.com/325614311): Get rid of this method/property completely.
- (id<BrowserProviderInterface>)browserProviderInterfaceDoNotUse {
if (self.appState.foregroundActiveScene) {
return self.appState.foregroundActiveScene.browserProviderInterface;
}
NSArray<SceneState*>* connectedScenes = self.appState.connectedScenes;
return connectedScenes.count == 0
? nil
: connectedScenes[0].browserProviderInterface;
}
- (BOOL)isFirstLaunchAfterUpgrade {
return [[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade];
}
#pragma mark - StartupInformation implementation.
- (FirstUserActionRecorder*)firstUserActionRecorder {
return _firstUserActionRecorder.get();
}
- (void)resetFirstUserActionRecorder {
_firstUserActionRecorder.reset();
}
- (void)expireFirstUserActionRecorderAfterDelay:(NSTimeInterval)delay {
[self performSelector:@selector(expireFirstUserActionRecorder)
withObject:nil
afterDelay:delay];
}
- (void)activateFirstUserActionRecorderWithBackgroundTime:
(NSTimeInterval)backgroundTime {
base::TimeDelta delta = base::Seconds(backgroundTime);
_firstUserActionRecorder.reset(new FirstUserActionRecorder(delta));
}
- (void)stopChromeMain {
for (SpotlightManager* spotlightManager : _spotlightManagers) {
[spotlightManager shutdown];
}
[_spotlightManagers removeAllObjects];
_extensionSearchEngineDataUpdaters.clear();
// _localStatePrefChangeRegistrar is observing the PrefService, which is owned
// indirectly by _chromeMain (through the ChromeBrowserState).
// Unregister the observer before the service is destroyed.
_localStatePrefChangeRegistrar.RemoveAll();
// Under the UIScene API, the scene delegate does not receive
// sceneDidDisconnect: notifications on app termination. We mark remaining
// connected scene states as disconnected in order to allow services to
// properly unregister their observers and tear down remaining UI.
for (SceneState* sceneState in self.appState.connectedScenes) {
sceneState.activationLevel = SceneActivationLevelDisconnected;
}
#if BUILDFLAG(FAST_APP_TERMINATE_ENABLED)
// _chromeMain.reset() is a blocking call that regularly causes
// applicationWillTerminate to fail after a 5s delay. Experiment with skipping
// this shutdown call. See: crbug.com/1328891
if (base::FeatureList::IsEnabled(kFastApplicationWillTerminate)) {
// Expected number of time the `closure` defined below needs to
// be called before it signal the semaphore. This corresponds to the
// number of services that needs to be waited for.
uint32_t expectedCount = 0;
// MetricsService doesn't depend on a browser state.
metrics::MetricsService* metrics =
GetApplicationContext()->GetMetricsService();
if (metrics) {
expectedCount += 1;
}
const std::vector<ChromeBrowserState*> loadedProfiles =
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles();
for (ChromeBrowserState* browserState : loadedProfiles) {
expectedCount += 1;
if (browserState->HasOffTheRecordChromeBrowserState()) {
expectedCount += 1;
}
}
// `dispatch_semaphore_signal` is called only once when `closure` is called
// `expectedCount` times.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
base::RepeatingClosure closure =
base::BarrierClosure(expectedCount, base::BindOnce(^{
dispatch_semaphore_signal(semaphore);
}));
for (ChromeBrowserState* browserState : loadedProfiles) {
SessionRestorationServiceFactory::GetForBrowserState(browserState)
->InvokeClosureWhenBackgroundProcessingDone(closure);
if (browserState->HasOffTheRecordChromeBrowserState()) {
ChromeBrowserState* otrBrowserState =
browserState->GetOffTheRecordChromeBrowserState();
SessionRestorationServiceFactory::GetForBrowserState(otrBrowserState)
->InvokeClosureWhenBackgroundProcessingDone(closure);
}
}
if (metrics) {
metrics->Stop();
// MetricsService::Stop() depends on a committed local state, and does
// so asynchronously. To avoid losing metrics, this minimum wait is
// required. This will introduce a wait that will likely be the source
// of a number of watchdog kills, but it should still be fewer than the
// number of kills `_chromeMain.reset()` is responsible for.
GetApplicationContext()->GetLocalState()->CommitPendingWrite({}, closure);
}
dispatch_time_t dispatchTime =
dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC);
dispatch_semaphore_wait(semaphore, dispatchTime);
return;
}
#endif // BUILDFLAG(FAST_APP_TERMINATE_ENABLED)
_chromeMain.reset();
}
#pragma mark - Startup tasks
// Continues foreground initialization iff both the init stage and activation
// level are ready.
- (void)maybeContinueForegroundInitialization {
if (self.appState.foregroundScenes.count > 0 &&
self.appState.initStage == InitStageBrowserObjectsForUI) {
DCHECK(self.appState.userInteracted);
[self startUpBrowserForegroundInitialization];
[self.appState queueTransitionToNextInitStage];
}
}
- (void)sendQueuedFeedback {
if (ios::provider::IsUserFeedbackSupported()) {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kSendQueuedFeedback
block:^{
ios::provider::UploadAllPendingUserFeedback();
}];
}
}
- (void)orientationDidChange:(NSNotification*)notification {
crash_keys::SetCurrentOrientation(GetInterfaceOrientation(),
[[UIDevice currentDevice] orientation]);
}
- (void)registerForOrientationChangeNotifications {
// Register device orientation. UI orientation will be registered by
// each window BVC. These two events may be triggered independently.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(orientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)schedulePrefObserverInitialization {
__weak MainController* weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kPrefObserverInit
block:^{
[weakSelf initializePrefObservers];
}];
}
- (void)initializePrefObservers {
// Track changes to local state prefs.
PrefService* localState = GetApplicationContext()->GetLocalState();
_localStatePrefChangeRegistrar.Init(localState);
_localStatePrefObserverBridge = std::make_unique<PrefObserverBridge>(self);
_localStatePrefObserverBridge->ObserveChangesForPreference(
metrics::prefs::kMetricsReportingEnabled,
&_localStatePrefChangeRegistrar);
// Calls the onPreferenceChanged function in case there was a change to the
// observed preferences before the observer bridge was set up. However, if the
// metrics reporting pref is still unset (has default value), then do not
// call. This likely means that the user is still on the welcome screen during
// the first run experience (FRE), and calling onPreferenceChanged here would
// clear the provisional client ID (in
// MetricsMediator::updateMetricsPrefsOnPermissionChange). The provisional
// client ID is crucial for field trial assignment consistency between the
// first session and follow-up sessions, and is promoted to be the real client
// ID if the user enables metrics reporting in the FRE. Otherwise, it is
// discarded, as would happen here if onPreferenceChanged was called while the
// user was still on the welcome screen and did yet enable/disable metrics
// reporting.
if (!localState->FindPreference(metrics::prefs::kMetricsReportingEnabled)
->IsDefaultValue()) {
[self onPreferenceChanged:metrics::prefs::kMetricsReportingEnabled];
}
// Track changes to default search engine for all loaded browserStates.
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
TemplateURLService* service =
ios::TemplateURLServiceFactory::GetForBrowserState(browserState);
_extensionSearchEngineDataUpdaters.push_back(
std::make_unique<ExtensionSearchEngineDataUpdater>(service));
}
}
- (void)scheduleAppDistributionPings {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kSendInstallPingIfNecessary
block:^{
[self pingDistributionServices];
}];
}
- (void)scheduleStartupAttemptReset {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kStartupAttemptReset
block:^{
crash_util::ResetFailedStartupAttemptCount();
}];
}
- (void)scheduleCrashReportUpload {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kUploadCrashReports
block:^{
crash_helper::UploadCrashReports();
}];
}
- (void)scheduleDiscardedSessionsCleanup {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kCleanupDiscardedSessions
block:^{
[self cleanupDiscardedSessions];
}];
}
- (void)scheduleSnapshotsCleanup {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kCleanupSnapshots
block:^{
[self cleanupSnapshots];
}];
}
- (void)scheduleSessionStateCacheCleanup {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kPurgeWebSessionStates
block:^{
[self cleanupSessionStateCache];
}];
}
- (void)scheduleStartupCleanupTasks {
// Schedule the prefs observer init first to ensure kMetricsReportingEnabled
// is synced before starting uploads.
[self schedulePrefObserverInitialization];
[self scheduleCrashReportUpload];
// ClearSessionCookies() is not synchronous.
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
if (cookie_util::ShouldClearSessionCookies(browserState->GetPrefs())) {
cookie_util::ClearSessionCookies(
browserState->GetOriginalChromeBrowserState());
if (browserState->HasOffTheRecordChromeBrowserState()) {
cookie_util::ClearSessionCookies(
browserState->GetOffTheRecordChromeBrowserState());
}
}
}
// Remove all discarded sessions from disk.
[self scheduleDiscardedSessionsCleanup];
// If the user chooses to restore their session, some cached snapshots and
// session states may be needed. Otherwise, cleanup the snapshots and session
// states
[self scheduleSnapshotsCleanup];
[self scheduleSessionStateCacheCleanup];
}
- (void)scheduleMemoryDebuggingTools {
if (experimental_flags::IsMemoryDebuggingEnabled()) {
__weak MainController* weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kMemoryDebuggingToolsStartup
block:^{
[weakSelf initializedMemoryDebuggingTools];
}];
}
}
- (void)initializedMemoryDebuggingTools {
DCHECK(!_memoryDebuggerManager);
DCHECK(experimental_flags::IsMemoryDebuggingEnabled());
_memoryDebuggerManager = [[MemoryDebuggerManager alloc]
initWithView:self.appState.foregroundActiveScene.window
prefs:GetApplicationContext()->GetLocalState()];
}
- (void)initializeMailtoHandling {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kMailtoHandlingInitialization
block:^{
[self createMailtoHandlerServices];
}];
}
- (void)createMailtoHandlerServices {
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
MailtoHandlerServiceFactory::GetForBrowserState(browserState);
}
}
// Schedule a call to `scheduleSaveFieldTrialValuesForExternals` for deferred
// execution. Externals can be extensions or 1st party apps.
- (void)scheduleSaveFieldTrialValuesForExternals {
__weak __typeof(self) weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kSaveFieldTrialValues
block:^{
[weakSelf saveFieldTrialValuesForExtensions];
[weakSelf saveFieldTrialValuesForGroupApp];
}];
}
// Some experiments value may be useful for first-party applications, so save
// the value in the shared application group.
- (void)saveFieldTrialValuesForGroupApp {
NSUserDefaults* sharedDefaults = app_group::GetCommonGroupUserDefaults();
NSNumber* supportsShowDefaultBrowserPromo =
@(base::FeatureList::IsEnabled(kDefaultBrowserIntentsShowSettings));
NSDictionary* capabilities = @{
app_group::
kChromeShowDefaultBrowserPromoCapability : supportsShowDefaultBrowserPromo
};
[sharedDefaults setObject:capabilities
forKey:app_group::kChromeCapabilitiesPreference];
}
// Some extensions need the value of field trials but can't get them because the
// field trial infrastructure isn't in extensions. Save the necessary values to
// NSUserDefaults here.
- (void)saveFieldTrialValuesForExtensions {
NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
// Add other field trial values here if they are needed by extensions.
// The general format is
// {
// name: {
// value: NSNumber bool,
// version: NSNumber int,
// }
// }
NSDictionary* fieldTrialValues = @{
kWidgetKitRefreshFiveMinutes : @{
kFieldTrialValueKey : @([[NSUserDefaults standardUserDefaults]
boolForKey:kWidgetKitRefreshFiveMinutes]),
kFieldTrialVersionKey : @1,
},
};
[sharedDefaults setObject:fieldTrialValues
forKey:app_group::kChromeExtensionFieldTrialPreference];
}
// Schedules a call to `logIfEnterpriseManagedDevice` for deferred
// execution.
- (void)scheduleEnterpriseManagedDeviceCheck {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kEnterpriseManagedDeviceCheck
block:^{
[self logIfEnterpriseManagedDevice];
}];
}
- (void)logIfEnterpriseManagedDevice {
NSString* managedKey = @"com.apple.configuration.managed";
BOOL isManagedDevice = [[NSUserDefaults standardUserDefaults]
dictionaryForKey:managedKey] != nil;
base::UmaHistogramBoolean("EnterpriseCheck.IsManaged2", isManagedDevice);
}
- (void)startFreeMemoryMonitoring {
// No need for a post-task or a deferred initialisation as the memory
// monitoring already happens on a background sequence.
StartFreeMemoryMonitor();
}
- (void)scheduleLowPriorityStartupTasks {
[_startupTasks initializeOmaha];
// Deferred tasks.
[self scheduleMemoryDebuggingTools];
[StartupTasks
scheduleDeferredBrowserStateInitialization:self.appState.mainProfile
.browserState];
[self sendQueuedFeedback];
[self scheduleSpotlightResync];
[self scheduleDeleteTempDownloadsDirectory];
[self scheduleDeleteTempPasswordsDirectory];
[self scheduleLogSiriShortcuts];
[self scheduleStartupAttemptReset];
[self startFreeMemoryMonitoring];
[self scheduleAppDistributionPings];
[self initializeMailtoHandling];
[self scheduleSaveFieldTrialValuesForExternals];
[self scheduleEnterpriseManagedDeviceCheck];
[self scheduleFaviconsCleanup];
#if !TARGET_IPHONE_SIMULATOR
[self scheduleLogDocumentsSize];
#endif
#if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
[self scheduleDumpDocumentsStatistics];
#endif // BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
}
- (void)scheduleExternalFileClenup {
if (GetApplicationContext()->WasLastShutdownClean()) {
// Delay the cleanup of the unreferenced files to not impact startup
// performance.
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
ExternalFileRemoverFactory::GetForBrowserState(browserState)
->RemoveAfterDelay(base::Seconds(kExternalFilesCleanupDelaySeconds),
base::OnceClosure());
}
}
}
- (void)scheduleDeleteTempDownloadsDirectory {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kDeleteDownloads
block:^{
DeleteTempDownloadsDirectory();
}];
}
- (void)scheduleDeleteTempPasswordsDirectory {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kDeleteTempPasswords
block:^{
password_manager::DeletePasswordsDirectory();
}];
}
- (void)scheduleLogSiriShortcuts {
__weak StartupTasks* startupTasks = _startupTasks;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kLogSiriShortcuts
block:^{
[startupTasks logSiriShortcuts];
}];
}
- (void)scheduleSpotlightResync {
__weak MainController* weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kStartSpotlightBookmarksIndexing
block:^{
[weakSelf resyncIndex];
}];
}
- (void)resyncIndex {
for (SpotlightManager* manager : _spotlightManagers) {
[manager resyncIndex];
}
}
- (void)scheduleFaviconsCleanup {
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
__weak MainController* weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kFaviconsCleanup
block:^{
[weakSelf performFaviconsCleanup];
}];
#endif
}
#if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
- (void)scheduleDumpDocumentsStatistics {
if ([[NSUserDefaults standardUserDefaults]
boolForKey:@"EnableDumpSandboxFileStatistics"]) {
// Reset the pref to prevent dumping statistics on every launch.
[[NSUserDefaults standardUserDefaults]
setBool:NO
forKey:@"EnableDumpSandboxFileStatistics"];
documents_statistics::DumpSandboxFileStatistics();
}
}
#endif // BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
- (void)scheduleLogDocumentsSize {
if (!base::FeatureList::IsEnabled(kLogApplicationStorageSizeMetrics)) {
return;
}
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
PrefService* prefService = browserState->GetPrefs();
const base::Time lastLogged =
prefService->GetTime(prefs::kLastApplicationStorageMetricsLogTime);
if (lastLogged != base::Time() &&
base::Time::Now() - lastLogged <
kMinimumTimeBetweenDocumentsSizeLogging) {
continue;
}
// TODO(crbug.com/356657400): Consider doing this a bit later in startup, or
// only ifif metrics are enabled.
prefService->SetTime(prefs::kLastApplicationStorageMetricsLogTime,
base::Time::Now());
base::FilePath profilePath = browserState->GetStatePath();
base::FilePath offTheRecordStatePath =
browserState->GetOffTheRecordStatePath();
LogApplicationStorageMetrics(profilePath, offTheRecordStatePath);
}
}
- (void)expireFirstUserActionRecorder {
// Clear out any scheduled calls to this method. For example, the app may have
// been backgrounded before the `kFirstUserActionTimeout` expired.
[NSObject
cancelPreviousPerformRequestsWithTarget:self
selector:@selector(
expireFirstUserActionRecorder)
object:nil];
if (_firstUserActionRecorder) {
_firstUserActionRecorder->Expire();
_firstUserActionRecorder.reset();
}
}
- (void)crashIfRequested {
if (experimental_flags::IsStartupCrashEnabled()) {
// Flush out the value cached for crash_helper::SetEnabled().
[[NSUserDefaults standardUserDefaults] synchronize];
int* x = NULL;
*x = 0;
}
}
#pragma mark - Preferences Management
- (void)onPreferenceChanged:(const std::string&)preferenceName {
// Turn on or off metrics & crash reporting when either preference changes.
if (preferenceName == metrics::prefs::kMetricsReportingEnabled) {
[_metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:YES];
}
}
#pragma mark - Helper methods.
- (void)cleanupSessionStateCache {
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
SessionRestorationServiceFactory::GetForBrowserState(browserState)
->PurgeUnassociatedData(base::DoNothing());
}
}
- (void)cleanupSnapshots {
// TODO(crbug.com/40144759): Browsers for disconnected scenes are not in the
// BrowserList, so this may not reach all folders.
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
BrowserList* browserList =
BrowserListFactory::GetForBrowserState(browserState);
for (Browser* browser :
browserList->BrowsersOfType(BrowserList::BrowserType::kAll)) {
SnapshotBrowserAgent::FromBrowser(browser)->PerformStorageMaintenance();
}
}
}
- (void)cleanupDiscardedSessions {
const std::set<std::string> discardedSessionIDs =
sessions_storage_util::GetDiscardedSessions();
if (discardedSessionIDs.empty()) {
return;
}
const std::set<std::string> connectedSessionIDs = [self connectedSessionIDs];
std::set<std::string> identifiers;
std::set<std::string> postponedRemovals;
for (const std::string& sessionID : discardedSessionIDs) {
// TODO(crbug.com/350946190): it looks like it is possible for the OS to
// inform the application that a scene is discarded even though the scene
// is still connected. If this happens, postpone the removal until the
// next execution of the application.
if (connectedSessionIDs.contains(sessionID)) {
postponedRemovals.insert(sessionID);
} else {
// Need to remove storage for both regular and inactive Browser. Removing
// data does nothing if there are no data to delete, so there is no need
// to check whether inactive tabs are enabled here.
identifiers.insert(session_util::GetSessionIdentifier(sessionID, false));
identifiers.insert(session_util::GetSessionIdentifier(sessionID, true));
}
}
// If all sessions to discard are still mapped, postpone everything.
if (identifiers.empty()) {
return;
}
// Will execute the closure passed to `Done()` when all the callbacks have
// completed.
base::ConcurrentClosures concurrent;
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
SessionRestorationServiceFactory::GetForBrowserState(browserState)
->DeleteDataForDiscardedSessions(identifiers,
concurrent.CreateClosure());
if (browserState->HasOffTheRecordChromeBrowserState()) {
ChromeBrowserState* otrBrowserState =
browserState->GetOffTheRecordChromeBrowserState();
SessionRestorationServiceFactory::GetForBrowserState(otrBrowserState)
->DeleteDataForDiscardedSessions(identifiers,
concurrent.CreateClosure());
}
}
base::OnceClosure closure =
base::BindOnce(&sessions_storage_util::ResetDiscardedSessions);
if (!postponedRemovals.empty()) {
closure = std::move(closure).Then(
base::BindOnce(&sessions_storage_util::MarkSessionsForRemoval,
std::move(postponedRemovals)));
}
std::move(concurrent).Done(std::move(closure));
}
- (void)pingDistributionServices {
const base::Time installDate =
base::Time::FromTimeT(GetApplicationContext()->GetLocalState()->GetInt64(
metrics::prefs::kInstallDate));
auto URLLoaderFactory = GetApplicationContext()->GetSharedURLLoaderFactory();
const bool isFirstRun = FirstRun::IsChromeFirstRun();
ios::provider::ScheduleAppDistributionNotifications(URLLoaderFactory,
isFirstRun);
ios::provider::InitializeFirebase(installDate, isFirstRun);
}
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
- (void)performFaviconsCleanup {
for (ChromeBrowserState* browserState :
GetApplicationContext()->GetProfileManager()->GetLoadedProfiles()) {
syncer::SyncService* syncService =
SyncServiceFactory::GetForBrowserState(browserState);
// Only use the fallback to the Google server when fetching favicons for
// normal encryption users saving to the account, because they are the only
// users who consented to share data to Google.
BOOL fallbackToGoogleServer =
password_manager_util::IsSavingPasswordsToAccountWithNormalEncryption(
syncService);
if (fallbackToGoogleServer) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&UpdateFaviconsStorageForBrowserState,
browserState->AsWeakPtr(), fallbackToGoogleServer));
}
}
}
#endif
#pragma mark - BlockingSceneCommands
- (void)activateBlockingScene:(UIScene*)requestingScene {
id<UIBlockerTarget> uiBlocker = self.appState.currentUIBlocker;
if (!uiBlocker) {
return;
}
[uiBlocker bringBlockerToFront:requestingScene];
}
#pragma mark - Private
// Returns the set of Session identifiers for all connected scenes.
- (std::set<std::string>)connectedSessionIDs {
std::set<std::string> connectedSessionIDs;
for (SceneState* sceneState in self.appState.connectedScenes) {
connectedSessionIDs.insert(
base::SysNSStringToUTF8(sceneState.sceneSessionID));
}
return connectedSessionIDs;
}
@end