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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/app/application_delegate/app_state.h"
6
7 #include "base/critical_closure.h"
8 #import "base/mac/bind_objc_block.h"
9 #import "base/mac/scoped_nsobject.h"
10 #include "components/metrics/metrics_service.h"
11 #import "ios/chrome/app/application_delegate/app_navigation.h"
12 #import "ios/chrome/app/application_delegate/browser_launcher.h"
13 #import "ios/chrome/app/application_delegate/memory_warning_helper.h"
14 #import "ios/chrome/app/application_delegate/metrics_mediator.h"
15 #import "ios/chrome/app/application_delegate/startup_information.h"
16 #import "ios/chrome/app/application_delegate/tab_opening.h"
17 #import "ios/chrome/app/application_delegate/tab_switching.h"
18 #import "ios/chrome/app/application_delegate/user_activity_handler.h"
19 #import "ios/chrome/app/deferred_initialization_runner.h"
20 #import "ios/chrome/app/safe_mode/safe_mode_coordinator.h"
21 #import "ios/chrome/app/safe_mode_crashing_modules_config.h"
22 #include "ios/chrome/browser/application_context.h"
23 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
24 #include "ios/chrome/browser/chrome_constants.h"
25 #include "ios/chrome/browser/crash_loop_detection_util.h"
26 #include "ios/chrome/browser/crash_report/breakpad_helper.h"
27 #import "ios/chrome/browser/crash_report/crash_report_background_uploader.h"
28 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
29 #import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
30 #import "ios/chrome/browser/metrics/previous_session_info.h"
31 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller .h"
32 #include "ios/chrome/browser/ui/background_generator.h"
33 #import "ios/chrome/browser/ui/browser_view_controller.h"
34 #import "ios/chrome/browser/ui/main/browser_view_information.h"
35 #include "ios/net/cookies/cookie_store_ios.h"
36 #include "ios/net/cookies/system_cookie_util.h"
37 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
38 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provi der.h"
39 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider .h"
40 #include "ios/web/net/request_tracker_impl.h"
41 #include "net/url_request/url_request_context.h"
42
43 namespace {
44 // Helper method to post |closure| on the UI thread.
45 void PostTaskOnUIThread(const base::Closure& closure) {
46 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, closure);
47 }
48 NSString* const kStartupAttemptReset = @"StartupAttempReset";
49 } // namespace
50
51 @interface AppState ()<SafeModeCoordinatorDelegate> {
52 // Container for startup information.
53 base::WeakNSProtocol<id<StartupInformation>> _startupInformation;
54 // Browser launcher to launch browser in different states.
55 base::WeakNSProtocol<id<BrowserLauncher>> _browserLauncher;
56 // UIApplicationDelegate for the application.
57 base::WeakNSObject<MainApplicationDelegate> _mainApplicationDelegate;
58 // Window for the application.
59 base::WeakNSObject<UIWindow> _window;
60
61 // Variables backing properties of same name.
62 base::scoped_nsobject<SafeModeCoordinator> _safeModeCoordinator;
63
64 // Start of the current session, used for UMA.
65 base::TimeTicks _sessionStartTime;
66 // YES if the app is currently in the process of terminating.
67 BOOL _appIsTerminating;
68 // Indicates if an NTP tab should be opened once the application has become
69 // active.
70 BOOL _shouldOpenNTPTabOnActive;
71 // Interstitial view used to block any incognito tabs after backgrounding.
72 base::scoped_nsobject<UIView> _incognitoBlocker;
73 // Whether the application is currently in the background.
74 // This is a workaround for rdar://22392526 where
75 // -applicationDidEnterBackground: can be called twice.
76 // TODO(crbug.com/546196): Remove this once rdar://22392526 is fixed.
77 BOOL _applicationInBackground;
78 // YES if cookies are currently being flushed to disk.
79 BOOL _savingCookies;
80 }
81
82 // Safe mode coordinator. If this is non-nil, the app is displaying the safe
83 // mode UI.
84 @property(nonatomic, retain) SafeModeCoordinator* safeModeCoordinator;
85
86 // Return value for -requiresHandlingAfterLaunchWithOptions that determines if
87 // UIKit should make followup delegate calls such as
88 // -performActionForShortcutItem or -openURL.
89 @property(nonatomic, assign) BOOL shouldPerformAdditionalDelegateHandling;
90
91 // This method is the first to be called when user launches the application.
92 // Depending on the background tasks history, the state of the application is
93 // either INITIALIZATION_STAGE_BASIC or INITIALIZATION_STAGE_BACKGROUND so this
94 // step cannot be included in the |startUpBrowserToStage:| method.
95 - (void)initializeUI;
96 // Saves the current launch details to user defaults.
97 - (void)saveLaunchDetailsToDefaults;
98
99 @end
100
101 @implementation AppState
102
103 @synthesize shouldPerformAdditionalDelegateHandling =
104 _shouldPerformAdditionalDelegateHandling;
105 @synthesize userInteracted = _userInteracted;
106
107 - (instancetype)init {
108 NOTREACHED();
109 return nil;
110 }
111
112 - (instancetype)
113 initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
114 startupInformation:(id<StartupInformation>)startupInformation
115 applicationDelegate:(MainApplicationDelegate*)applicationDelegate {
116 self = [super init];
117 if (self) {
118 _startupInformation.reset(startupInformation);
119 _browserLauncher.reset(browserLauncher);
120 _mainApplicationDelegate.reset(applicationDelegate);
121 }
122 return self;
123 }
124
125 #pragma mark - Properties implementation
126
127 - (SafeModeCoordinator*)safeModeCoordinator {
128 return _safeModeCoordinator;
129 }
130
131 - (void)setSafeModeCoordinator:(SafeModeCoordinator*)safeModeCoordinator {
132 _safeModeCoordinator.reset([safeModeCoordinator retain]);
133 }
134
135 - (void)setWindow:(UIWindow*)window {
136 _window.reset(window);
137 }
138
139 - (UIWindow*)window {
140 return _window;
141 }
142
143 #pragma mark - Public methods.
144
145 - (void)applicationDidEnterBackground:(UIApplication*)application
146 memoryHelper:(MemoryWarningHelper*)memoryHelper
147 tabSwitcherIsActive:(BOOL)tabSwitcherIsActive {
148 if ([self isInSafeMode]) {
149 // Force a crash when backgrounding and in safe mode, so users don't get
150 // stuck in safe mode.
151 breakpad_helper::SetEnabled(false);
152 exit(0);
153 return;
154 }
155
156 if (_applicationInBackground) {
157 return;
158 }
159 _applicationInBackground = YES;
160
161 breakpad_helper::SetCurrentlyInBackground(true);
162
163 if ([_browserLauncher browserInitializationStage] <
164 INITIALIZATION_STAGE_FOREGROUND) {
165 // The clean-up done in |-applicationDidEnterBackground:| is only valid for
166 // the case when the application is started in foreground, so there is
167 // nothing to clean up as the application was not initialized for foregound.
168 //
169 // From the stack trace of the crash bug http://crbug.com/437307 , it
170 // seems that |-applicationDidEnterBackground:| may be called when the app
171 // is started in background and before the initialization for background
172 // stage is done. Note that the crash bug could not be reproduced though.
173 return;
174 }
175
176 [MetricsMediator
177 applicationDidEnterBackground:[memoryHelper
178 foregroundMemoryWarningCount]];
179
180 [_startupInformation expireFirstUserActionRecorder];
181
182 // If the current BVC is incognito, or if we are in the tab switcher and there
183 // are incognito tabs visible, place a full screen view containing the
184 // switcher background to hide any incognito content.
185 if (([[_browserLauncher browserViewInformation] currentBrowserState] &&
186 [[_browserLauncher browserViewInformation] currentBrowserState]
187 ->IsOffTheRecord()) ||
188 (tabSwitcherIsActive &&
189 ![[[_browserLauncher browserViewInformation] otrTabModel] isEmpty])) {
190 // Cover the largest area potentially shown in the app switcher, in case the
191 // screenshot is reused in a different orientation or size class.
192 CGRect screenBounds = [[UIScreen mainScreen] bounds];
193 CGFloat maxDimension =
194 std::max(CGRectGetWidth(screenBounds), CGRectGetHeight(screenBounds));
195 _incognitoBlocker.reset([[UIView alloc]
196 initWithFrame:CGRectMake(0, 0, maxDimension, maxDimension)]);
197 InstallBackgroundInView(_incognitoBlocker);
198 [_window addSubview:_incognitoBlocker];
199 }
200
201 // Do not save cookies if it is already in progress.
202 if ([[_browserLauncher browserViewInformation] currentBVC].browserState &&
203 !_savingCookies) {
204 // Save cookies to disk. The empty critical closure guarantees that the task
205 // will be run before backgrounding.
206 scoped_refptr<net::URLRequestContextGetter> getter =
207 [[_browserLauncher browserViewInformation] currentBVC]
208 .browserState->GetRequestContext();
209 _savingCookies = YES;
210 base::Closure criticalClosure = base::MakeCriticalClosure(base::BindBlock(^{
211 DCHECK_CURRENTLY_ON(web::WebThread::UI);
212 _savingCookies = NO;
213 }));
214 web::WebThread::PostTask(
215 web::WebThread::IO, FROM_HERE, base::BindBlock(^{
216 net::CookieStoreIOS* store = static_cast<net::CookieStoreIOS*>(
217 getter->GetURLRequestContext()->cookie_store());
218 // FlushStore() runs its callback on any thread. Jump back to UI.
219 store->FlushStore(base::Bind(&PostTaskOnUIThread, criticalClosure));
220 }));
221 }
222
223 // Mark the startup as clean if it hasn't already been.
224 [[DeferredInitializationRunner sharedInstance]
225 runBlockIfNecessary:kStartupAttemptReset];
226 // Set date/time that the background fetch handler was called in the user
227 // defaults.
228 [MetricsMediator logDateInUserDefaults];
229 // Clear the memory warning flag since the app is now safely in background.
230 [[PreviousSessionInfo sharedInstance] resetMemoryWarningFlag];
231
232 // Turn off uploading of crash reports and metrics, in case the method of
233 // communication changes while in the background.
234 [MetricsMediator disableReporting];
235
236 GetApplicationContext()->OnAppEnterBackground();
237 if (![[CrashReportBackgroundUploader sharedInstance]
238 hasPendingCrashReportsToUploadAtStartup]) {
239 [application setMinimumBackgroundFetchInterval:
240 UIApplicationBackgroundFetchIntervalNever];
241 }
242 }
243
244 - (void)applicationWillEnterForeground:(UIApplication*)application
245 metricsMediator:(MetricsMediator*)metricsMediator
246 memoryHelper:(MemoryWarningHelper*)memoryHelper
247 tabOpener:(id<TabOpening>)tabOpener
248 appNavigation:(id<AppNavigation>)appNavigation {
249 if ([_browserLauncher browserInitializationStage] <
250 INITIALIZATION_STAGE_FOREGROUND) {
251 // The application has been launched in background and the initialization
252 // is not complete.
253 [self initializeUI];
254 return;
255 }
256 if ([self isInSafeMode])
257 return;
258
259 _applicationInBackground = NO;
260
261 [_incognitoBlocker removeFromSuperview];
262 _incognitoBlocker.reset();
263
264 breakpad_helper::SetCurrentlyInBackground(false);
265
266 // Update the state of metrics and crash reporting, as the method of
267 // communication may have changed while the app was in the background.
268 [metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:NO];
269
270 // Send any feedback that might be still on temporary storage.
271 ios::GetChromeBrowserProvider()->GetUserFeedbackProvider()->Synchronize();
272
273 GetApplicationContext()->OnAppEnterForeground();
274
275 [MetricsMediator
276 logLaunchMetricsWithStartupInformation:_startupInformation
277 browserViewInformation:[_browserLauncher
278 browserViewInformation]];
279 [memoryHelper resetForegroundMemoryWarningCount];
280
281 // Check if a NTP tab should be opened; the tab will actually be opened in
282 // |applicationDidBecomeActive| after the application gets prepared to
283 // record user actions.
284 // TODO(crbug.com/623491): opening a tab when the application is launched
285 // without a tab should not be counted as a user action. Revisit the way tab
286 // creation is counted.
287 _shouldOpenNTPTabOnActive = [tabOpener
288 shouldOpenNTPTabOnActivationOfTabModel:[[_browserLauncher
289 browserViewInformation]
290 currentTabModel]];
291
292 ios::ChromeBrowserState* currentBrowserState =
293 [[_browserLauncher browserViewInformation] currentBrowserState];
294 if ([SignedInAccountsViewController
295 shouldBePresentedForBrowserState:currentBrowserState]) {
296 [appNavigation presentSignedInAccountsViewControllerForBrowserState:
297 currentBrowserState];
298 }
299
300 // If the current browser state is not OTR, check for cookie loss.
301 if (currentBrowserState && !currentBrowserState->IsOffTheRecord() &&
302 currentBrowserState->GetOriginalChromeBrowserState()
303 ->GetStatePath()
304 .BaseName()
305 .value() == kIOSChromeInitialBrowserState) {
306 NSUInteger cookie_count =
307 [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] count];
308 UMA_HISTOGRAM_COUNTS_10000("CookieIOS.CookieCountOnForegrounding",
309 cookie_count);
310 net::CheckForCookieLoss(cookie_count,
311 net::COOKIES_APPLICATION_FOREGROUNDED);
312 }
313 }
314
315 - (void)resumeSessionWithTabOpener:(id<TabOpening>)tabOpener
316 tabSwitcher:(id<TabSwitching>)tabSwitcher {
317 [_incognitoBlocker removeFromSuperview];
318 _incognitoBlocker.reset();
319
320 DCHECK([_browserLauncher browserInitializationStage] ==
321 INITIALIZATION_STAGE_FOREGROUND);
322 _sessionStartTime = base::TimeTicks::Now();
323 [[[_browserLauncher browserViewInformation] mainTabModel]
324 resetSessionMetrics];
325
326 if ([_startupInformation startupParameters]) {
327 [UserActivityHandler
328 handleStartupParametersWithTabOpener:tabOpener
329 startupInformation:_startupInformation
330 browserViewInformation:[_browserLauncher
331 browserViewInformation]];
332 } else if (_shouldOpenNTPTabOnActive) {
333 if (![tabSwitcher openNewTabFromTabSwitcher]) {
334 [[[_browserLauncher browserViewInformation] currentBVC] newTab:nil];
335 }
336 _shouldOpenNTPTabOnActive = NO;
337 }
338
339 [MetricsMediator logStartupDuration:_startupInformation];
340 }
341
342 - (void)applicationWillTerminate:(UIApplication*)application
343 applicationNavigation:(id<AppNavigation>)appNavigation {
344 if (_appIsTerminating) {
345 // Previous handling of this method spun the runloop, resulting in
346 // recursive calls; this does not appear to happen with the new shutdown
347 // flow, but this is here to ensure that if it can happen, it gets noticed
348 // and fixed.
349 CHECK(false);
350 }
351 _appIsTerminating = YES;
352
353 // Dismiss any UI that is presented on screen and that is listening for
354 // profile notifications.
355 if ([appNavigation settingsNavigationController])
356 [appNavigation closeSettingsAnimated:NO completion:nil];
357
358 // Clean up the device sharing manager before the main browser state is shut
359 // down.
360 if ([_browserLauncher browserInitializationStage] >=
361 INITIALIZATION_STAGE_FOREGROUND) {
362 [[_browserLauncher browserViewInformation] cleanDeviceSharingManager];
363 }
364
365 // Cancel any in-flight distribution notifications.
366 ios::GetChromeBrowserProvider()
367 ->GetAppDistributionProvider()
368 ->CancelDistributionNotifications();
369
370 // Halt the tabs, so any outstanding requests get cleaned up, without actually
371 // closing the tabs.
372 if ([_browserLauncher browserInitializationStage] >=
373 INITIALIZATION_STAGE_FOREGROUND) {
374 [[_browserLauncher browserViewInformation] haltAllTabs];
375 }
376
377 // TODO(crbug.com/585700): remove this.
378 web::RequestTrackerImpl::BlockUntilTrackersShutdown();
379
380 [_startupInformation stopChromeMain];
381
382 if (![[CrashReportBackgroundUploader sharedInstance]
383 hasPendingCrashReportsToUploadAtStartup]) {
384 [application setMinimumBackgroundFetchInterval:
385 UIApplicationBackgroundFetchIntervalNever];
386 }
387 }
388
389 - (void)willResignActiveTabModel {
390 if ([_browserLauncher browserInitializationStage] <
391 INITIALIZATION_STAGE_FOREGROUND) {
392 // If the application did not pass the foreground initialization stage,
393 // there is no active tab model to resign.
394 return;
395 }
396
397 // Set [_startupInformation isColdStart] to NO in anticipation of the next
398 // time the app becomes active.
399 [_startupInformation setIsColdStart:NO];
400
401 base::TimeDelta duration = base::TimeTicks::Now() - _sessionStartTime;
402 UMA_HISTOGRAM_LONG_TIMES("Session.TotalDuration", duration);
403 [[[_browserLauncher browserViewInformation] mainTabModel]
404 recordSessionMetrics];
405 }
406
407 - (BOOL)requiresHandlingAfterLaunchWithOptions:(NSDictionary*)launchOptions
408 stateBackground:(BOOL)stateBackground {
409 [_browserLauncher setLaunchOptions:launchOptions];
410 self.shouldPerformAdditionalDelegateHandling = YES;
411
412 [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_BASIC];
413 if (!stateBackground) {
414 [self initializeUI];
415 }
416
417 return self.shouldPerformAdditionalDelegateHandling;
418 }
419
420 - (BOOL)isInSafeMode {
421 return self.safeModeCoordinator != nil;
422 }
423
424 - (void)launchFromURLHandled:(BOOL)URLHandled {
425 self.shouldPerformAdditionalDelegateHandling = !URLHandled;
426 }
427
428 #pragma mark - SafeModeCoordinatorDelegate Implementation
429
430 - (void)coordinatorDidExitSafeMode:(nonnull SafeModeCoordinator*)coordinator {
431 self.safeModeCoordinator = nil;
432 [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
433 [_mainApplicationDelegate
434 applicationDidBecomeActive:[UIApplication sharedApplication]];
435 }
436
437 #pragma mark - Internal methods.
438
439 - (void)initializeUI {
440 _userInteracted = YES;
441 [self saveLaunchDetailsToDefaults];
442
443 DCHECK([_window rootViewController] == nil);
444 if ([SafeModeCoordinator shouldStart]) {
445 SafeModeCoordinator* safeModeCoordinator =
446 [[[SafeModeCoordinator alloc] initWithWindow:_window] autorelease];
447
448 self.safeModeCoordinator = safeModeCoordinator;
449 [self.safeModeCoordinator setDelegate:self];
450
451 // Activate the main window, which will prompt the views to load.
452 [_window makeKeyAndVisible];
453
454 [self.safeModeCoordinator start];
455 return;
456 }
457
458 // Don't add code here. Add it in MainController's
459 // -startUpBrowserForegroundInitialization.
460 DCHECK([_startupInformation isColdStart]);
461 [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
462 }
463
464 - (void)saveLaunchDetailsToDefaults {
465 // Reset the failure count on first launch, increment it on other launches.
466 if ([[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade])
467 crash_util::ResetFailedStartupAttemptCount();
468 else
469 crash_util::IncrementFailedStartupAttemptCount(false);
470
471 // The startup failure count *must* be synchronized now, since the crashes it
472 // is trying to count are during startup.
473 // -[PreviousSessionInfo beginRecordingCurrentSession] calls |synchronize| on
474 // the user defaults, so leverage that to prevent calling it twice.
475
476 // Start recording info about this session.
477 [[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession];
478 }
479
480 @end
481
482 @implementation AppState (Testing)
483
484 - (instancetype)
485 initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
486 startupInformation:(id<StartupInformation>)startupInformation
487 applicationDelegate:(MainApplicationDelegate*)applicationDelegate
488 window:(UIWindow*)window
489 shouldOpenNTP:(BOOL)shouldOpenNTP {
490 self = [self initWithBrowserLauncher:browserLauncher
491 startupInformation:startupInformation
492 applicationDelegate:applicationDelegate];
493 if (self) {
494 _shouldOpenNTPTabOnActive = shouldOpenNTP;
495 }
496 return self;
497 }
498
499 @end
OLDNEW
« 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