OLD | NEW |
(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/browser/ui/main/browser_view_wrangler.h" |
| 6 |
| 7 #include "base/mac/objc_property_releaser.h" |
| 8 #import "base/mac/scoped_nsobject.h" |
| 9 #include "ios/chrome/browser/application_context.h" |
| 10 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 11 #import "ios/chrome/browser/browsing_data/browsing_data_removal_controller.h" |
| 12 #include "ios/chrome/browser/browsing_data/ios_chrome_browsing_data_remover.h" |
| 13 #include "ios/chrome/browser/crash_report/crash_report_helper.h" |
| 14 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h" |
| 15 #import "ios/chrome/browser/physical_web/start_physical_web_discovery.h" |
| 16 #import "ios/chrome/browser/sessions/session_service.h" |
| 17 #import "ios/chrome/browser/sessions/session_window.h" |
| 18 #import "ios/chrome/browser/tabs/tab.h" |
| 19 #import "ios/chrome/browser/tabs/tab_model.h" |
| 20 #import "ios/chrome/browser/tabs/tab_model_observer.h" |
| 21 #import "ios/chrome/browser/ui/browser_view_controller.h" |
| 22 #import "ios/chrome/browser/ui/browser_view_controller_dependency_factory.h" |
| 23 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| 24 |
| 25 @interface BrowserViewWrangler ()<TabModelObserver> { |
| 26 ios::ChromeBrowserState* _browserState; |
| 27 __unsafe_unretained id<TabModelObserver> _tabModelObserver; |
| 28 |
| 29 base::mac::ObjCPropertyReleaser _propertyReleaser_BrowserViewWrangler; |
| 30 } |
| 31 |
| 32 // Responsible for maintaining all state related to sharing to other devices. |
| 33 // Redeclared readwrite from the readonly declaration in the Testing interface. |
| 34 @property(nonatomic, retain, readwrite) |
| 35 DeviceSharingManager* deviceSharingManager; |
| 36 |
| 37 // Creates a new autoreleased tab model for |browserState|; if |empty| is NO, |
| 38 // then any existing tabs that have been saved for |browserState| will be |
| 39 // loaded; otherwise, the tab model will be created empty. |
| 40 - (TabModel*)tabModelForBrowserState:(ios::ChromeBrowserState*)browserState |
| 41 empty:(BOOL)empty; |
| 42 |
| 43 // Creates a new off-the-record ("incognito") browser state for |_browserState|, |
| 44 // then calls -tabModelForBrowserState:empty: and returns the (autoreleased) |
| 45 // result. |
| 46 - (TabModel*)buildOtrTabModel:(BOOL)empty; |
| 47 |
| 48 // Creates the correct BrowserViewController for the corresponding browser state |
| 49 // and tab model. |
| 50 - (BrowserViewController*)bvcForBrowserState: |
| 51 (ios::ChromeBrowserState*)browserState |
| 52 tabModel:(TabModel*)tabModel; |
| 53 @end |
| 54 |
| 55 @implementation BrowserViewWrangler |
| 56 |
| 57 // Properties defined in the BrowserViewInformation protocol. |
| 58 @synthesize mainBVC = _mainBVC; |
| 59 @synthesize mainTabModel = _mainTabModel; |
| 60 @synthesize otrBVC = _otrBVC; |
| 61 @synthesize otrTabModel = _otrTabModel; |
| 62 @synthesize currentBVC = _currentBVC; |
| 63 // Private properies. |
| 64 @synthesize deviceSharingManager = _deviceSharingManager; |
| 65 |
| 66 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState |
| 67 tabModelObserver:(id<TabModelObserver>)tabModelObserver { |
| 68 if ((self = [super init])) { |
| 69 _propertyReleaser_BrowserViewWrangler.Init(self, |
| 70 [BrowserViewWrangler class]); |
| 71 _browserState = browserState; |
| 72 _tabModelObserver = tabModelObserver; |
| 73 } |
| 74 return self; |
| 75 } |
| 76 |
| 77 - (instancetype)init { |
| 78 NOTREACHED(); |
| 79 return nil; |
| 80 } |
| 81 |
| 82 - (void)dealloc { |
| 83 if (_tabModelObserver) { |
| 84 [_mainTabModel removeObserver:_tabModelObserver]; |
| 85 [_otrTabModel removeObserver:_tabModelObserver]; |
| 86 } |
| 87 [_mainTabModel removeObserver:self]; |
| 88 [_otrTabModel removeObserver:self]; |
| 89 |
| 90 // Stop URL monitoring of the main tab model. |
| 91 ios_internal::breakpad::StopMonitoringURLsForTabModel(_mainTabModel); |
| 92 |
| 93 // Stop Breakpad state monitoring of both tab models (if necessary). |
| 94 ios_internal::breakpad::StopMonitoringTabStateForTabModel(_mainTabModel); |
| 95 ios_internal::breakpad::StopMonitoringTabStateForTabModel(_otrTabModel); |
| 96 |
| 97 // Normally other objects will take care of unhooking the tab models from |
| 98 // the browser state, but this code should ensure that it happens regardless. |
| 99 [_mainTabModel browserStateDestroyed]; |
| 100 [_otrTabModel browserStateDestroyed]; |
| 101 |
| 102 [super dealloc]; |
| 103 } |
| 104 |
| 105 #pragma mark - BrowserViewInformation property implementations |
| 106 |
| 107 - (BrowserViewController*)mainBVC { |
| 108 if (!_mainBVC) { |
| 109 // |_browserState| should always be set before trying to create |
| 110 // |_mainBVC|. |
| 111 DCHECK(_browserState); |
| 112 self.mainBVC = |
| 113 [self bvcForBrowserState:_browserState tabModel:self.mainTabModel]; |
| 114 DCHECK(_mainBVC); |
| 115 } |
| 116 return _mainBVC; |
| 117 } |
| 118 |
| 119 - (TabModel*)mainTabModel { |
| 120 if (!_mainTabModel) { |
| 121 self.mainTabModel = [self tabModelForBrowserState:_browserState empty:NO]; |
| 122 // Follow loaded URLs in the main tab model to send those in case of |
| 123 // crashes. |
| 124 ios_internal::breakpad::MonitorURLsForTabModel(_mainTabModel); |
| 125 ios::GetChromeBrowserProvider()->InitializeCastService(_mainTabModel); |
| 126 } |
| 127 return _mainTabModel; |
| 128 } |
| 129 |
| 130 - (BrowserViewController*)otrBVC { |
| 131 if (!_otrBVC) { |
| 132 // |_browserState| should always be set before trying to create |
| 133 // |_otrBVC|. |
| 134 DCHECK(_browserState); |
| 135 ios::ChromeBrowserState* otrBrowserState = |
| 136 _browserState->GetOffTheRecordChromeBrowserState(); |
| 137 DCHECK(otrBrowserState); |
| 138 self.otrBVC = |
| 139 [self bvcForBrowserState:otrBrowserState tabModel:self.otrTabModel]; |
| 140 DCHECK(_otrBVC); |
| 141 } |
| 142 return _otrBVC; |
| 143 } |
| 144 |
| 145 - (TabModel*)otrTabModel { |
| 146 if (!_otrTabModel) { |
| 147 self.otrTabModel = [self buildOtrTabModel:NO]; |
| 148 } |
| 149 return _otrTabModel; |
| 150 } |
| 151 |
| 152 - (void)setCurrentBVC:(BrowserViewController*)bvc |
| 153 storageSwitcher:(id<BrowserStateStorageSwitching>)storageSwitcher { |
| 154 DCHECK(bvc != nil); |
| 155 // |bvc| should be one of the BrowserViewControllers this class already owns. |
| 156 DCHECK(_mainBVC == bvc || _otrBVC == bvc); |
| 157 if (self.currentBVC == bvc) { |
| 158 return; |
| 159 } |
| 160 |
| 161 if (self.currentBVC) { |
| 162 // Tell the current BVC it moved to the background. |
| 163 [self.currentBVC setPrimary:NO]; |
| 164 |
| 165 // Data storage for the browser is always owned by the current BVC, so it |
| 166 // must be updated when switching between BVCs. |
| 167 [storageSwitcher changeStorageFromBrowserState:self.currentBVC.browserState |
| 168 toBrowserState:bvc.browserState]; |
| 169 } |
| 170 |
| 171 self.currentBVC = bvc; |
| 172 |
| 173 // The internal state of the Handoff Manager depends on the current BVC. |
| 174 [self updateDeviceSharingManager]; |
| 175 |
| 176 // By default, Physical Web discovery will not be started if the browser |
| 177 // launches into an Incognito tab. On switching modes, check if discovery |
| 178 // should be started. |
| 179 StartPhysicalWebDiscovery(GetApplicationContext()->GetLocalState(), |
| 180 [self currentBrowserState]); |
| 181 } |
| 182 |
| 183 #pragma mark - BrowserViewInformation methods |
| 184 |
| 185 - (TabModel*)currentTabModel { |
| 186 return self.currentBVC.tabModel; |
| 187 } |
| 188 |
| 189 - (ios::ChromeBrowserState*)currentBrowserState { |
| 190 return self.currentBVC.browserState; |
| 191 } |
| 192 |
| 193 - (void)haltAllTabs { |
| 194 [self.mainTabModel haltAllTabs]; |
| 195 [self.otrTabModel haltAllTabs]; |
| 196 } |
| 197 |
| 198 - (void)cleanDeviceSharingManager { |
| 199 [self.deviceSharingManager updateBrowserState:NULL]; |
| 200 } |
| 201 |
| 202 #pragma mark - TabModelObserver |
| 203 |
| 204 - (void)tabModel:(TabModel*)model |
| 205 didChangeActiveTab:(Tab*)newTab |
| 206 previousTab:(Tab*)previousTab |
| 207 atIndex:(NSUInteger)index { |
| 208 [self updateDeviceSharingManager]; |
| 209 } |
| 210 |
| 211 - (void)tabModel:(TabModel*)model didChangeTab:(Tab*)tab { |
| 212 [self updateDeviceSharingManager]; |
| 213 } |
| 214 |
| 215 // Called when the number of tabs changes. Updates the switcher button |
| 216 // visibility in the various modes on tablet. |
| 217 - (void)tabModelDidChangeTabCount:(TabModel*)notifiedTabModel { |
| 218 // If in tablet, update the mode switcher icon based on the number of tabs |
| 219 // in the incognito tab strip. Doing this all the time simplifies a lot |
| 220 // of the state transition logic and setting the property to the same value |
| 221 // incurs no re-layout penalty. |
| 222 [self updateModeToggle]; |
| 223 } |
| 224 |
| 225 #pragma mark - Other public methods |
| 226 |
| 227 - (void)updateDeviceSharingManager { |
| 228 if (!self.deviceSharingManager) { |
| 229 self.deviceSharingManager = [[DeviceSharingManager alloc] init]; |
| 230 } |
| 231 [self.deviceSharingManager updateBrowserState:_browserState]; |
| 232 |
| 233 GURL activeURL; |
| 234 Tab* currentTab = [self.currentBVC tabModel].currentTab; |
| 235 // Set the active URL if there's a current tab and the current BVC is not OTR. |
| 236 if (currentTab && self.currentBVC != self.otrBVC) { |
| 237 activeURL = currentTab.url; |
| 238 } |
| 239 [self.deviceSharingManager updateActiveURL:activeURL]; |
| 240 } |
| 241 |
| 242 - (void)deleteIncognitoTabModelState: |
| 243 (BrowsingDataRemovalController*)removalController { |
| 244 // It is theoretically possible that a Tab has been added to |_otrTabModel| |
| 245 // since the deletion has been scheduled. It is unlikely to happen for real |
| 246 // because it would require superhuman speed. |
| 247 DCHECK(![_otrTabModel count]); |
| 248 DCHECK(_browserState); |
| 249 |
| 250 // Stop watching the OTR tab model's state for crashes. |
| 251 ios_internal::breakpad::StopMonitoringTabStateForTabModel(self.otrTabModel); |
| 252 |
| 253 // At this stage, a new OTR BVC shouldn't be lazily constructed by calling the |
| 254 // .otrBVC property getter. Instead, the ivar is accessed directly through the |
| 255 // following code. |
| 256 BOOL otrBVCIsCurrent = self.currentBVC == _otrBVC; |
| 257 @autoreleasepool { |
| 258 ios::ChromeBrowserState* otrBrowserState = |
| 259 _browserState->GetOffTheRecordChromeBrowserState(); |
| 260 [removalController browserStateDestroyed:otrBrowserState]; |
| 261 [_otrBVC browserStateDestroyed]; |
| 262 [_otrBVC release]; |
| 263 _otrBVC = nil; |
| 264 // There's no guarantee the tab model was ever added to the BVC (or even |
| 265 // that the BVC was created), so ensure the tab model gets notified. |
| 266 [_otrTabModel browserStateDestroyed]; |
| 267 if (_tabModelObserver) { |
| 268 [_otrTabModel removeObserver:_tabModelObserver]; |
| 269 } |
| 270 [_otrTabModel removeObserver:self]; |
| 271 [_otrTabModel release]; |
| 272 _otrTabModel = nil; |
| 273 if (otrBVCIsCurrent) { |
| 274 _currentBVC = nil; |
| 275 } |
| 276 } |
| 277 |
| 278 _browserState->DestroyOffTheRecordChromeBrowserState(); |
| 279 |
| 280 // An empty _otrTabModel must be created at this point, because it is then |
| 281 // possible to prevent the tabChanged notification being sent. Otherwise, |
| 282 // when it is created, a notification with no tabs will be sent, and it will |
| 283 // be immediately deleted. |
| 284 self.otrTabModel = [self buildOtrTabModel:YES]; |
| 285 DCHECK(![self.otrTabModel count]); |
| 286 DCHECK(_browserState->HasOffTheRecordChromeBrowserState()); |
| 287 |
| 288 if (otrBVCIsCurrent) { |
| 289 _currentBVC = self.otrBVC; |
| 290 } |
| 291 } |
| 292 |
| 293 - (void)updateModeToggle { |
| 294 if (IsIPadIdiom()) { |
| 295 self.mainBVC.hasModeToggleSwitch = self.otrTabModel.count ? YES : NO; |
| 296 self.otrBVC.hasModeToggleSwitch = YES; |
| 297 } |
| 298 } |
| 299 |
| 300 #pragma mark - Internal methods |
| 301 |
| 302 - (TabModel*)buildOtrTabModel:(BOOL)empty { |
| 303 DCHECK(_browserState); |
| 304 // Ensure that the OTR ChromeBrowserState is created. |
| 305 ios::ChromeBrowserState* otrBrowserState = |
| 306 _browserState->GetOffTheRecordChromeBrowserState(); |
| 307 DCHECK(otrBrowserState); |
| 308 return [self tabModelForBrowserState:otrBrowserState empty:empty]; |
| 309 } |
| 310 |
| 311 - (TabModel*)tabModelForBrowserState:(ios::ChromeBrowserState*)browserState |
| 312 empty:(BOOL)empty { |
| 313 SessionWindowIOS* sessionWindow = nil; |
| 314 if (!empty) { |
| 315 // Load existing saved tab model state. |
| 316 sessionWindow = [[SessionServiceIOS sharedService] |
| 317 loadWindowForBrowserState:browserState]; |
| 318 } |
| 319 |
| 320 // Create tab model from saved session (nil is ok). |
| 321 TabModel* tabModel = |
| 322 [[[TabModel alloc] initWithSessionWindow:sessionWindow |
| 323 sessionService:[SessionServiceIOS sharedService] |
| 324 browserState:browserState] autorelease]; |
| 325 // Add observers. |
| 326 if (_tabModelObserver) { |
| 327 [tabModel addObserver:_tabModelObserver]; |
| 328 [tabModel addObserver:self]; |
| 329 } |
| 330 ios_internal::breakpad::MonitorTabStateForTabModel(tabModel); |
| 331 |
| 332 return tabModel; |
| 333 } |
| 334 |
| 335 - (BrowserViewController*)bvcForBrowserState: |
| 336 (ios::ChromeBrowserState*)browserState |
| 337 tabModel:(TabModel*)tabModel { |
| 338 base::scoped_nsobject<BrowserViewControllerDependencyFactory> factory( |
| 339 [[BrowserViewControllerDependencyFactory alloc] |
| 340 initWithBrowserState:browserState]); |
| 341 return [[[BrowserViewController alloc] initWithTabModel:tabModel |
| 342 browserState:browserState |
| 343 dependencyFactory:factory] autorelease]; |
| 344 } |
| 345 |
| 346 @end |
OLD | NEW |