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 @@
#import <UIKit/UIKit.h>
@class AppState;
@protocol BrowserLauncher;
@class SceneState;
@class MainApplicationDelegate;
......@@ -16,6 +17,16 @@
@protocol TabOpening;
@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
// and system events.
@interface AppState : NSObject
......@@ -88,6 +99,13 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
// Returns a list of all connected scenes.
- (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
#endif // IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_H_
......@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/critical_closure.h"
#import "base/ios/crb_protocol_observers.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/metrics/histogram_macros.h"
......@@ -74,6 +75,16 @@ void PostTaskOnUIThread(base::OnceClosure closure) {
NSString* const kStartupAttemptReset = @"StartupAttempReset";
} // namespace
#pragma mark - AppStateObserverList
@interface AppStateObserverList : CRBProtocolObservers <AppStateObserver>
@end
@implementation AppStateObserverList
@end
#pragma mark - AppState
@interface AppState ()<SafeModeCoordinatorDelegate> {
// Container for startup information.
__weak id<StartupInformation> _startupInformation;
......@@ -102,6 +113,9 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset";
BOOL _savingCookies;
}
// Container for observers.
@property(nonatomic, strong) AppStateObserverList* observers;
// Safe mode coordinator. If this is non-nil, the app is displaying the safe
// mode UI.
@property(nonatomic, strong) SafeModeCoordinator* safeModeCoordinator;
......@@ -119,6 +133,10 @@ NSString* const kStartupAttemptReset = @"StartupAttempReset";
// Saves the current launch details to user defaults.
- (void)saveLaunchDetailsToDefaults;
// This flag is set when the first scene has activated since the startup, and
// never reset.
@property(nonatomic, assign) BOOL firstSceneHasActivated;
@end
@implementation AppState
......@@ -133,9 +151,22 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
applicationDelegate:(MainApplicationDelegate*)applicationDelegate {
self = [super init];
if (self) {
_observers = [AppStateObserverList
observersWithProtocol:@protocol(AppStateObserver)];
_startupInformation = startupInformation;
_browserLauncher = browserLauncher;
_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;
}
......@@ -464,6 +495,14 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
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
- (NSArray<SceneState*>*)connectedScenes {
......@@ -536,4 +575,23 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
[[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
......@@ -73,6 +73,7 @@
startupInformation:_startupInformation
applicationDelegate:self];
[_mainController setAppState:_appState];
[_appState addObserver:_mainController];
if (!IsMultiwindowSupported()) {
// When multiwindow is not supported, this object holds a "scene" state
......
......@@ -7,6 +7,7 @@
#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/startup_information.h"
#import "ios/chrome/app/main_controller_guts.h"
......@@ -30,7 +31,8 @@
@interface MainController : NSObject <BrowserLauncher,
MainControllerGuts,
StartupInformation,
BrowsingDataCommands>
BrowsingDataCommands,
AppStateObserver>
// The application window.
@property(nonatomic, strong) UIWindow* window;
......
......@@ -24,7 +24,6 @@
#include "components/prefs/pref_change_registrar.h"
#include "components/ukm/ios/features.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/deferred_initialization_runner.h"
#import "ios/chrome/app/memory_monitor.h"
......@@ -394,11 +393,6 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
selector:@selector(sceneWillConnect:)
name:UISceneWillConnectNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(sceneDidActivate:)
name:UISceneDidActivateNotification
object:nil];
}
}
}
......@@ -616,6 +610,14 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
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
// Handler for UISceneWillConnectNotification.
......@@ -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.
- (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