Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(536)

Unified Diff: ios/chrome/app/application_delegate/app_state.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/app/application_delegate/app_state.mm
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
new file mode 100644
index 0000000000000000000000000000000000000000..593da2515eca72d7e6ac80e65c1aa12d5da70114
--- /dev/null
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -0,0 +1,499 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/app/application_delegate/app_state.h"
+
+#include "base/critical_closure.h"
+#import "base/mac/bind_objc_block.h"
+#import "base/mac/scoped_nsobject.h"
+#include "components/metrics/metrics_service.h"
+#import "ios/chrome/app/application_delegate/app_navigation.h"
+#import "ios/chrome/app/application_delegate/browser_launcher.h"
+#import "ios/chrome/app/application_delegate/memory_warning_helper.h"
+#import "ios/chrome/app/application_delegate/metrics_mediator.h"
+#import "ios/chrome/app/application_delegate/startup_information.h"
+#import "ios/chrome/app/application_delegate/tab_opening.h"
+#import "ios/chrome/app/application_delegate/tab_switching.h"
+#import "ios/chrome/app/application_delegate/user_activity_handler.h"
+#import "ios/chrome/app/deferred_initialization_runner.h"
+#import "ios/chrome/app/safe_mode/safe_mode_coordinator.h"
+#import "ios/chrome/app/safe_mode_crashing_modules_config.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_constants.h"
+#include "ios/chrome/browser/crash_loop_detection_util.h"
+#include "ios/chrome/browser/crash_report/breakpad_helper.h"
+#import "ios/chrome/browser/crash_report/crash_report_background_uploader.h"
+#import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
+#import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
+#import "ios/chrome/browser/metrics/previous_session_info.h"
+#import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.h"
+#include "ios/chrome/browser/ui/background_generator.h"
+#import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/ui/main/browser_view_information.h"
+#include "ios/net/cookies/cookie_store_ios.h"
+#include "ios/net/cookies/system_cookie_util.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
+#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
+#include "ios/web/net/request_tracker_impl.h"
+#include "net/url_request/url_request_context.h"
+
+namespace {
+// Helper method to post |closure| on the UI thread.
+void PostTaskOnUIThread(const base::Closure& closure) {
+ web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, closure);
+}
+NSString* const kStartupAttemptReset = @"StartupAttempReset";
+} // namespace
+
+@interface AppState ()<SafeModeCoordinatorDelegate> {
+ // Container for startup information.
+ base::WeakNSProtocol<id<StartupInformation>> _startupInformation;
+ // Browser launcher to launch browser in different states.
+ base::WeakNSProtocol<id<BrowserLauncher>> _browserLauncher;
+ // UIApplicationDelegate for the application.
+ base::WeakNSObject<MainApplicationDelegate> _mainApplicationDelegate;
+ // Window for the application.
+ base::WeakNSObject<UIWindow> _window;
+
+ // Variables backing properties of same name.
+ base::scoped_nsobject<SafeModeCoordinator> _safeModeCoordinator;
+
+ // Start of the current session, used for UMA.
+ base::TimeTicks _sessionStartTime;
+ // YES if the app is currently in the process of terminating.
+ BOOL _appIsTerminating;
+ // Indicates if an NTP tab should be opened once the application has become
+ // active.
+ BOOL _shouldOpenNTPTabOnActive;
+ // Interstitial view used to block any incognito tabs after backgrounding.
+ base::scoped_nsobject<UIView> _incognitoBlocker;
+ // Whether the application is currently in the background.
+ // This is a workaround for rdar://22392526 where
+ // -applicationDidEnterBackground: can be called twice.
+ // TODO(crbug.com/546196): Remove this once rdar://22392526 is fixed.
+ BOOL _applicationInBackground;
+ // YES if cookies are currently being flushed to disk.
+ BOOL _savingCookies;
+}
+
+// Safe mode coordinator. If this is non-nil, the app is displaying the safe
+// mode UI.
+@property(nonatomic, retain) SafeModeCoordinator* safeModeCoordinator;
+
+// Return value for -requiresHandlingAfterLaunchWithOptions that determines if
+// UIKit should make followup delegate calls such as
+// -performActionForShortcutItem or -openURL.
+@property(nonatomic, assign) BOOL shouldPerformAdditionalDelegateHandling;
+
+// This method is the first to be called when user launches the application.
+// Depending on the background tasks history, the state of the application is
+// either INITIALIZATION_STAGE_BASIC or INITIALIZATION_STAGE_BACKGROUND so this
+// step cannot be included in the |startUpBrowserToStage:| method.
+- (void)initializeUI;
+// Saves the current launch details to user defaults.
+- (void)saveLaunchDetailsToDefaults;
+
+@end
+
+@implementation AppState
+
+@synthesize shouldPerformAdditionalDelegateHandling =
+ _shouldPerformAdditionalDelegateHandling;
+@synthesize userInteracted = _userInteracted;
+
+- (instancetype)init {
+ NOTREACHED();
+ return nil;
+}
+
+- (instancetype)
+initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
+ startupInformation:(id<StartupInformation>)startupInformation
+ applicationDelegate:(MainApplicationDelegate*)applicationDelegate {
+ self = [super init];
+ if (self) {
+ _startupInformation.reset(startupInformation);
+ _browserLauncher.reset(browserLauncher);
+ _mainApplicationDelegate.reset(applicationDelegate);
+ }
+ return self;
+}
+
+#pragma mark - Properties implementation
+
+- (SafeModeCoordinator*)safeModeCoordinator {
+ return _safeModeCoordinator;
+}
+
+- (void)setSafeModeCoordinator:(SafeModeCoordinator*)safeModeCoordinator {
+ _safeModeCoordinator.reset([safeModeCoordinator retain]);
+}
+
+- (void)setWindow:(UIWindow*)window {
+ _window.reset(window);
+}
+
+- (UIWindow*)window {
+ return _window;
+}
+
+#pragma mark - Public methods.
+
+- (void)applicationDidEnterBackground:(UIApplication*)application
+ memoryHelper:(MemoryWarningHelper*)memoryHelper
+ tabSwitcherIsActive:(BOOL)tabSwitcherIsActive {
+ if ([self isInSafeMode]) {
+ // Force a crash when backgrounding and in safe mode, so users don't get
+ // stuck in safe mode.
+ breakpad_helper::SetEnabled(false);
+ exit(0);
+ return;
+ }
+
+ if (_applicationInBackground) {
+ return;
+ }
+ _applicationInBackground = YES;
+
+ breakpad_helper::SetCurrentlyInBackground(true);
+
+ if ([_browserLauncher browserInitializationStage] <
+ INITIALIZATION_STAGE_FOREGROUND) {
+ // The clean-up done in |-applicationDidEnterBackground:| is only valid for
+ // the case when the application is started in foreground, so there is
+ // nothing to clean up as the application was not initialized for foregound.
+ //
+ // From the stack trace of the crash bug http://crbug.com/437307 , it
+ // seems that |-applicationDidEnterBackground:| may be called when the app
+ // is started in background and before the initialization for background
+ // stage is done. Note that the crash bug could not be reproduced though.
+ return;
+ }
+
+ [MetricsMediator
+ applicationDidEnterBackground:[memoryHelper
+ foregroundMemoryWarningCount]];
+
+ [_startupInformation expireFirstUserActionRecorder];
+
+ // If the current BVC is incognito, or if we are in the tab switcher and there
+ // are incognito tabs visible, place a full screen view containing the
+ // switcher background to hide any incognito content.
+ if (([[_browserLauncher browserViewInformation] currentBrowserState] &&
+ [[_browserLauncher browserViewInformation] currentBrowserState]
+ ->IsOffTheRecord()) ||
+ (tabSwitcherIsActive &&
+ ![[[_browserLauncher browserViewInformation] otrTabModel] isEmpty])) {
+ // Cover the largest area potentially shown in the app switcher, in case the
+ // screenshot is reused in a different orientation or size class.
+ CGRect screenBounds = [[UIScreen mainScreen] bounds];
+ CGFloat maxDimension =
+ std::max(CGRectGetWidth(screenBounds), CGRectGetHeight(screenBounds));
+ _incognitoBlocker.reset([[UIView alloc]
+ initWithFrame:CGRectMake(0, 0, maxDimension, maxDimension)]);
+ InstallBackgroundInView(_incognitoBlocker);
+ [_window addSubview:_incognitoBlocker];
+ }
+
+ // Do not save cookies if it is already in progress.
+ if ([[_browserLauncher browserViewInformation] currentBVC].browserState &&
+ !_savingCookies) {
+ // Save cookies to disk. The empty critical closure guarantees that the task
+ // will be run before backgrounding.
+ scoped_refptr<net::URLRequestContextGetter> getter =
+ [[_browserLauncher browserViewInformation] currentBVC]
+ .browserState->GetRequestContext();
+ _savingCookies = YES;
+ base::Closure criticalClosure = base::MakeCriticalClosure(base::BindBlock(^{
+ DCHECK_CURRENTLY_ON(web::WebThread::UI);
+ _savingCookies = NO;
+ }));
+ web::WebThread::PostTask(
+ web::WebThread::IO, FROM_HERE, base::BindBlock(^{
+ net::CookieStoreIOS* store = static_cast<net::CookieStoreIOS*>(
+ getter->GetURLRequestContext()->cookie_store());
+ // FlushStore() runs its callback on any thread. Jump back to UI.
+ store->FlushStore(base::Bind(&PostTaskOnUIThread, criticalClosure));
+ }));
+ }
+
+ // Mark the startup as clean if it hasn't already been.
+ [[DeferredInitializationRunner sharedInstance]
+ runBlockIfNecessary:kStartupAttemptReset];
+ // Set date/time that the background fetch handler was called in the user
+ // defaults.
+ [MetricsMediator logDateInUserDefaults];
+ // Clear the memory warning flag since the app is now safely in background.
+ [[PreviousSessionInfo sharedInstance] resetMemoryWarningFlag];
+
+ // Turn off uploading of crash reports and metrics, in case the method of
+ // communication changes while in the background.
+ [MetricsMediator disableReporting];
+
+ GetApplicationContext()->OnAppEnterBackground();
+ if (![[CrashReportBackgroundUploader sharedInstance]
+ hasPendingCrashReportsToUploadAtStartup]) {
+ [application setMinimumBackgroundFetchInterval:
+ UIApplicationBackgroundFetchIntervalNever];
+ }
+}
+
+- (void)applicationWillEnterForeground:(UIApplication*)application
+ metricsMediator:(MetricsMediator*)metricsMediator
+ memoryHelper:(MemoryWarningHelper*)memoryHelper
+ tabOpener:(id<TabOpening>)tabOpener
+ appNavigation:(id<AppNavigation>)appNavigation {
+ if ([_browserLauncher browserInitializationStage] <
+ INITIALIZATION_STAGE_FOREGROUND) {
+ // The application has been launched in background and the initialization
+ // is not complete.
+ [self initializeUI];
+ return;
+ }
+ if ([self isInSafeMode])
+ return;
+
+ _applicationInBackground = NO;
+
+ [_incognitoBlocker removeFromSuperview];
+ _incognitoBlocker.reset();
+
+ breakpad_helper::SetCurrentlyInBackground(false);
+
+ // Update the state of metrics and crash reporting, as the method of
+ // communication may have changed while the app was in the background.
+ [metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:NO];
+
+ // Send any feedback that might be still on temporary storage.
+ ios::GetChromeBrowserProvider()->GetUserFeedbackProvider()->Synchronize();
+
+ GetApplicationContext()->OnAppEnterForeground();
+
+ [MetricsMediator
+ logLaunchMetricsWithStartupInformation:_startupInformation
+ browserViewInformation:[_browserLauncher
+ browserViewInformation]];
+ [memoryHelper resetForegroundMemoryWarningCount];
+
+ // Check if a NTP tab should be opened; the tab will actually be opened in
+ // |applicationDidBecomeActive| after the application gets prepared to
+ // record user actions.
+ // TODO(crbug.com/623491): opening a tab when the application is launched
+ // without a tab should not be counted as a user action. Revisit the way tab
+ // creation is counted.
+ _shouldOpenNTPTabOnActive = [tabOpener
+ shouldOpenNTPTabOnActivationOfTabModel:[[_browserLauncher
+ browserViewInformation]
+ currentTabModel]];
+
+ ios::ChromeBrowserState* currentBrowserState =
+ [[_browserLauncher browserViewInformation] currentBrowserState];
+ if ([SignedInAccountsViewController
+ shouldBePresentedForBrowserState:currentBrowserState]) {
+ [appNavigation presentSignedInAccountsViewControllerForBrowserState:
+ currentBrowserState];
+ }
+
+ // If the current browser state is not OTR, check for cookie loss.
+ if (currentBrowserState && !currentBrowserState->IsOffTheRecord() &&
+ currentBrowserState->GetOriginalChromeBrowserState()
+ ->GetStatePath()
+ .BaseName()
+ .value() == kIOSChromeInitialBrowserState) {
+ NSUInteger cookie_count =
+ [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count];
+ UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieCountOnForegrounding",
+ cookie_count);
+ net::CheckForCookieLoss(cookie_count,
+ net::COOKIES_APPLICATION_FOREGROUNDED);
+ }
+}
+
+- (void)resumeSessionWithTabOpener:(id<TabOpening>)tabOpener
+ tabSwitcher:(id<TabSwitching>)tabSwitcher {
+ [_incognitoBlocker removeFromSuperview];
+ _incognitoBlocker.reset();
+
+ DCHECK([_browserLauncher browserInitializationStage] ==
+ INITIALIZATION_STAGE_FOREGROUND);
+ _sessionStartTime = base::TimeTicks::Now();
+ [[[_browserLauncher browserViewInformation] mainTabModel]
+ resetSessionMetrics];
+
+ if ([_startupInformation startupParameters]) {
+ [UserActivityHandler
+ handleStartupParametersWithTabOpener:tabOpener
+ startupInformation:_startupInformation
+ browserViewInformation:[_browserLauncher
+ browserViewInformation]];
+ } else if (_shouldOpenNTPTabOnActive) {
+ if (![tabSwitcher openNewTabFromTabSwitcher]) {
+ [[[_browserLauncher browserViewInformation] currentBVC] newTab:nil];
+ }
+ _shouldOpenNTPTabOnActive = NO;
+ }
+
+ [MetricsMediator logStartupDuration:_startupInformation];
+}
+
+- (void)applicationWillTerminate:(UIApplication*)application
+ applicationNavigation:(id<AppNavigation>)appNavigation {
+ if (_appIsTerminating) {
+ // Previous handling of this method spun the runloop, resulting in
+ // recursive calls; this does not appear to happen with the new shutdown
+ // flow, but this is here to ensure that if it can happen, it gets noticed
+ // and fixed.
+ CHECK(false);
+ }
+ _appIsTerminating = YES;
+
+ // Dismiss any UI that is presented on screen and that is listening for
+ // profile notifications.
+ if ([appNavigation settingsNavigationController])
+ [appNavigation closeSettingsAnimated:NO completion:nil];
+
+ // Clean up the device sharing manager before the main browser state is shut
+ // down.
+ if ([_browserLauncher browserInitializationStage] >=
+ INITIALIZATION_STAGE_FOREGROUND) {
+ [[_browserLauncher browserViewInformation] cleanDeviceSharingManager];
+ }
+
+ // Cancel any in-flight distribution notifications.
+ ios::GetChromeBrowserProvider()
+ ->GetAppDistributionProvider()
+ ->CancelDistributionNotifications();
+
+ // Halt the tabs, so any outstanding requests get cleaned up, without actually
+ // closing the tabs.
+ if ([_browserLauncher browserInitializationStage] >=
+ INITIALIZATION_STAGE_FOREGROUND) {
+ [[_browserLauncher browserViewInformation] haltAllTabs];
+ }
+
+ // TODO(crbug.com/585700): remove this.
+ web::RequestTrackerImpl::BlockUntilTrackersShutdown();
+
+ [_startupInformation stopChromeMain];
+
+ if (![[CrashReportBackgroundUploader sharedInstance]
+ hasPendingCrashReportsToUploadAtStartup]) {
+ [application setMinimumBackgroundFetchInterval:
+ UIApplicationBackgroundFetchIntervalNever];
+ }
+}
+
+- (void)willResignActiveTabModel {
+ if ([_browserLauncher browserInitializationStage] <
+ INITIALIZATION_STAGE_FOREGROUND) {
+ // If the application did not pass the foreground initialization stage,
+ // there is no active tab model to resign.
+ return;
+ }
+
+ // Set [_startupInformation isColdStart] to NO in anticipation of the next
+ // time the app becomes active.
+ [_startupInformation setIsColdStart:NO];
+
+ base::TimeDelta duration = base::TimeTicks::Now() - _sessionStartTime;
+ UMA_HISTOGRAM_LONG_TIMES("Session.TotalDuration", duration);
+ [[[_browserLauncher browserViewInformation] mainTabModel]
+ recordSessionMetrics];
+}
+
+- (BOOL)requiresHandlingAfterLaunchWithOptions:(NSDictionary*)launchOptions
+ stateBackground:(BOOL)stateBackground {
+ [_browserLauncher setLaunchOptions:launchOptions];
+ self.shouldPerformAdditionalDelegateHandling = YES;
+
+ [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_BASIC];
+ if (!stateBackground) {
+ [self initializeUI];
+ }
+
+ return self.shouldPerformAdditionalDelegateHandling;
+}
+
+- (BOOL)isInSafeMode {
+ return self.safeModeCoordinator != nil;
+}
+
+- (void)launchFromURLHandled:(BOOL)URLHandled {
+ self.shouldPerformAdditionalDelegateHandling = !URLHandled;
+}
+
+#pragma mark - SafeModeCoordinatorDelegate Implementation
+
+- (void)coordinatorDidExitSafeMode:(nonnull SafeModeCoordinator*)coordinator {
+ self.safeModeCoordinator = nil;
+ [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+ [_mainApplicationDelegate
+ applicationDidBecomeActive:[UIApplication sharedApplication]];
+}
+
+#pragma mark - Internal methods.
+
+- (void)initializeUI {
+ _userInteracted = YES;
+ [self saveLaunchDetailsToDefaults];
+
+ DCHECK([_window rootViewController] == nil);
+ if ([SafeModeCoordinator shouldStart]) {
+ SafeModeCoordinator* safeModeCoordinator =
+ [[[SafeModeCoordinator alloc] initWithWindow:_window] autorelease];
+
+ self.safeModeCoordinator = safeModeCoordinator;
+ [self.safeModeCoordinator setDelegate:self];
+
+ // Activate the main window, which will prompt the views to load.
+ [_window makeKeyAndVisible];
+
+ [self.safeModeCoordinator start];
+ return;
+ }
+
+ // Don't add code here. Add it in MainController's
+ // -startUpBrowserForegroundInitialization.
+ DCHECK([_startupInformation isColdStart]);
+ [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+}
+
+- (void)saveLaunchDetailsToDefaults {
+ // Reset the failure count on first launch, increment it on other launches.
+ if ([[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade])
+ crash_util::ResetFailedStartupAttemptCount();
+ else
+ crash_util::IncrementFailedStartupAttemptCount(false);
+
+ // The startup failure count *must* be synchronized now, since the crashes it
+ // is trying to count are during startup.
+ // -[PreviousSessionInfo beginRecordingCurrentSession] calls |synchronize| on
+ // the user defaults, so leverage that to prevent calling it twice.
+
+ // Start recording info about this session.
+ [[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession];
+}
+
+@end
+
+@implementation AppState (Testing)
+
+- (instancetype)
+initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
+ startupInformation:(id<StartupInformation>)startupInformation
+ applicationDelegate:(MainApplicationDelegate*)applicationDelegate
+ window:(UIWindow*)window
+ shouldOpenNTP:(BOOL)shouldOpenNTP {
+ self = [self initWithBrowserLauncher:browserLauncher
+ startupInformation:startupInformation
+ applicationDelegate:applicationDelegate];
+ if (self) {
+ _shouldOpenNTPTabOnActive = shouldOpenNTP;
+ }
+ return self;
+}
+
+@end
« no previous file with comments | « ios/chrome/app/application_delegate/app_state.h ('k') | ios/chrome/app/application_delegate/app_state_testing.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698