| 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 81d4528f24c9a787ebdaa95e55e618a7fdc818d2..c9799c0721c30102ab1d53e2f204d053cc2c7a69 100644
|
| --- a/ios/chrome/browser/tabs/tab_model.mm
|
| +++ b/ios/chrome/browser/tabs/tab_model.mm
|
| @@ -4,6 +4,7 @@
|
|
|
| #import "ios/chrome/browser/tabs/tab_model.h"
|
|
|
| +#include <cstdint>
|
| #include <utility>
|
| #include <vector>
|
|
|
| @@ -28,12 +29,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 +84,62 @@ 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 (int 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 TabModelWebStateProxyFactory : NSObject<WebStateProxyFactory>
|
| +@end
|
| +
|
| +@implementation TabModelWebStateProxyFactory
|
| +
|
| +- (id)proxyForWebState:(web::WebState*)webState {
|
| + return LegacyTabHelper::GetTabForWebState(webState);
|
| +}
|
| +
|
| +@end
|
| +
|
| @interface TabModel ()<TabUsageRecorderDelegate> {
|
| - // Array of |Tab| objects.
|
| - base::scoped_nsobject<NSMutableArray> _tabs;
|
| + // 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 own their WebState). Remove once
|
| + // WebState owns the associated Tab.
|
| + base::scoped_nsobject<NSMutableSet<Tab*>> _tabRetainer;
|
| +
|
| // Maintains policy for where new tabs go and the selection when a tab
|
| // is removed.
|
| base::scoped_nsobject<TabModelOrderController> _orderController;
|
| @@ -214,7 +243,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 +267,25 @@ void CleanCertificatePolicyCache(
|
| }
|
|
|
| - (BOOL)isEmpty {
|
| - return self.count == 0;
|
| + return _webStateList.empty();
|
| }
|
|
|
| - (NSUInteger)count {
|
| - return [_tabs count];
|
| + DCHECK_GE(_webStateList.count(), 0);
|
| + return static_cast<NSUInteger>(_webStateList.count());
|
| }
|
|
|
| - (instancetype)initWithSessionWindow:(SessionWindowIOS*)window
|
| sessionService:(SessionServiceIOS*)service
|
| browserState:(ios::ChromeBrowserState*)browserState {
|
| if ((self = [super init])) {
|
| + _tabRetainer.reset([[NSMutableSet alloc] init]);
|
| _observers.reset([[TabModelObservers observers] retain]);
|
|
|
| + _fastEnumerationHelper.reset([[WebStateListFastEnumerationHelper alloc]
|
| + initWithWebStateList:&_webStateList
|
| + proxyFactory:[[TabModelWebStateProxyFactory alloc] init]]);
|
| +
|
| _browserState = browserState;
|
| DCHECK(_browserState);
|
|
|
| @@ -267,7 +302,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
|
| @@ -316,7 +350,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
|
| @@ -324,17 +358,24 @@ void CleanCertificatePolicyCache(
|
| }
|
|
|
| - (Tab*)tabAtIndex:(NSUInteger)index {
|
| - return [_tabs objectAtIndex:index];
|
| + DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
|
| + return LegacyTabHelper::GetTabForWebState(
|
| + _webStateList.GetWebStateAt(static_cast<int>(index)));
|
| }
|
|
|
| - (NSUInteger)indexOfTab:(Tab*)tab {
|
| - return [_tabs indexOfObject:tab];
|
| + int index = _webStateList.GetIndexOfWebState(tab.webState);
|
| + if (index == WebStateList::kInvalidIndex)
|
| + return NSNotFound;
|
| +
|
| + DCHECK_GE(index, 0);
|
| + return static_cast<NSUInteger>(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;
|
| }
|
| @@ -353,8 +394,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 < self.count; ++i) {
|
| + Tab* current = [self tabAtIndex:i];
|
| DCHECK([current navigationManager]);
|
| CRWSessionController* sessionController =
|
| [current navigationManager]->GetSessionController();
|
| @@ -379,8 +420,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 < self.count; ++i) {
|
| + Tab* tabToCheck = [self tabAtIndex:i];
|
| DCHECK([tabToCheck navigationManager]);
|
| CRWSessionController* sessionController =
|
| [tabToCheck navigationManager]->GetSessionController();
|
| @@ -400,7 +441,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;
|
| }
|
| @@ -493,10 +534,12 @@ void CleanCertificatePolicyCache(
|
|
|
| - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index {
|
| DCHECK(tab);
|
| - DCHECK(index <= [_tabs count]);
|
| + DCHECK(![_tabRetainer containsObject:tab]);
|
| + DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
|
| [tab fetchFavicon];
|
| - [_tabs insertObject:tab atIndex:index];
|
|
|
| + [_tabRetainer addObject:tab];
|
| + _webStateList.InsertWebState(static_cast<int>(index), tab.webState);
|
| TabParentingGlobalObserver::GetInstance()->OnTabParented(tab.webState);
|
| [_observers tabModel:self didInsertTab:tab atIndex:index inForeground:NO];
|
| [_observers tabModelDidChangeTabCount:self];
|
| @@ -507,36 +550,41 @@ 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 {
|
| - 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) {
|
| + if ([self tabAtIndex:toIndex] == tab)
|
| return;
|
| - }
|
| -
|
| - base::scoped_nsobject<Tab> tabSaver([tab retain]);
|
| - [_tabs removeObject:tab];
|
| - [_tabs insertObject:tab atIndex:toIndex];
|
|
|
| + DCHECK([_tabRetainer containsObject:tab]);
|
| + DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX));
|
| + int fromIndex = _webStateList.GetIndexOfWebState(tab.webState);
|
| + _webStateList.MoveWebStateAt(fromIndex, static_cast<int>(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([_tabRetainer containsObject:oldTab]);
|
| + DCHECK(![_tabRetainer containsObject:newTab]);
|
| +
|
| + int index = _webStateList.GetIndexOfWebState(oldTab.webState);
|
| + DCHECK_NE(index, WebStateList::kInvalidIndex);
|
| + DCHECK_GE(index, 0);
|
|
|
| base::scoped_nsobject<Tab> tabSaver([oldTab retain]);
|
| [newTab fetchFavicon];
|
| - [_tabs replaceObjectAtIndex:index withObject:newTab];
|
| + [_tabRetainer removeObject:oldTab];
|
| + [_tabRetainer addObject:newTab];
|
| [newTab setParentTabModel:self];
|
|
|
| + _webStateList.ReplaceWebStateAt(index, newTab.webState);
|
| TabParentingGlobalObserver::GetInstance()->OnTabParented(newTab.webState);
|
| - [_observers tabModel:self didReplaceTab:oldTab withTab:newTab atIndex:index];
|
| + [_observers tabModel:self
|
| + didReplaceTab:oldTab
|
| + withTab:newTab
|
| + atIndex:static_cast<NSUInteger>(index)];
|
|
|
| if (self.currentTab == oldTab)
|
| [self changeSelectedTabFrom:nil to:newTab persistState:NO];
|
| @@ -550,8 +598,8 @@ void CleanCertificatePolicyCache(
|
| }
|
|
|
| - (void)closeTabAtIndex:(NSUInteger)index {
|
| - DCHECK(index < [_tabs count]);
|
| - [self closeTab:[_tabs objectAtIndex:index]];
|
| + DCHECK(index < self.count);
|
| + [self closeTab:[self tabAtIndex:index]];
|
| }
|
|
|
| - (void)closeTab:(Tab*)tab {
|
| @@ -571,7 +619,7 @@ void CleanCertificatePolicyCache(
|
| }
|
|
|
| - (void)haltAllTabs {
|
| - for (Tab* tab in _tabs.get()) {
|
| + for (Tab* tab in self) {
|
| [tab terminateNetworkActivity];
|
| }
|
| }
|
| @@ -608,7 +656,7 @@ void CleanCertificatePolicyCache(
|
| }
|
|
|
| - (void)resetAllWebViews {
|
| - for (Tab* tab in _tabs.get()) {
|
| + for (Tab* tab in self) {
|
| [tab.webController reinitializeWebViewAndReload:(tab == _currentTab)];
|
| }
|
| }
|
| @@ -617,7 +665,7 @@ void CleanCertificatePolicyCache(
|
| if (webUsageEnabled_ == webUsageEnabled)
|
| return;
|
| webUsageEnabled_ = webUsageEnabled;
|
| - for (Tab* tab in _tabs.get()) {
|
| + for (Tab* tab in self) {
|
| tab.webUsageEnabled = webUsageEnabled;
|
| }
|
| }
|
| @@ -632,7 +680,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];
|
| @@ -669,9 +717,12 @@ 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];
|
| DCHECK(closedTab);
|
| - DCHECK(closedTabIndex != NSNotFound);
|
| + DCHECK([_tabRetainer containsObject:closedTab]);
|
| + int closedTabIndex = _webStateList.GetIndexOfWebState(closedTab.webState);
|
| + DCHECK_NE(closedTabIndex, WebStateList::kInvalidIndex);
|
| + DCHECK_GE(closedTabIndex, 0);
|
| +
|
| // Let the sessions::TabRestoreService know about that new tab.
|
| sessions::TabRestoreService* restoreService =
|
| _browserState
|
| @@ -683,13 +734,13 @@ void CleanCertificatePolicyCache(
|
| if (restoreService && (![self isNTPTab:closedTab] || itemCount > 1)) {
|
| restoreService->CreateHistoricalTab(
|
| sessions::IOSLiveTab::GetForWebState(closedTab.webState),
|
| - static_cast<int>(closedTabIndex));
|
| + closedTabIndex);
|
| }
|
| // 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
|
| @@ -698,7 +749,13 @@ void CleanCertificatePolicyCache(
|
| if (closedTab == _currentTab)
|
| _currentTab.reset(nil);
|
|
|
| - [_observers tabModel:self didRemoveTab:closedTab atIndex:closedTabIndex];
|
| + DCHECK([_tabRetainer containsObject:closedTab]);
|
| + [_tabRetainer removeObject:closedTab];
|
| +
|
| + _webStateList.DetachWebStateAt(closedTabIndex);
|
| + [_observers tabModel:self
|
| + didRemoveTab:closedTab
|
| + atIndex:static_cast<NSUInteger>(closedTabIndex)];
|
| [_observers tabModelDidChangeTabCount:self];
|
|
|
| // Current tab has closed, update the selected tab and swap in its
|
| @@ -745,15 +802,16 @@ 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++;
|
| }
|
| @@ -924,7 +982,9 @@ void CleanCertificatePolicyCache(
|
| if (!sessions.count)
|
| return NO;
|
|
|
| - size_t oldCount = [_tabs count];
|
| + int oldCount = _webStateList.count();
|
| + DCHECK_GE(oldCount, 0);
|
| +
|
| web::WebState::CreateParams params(_browserState);
|
| scoped_refptr<web::CertificatePolicyCache> policyCache =
|
| web::BrowserState::GetCertificatePolicyCache(_browserState);
|
| @@ -934,19 +994,19 @@ void CleanCertificatePolicyCache(
|
| web::WebState::Create(params, session);
|
| DCHECK_EQ(webState->GetBrowserState(), _browserState);
|
| Tab* tab =
|
| - [self insertTabWithWebState:std::move(webState) atIndex:[_tabs count]];
|
| + [self insertTabWithWebState:std::move(webState) atIndex:self.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, self.count);
|
| DCHECK([self tabAtIndex:selectedIndex]);
|
| [self changeSelectedTabFrom:_currentTab
|
| to:[self tabAtIndex:selectedIndex]
|
| @@ -956,7 +1016,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;
|
| @@ -964,10 +1024,13 @@ void CleanCertificatePolicyCache(
|
| }
|
| }
|
| if (_tabUsageRecorder) {
|
| - _tabUsageRecorder->InitialRestoredTabs(
|
| - _currentTab,
|
| - [_tabs
|
| - subarrayWithRange:NSMakeRange(oldCount, [_tabs count] - oldCount)]);
|
| + NSMutableArray<Tab*>* restoredTabs =
|
| + [NSMutableArray arrayWithCapacity:_webStateList.count() - oldCount];
|
| + for (int index = oldCount; index < _webStateList.count(); ++index) {
|
| + web::WebState* webState = _webStateList.GetWebStateAt(index);
|
| + [restoredTabs addObject:LegacyTabHelper::GetTabForWebState(webState)];
|
| + }
|
| + _tabUsageRecorder->InitialRestoredTabs(_currentTab, restoredTabs);
|
| }
|
| return closedNTPTab;
|
| }
|
| @@ -992,7 +1055,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();
|
|
|