Commit e2951cca authored by Stepan Khapugin's avatar Stepan Khapugin Committed by Commit Bot

[multiball] Fix crash on backgrounding and reopening.

Creates an observer protocol for AppState and makes MainContorller an
observer. Creates an observer method that is called when the first
scene is activated, to drive one-time UI-related init. Makes MC run
its one-time init from tihs callback.

This prevents a crash where on backgrounding and reopening, the one
time startup is performed again, leading to DCHECKs.

Bug: 1045658
Change-Id: I608975e8690e9c874a1913f7458560a54429d611
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2128509
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754928}
parent 154fb055
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@class AppState;
@protocol BrowserLauncher; @protocol BrowserLauncher;
@class SceneState; @class SceneState;
@class MainApplicationDelegate; @class MainApplicationDelegate;
...@@ -16,6 +17,16 @@ ...@@ -16,6 +17,16 @@
@protocol TabOpening; @protocol TabOpening;
@protocol TabSwitching; @protocol TabSwitching;
@protocol AppStateObserver <NSObject>
@optional
// Called when the first scene becomes active.
- (void)appState:(AppState*)appState
firstSceneActivated:(SceneState*)sceneState;
@end
// Represents the application state and responds to application state changes // Represents the application state and responds to application state changes
// and system events. // and system events.
@interface AppState : NSObject @interface AppState : NSObject
...@@ -88,6 +99,13 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher ...@@ -88,6 +99,13 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
// Returns a list of all connected scenes. // Returns a list of all connected scenes.
- (NSArray<SceneState*>*)connectedScenes; - (NSArray<SceneState*>*)connectedScenes;
// Adds an observer to this app state. The observers will be notified about
// app state changes per AppStateObserver protocol.
- (void)addObserver:(id<AppStateObserver>)observer;
// Removes the observer. It's safe to call this at any time, including from
// AppStateObserver callbacks.
- (void)removeObserver:(id<AppStateObserver>)observer;
@end @end
#endif // IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_H_ #endif // IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_H_
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/critical_closure.h" #include "base/critical_closure.h"
#import "base/ios/crb_protocol_observers.h"
#include "base/mac/bundle_locations.h" #include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
...@@ -74,6 +75,16 @@ void PostTaskOnUIThread(base::OnceClosure closure) { ...@@ -74,6 +75,16 @@ void PostTaskOnUIThread(base::OnceClosure closure) {
NSString* const kStartupAttemptReset = @"StartupAttempReset"; NSString* const kStartupAttemptReset = @"StartupAttempReset";
} // namespace } // namespace
#pragma mark - AppStateObserverList
@interface AppStateObserverList : CRBProtocolObservers <AppStateObserver>
@end
@implementation AppStateObserverList
@end
#pragma mark - AppState
@interface AppState ()<SafeModeCoordinatorDelegate> { @interface AppState ()<SafeModeCoordinatorDelegate> {
// Container for startup information. // Container for startup information.
__weak id<StartupInformation> _startupInformation; __weak id<StartupInformation> _startupInformation;
...@@ -102,6 +113,9 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset"; ...@@ -102,6 +113,9 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset";
BOOL _savingCookies; BOOL _savingCookies;
} }
// Container for observers.
@property(nonatomic, strong) AppStateObserverList* observers;
// Safe mode coordinator. If this is non-nil, the app is displaying the safe // Safe mode coordinator. If this is non-nil, the app is displaying the safe
// mode UI. // mode UI.
@property(nonatomic, strong) SafeModeCoordinator* safeModeCoordinator; @property(nonatomic, strong) SafeModeCoordinator* safeModeCoordinator;
...@@ -119,6 +133,10 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset"; ...@@ -119,6 +133,10 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset";
// Saves the current launch details to user defaults. // Saves the current launch details to user defaults.
- (void)saveLaunchDetailsToDefaults; - (void)saveLaunchDetailsToDefaults;
// This flag is set when the first scene has activated since the startup, and
// never reset.
@property(nonatomic, assign) BOOL firstSceneHasActivated;
@end @end
@implementation AppState @implementation AppState
...@@ -133,9 +151,22 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher ...@@ -133,9 +151,22 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
applicationDelegate:(MainApplicationDelegate*)applicationDelegate { applicationDelegate:(MainApplicationDelegate*)applicationDelegate {
self = [super init]; self = [super init];
if (self) { if (self) {
_observers = [AppStateObserverList
observersWithProtocol:@protocol(AppStateObserver)];
_startupInformation = startupInformation; _startupInformation = startupInformation;
_browserLauncher = browserLauncher; _browserLauncher = browserLauncher;
_mainApplicationDelegate = applicationDelegate; _mainApplicationDelegate = applicationDelegate;
if (@available(iOS 13, *)) {
if (IsMultiwindowSupported()) {
// Subscribe for scene activation notifications.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(sceneDidActivate:)
name:UISceneDidActivateNotification
object:nil];
}
}
} }
return self; return self;
} }
...@@ -464,6 +495,14 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher ...@@ -464,6 +495,14 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
self.shouldPerformAdditionalDelegateHandling = !URLHandled; self.shouldPerformAdditionalDelegateHandling = !URLHandled;
} }
- (void)addObserver:(id<SceneStateObserver>)observer {
[self.observers addObserver:observer];
}
- (void)removeObserver:(id<SceneStateObserver>)observer {
[self.observers removeObserver:observer];
}
#pragma mark - Multiwindow-related #pragma mark - Multiwindow-related
- (NSArray<SceneState*>*)connectedScenes { - (NSArray<SceneState*>*)connectedScenes {
...@@ -536,4 +575,23 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher ...@@ -536,4 +575,23 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
[[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession]; [[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession];
} }
#pragma mark - Scene notifications
// Handler for UISceneDidActivateNotification.
- (void)sceneDidActivate:(NSNotification*)notification {
DCHECK(IsMultiwindowSupported());
if (@available(iOS 13, *)) {
if (!self.firstSceneHasActivated) {
self.firstSceneHasActivated = YES;
UIWindowScene* scene =
base::mac::ObjCCastStrict<UIWindowScene>(notification.object);
SceneDelegate* sceneDelegate =
base::mac::ObjCCastStrict<SceneDelegate>(scene.delegate);
[self.observers appState:self
firstSceneActivated:sceneDelegate.sceneState];
}
}
}
@end @end
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
startupInformation:_startupInformation startupInformation:_startupInformation
applicationDelegate:self]; applicationDelegate:self];
[_mainController setAppState:_appState]; [_mainController setAppState:_appState];
[_appState addObserver:_mainController];
if (!IsMultiwindowSupported()) { if (!IsMultiwindowSupported()) {
// When multiwindow is not supported, this object holds a "scene" state // When multiwindow is not supported, this object holds a "scene" state
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/browser_launcher.h" #import "ios/chrome/app/application_delegate/browser_launcher.h"
#import "ios/chrome/app/application_delegate/startup_information.h" #import "ios/chrome/app/application_delegate/startup_information.h"
#import "ios/chrome/app/main_controller_guts.h" #import "ios/chrome/app/main_controller_guts.h"
...@@ -30,7 +31,8 @@ ...@@ -30,7 +31,8 @@
@interface MainController : NSObject <BrowserLauncher, @interface MainController : NSObject <BrowserLauncher,
MainControllerGuts, MainControllerGuts,
StartupInformation, StartupInformation,
BrowsingDataCommands> BrowsingDataCommands,
AppStateObserver>
// The application window. // The application window.
@property(nonatomic, strong) UIWindow* window; @property(nonatomic, strong) UIWindow* window;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/ukm/ios/features.h" #include "components/ukm/ios/features.h"
#include "components/web_resource/web_resource_pref_names.h" #include "components/web_resource/web_resource_pref_names.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/metrics_mediator.h" #import "ios/chrome/app/application_delegate/metrics_mediator.h"
#import "ios/chrome/app/deferred_initialization_runner.h" #import "ios/chrome/app/deferred_initialization_runner.h"
#import "ios/chrome/app/memory_monitor.h" #import "ios/chrome/app/memory_monitor.h"
...@@ -394,11 +393,6 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData( ...@@ -394,11 +393,6 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
selector:@selector(sceneWillConnect:) selector:@selector(sceneWillConnect:)
name:UISceneWillConnectNotification name:UISceneWillConnectNotification
object:nil]; object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(sceneDidActivate:)
name:UISceneDidActivateNotification
object:nil];
} }
} }
} }
...@@ -616,6 +610,14 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData( ...@@ -616,6 +610,14 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
triggerSystemPromptForNewUser:YES]; triggerSystemPromptForNewUser:YES];
} }
#pragma mark - AppStateObserver
// Called when the first scene becomes active.
- (void)appState:(AppState*)appState
firstSceneActivated:(SceneState*)sceneState {
[self startUpAfterFirstWindowCreated];
}
#pragma mark - Scene notifications #pragma mark - Scene notifications
// Handler for UISceneWillConnectNotification. // Handler for UISceneWillConnectNotification.
...@@ -631,16 +633,6 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData( ...@@ -631,16 +633,6 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
} }
} }
// Handler for UISceneDidActivateNotification.
- (void)sceneDidActivate:(NSNotification*)notification {
DCHECK(IsMultiwindowSupported());
if (@available(iOS 13, *)) {
if (UIApplication.sharedApplication.connectedScenes.count == 1) {
[self startUpAfterFirstWindowCreated];
}
}
}
#pragma mark - Property implementation. #pragma mark - Property implementation.
- (id<BrowserInterfaceProvider>)interfaceProvider { - (id<BrowserInterfaceProvider>)interfaceProvider {
......
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