Commit 395a97e8 authored by Rohit Rao's avatar Rohit Rao Committed by Commit Bot

[ios] Fixes a crash when the app is shut down without being started.

If a user force-quits the app during launch (while the splash screen is
still visible), it's possible for the app to receive
applicationWillTerminate: without first receiving
application:didFinishLaunchingWithOptions:. In this scenario, invoking
the normal shutdown logic will lead to crashes, because the app was
never started up.

This CL adds logic to detect this case and return early from
applicationWillTerminate.

BUG=1115863,983504,1086396

Change-Id: I453f0ecae35a4431dc51490c6e38bbfd0d224f01
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2367986
Commit-Queue: Rohit Rao <rohitrao@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800986}
parent 92b59741
......@@ -57,6 +57,11 @@
// The controller for |sceneState|.
@property(nonatomic, strong) SceneController* sceneController;
// YES if application:didFinishLaunchingWithOptions: was called. Used to
// determine whether or not shutdown should be invoked from
// applicationWillTerminate:.
@property(nonatomic, assign) BOOL didFinishLaunching;
@end
@implementation MainApplicationDelegate
......@@ -113,6 +118,8 @@
// startup is fast, and the UI appears as soon as possible.
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
self.didFinishLaunching = YES;
startup_loggers::RegisterAppDidFinishLaunchingTime();
_mainController.window = self.window;
......@@ -204,6 +211,12 @@
}
- (void)applicationWillTerminate:(UIApplication*)application {
// If |self.didFinishLaunching| is NO, that indicates that the app was
// terminated before startup could be run. In this situation, skip running
// shutdown, since the app was never fully started.
if (!self.didFinishLaunching)
return;
if ([_appState isInSafeMode])
return;
......
......@@ -54,3 +54,25 @@ TEST_F(MainApplicationDelegateTest, CrashIfNotInitialized) {
delete ios::GetChromeBrowserProvider();
ios::SetChromeBrowserProvider(stashed_chrome_browser_provider);
}
// Tests that the application does not crash if |applicationWillTerminate:| is
// called before a previous call to |application:didFinishLaunchingWithOptions:|
// set up the ChromeBrowserProvider. This can happen if the app is force-quit
// while the splash screen is still visible.
TEST_F(MainApplicationDelegateTest, TerminateCalledWithNoBrowserProvider) {
id application = [OCMockObject niceMockForClass:[UIApplication class]];
// The test fixture automatically registers a ChromeBrowserProvider, but this
// test is trying to verify behavior in the case where
// ios::GetChromeBrowserProvider() return nullptr. Clear the previously-set
// provider before proceeding.
ios::ChromeBrowserProvider* stashed_chrome_browser_provider =
ios::GetChromeBrowserProvider();
ios::SetChromeBrowserProvider(nullptr);
MainApplicationDelegate* delegate = [[MainApplicationDelegate alloc] init];
[delegate applicationWillTerminate:application];
// Restore ChromeBrowserProvider to its original value.
ios::SetChromeBrowserProvider(stashed_chrome_browser_provider);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment