Chromium Code Reviews| Index: ios/chrome/browser/tabs/tab_model.mm |
| diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm |
| index 7a5c68f84f4c48e2a8e148d0691eaf8d1d6f06d1..2a344082d667b86259ade56e52b5ad9f66d541aa 100644 |
| --- a/ios/chrome/browser/tabs/tab_model.mm |
| +++ b/ios/chrome/browser/tabs/tab_model.mm |
| @@ -28,12 +28,16 @@ |
| #import "ios/chrome/browser/sessions/session_window.h" |
| #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
| #include "ios/chrome/browser/tab_parenting_global_observer.h" |
| +#import "ios/chrome/browser/tabs/legacy_tab_helper.h" |
| #import "ios/chrome/browser/tabs/tab.h" |
| #import "ios/chrome/browser/tabs/tab_model_list.h" |
| #import "ios/chrome/browser/tabs/tab_model_observers.h" |
| #import "ios/chrome/browser/tabs/tab_model_order_controller.h" |
| #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" |
| #import "ios/chrome/browser/xcallback_parameters.h" |
| +#import "ios/shared/chrome/browser/tabs/web_state_list.h" |
| +#import "ios/shared/chrome/browser/tabs/web_state_list_fast_enumeration_helper.h" |
| +#import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" |
| #import "ios/web/navigation/crw_session_certificate_policy_manager.h" |
| #import "ios/web/navigation/crw_session_controller.h" |
| #include "ios/web/public/browser_state.h" |
| @@ -79,38 +83,51 @@ void UpdateCertificatePolicyCacheFromWebState( |
| updateCertificatePolicyCache:policy_cache]; |
| } |
| -// Populates the certificate policy cache based on the WebStates of |tab_model|. |
| +// Populates the certificate policy cache based on the WebStates of |
| +// |web_state_list|. |
| void RestoreCertificatePolicyCacheFromModel( |
| const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
| - TabModel* tab_model) { |
| + WebStateList* web_state_list) { |
| DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| - for (Tab* tab in tab_model) |
| - UpdateCertificatePolicyCacheFromWebState(policy_cache, tab.webState); |
| + for (size_t index = 0; index < web_state_list->count(); ++index) { |
| + UpdateCertificatePolicyCacheFromWebState( |
| + policy_cache, web_state_list->GetWebStateAt(index)); |
| + } |
| } |
| // Scrubs the certificate policy cache of all certificates policies except |
| -// those for the current entries in |tab_model|. |
| +// those for the current entries in |web_state_list|. |
| void CleanCertificatePolicyCache( |
| base::CancelableTaskTracker* task_tracker, |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
| - TabModel* tab_model) { |
| - DCHECK(tab_model); |
| + WebStateList* web_state_list) { |
| DCHECK(policy_cache); |
| + DCHECK(web_state_list); |
| DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| task_tracker->PostTaskAndReply( |
| task_runner.get(), FROM_HERE, |
| base::Bind(&web::CertificatePolicyCache::ClearCertificatePolicies, |
| policy_cache), |
| base::Bind(&RestoreCertificatePolicyCacheFromModel, policy_cache, |
| - base::Unretained(tab_model))); |
| + base::Unretained(web_state_list))); |
| } |
| } // anonymous namespace |
| -@interface TabModel ()<TabUsageRecorderDelegate> { |
| - // Array of |Tab| objects. |
| - base::scoped_nsobject<NSMutableArray> _tabs; |
| +@interface TabModel ()<TabUsageRecorderDelegate, WebStateProxyFactory> { |
| + // Underlying shared model implementation. |
| + WebStateList _webStateList; |
| + |
| + // Helper providing NSFastEnumeration implementation over the WebStateList. |
| + base::scoped_nsobject<WebStateListFastEnumerationHelper> |
| + _fastEnumerationHelper; |
| + |
| + // Used to keep the Tabs alive while the corresponding WebStates are stored |
| + // in the WebStateList (as Tabs currently owns their WebState). Remove once |
|
rohitrao (ping after 24h)
2017/02/14 14:37:12
"own"
sdefresne
2017/02/14 16:45:46
Done.
|
| + // WebState owns the associated Tab. |
| + base::scoped_nsobject<NSMutableSet<Tab*>> _tabs; |
|
rohitrao (ping after 24h)
2017/02/14 14:37:12
Another naming option is to call this something li
sdefresne
2017/02/14 16:45:46
Done.
|
| + |
| // Maintains policy for where new tabs go and the selection when a tab |
| // is removed. |
| base::scoped_nsobject<TabModelOrderController> _orderController; |
| @@ -214,7 +231,7 @@ void CleanCertificatePolicyCache( |
| } |
| - (void)setCurrentTab:(Tab*)newTab { |
| - DCHECK([_tabs containsObject:newTab]); |
| + DCHECK_NE([self indexOfTab:newTab], static_cast<NSUInteger>(NSNotFound)); |
| if (_currentTab != newTab) { |
| base::RecordAction(base::UserMetricsAction("MobileTabSwitched")); |
| [self updateSnapshotCache:newTab]; |
| @@ -238,19 +255,23 @@ void CleanCertificatePolicyCache( |
| } |
| - (BOOL)isEmpty { |
| - return self.count == 0; |
| + return _webStateList.empty(); |
| } |
| - (NSUInteger)count { |
| - return [_tabs count]; |
| + return _webStateList.count(); |
| } |
| - (instancetype)initWithSessionWindow:(SessionWindowIOS*)window |
| sessionService:(SessionServiceIOS*)service |
| browserState:(ios::ChromeBrowserState*)browserState { |
| if ((self = [super init])) { |
| - _observers.reset([[TabModelObservers |
| - observersWithProtocol:@protocol(TabModelObserver)] retain]); |
| + _tabs.reset([[NSMutableSet alloc] init]); |
| + _observers.reset([[TabModelObservers observers] retain]); |
| + |
| + _fastEnumerationHelper.reset([[WebStateListFastEnumerationHelper alloc] |
| + initWithWebStateList:&_webStateList |
| + proxyFactory:self]); |
| _browserState = browserState; |
| DCHECK(_browserState); |
| @@ -268,7 +289,6 @@ void CleanCertificatePolicyCache( |
| } |
| _syncedWindowDelegate.reset(new TabModelSyncedWindowDelegate(self)); |
| - _tabs.reset([[NSMutableArray alloc] init]); |
| if (window) { |
| DCHECK([_observers empty]); |
| // Restore the session and reset the session metrics (as the event have |
| @@ -317,7 +337,7 @@ void CleanCertificatePolicyCache( |
| - (void)saveSessionImmediately:(BOOL)immediately { |
| // Do nothing if there are tabs in the model but no selected tab. This is |
| // a transitional state. |
| - if ((!_currentTab && [_tabs count]) || !_browserState) |
| + if ((!_currentTab && _webStateList.count()) || !_browserState) |
| return; |
| [_sessionService saveWindow:self.windowForSavingSession |
| forBrowserState:_browserState |
| @@ -325,17 +345,19 @@ void CleanCertificatePolicyCache( |
| } |
| - (Tab*)tabAtIndex:(NSUInteger)index { |
| - return [_tabs objectAtIndex:index]; |
| + web::WebState* webState = _webStateList.GetWebStateAt(index); |
| + return LegacyTabHelper::GetTabForWebState(webState); |
| } |
| - (NSUInteger)indexOfTab:(Tab*)tab { |
| - return [_tabs indexOfObject:tab]; |
| + size_t index = _webStateList.GetIndexOfWebState(tab.webState); |
| + return index == WebStateList::kInvalidIndex ? NSNotFound : index; |
| } |
| - (Tab*)tabWithWindowName:(NSString*)windowName { |
| if (!windowName) |
| return nil; |
| - for (Tab* tab in _tabs.get()) { |
| + for (Tab* tab in self) { |
| if ([windowName isEqualToString:tab.windowName]) { |
| return tab; |
| } |
| @@ -354,8 +376,8 @@ void CleanCertificatePolicyCache( |
| if (startIndex == NSNotFound) |
| return nil; |
| NSString* parentID = tab.tabId; |
| - for (NSUInteger i = startIndex + 1; i < [_tabs count]; ++i) { |
| - Tab* current = [_tabs objectAtIndex:i]; |
| + for (NSUInteger i = startIndex + 1; i < _webStateList.count(); ++i) { |
| + Tab* current = [self tabAtIndex:i]; |
| DCHECK([current navigationManager]); |
| CRWSessionController* sessionController = |
| [current navigationManager]->GetSessionController(); |
| @@ -380,7 +402,7 @@ void CleanCertificatePolicyCache( |
| DCHECK([tab navigationManager]); |
| NSInteger parentNavIndex = [tab navigationManager]->GetCurrentItemIndex(); |
| for (NSUInteger i = 0; i < stopIndex; ++i) { |
| - Tab* tabToCheck = [_tabs objectAtIndex:i]; |
| + Tab* tabToCheck = [self tabAtIndex:i]; |
| DCHECK([tabToCheck navigationManager]); |
| CRWSessionController* sessionController = |
| [tabToCheck navigationManager]->GetSessionController(); |
| @@ -407,8 +429,8 @@ void CleanCertificatePolicyCache( |
| // tabs whose opener's id and opener's navigation index match. The navigation |
| // index is used in addition to the session id to detect navigations changes |
| // within the same session. |
| - for (NSUInteger i = startIndex + 1; i < [_tabs count]; ++i) { |
| - Tab* tabToCheck = [_tabs objectAtIndex:i]; |
| + for (NSUInteger i = startIndex + 1; i < _webStateList.count(); ++i) { |
| + Tab* tabToCheck = [self tabAtIndex:i]; |
| DCHECK([tabToCheck navigationManager]); |
| CRWSessionController* sessionController = |
| [tabToCheck navigationManager]->GetSessionController(); |
| @@ -428,7 +450,7 @@ void CleanCertificatePolicyCache( |
| NSString* openerId = [tab navigationManager]->GetSessionController().openerId; |
| if (!openerId.length) // Short-circuit if opener is empty. |
| return nil; |
| - for (Tab* iteratedTab in _tabs.get()) { |
| + for (Tab* iteratedTab in self) { |
| if ([iteratedTab.tabId isEqualToString:openerId]) |
| return iteratedTab; |
| } |
| @@ -521,10 +543,11 @@ void CleanCertificatePolicyCache( |
| - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index { |
| DCHECK(tab); |
| - DCHECK(index <= [_tabs count]); |
| + DCHECK(![_tabs containsObject:tab]); |
| [tab fetchFavicon]; |
| - [_tabs insertObject:tab atIndex:index]; |
| + [_tabs addObject:tab]; |
| + _webStateList.InsertWebState(index, tab.webState); |
| TabParentingGlobalObserver::GetInstance()->OnTabParented(tab.webState); |
| [_observers tabModel:self didInsertTab:tab atIndex:index inForeground:NO]; |
| [_observers tabModelDidChangeTabCount:self]; |
| @@ -535,34 +558,29 @@ void CleanCertificatePolicyCache( |
| // state properly. If it does eventually become active, another save will |
| // be triggered to properly capture the end result. |
| [self saveSessionImmediately:NO]; |
| + |
| ++_newTabCount; |
| } |
| - (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex { |
|
rohitrao (ping after 24h)
2017/02/14 14:37:12
DCHECK([_tabs containsObject:tab]);
|
| - NSUInteger fromIndex = [self indexOfTab:tab]; |
| - DCHECK_NE(NSNotFound, static_cast<NSInteger>(fromIndex)); |
| - DCHECK_LT(toIndex, self.count); |
| - if (fromIndex == NSNotFound || toIndex >= self.count || |
| - fromIndex == toIndex) { |
| - return; |
| - } |
| - |
| - base::scoped_nsobject<Tab> tabSaver([tab retain]); |
| - [_tabs removeObject:tab]; |
| - [_tabs insertObject:tab atIndex:toIndex]; |
| - |
| + size_t fromIndex = _webStateList.GetIndexOfWebState(tab.webState); |
| + _webStateList.MoveWebStateAt(fromIndex, toIndex); |
| [_observers tabModel:self didMoveTab:tab fromIndex:fromIndex toIndex:toIndex]; |
| } |
| - (void)replaceTab:(Tab*)oldTab withTab:(Tab*)newTab { |
| NSUInteger index = [self indexOfTab:oldTab]; |
| DCHECK_NE(NSNotFound, static_cast<NSInteger>(index)); |
| + DCHECK([_tabs containsObject:oldTab]); |
| + DCHECK(![_tabs containsObject:newTab]); |
| base::scoped_nsobject<Tab> tabSaver([oldTab retain]); |
| [newTab fetchFavicon]; |
| - [_tabs replaceObjectAtIndex:index withObject:newTab]; |
| + [_tabs removeObject:oldTab]; |
| + [_tabs addObject:newTab]; |
| [newTab setParentTabModel:self]; |
| + _webStateList.ReplaceWebStateAt(index, newTab.webState); |
| TabParentingGlobalObserver::GetInstance()->OnTabParented(newTab.webState); |
| [_observers tabModel:self didReplaceTab:oldTab withTab:newTab atIndex:index]; |
| @@ -578,8 +596,8 @@ void CleanCertificatePolicyCache( |
| } |
| - (void)closeTabAtIndex:(NSUInteger)index { |
| - DCHECK(index < [_tabs count]); |
| - [self closeTab:[_tabs objectAtIndex:index]]; |
| + DCHECK(index < _webStateList.count()); |
| + [self closeTab:[self tabAtIndex:index]]; |
| } |
| - (void)closeTab:(Tab*)tab { |
| @@ -599,7 +617,7 @@ void CleanCertificatePolicyCache( |
| } |
| - (void)haltAllTabs { |
| - for (Tab* tab in _tabs.get()) { |
| + for (Tab* tab in self) { |
| [tab terminateNetworkActivity]; |
| } |
| } |
| @@ -636,7 +654,7 @@ void CleanCertificatePolicyCache( |
| } |
| - (void)resetAllWebViews { |
| - for (Tab* tab in _tabs.get()) { |
| + for (Tab* tab in self) { |
| [tab.webController reinitializeWebViewAndReload:(tab == _currentTab)]; |
| } |
| } |
| @@ -645,7 +663,7 @@ void CleanCertificatePolicyCache( |
| if (webUsageEnabled_ == webUsageEnabled) |
| return; |
| webUsageEnabled_ = webUsageEnabled; |
| - for (Tab* tab in _tabs.get()) { |
| + for (Tab* tab in self) { |
| tab.webUsageEnabled = webUsageEnabled; |
| } |
| } |
| @@ -660,7 +678,7 @@ void CleanCertificatePolicyCache( |
| if (!_browserState) |
| return referencedFiles; |
| // Check the currently open tabs for external files. |
| - for (Tab* tab in _tabs.get()) { |
| + for (Tab* tab in self) { |
| if (UrlIsExternalFileReference(tab.url)) { |
| NSString* fileName = base::SysUTF8ToNSString(tab.url.ExtractFileName()); |
| [referencedFiles addObject:fileName]; |
| @@ -697,7 +715,7 @@ void CleanCertificatePolicyCache( |
| // Called when a tab is closing, but before its CRWWebController is destroyed. |
| // Equivalent to DetachTabContentsAt() in Chrome's TabStripModel. |
| - (void)didCloseTab:(Tab*)closedTab { |
| - NSUInteger closedTabIndex = [_tabs indexOfObject:closedTab]; |
| + NSUInteger closedTabIndex = [self indexOfTab:closedTab]; |
| DCHECK(closedTab); |
| DCHECK(closedTabIndex != NSNotFound); |
| // Let the sessions::TabRestoreService know about that new tab. |
| @@ -716,8 +734,8 @@ void CleanCertificatePolicyCache( |
| // This needs to be called before the tab is removed from the list. |
| Tab* newSelection = |
| [_orderController determineNewSelectedTabFromRemovedTab:closedTab]; |
| + |
| base::scoped_nsobject<Tab> kungFuDeathGrip([closedTab retain]); |
| - [_tabs removeObject:closedTab]; |
| // If closing the current tab, clear |_currentTab| before sending any |
| // notification. This avoids various parts of the code getting confused |
| @@ -726,6 +744,10 @@ void CleanCertificatePolicyCache( |
| if (closedTab == _currentTab) |
| _currentTab.reset(nil); |
| + DCHECK([_tabs containsObject:closedTab]); |
| + [_tabs removeObject:closedTab]; |
| + |
| + _webStateList.RemoveWebStateAt(closedTabIndex); |
| [_observers tabModel:self didRemoveTab:closedTab atIndex:closedTabIndex]; |
| [_observers tabModelDidChangeTabCount:self]; |
| @@ -773,21 +795,28 @@ void CleanCertificatePolicyCache( |
| - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state |
| objects:(id*)objects |
| count:(NSUInteger)count { |
| - return [_tabs countByEnumeratingWithState:state objects:objects count:count]; |
| + return [_fastEnumerationHelper countByEnumeratingWithState:state |
| + objects:objects |
| + count:count]; |
| } |
| #pragma mark - TabUsageRecorderDelegate |
| - (NSUInteger)liveTabsCount { |
| NSUInteger count = 0; |
| - NSArray* tabs = _tabs.get(); |
| - for (Tab* tab in tabs) { |
| + for (Tab* tab in self) { |
| if ([tab.webController isViewAlive]) |
| count++; |
| } |
| return count; |
| } |
| +#pragma mark - WebStateProxyFactory |
| + |
| +- (id)proxyForWebState:(web::WebState*)webState { |
|
rohitrao (ping after 24h)
2017/02/14 14:37:12
What is this for?
|
| + return LegacyTabHelper::GetTabForWebState(webState); |
| +} |
| + |
| #pragma mark - Private methods |
| - (SessionWindowIOS*)windowForSavingSession { |
| @@ -952,7 +981,7 @@ void CleanCertificatePolicyCache( |
| if (!sessions.count) |
| return NO; |
| - size_t oldCount = [_tabs count]; |
| + size_t oldCount = _webStateList.count(); |
| web::WebState::CreateParams params(_browserState); |
| scoped_refptr<web::CertificatePolicyCache> policyCache = |
| web::BrowserState::GetCertificatePolicyCache(_browserState); |
| @@ -961,20 +990,20 @@ void CleanCertificatePolicyCache( |
| std::unique_ptr<web::WebState> webState = |
| web::WebState::Create(params, session); |
| DCHECK_EQ(webState->GetBrowserState(), _browserState); |
| - Tab* tab = |
| - [self insertTabWithWebState:std::move(webState) atIndex:[_tabs count]]; |
| + Tab* tab = [self insertTabWithWebState:std::move(webState) |
| + atIndex:_webStateList.count()]; |
| tab.webController.usePlaceholderOverlay = YES; |
| // Restore the CertificatePolicyCache (note that webState is invalid after |
| // passing it via move semantic to -insertTabWithWebState:atIndex:). |
| UpdateCertificatePolicyCacheFromWebState(policyCache, tab.webState); |
| } |
| - DCHECK_GT([_tabs count], oldCount); |
| + DCHECK_GT(_webStateList.count(), oldCount); |
| // Update the selected tab if there was a selected Tab in the saved session. |
| if (window.selectedIndex != NSNotFound) { |
| NSUInteger selectedIndex = window.selectedIndex + oldCount; |
| - DCHECK_LT(selectedIndex, [_tabs count]); |
| + DCHECK_LT(selectedIndex, _webStateList.count()); |
| DCHECK([self tabAtIndex:selectedIndex]); |
| [self changeSelectedTabFrom:_currentTab |
| to:[self tabAtIndex:selectedIndex] |
| @@ -984,7 +1013,7 @@ void CleanCertificatePolicyCache( |
| // If there was only one tab and it was the new tab page, clobber it. |
| BOOL closedNTPTab = NO; |
| if (oldCount == 1) { |
| - Tab* tab = [_tabs objectAtIndex:0]; |
| + Tab* tab = [self tabAtIndex:0]; |
| if (tab.url == GURL(kChromeUINewTabURL)) { |
| [self closeTab:tab]; |
| closedNTPTab = YES; |
| @@ -992,10 +1021,12 @@ void CleanCertificatePolicyCache( |
| } |
| } |
| if (_tabUsageRecorder) { |
| - _tabUsageRecorder->InitialRestoredTabs( |
| - _currentTab, |
| - [_tabs |
| - subarrayWithRange:NSMakeRange(oldCount, [_tabs count] - oldCount)]); |
| + NSMutableArray<Tab*>* restoredTabs = |
| + [NSMutableArray arrayWithCapacity:_webStateList.count() - oldCount]; |
| + for (size_t index = oldCount; index < _webStateList.count(); ++index) { |
| + [restoredTabs addObject:[self tabAtIndex:index]]; |
| + } |
| + _tabUsageRecorder->InitialRestoredTabs(_currentTab, restoredTabs); |
| } |
| return closedNTPTab; |
| } |
| @@ -1020,7 +1051,8 @@ void CleanCertificatePolicyCache( |
| CleanCertificatePolicyCache( |
| &_clearPoliciesTaskTracker, |
| web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), |
| - web::BrowserState::GetCertificatePolicyCache(_browserState), self); |
| + web::BrowserState::GetCertificatePolicyCache(_browserState), |
| + &_webStateList); |
| if (_tabUsageRecorder) |
| _tabUsageRecorder->AppDidEnterBackground(); |