[go: nahoru, domu]

blob: 2de4f650273b41ac9bebe3ac754e2f8d61677720 [file] [log] [blame]
// 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/feature_list.h"
#import "base/functional/callback.h"
#import "base/ios/ios_util.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/safety_tips_component_installer.h"
#import "components/component_updater/url_param_filter_remover.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/base/features.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/blocking_scene_commands.h"
#import "ios/chrome/app/deferred_initialization_runner.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/memory_monitor.h"
#import "ios/chrome/app/post_restore_app_agent.h"
#import "ios/chrome/app/safe_mode_app_state_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/window_accessibility_change_notifier_app_agent.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state_removal_controller.h"
#import "ios/chrome/browser/browsing_data/browsing_data_remover.h"
#import "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h"
#import "ios/chrome/browser/browsing_data/sessions_storage_util.h"
#import "ios/chrome/browser/crash_report/crash_helper.h"
#import "ios/chrome/browser/crash_report/crash_keys_helper.h"
#import "ios/chrome/browser/crash_report/crash_loop_detection_util.h"
#import "ios/chrome/browser/crash_report/crash_report_helper.h"
#import "ios/chrome/browser/credential_provider/credential_provider_buildflags.h"
#import "ios/chrome/browser/default_browser/utils.h"
#import "ios/chrome/browser/download/download_directory_util.h"
#import "ios/chrome/browser/external_files/external_file_remover_factory.h"
#import "ios/chrome/browser/external_files/external_file_remover_impl.h"
#import "ios/chrome/browser/feature_engagement/tracker_factory.h"
#import "ios/chrome/browser/first_run/first_run.h"
#import "ios/chrome/browser/mailto_handler/mailto_handler_service.h"
#import "ios/chrome/browser/mailto_handler/mailto_handler_service_factory.h"
#import "ios/chrome/browser/memory/memory_debugger_manager.h"
#import "ios/chrome/browser/metrics/first_user_action_recorder.h"
#import "ios/chrome/browser/metrics/incognito_usage_app_state_agent.h"
#import "ios/chrome/browser/metrics/window_configuration_recorder.h"
#import "ios/chrome/browser/omaha/omaha_service.h"
#import "ios/chrome/browser/passwords/password_manager_util_ios.h"
#import "ios/chrome/browser/promos_manager/promos_manager_factory.h"
#import "ios/chrome/browser/screenshot/screenshot_metrics_recorder.h"
#import "ios/chrome/browser/search_engines/extension_search_engine_data_updater.h"
#import "ios/chrome/browser/search_engines/search_engines_util.h"
#import "ios/chrome/browser/search_engines/template_url_service_factory.h"
#import "ios/chrome/browser/sessions/session_service_ios.h"
#import "ios/chrome/browser/share_extension/share_extension_service.h"
#import "ios/chrome/browser/share_extension/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/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state_manager.h"
#import "ios/chrome/browser/shared/model/paths/paths.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/authentication_service_delegate.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/snapshots/snapshot_browser_agent.h"
#import "ios/chrome/browser/snapshots/snapshot_cache.h"
#import "ios/chrome/browser/sync/sync_service_factory.h"
#import "ios/chrome/browser/ui/appearance/appearance_customization.h"
#import "ios/chrome/browser/ui/first_run/first_run_util.h"
#import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
#import "ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h"
#import "ios/chrome/browser/url_loading/url_loading_params.h"
#import "ios/chrome/browser/web/certificate_policy_app_agent.h"
#import "ios/chrome/browser/web/features.h"
#import "ios/chrome/browser/web/session_state/web_session_state_cache.h"
#import "ios/chrome/browser/web/session_state/web_session_state_cache_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/ui_utils/ui_utils_api.h"
#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_api.h"
#import "ios/web/common/features.h"
#import "ios/web/public/webui/web_ui_ios_controller_factory.h"
#import "net/base/mac/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/credential_provider_service_factory.h"
#import "ios/chrome/browser/credential_provider/credential_provider_support.h"
#import "ios/chrome/browser/credential_provider/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)
// To get access to UseSessionSerializationOptimizations().
// TODO(crbug.com/1383087): remove once the feature is fully launched.
#import "ios/web/common/features.h"
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 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 in seconds) between calculating and
// logging metrics about the amount of device storage space used by Chrome.
const NSTimeInterval kMinimumTimeBetweenDocumentsSizeLogging =
60.0 * 60.0 * 24.0 * 14.0;
// 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);
}
// The delay, in seconds, for cleaning external files.
const int kExternalFilesCleanupDelaySeconds = 60;
// Delegate for the AuthenticationService.
class MainControllerAuthenticationServiceDelegate
: public AuthenticationServiceDelegate {
public:
MainControllerAuthenticationServiceDelegate(
ChromeBrowserState* browser_state,
id<BrowsingDataCommands> dispatcher);
MainControllerAuthenticationServiceDelegate(
const MainControllerAuthenticationServiceDelegate&) = delete;
MainControllerAuthenticationServiceDelegate& operator=(
const MainControllerAuthenticationServiceDelegate&) = delete;
~MainControllerAuthenticationServiceDelegate() override;
// AuthenticationServiceDelegate implementation.
void ClearBrowsingData(ProceduralBlock completion) override;
private:
ChromeBrowserState* browser_state_ = nullptr;
__weak id<BrowsingDataCommands> dispatcher_ = nil;
};
MainControllerAuthenticationServiceDelegate::
MainControllerAuthenticationServiceDelegate(
ChromeBrowserState* browser_state,
id<BrowsingDataCommands> dispatcher)
: browser_state_(browser_state), dispatcher_(dispatcher) {}
MainControllerAuthenticationServiceDelegate::
~MainControllerAuthenticationServiceDelegate() = default;
void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
ProceduralBlock completion) {
[dispatcher_
removeBrowsingDataForBrowserState:browser_state_
timePeriod:browsing_data::TimePeriod::ALL_TIME
removeMask:BrowsingDataRemoveMask::REMOVE_ALL
completionBlock: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;
// Updates data about the current default search engine to be accessed in
// extensions.
std::unique_ptr<ExtensionSearchEngineDataUpdater>
_extensionSearchEngineDataUpdater;
// 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.
SpotlightManager* _spotlightManager;
// Variable backing metricsMediator property.
__weak MetricsMediator* _metricsMediator;
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 snapshots on disk.
- (void)cleanupSnapshots;
// Cleanup discarded sessions on disk.
- (void)cleanupDiscardedSessions;
// 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;
// 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 tasks that require a fully-functional BVC to be performed.
- (void)scheduleTasksRequiringBVCWithBrowserState;
// 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.
- (void)startUpBrowserBackgroundInitialization;
// Initializes the browser objects for the browser UI (e.g., the browser
// state).
- (void)startUpBrowserForegroundInitialization;
@end
@implementation MainController
// Defined by public protocols.
// - StartupInformation
@synthesize isColdStart = _isColdStart;
@synthesize appLaunchTime = _appLaunchTime;
@synthesize isFirstRun = _isFirstRun;
@synthesize didFinishLaunchingTime = _didFinishLaunchingTime;
@synthesize firstSceneConnectionTime = _firstSceneConnectionTime;
#pragma mark - Application lifecycle
- (instancetype)init {
if ((self = [super init])) {
_isFirstRun = ShouldPresentFirstRunExperience();
_startupTasks = [[StartupTasks alloc] init];
}
return self;
}
- (void)dealloc {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)startUpBrowserBasicInitialization {
_appLaunchTime = IOSChromeMain::StartTime();
_isColdStart = YES;
if (@available(iOS 15, *)) {
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)];
[self.appState.appCommandDispatcher
startDispatchingToTarget:self
forProtocol:@protocol(BrowsingDataCommands)];
}
- (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];
// Remove the extra browser states as Chrome iOS is single profile in M48+.
ChromeBrowserStateRemovalController::GetInstance()
->RemoveBrowserStatesIfNecessary();
ChromeBrowserState* chromeBrowserState = GetApplicationContext()
->GetChromeBrowserStateManager()
->GetLastUsedBrowserState();
// Initialize and set the main browser state.
[self initializeBrowserState:chromeBrowserState];
self.appState.mainBrowserState = chromeBrowserState;
// Give tests a chance to prepare for testing.
tests_hook::SetUpTestsIfPresent();
// 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(
self.appState.mainBrowserState,
std::make_unique<MainControllerAuthenticationServiceDelegate>(
self.appState.mainBrowserState, self));
// 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/1232027): 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]];
ChromeBrowserState* browserState = self.appState.mainBrowserState;
DCHECK(browserState);
[self.appState
addAgent:
[[PostRestoreAppAgent alloc]
initWithPromosManager:PromosManagerFactory::GetForBrowserState(
browserState)
authenticationService:AuthenticationServiceFactory::
GetForBrowserState(browserState)
localState:GetApplicationContext()->GetLocalState()]];
}
// 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();
#if !defined(NDEBUG)
// Legacy code used GetLastUsedBrowserState() in this method. We changed it to
// use self.appState.mainBrowserState instead. The DCHECK ensures that
// invariant holds true.
ChromeBrowserState* chromeBrowserState = GetApplicationContext()
->GetChromeBrowserStateManager()
->GetLastUsedBrowserState();
DCHECK_EQ(chromeBrowserState, self.appState.mainBrowserState);
#endif // !defined(NDEBUG)
if (!base::ios::IsMultipleScenesSupported()) {
NSSet<NSString*>* previousSessions =
[PreviousSessionInfo sharedInstance].connectedSceneSessionsIDs;
DCHECK(previousSessions.count <= 1);
self.appState.previousSingleWindowSessionID = [previousSessions anyObject];
}
[[PreviousSessionInfo sharedInstance] resetConnectedSceneSessionIDs];
feature_engagement::Tracker* tracker =
feature_engagement::TrackerFactory::GetForBrowserState(
self.appState.mainBrowserState);
// Send "Chrome Opened" event to the feature_engagement::Tracker on cold
// start.
tracker->NotifyEvent(feature_engagement::events::kChromeOpened);
// Send "default_browser_video_promo_conditions_met" event to the
// feature_engagement::Tracker on cold start.
if (HasAppLaunchedOnColdStartAndRecordsLaunch()) {
tracker->NotifyEvent(
feature_engagement::events::kDefaultBrowserVideoPromoConditionsMet);
}
_spotlightManager = [SpotlightManager
spotlightManagerWithBrowserState:self.appState.mainBrowserState];
ShareExtensionService* service =
ShareExtensionServiceFactory::GetForBrowserState(
self.appState.mainBrowserState);
service->Initialize();
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
if (IsCredentialProviderExtensionSupported()) {
CredentialProviderServiceFactory::GetForBrowserState(
self.appState.mainBrowserState);
}
#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 scheduleTasksRequiringBVCWithBrowserState];
CustomizeUIAppearance();
[self scheduleStartupCleanupTasks];
[MetricsMediator
logLaunchMetricsWithStartupInformation:self
connectedScenes:self.appState.connectedScenes];
ios::provider::InstallOverrides();
[self scheduleLowPriorityStartupTasks];
// 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/1232027): 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());
search_engines::UpdateSearchEnginesIfNeeded(
browserState->GetPrefs(),
ios::TemplateURLServiceFactory::GetForBrowserState(browserState));
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
[sceneState addObserver:self];
}
// 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/1213955): 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/1213955): 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];
[appState queueTransitionToNextInitStage];
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 InitStageFinal:
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:[[CredentialProviderAppAgent 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:[[FeedAppAgent alloc] init]];
[appState addAgent:[[VariationsAppStateAgent alloc] init]];
// Create the window accessibility agent only when multiple windows are
// possible.
if (base::ios::IsMultipleScenesSupported()) {
[appState addAgent:[[WindowAccessibilityChangeNotifierAppAgent alloc] init]];
}
}
- (id<BrowserProviderInterface>)browserProviderInterface {
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 {
[_spotlightManager shutdown];
_spotlightManager = nil;
_extensionSearchEngineDataUpdater = nullptr;
// _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)) {
metrics::MetricsService* metrics =
GetApplicationContext()->GetMetricsService();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
static std::atomic<uint32_t> counter{metrics ? 2u : 1u};
ProceduralBlock completionBlock = ^{
if (!--counter) {
dispatch_semaphore_signal(semaphore);
}
};
if (!web::features::UseSessionSerializationOptimizations()) {
[[SessionServiceIOS sharedService]
shutdownWithCompletion:completionBlock];
} else {
completionBlock();
}
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(
{}, base::BindOnce(completionBlock));
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.
TemplateURLService* service =
ios::TemplateURLServiceFactory::GetForBrowserState(
self.appState.mainBrowserState);
_extensionSearchEngineDataUpdater =
std::make_unique<ExtensionSearchEngineDataUpdater>(service);
}
- (void)scheduleAppDistributionPings {
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kSendInstallPingIfNecessary
block:^{
auto URLLoaderFactory = self.appState.mainBrowserState
->GetSharedURLLoaderFactory();
const bool is_first_run = FirstRun::IsChromeFirstRun();
ios::provider::ScheduleAppDistributionNotifications(
URLLoaderFactory, is_first_run);
const base::Time install_date = base::Time::FromTimeT(
GetApplicationContext()->GetLocalState()->GetInt64(
metrics::prefs::kInstallDate));
ios::provider::InitializeFirebase(install_date,
is_first_run);
}];
}
- (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:^{
if (web::UseNativeSessionRestorationCache()) {
WebSessionStateCache* cache =
WebSessionStateCacheFactory::GetForBrowserState(
self.appState.mainBrowserState);
[cache purgeUnassociatedData];
}
}];
}
- (void)scheduleStartupCleanupTasks {
// Schedule the prefs observer init first to ensure kMetricsReportingEnabled
// is synced before starting uploads.
[self schedulePrefObserverInitialization];
[self scheduleCrashReportUpload];
// ClearSessionCookies() is not synchronous.
if (cookie_util::ShouldClearSessionCookies()) {
cookie_util::ClearSessionCookies(
self.appState.mainBrowserState->GetOriginalChromeBrowserState());
Browser* otrBrowser =
self.browserProviderInterface.incognitoBrowserProvider.browser;
if (otrBrowser && !(otrBrowser->GetWebStateList()->empty())) {
cookie_util::ClearSessionCookies(
self.appState.mainBrowserState->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 {
__weak __typeof(self) weakSelf = self;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kMailtoHandlingInitialization
block:^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf || !strongSelf.appState.mainBrowserState) {
return;
}
// Force the creation of the MailtoHandlerService.
MailtoHandlerServiceFactory::GetForBrowserState(
strongSelf.appState.mainBrowserState);
}];
}
// 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();
NSNumber* credentialProviderExtensionPasswordNotesValue =
[NSNumber numberWithBool:base::FeatureList::IsEnabled(
syncer::kPasswordNotesWithBackup)];
NSNumber* credentialProviderExtensionPasswordNotesVersion =
[NSNumber numberWithInt:kCredentialProviderExtensionPasswordNotesVersion];
// 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 = @{
base::SysUTF8ToNSString(syncer::kPasswordNotesWithBackup.name) : @{
kFieldTrialValueKey : credentialProviderExtensionPasswordNotesValue,
kFieldTrialVersionKey : credentialProviderExtensionPasswordNotesVersion,
},
};
[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
.mainBrowserState];
[self sendQueuedFeedback];
[self scheduleSpotlightResync];
[self scheduleDeleteTempDownloadsDirectory];
[self scheduleDeleteTempPasswordsDirectory];
[self scheduleLogSiriShortcuts];
[self scheduleStartupAttemptReset];
[self startFreeMemoryMonitoring];
[self scheduleAppDistributionPings];
[self initializeMailtoHandling];
[self scheduleSaveFieldTrialValuesForExternals];
[self scheduleEnterpriseManagedDeviceCheck];
[self scheduleFaviconsCleanup];
[self scheduleLogDocumentsSize];
#if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
[self scheduleDumpDocumentsStatistics];
#endif // BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
}
- (void)scheduleTasksRequiringBVCWithBrowserState {
if (GetApplicationContext()->WasLastShutdownClean()) {
// Delay the cleanup of the unreferenced files to not impact startup
// performance.
ExternalFileRemoverFactory::GetForBrowserState(
self.appState.mainBrowserState)
->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 SpotlightManager* spotlightManager = _spotlightManager;
[[DeferredInitializationRunner sharedInstance]
enqueueBlockNamed:kStartSpotlightBookmarksIndexing
block:^{
[spotlightManager 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;
}
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSDate* lastLogged = base::apple::ObjCCast<NSDate>(
[defaults objectForKey:kLastApplicationStorageMetricsLogTime]);
if (lastLogged && [[NSDate date] timeIntervalSinceDate:lastLogged] <
kMinimumTimeBetweenDocumentsSizeLogging) {
return;
}
base::FilePath profilePath = self.appState.mainBrowserState->GetStatePath();
LogApplicationStorageMetrics(profilePath);
}
- (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 backed by interfaces.
- (ChromeBrowserState*)currentBrowserState {
if (!self.browserProviderInterface.currentBrowserProvider.browser) {
return nullptr;
}
return self.browserProviderInterface.currentBrowserProvider.browser
->GetBrowserState();
}
- (void)cleanupSnapshots {
// TODO(crbug.com/1116496): Browsers for disconnected scenes are not in the
// BrowserList, so this may not reach all folders.
BrowserList* browser_list =
BrowserListFactory::GetForBrowserState(self.appState.mainBrowserState);
for (Browser* browser : browser_list->AllRegularBrowsers()) {
SnapshotBrowserAgent::FromBrowser(browser)->PerformStorageMaintenance();
}
for (Browser* browser : browser_list->AllIncognitoBrowsers()) {
SnapshotBrowserAgent::FromBrowser(browser)->PerformStorageMaintenance();
}
}
- (void)cleanupDiscardedSessions {
NSArray<NSString*>* sessionIDs =
sessions_storage_util::GetDiscardedSessions();
if (!sessionIDs)
return;
BrowsingDataRemoverFactory::GetForBrowserState(
self.appState.mainBrowserState->GetOriginalChromeBrowserState())
->RemoveSessionsData(sessionIDs);
BrowsingDataRemoverFactory::GetForBrowserState(
self.appState.mainBrowserState->GetOffTheRecordChromeBrowserState())
->RemoveSessionsData(sessionIDs);
sessions_storage_util::ResetDiscardedSessions();
}
#pragma mark - BrowsingDataCommands
- (void)removeBrowsingDataForBrowserState:(ChromeBrowserState*)browserState
timePeriod:(browsing_data::TimePeriod)timePeriod
removeMask:(BrowsingDataRemoveMask)removeMask
completionBlock:(ProceduralBlock)completionBlock {
BOOL willShowActivityIndicator =
!browserState->IsOffTheRecord() &&
IsRemoveDataMaskSet(removeMask, BrowsingDataRemoveMask::REMOVE_SITE_DATA);
BOOL didShowActivityIndicator = NO;
for (SceneState* sceneState in self.appState.connectedScenes) {
// Assumes all scenes share `browserState`.
id<BrowserProviderInterface> browserProviderInterface =
sceneState.browserProviderInterface;
if (willShowActivityIndicator) {
// Show activity overlay so users know that clear browsing data is in
// progress.
if (browserProviderInterface.mainBrowserProvider.browser) {
didShowActivityIndicator = YES;
id<BrowserCoordinatorCommands> handler =
HandlerForProtocol(browserProviderInterface.mainBrowserProvider
.browser->GetCommandDispatcher(),
BrowserCoordinatorCommands);
[handler showActivityOverlay];
}
}
}
auto removalCompletion = ^{
// Activates browsing and enables web views.
// Must be called only on the main thread.
DCHECK([NSThread isMainThread]);
for (SceneState* sceneState in self.appState.connectedScenes) {
// Assumes all scenes share `browserState`.
id<BrowserProviderInterface> browserProviderInterface =
sceneState.browserProviderInterface;
if (willShowActivityIndicator) {
// User interaction still needs to be disabled as a way to
// force reload all the web states and to reset NTPs.
browserProviderInterface.mainBrowserProvider.userInteractionEnabled =
NO;
browserProviderInterface.incognitoBrowserProvider
.userInteractionEnabled = NO;
if (didShowActivityIndicator &&
browserProviderInterface.mainBrowserProvider.browser) {
id<BrowserCoordinatorCommands> handler =
HandlerForProtocol(browserProviderInterface.mainBrowserProvider
.browser->GetCommandDispatcher(),
BrowserCoordinatorCommands);
[handler hideActivityOverlay];
}
}
browserProviderInterface.mainBrowserProvider.userInteractionEnabled = YES;
browserProviderInterface.incognitoBrowserProvider.userInteractionEnabled =
YES;
[browserProviderInterface.currentBrowserProvider setPrimary:YES];
}
// `completionBlock` is run once, not once per scene.
if (completionBlock)
completionBlock();
};
BrowsingDataRemoverFactory::GetForBrowserState(browserState)
->Remove(timePeriod, removeMask, base::BindOnce(removalCompletion));
}
#if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
- (void)performFaviconsCleanup {
ChromeBrowserState* browserState = self.currentBrowserState;
if (!browserState)
return;
syncer::SyncService* syncService =
SyncServiceFactory::GetForBrowserState(browserState);
// Only use the fallback to the Google server when fetching favicons for
// normal encryption synced users because they are the only users who
// consented to share data to Google. The other types of synced users did not.
BOOL isPasswordSyncEnabled =
password_manager_util::IsPasswordSyncNormalEncryptionEnabled(syncService);
if (isPasswordSyncEnabled) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&UpdateFaviconsStorageForBrowserState,
browserState->AsWeakPtr(),
/*sync_enabled=*/isPasswordSyncEnabled));
}
}
#endif
#pragma mark - BlockingSceneCommands
- (void)activateBlockingScene:(UIScene*)requestingScene {
id<UIBlockerTarget> uiBlocker = self.appState.currentUIBlocker;
if (!uiBlocker) {
return;
}
[uiBlocker bringBlockerToFront:requestingScene];
}
@end