Commit 18eb22e5 authored by Mark Cogan's avatar Mark Cogan Committed by Commit Bot

[iOS] Consolidate SceneController observation ownership.

This is a speculative bug fix; I wasn't able to repro the issue
on trunk. It's a shutdown crash where the SceneController web state
list observer method is triggered by TabModel closing all open tabs,
while the interface provider isn't in a consistent state.

In general, having BrowserViewWrangler be responsible for setting an
observer on the web state lists in the browsers it creates seems
problematic, since it doesn't know the purpose of the observation being
set.

Since SceneController is the one who cares about this observation, it
(with this CL) becomes responsible for adding/removing itself as an
observer. The specific fix for this bug is for SceneController (which
observes the web state list so it can handle the last tab closing) to
stop observing before shutting down BrowserViewWrangler.

I wasn't able to repro the crash, so I couldn't verify the fix, but
I could verify that with this fix in place, a force-quit in the
simulator didn't cause any crashes.

I also updated the comments for BrowserInterfaceProvider to not say
some things that aren't true anymore.

Bug: 1062241
Change-Id: I0d4b69f5889e141a243754c61450f77ea39ac523
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2128068Reviewed-by: default avatarStepan Khapugin <stkhapugin@chromium.org>
Commit-Queue: Mark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754899}
parent a6c10286
...@@ -25,8 +25,6 @@ class ChromeBrowserState; ...@@ -25,8 +25,6 @@ class ChromeBrowserState;
// interface; this protocol allows for easy encapsulation of that process. // interface; this protocol allows for easy encapsulation of that process.
// For legacy reasons, the primary UI entry point for an interface is a visible // For legacy reasons, the primary UI entry point for an interface is a visible
// tab. // tab.
// A given interface is scoped (currently) to a browser state; thus there can
// be two interfaces available (a one incognito and the other not).
@protocol BrowserInterface @protocol BrowserInterface
// The view controller showing the current tab for this interface. This property // The view controller showing the current tab for this interface. This property
...@@ -67,9 +65,7 @@ class ChromeBrowserState; ...@@ -67,9 +65,7 @@ class ChromeBrowserState;
// |currentInterface| is an implementation decision, but |mainInterface| is // |currentInterface| is an implementation decision, but |mainInterface| is
// typical. // typical.
// Changing this value may or may not trigger actual UI changes, or may just be // Changing this value may or may not trigger actual UI changes, or may just be
// bookkeeping associated with UI changes handled elsewhere. The only invariant // bookkeeping associated with UI changes handled elsewhere.
// is that |currentInterface.current| must be YES, and the |current| value of
// any other interface must be NO.
@property(nonatomic, weak) id<BrowserInterface> currentInterface; @property(nonatomic, weak) id<BrowserInterface> currentInterface;
// The "main" (meaning non-incognito -- the nomenclature is legacy) interface. // The "main" (meaning non-incognito -- the nomenclature is legacy) interface.
// This interface's |incognito| property is expected to be NO. // This interface's |incognito| property is expected to be NO.
......
...@@ -12,10 +12,8 @@ ...@@ -12,10 +12,8 @@
@protocol ApplicationCommands; @protocol ApplicationCommands;
class AppUrlLoadingService; class AppUrlLoadingService;
@class BrowserCoordinator;
@protocol BrowsingDataCommands; @protocol BrowsingDataCommands;
class ChromeBrowserState; class ChromeBrowserState;
@protocol WebStateListObserving;
namespace { namespace {
...@@ -38,7 +36,6 @@ NSString* kIncognitoCurrentKey = @"IncognitoActive"; ...@@ -38,7 +36,6 @@ NSString* kIncognitoCurrentKey = @"IncognitoActive";
// storage associated with the interfaces when the current interface changes; // storage associated with the interfaces when the current interface changes;
// this is handled in the implementation of -setCurrentInterface:. // this is handled in the implementation of -setCurrentInterface:.
- (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState - (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState
webStateListObserver:(id<WebStateListObserving>)observer
applicationCommandEndpoint: applicationCommandEndpoint:
(id<ApplicationCommands>)applicationCommandEndpoint (id<ApplicationCommands>)applicationCommandEndpoint
browsingDataCommandEndpoint: browsingDataCommandEndpoint:
......
...@@ -26,10 +26,6 @@ ...@@ -26,10 +26,6 @@
#import "ios/chrome/browser/ui/commands/browsing_data_commands.h" #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h" #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/url_loading/app_url_loading_service.h" #import "ios/chrome/browser/url_loading/app_url_loading_service.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
#import "ios/web/public/web_state.h"
#import "ios/web/public/web_state_observer_bridge.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -107,7 +103,6 @@ ...@@ -107,7 +103,6 @@
std::unique_ptr<Browser> _mainBrowser; std::unique_ptr<Browser> _mainBrowser;
std::unique_ptr<Browser> _otrBrowser; std::unique_ptr<Browser> _otrBrowser;
std::unique_ptr<WebStateListObserverBridge> _webStateListForwardingObserver;
} }
@property(nonatomic, strong, readwrite) WrangledBrowser* mainInterface; @property(nonatomic, strong, readwrite) WrangledBrowser* mainInterface;
...@@ -125,9 +120,6 @@ ...@@ -125,9 +120,6 @@
// saved for the |browser| will be loaded. // saved for the |browser| will be loaded.
- (void)restoreSessionToBrowser:(Browser*)browser; - (void)restoreSessionToBrowser:(Browser*)browser;
// Add the common observers to the browser's |webStateList|.
- (void)addObserversToWebStateList:(WebStateList*)webStateList;
// Setters for the main and otr Browsers. // Setters for the main and otr Browsers.
- (void)setMainBrowser:(std::unique_ptr<Browser>)browser; - (void)setMainBrowser:(std::unique_ptr<Browser>)browser;
- (void)setOtrBrowser:(std::unique_ptr<Browser>)browser; - (void)setOtrBrowser:(std::unique_ptr<Browser>)browser;
...@@ -146,7 +138,6 @@ ...@@ -146,7 +138,6 @@
@synthesize currentInterface = _currentInterface; @synthesize currentInterface = _currentInterface;
- (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState - (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState
webStateListObserver:(id<WebStateListObserving>)observer
applicationCommandEndpoint: applicationCommandEndpoint:
(id<ApplicationCommands>)applicationCommandEndpoint (id<ApplicationCommands>)applicationCommandEndpoint
browsingDataCommandEndpoint: browsingDataCommandEndpoint:
...@@ -158,8 +149,6 @@ ...@@ -158,8 +149,6 @@
_applicationCommandEndpoint = applicationCommandEndpoint; _applicationCommandEndpoint = applicationCommandEndpoint;
_browsingDataCommandEndpoint = browsingDataCommandEndpoint; _browsingDataCommandEndpoint = browsingDataCommandEndpoint;
_appURLLoadingService = appURLLoadingService; _appURLLoadingService = appURLLoadingService;
_webStateListForwardingObserver =
std::make_unique<WebStateListObserverBridge>(observer);
} }
return self; return self;
} }
...@@ -175,8 +164,7 @@ ...@@ -175,8 +164,7 @@
browserList->AddBrowser(_mainBrowser.get()); browserList->AddBrowser(_mainBrowser.get());
[self dispatchToEndpointsForBrowser:_mainBrowser.get()]; [self dispatchToEndpointsForBrowser:_mainBrowser.get()];
[self restoreSessionToBrowser:_mainBrowser.get()]; [self restoreSessionToBrowser:_mainBrowser.get()];
[self addObserversToWebStateList:_mainBrowser->GetWebStateList()]; breakpad::MonitorTabStateForWebStateList(_mainBrowser->GetWebStateList());
// Follow loaded URLs in the main tab model to send those in case of // Follow loaded URLs in the main tab model to send those in case of
// crashes. // crashes.
breakpad::MonitorURLsForWebStateList(self.mainBrowser->GetWebStateList()); breakpad::MonitorURLsForWebStateList(self.mainBrowser->GetWebStateList());
...@@ -255,7 +243,6 @@ ...@@ -255,7 +243,6 @@
breakpad::StopMonitoringTabStateForWebStateList(webStateList); breakpad::StopMonitoringTabStateForWebStateList(webStateList);
breakpad::StopMonitoringURLsForWebStateList(webStateList); breakpad::StopMonitoringURLsForWebStateList(webStateList);
[tabModel disconnect]; [tabModel disconnect];
webStateList->RemoveObserver(_webStateListForwardingObserver.get());
} }
_mainBrowser = std::move(mainBrowser); _mainBrowser = std::move(mainBrowser);
...@@ -267,7 +254,6 @@ ...@@ -267,7 +254,6 @@
WebStateList* webStateList = self.otrBrowser->GetWebStateList(); WebStateList* webStateList = self.otrBrowser->GetWebStateList();
breakpad::StopMonitoringTabStateForWebStateList(webStateList); breakpad::StopMonitoringTabStateForWebStateList(webStateList);
[tabModel disconnect]; [tabModel disconnect];
webStateList->RemoveObserver(_webStateListForwardingObserver.get());
} }
_otrBrowser = std::move(otrBrowser); _otrBrowser = std::move(otrBrowser);
...@@ -389,7 +375,7 @@ ...@@ -389,7 +375,7 @@
if (restorePersistedState) if (restorePersistedState)
[self restoreSessionToBrowser:browser.get()]; [self restoreSessionToBrowser:browser.get()];
[self addObserversToWebStateList:browser->GetWebStateList()]; breakpad::MonitorTabStateForWebStateList(browser->GetWebStateList());
return browser; return browser;
} }
...@@ -435,9 +421,4 @@ ...@@ -435,9 +421,4 @@
sessionWindow); sessionWindow);
} }
- (void)addObserversToWebStateList:(WebStateList*)webStateList {
webStateList->AddObserver(_webStateListForwardingObserver.get());
breakpad::MonitorTabStateForWebStateList(webStateList);
}
@end @end
...@@ -39,7 +39,6 @@ TEST_F(BrowserViewWranglerTest, TestInitNilObserver) { ...@@ -39,7 +39,6 @@ TEST_F(BrowserViewWranglerTest, TestInitNilObserver) {
@autoreleasepool { @autoreleasepool {
BrowserViewWrangler* wrangler = [[BrowserViewWrangler alloc] BrowserViewWrangler* wrangler = [[BrowserViewWrangler alloc]
initWithBrowserState:chrome_browser_state_.get() initWithBrowserState:chrome_browser_state_.get()
webStateListObserver:nil
applicationCommandEndpoint:(id<ApplicationCommands>)nil applicationCommandEndpoint:(id<ApplicationCommands>)nil
browsingDataCommandEndpoint:nil browsingDataCommandEndpoint:nil
appURLLoadingService:nil]; appURLLoadingService:nil];
...@@ -70,7 +69,6 @@ TEST_F(BrowserViewWranglerTest, TestBrowserList) { ...@@ -70,7 +69,6 @@ TEST_F(BrowserViewWranglerTest, TestBrowserList) {
BrowserViewWrangler* wrangler = [[BrowserViewWrangler alloc] BrowserViewWrangler* wrangler = [[BrowserViewWrangler alloc]
initWithBrowserState:chrome_browser_state_.get() initWithBrowserState:chrome_browser_state_.get()
webStateListObserver:nil
applicationCommandEndpoint:nil applicationCommandEndpoint:nil
browsingDataCommandEndpoint:nil browsingDataCommandEndpoint:nil
appURLLoadingService:nil]; appURLLoadingService:nil];
......
...@@ -116,7 +116,9 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -116,7 +116,9 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
@interface SceneController () <UserFeedbackDataSource, @interface SceneController () <UserFeedbackDataSource,
SettingsNavigationControllerDelegate, SettingsNavigationControllerDelegate,
WebStateListObserving> WebStateListObserving> {
std::unique_ptr<WebStateListObserverBridge> _webStateListForwardingObserver;
}
// Navigation View controller for the settings. // Navigation View controller for the settings.
@property(nonatomic, strong) @property(nonatomic, strong)
...@@ -212,6 +214,9 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -212,6 +214,9 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
} }
_appURLLoadingService = new AppUrlLoadingService(); _appURLLoadingService = new AppUrlLoadingService();
_appURLLoadingService->SetDelegate(self); _appURLLoadingService->SetDelegate(self);
_webStateListForwardingObserver =
std::make_unique<WebStateListObserverBridge>(self);
} }
return self; return self;
} }
...@@ -303,12 +308,11 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -303,12 +308,11 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
self.browserViewWrangler = [[BrowserViewWrangler alloc] self.browserViewWrangler = [[BrowserViewWrangler alloc]
initWithBrowserState:self.mainController.mainBrowserState initWithBrowserState:self.mainController.mainBrowserState
webStateListObserver:self
applicationCommandEndpoint:self applicationCommandEndpoint:self
browsingDataCommandEndpoint:self.mainController browsingDataCommandEndpoint:self.mainController
appURLLoadingService:self.appURLLoadingService]; appURLLoadingService:self.appURLLoadingService];
// Ensure the main tab model is created. This also creates the BVC. // Ensure the main browser is created. This also creates the BVC.
[self.browserViewWrangler createMainBrowser]; [self.browserViewWrangler createMainBrowser];
// Only create the restoration helper if the browser state was backed up // Only create the restoration helper if the browser state was backed up
...@@ -364,6 +368,12 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -364,6 +368,12 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
TabModel* mainTabModel = self.mainInterface.tabModel; TabModel* mainTabModel = self.mainInterface.tabModel;
TabModel* otrTabModel = self.incognitoInterface.tabModel; TabModel* otrTabModel = self.incognitoInterface.tabModel;
// Observe the web state lists for both browsers.
self.mainInterface.browser->GetWebStateList()->AddObserver(
_webStateListForwardingObserver.get());
self.incognitoInterface.browser->GetWebStateList()->AddObserver(
_webStateListForwardingObserver.get());
// Enables UI initializations to query the keyWindow's size. // Enables UI initializations to query the keyWindow's size.
[self.sceneState.window makeKeyAndVisible]; [self.sceneState.window makeKeyAndVisible];
...@@ -431,6 +441,13 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -431,6 +441,13 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
[_mainCoordinator stop]; [_mainCoordinator stop];
_mainCoordinator = nil; _mainCoordinator = nil;
// Stop observing web state list changes before tearing down the web state
// lists.
self.mainInterface.browser->GetWebStateList()->RemoveObserver(
_webStateListForwardingObserver.get());
self.incognitoInterface.browser->GetWebStateList()->RemoveObserver(
_webStateListForwardingObserver.get());
[self.browserViewWrangler shutdown]; [self.browserViewWrangler shutdown];
self.browserViewWrangler = nil; self.browserViewWrangler = nil;
...@@ -1576,6 +1593,8 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -1576,6 +1593,8 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
} }
} }
#pragma mark - Helpers for web state list events
// Called when the last incognito tab was closed. // Called when the last incognito tab was closed.
- (void)lastIncognitoTabClosed { - (void)lastIncognitoTabClosed {
// This seems the best place to mark the start of destroying the incognito // This seems the best place to mark the start of destroying the incognito
...@@ -1690,7 +1709,11 @@ const NSTimeInterval kDisplayPromoDelay = 0.1; ...@@ -1690,7 +1709,11 @@ const NSTimeInterval kDisplayPromoDelay = 0.1;
self.incognitoInterface.browserState)); self.incognitoInterface.browserState));
} }
self.incognitoInterface.browser->GetWebStateList()->RemoveObserver(
_webStateListForwardingObserver.get());
[self.browserViewWrangler destroyAndRebuildIncognitoBrowser]; [self.browserViewWrangler destroyAndRebuildIncognitoBrowser];
self.incognitoInterface.browser->GetWebStateList()->AddObserver(
_webStateListForwardingObserver.get());
if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) { if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) {
breakpad::MonitorBreadcrumbManagerService( breakpad::MonitorBreadcrumbManagerService(
......
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