| Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_model.mm
|
| diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_model.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_model.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6f1a4e5df90d59c11346724d2d7f21dbb88f08ed
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_model.mm
|
| @@ -0,0 +1,410 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "components/browser_sync/profile_sync_service.h"
|
| +#include "components/sessions/core/session_id.h"
|
| +#include "components/signin/core/browser/signin_manager.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#include "ios/chrome/browser/signin/signin_manager_factory.h"
|
| +#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
|
| +#include "ios/chrome/browser/sync/sync_setup_service.h"
|
| +#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
|
| +#import "ios/chrome/browser/tabs/tab.h"
|
| +#import "ios/chrome/browser/tabs/tab_model.h"
|
| +#include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h"
|
| +#import "ios/chrome/browser/ui/tab_switcher/session_changes.h"
|
| +#import "ios/chrome/browser/ui/tab_switcher/tab_model_snapshot.h"
|
| +#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h"
|
| +#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model_private.h"
|
| +#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
|
| +
|
| +namespace ios_internal {
|
| +
|
| +bool IsLocalSession(SessionType sessionType) {
|
| + return sessionType == SessionType::OFF_THE_RECORD_SESSION ||
|
| + sessionType == SessionType::REGULAR_SESSION;
|
| +}
|
| +
|
| +} // namespace ios_internal
|
| +
|
| +namespace {
|
| +
|
| +class TagAndIndex {
|
| + public:
|
| + TagAndIndex(std::string const& tag, size_t index)
|
| + : tag_(tag), index_(index) {}
|
| + std::string tag_;
|
| + size_t index_;
|
| + bool operator<(const TagAndIndex& other) const { return tag_ < other.tag_; }
|
| +};
|
| +
|
| +void FillSetUsingSessions(synced_sessions::SyncedSessions const& sessions,
|
| + std::set<TagAndIndex>* set) {
|
| + DCHECK(set);
|
| + DCHECK(set->empty());
|
| + for (size_t i = 0; i < sessions.GetSessionCount(); ++i) {
|
| + set->insert(TagAndIndex(sessions.GetSession(i)->tag, i));
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +@interface TabSwitcherModel () {
|
| + // The browser state.
|
| + ios::ChromeBrowserState* _browserState; // weak
|
| + // The tab models.
|
| + TabModel* _mainTabModel; // weak
|
| + TabModel* _otrTabModel; // weak
|
| + // The delegate for event callbacks.
|
| + id<TabSwitcherModelDelegate> _delegate; // weak, owns us.
|
| + // The synced sessions. Must never be null.
|
| + std::unique_ptr<synced_sessions::SyncedSessions> _syncedSessions;
|
| + // The synced sessions change observer.
|
| + std::unique_ptr<synced_sessions::SyncedSessionsObserverBridge>
|
| + _syncedSessionsObserver;
|
| + // Snapshots of the |_mainTabModel| and |_otrTabModel|.
|
| + std::unique_ptr<TabModelSnapshot> _mainTabModelSnapshot;
|
| + std::unique_ptr<TabModelSnapshot> _otrTabModelSnapshot;
|
| + // The cache holding resized tabs snapshots.
|
| + base::scoped_nsobject<TabSwitcherCache> _cache;
|
| +}
|
| +
|
| +// Returns the type of the local session corresponding to the given |tabModel|.
|
| +// |tabModel| MUST be equal to either |_mainTabModel|, or |_otrTabModel|.
|
| +- (ios_internal::SessionType)typeOfLocalSessionForTabModel:(TabModel*)tabModel;
|
| +@end
|
| +
|
| +@implementation TabSwitcherModel
|
| +
|
| +@synthesize mainTabModel = _mainTabModel;
|
| +@synthesize otrTabModel = _otrTabModel;
|
| +
|
| +- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
|
| + delegate:(id<TabSwitcherModelDelegate>)delegate
|
| + mainTabModel:(TabModel*)mainTabModel
|
| + otrTabModel:(TabModel*)otrTabModel
|
| + withCache:(TabSwitcherCache*)cache {
|
| + DCHECK(browserState);
|
| + DCHECK(delegate);
|
| + // TODO(jif): DCHECK |mainTabModel| and |otrTabModel|.
|
| + self = [self init];
|
| + if (self) {
|
| + _browserState = browserState;
|
| + _delegate = delegate;
|
| + _syncedSessions.reset(new synced_sessions::SyncedSessions());
|
| + _syncedSessionsObserver.reset(
|
| + new synced_sessions::SyncedSessionsObserverBridge(self, _browserState));
|
| + _mainTabModel = mainTabModel;
|
| + _otrTabModel = otrTabModel;
|
| + _mainTabModelSnapshot.reset(new TabModelSnapshot(mainTabModel));
|
| + _otrTabModelSnapshot.reset(new TabModelSnapshot(otrTabModel));
|
| + [_mainTabModel addObserver:self];
|
| + [_otrTabModel addObserver:self];
|
| + _cache.reset([cache retain]);
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)setMainTabModel:(TabModel*)mainTabModel
|
| + otrTabModel:(TabModel*)otrTabModel {
|
| + [self replaceOldTabModel:&_mainTabModel withTabModel:mainTabModel];
|
| + [self replaceOldTabModel:&_otrTabModel withTabModel:otrTabModel];
|
| +}
|
| +
|
| +- (void)replaceOldTabModel:(TabModel**)oldTabModel
|
| + withTabModel:(TabModel*)newTabModel {
|
| + if (*oldTabModel == newTabModel)
|
| + return;
|
| + [*oldTabModel removeObserver:self];
|
| + *oldTabModel = newTabModel;
|
| + [newTabModel addObserver:self];
|
| + // Calling |tabModelChanged:| may trigger an animated refresh of the
|
| + // Tab Switcher's collection view.
|
| + // Here in |replaceOldTabModel:withTabModel:| the animation is undesirable.
|
| + [UIView performWithoutAnimation:^{
|
| + [self tabModelChanged:newTabModel];
|
| + }];
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + [_mainTabModel removeObserver:self];
|
| + [_otrTabModel removeObserver:self];
|
| + [super dealloc];
|
| +}
|
| +
|
| +- (NSInteger)sessionCount {
|
| + const NSInteger mainTabModelSessionCount = 1;
|
| + const NSInteger otrTabModelSessionCount = 1;
|
| + return mainTabModelSessionCount + otrTabModelSessionCount +
|
| + [self distantSessionCount];
|
| +}
|
| +
|
| +- (NSInteger)distantSessionCount {
|
| + return _syncedSessions->GetSessionCount();
|
| +}
|
| +
|
| +- (ios::ChromeBrowserState*)browserState {
|
| + return _browserState;
|
| +}
|
| +
|
| +- (TabModel*)tabModelForSessionOfType:(ios_internal::SessionType)type {
|
| + DCHECK(type == ios_internal::SessionType::OFF_THE_RECORD_SESSION ||
|
| + type == ios_internal::SessionType::REGULAR_SESSION);
|
| + return type == ios_internal::SessionType::OFF_THE_RECORD_SESSION
|
| + ? _otrTabModel
|
| + : _mainTabModel;
|
| +}
|
| +
|
| +- (NSInteger)numberOfTabsInLocalSessionOfType:(ios_internal::SessionType)type {
|
| + TabModelSnapshot* tabModelSnapshot = [self tabModelSnapshotForSession:type];
|
| + return tabModelSnapshot->tabs().size();
|
| +}
|
| +
|
| +- (Tab*)tabAtIndex:(NSUInteger)index
|
| + inLocalSessionOfType:(ios_internal::SessionType)type {
|
| + TabModelSnapshot* tabModelSnapshot = [self tabModelSnapshotForSession:type];
|
| + return tabModelSnapshot->tabs()[index];
|
| +}
|
| +
|
| +- (std::unique_ptr<TabModelSnapshot>)tabModelSnapshotForLocalSession:
|
| + (ios_internal::SessionType)type {
|
| + TabModel* tm = nullptr;
|
| + switch (type) {
|
| + case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
|
| + tm = _otrTabModel;
|
| + break;
|
| + case ios_internal::SessionType::REGULAR_SESSION:
|
| + tm = _mainTabModel;
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| + return base::MakeUnique<TabModelSnapshot>(tm);
|
| +}
|
| +
|
| +- (std::unique_ptr<const synced_sessions::DistantSession>)distantSessionForTag:
|
| + (std::string const&)tag {
|
| + syncer::SyncService* syncService =
|
| + IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
|
| + return base::MakeUnique<synced_sessions::DistantSession>(syncService, tag);
|
| +}
|
| +
|
| +- (std::string const&)tagOfDistantSessionAtIndex:(int)index {
|
| + return _syncedSessions->GetSession(index)->tag;
|
| +}
|
| +
|
| +- (TabSwitcherSignInPanelsType)signInPanelType {
|
| + TabSwitcherSignInPanelsType panelType = TabSwitcherSignInPanelsType::NO_PANEL;
|
| + if (![self isSignedIn]) {
|
| + panelType = TabSwitcherSignInPanelsType::PANEL_USER_SIGNED_OUT;
|
| + } else {
|
| + if (![self isSyncTabsEnabled]) {
|
| + panelType = TabSwitcherSignInPanelsType::PANEL_USER_SIGNED_IN_SYNC_OFF;
|
| + } else {
|
| + if (_syncedSessions->GetSessionCount() == 0) {
|
| + if ([self isFirstSyncCycleCompleted]) {
|
| + panelType = TabSwitcherSignInPanelsType::
|
| + PANEL_USER_SIGNED_IN_SYNC_ON_NO_SESSIONS;
|
| + } else {
|
| + panelType = TabSwitcherSignInPanelsType::
|
| + PANEL_USER_SIGNED_IN_SYNC_IN_PROGRESS;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return panelType;
|
| +}
|
| +
|
| +- (void)syncedSessionsChanged {
|
| + syncer::SyncService* syncService =
|
| + IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState);
|
| +
|
| + std::unique_ptr<synced_sessions::SyncedSessions> oldSyncedSessions(
|
| + new synced_sessions::SyncedSessions(syncService));
|
| + _syncedSessions.swap(oldSyncedSessions);
|
| +
|
| + // Notify about change in the sign in panel.
|
| + TabSwitcherSignInPanelsType panelType = [self signInPanelType];
|
| + [_delegate signInPanelChangedTo:panelType];
|
| +
|
| + if (panelType != TabSwitcherSignInPanelsType::NO_PANEL) {
|
| + // Do not show synced sessions.
|
| + _syncedSessions.reset(new synced_sessions::SyncedSessions());
|
| + }
|
| +
|
| + // Notify about changes in the synced sessions.
|
| + [TabSwitcherModel notifyDelegate:_delegate
|
| + aboutChangeFrom:*oldSyncedSessions
|
| + to:*_syncedSessions];
|
| +}
|
| +
|
| +- (BOOL)isSignedIn {
|
| + SigninManager* signin_manager =
|
| + ios::SigninManagerFactory::GetForBrowserState(_browserState);
|
| + return signin_manager->IsAuthenticated();
|
| +}
|
| +
|
| +- (BOOL)isSyncTabsEnabled {
|
| + DCHECK([self isSignedIn]);
|
| + SyncSetupService* service =
|
| + SyncSetupServiceFactory::GetForBrowserState(_browserState);
|
| + return !service->UserActionIsRequiredToHaveSyncWork();
|
| +}
|
| +
|
| +- (BOOL)isFirstSyncCycleCompleted {
|
| + return _syncedSessionsObserver->IsFirstSyncCycleCompleted();
|
| +}
|
| +
|
| ++ (void)notifyDelegate:(id<TabSwitcherModelDelegate>)delegate
|
| + aboutChangeFrom:(synced_sessions::SyncedSessions&)oldSessions
|
| + to:(synced_sessions::SyncedSessions&)newSessions {
|
| + // Compute and notify the delegate about removed or inserted sessions.
|
| + std::set<TagAndIndex> tagsOfOldSessions;
|
| + std::set<TagAndIndex> tagsOfNewSessions;
|
| + FillSetUsingSessions(oldSessions, &tagsOfOldSessions);
|
| + FillSetUsingSessions(newSessions, &tagsOfNewSessions);
|
| +
|
| + std::set<TagAndIndex> tagsOfRemovedSessions;
|
| + std::set<TagAndIndex> tagsOfInsertedSessions;
|
| + std::set_difference(
|
| + tagsOfOldSessions.begin(), tagsOfOldSessions.end(),
|
| + tagsOfNewSessions.begin(), tagsOfNewSessions.end(),
|
| + std::inserter(tagsOfRemovedSessions, tagsOfRemovedSessions.end()));
|
| + std::set_difference(
|
| + tagsOfNewSessions.begin(), tagsOfNewSessions.end(),
|
| + tagsOfOldSessions.begin(), tagsOfOldSessions.end(),
|
| + std::inserter(tagsOfInsertedSessions, tagsOfInsertedSessions.end()));
|
| +
|
| + NSArray* removedIndexesSorted = nil;
|
| + NSArray* insertedIndexesSorted = nil;
|
| + if (!tagsOfRemovedSessions.empty()) {
|
| + NSMutableArray* removedIndexes = [NSMutableArray array];
|
| + for (auto& tagAndIndex : tagsOfRemovedSessions) {
|
| + [removedIndexes addObject:[NSNumber numberWithInt:tagAndIndex.index_]];
|
| + }
|
| + removedIndexesSorted =
|
| + [removedIndexes sortedArrayUsingSelector:@selector(compare:)];
|
| + }
|
| + if (!tagsOfInsertedSessions.empty()) {
|
| + NSMutableArray* insertedIndexes = [NSMutableArray array];
|
| + for (auto& tagAndIndex : tagsOfInsertedSessions) {
|
| + [insertedIndexes addObject:[NSNumber numberWithInt:tagAndIndex.index_]];
|
| + }
|
| + insertedIndexesSorted =
|
| + [insertedIndexes sortedArrayUsingSelector:@selector(compare:)];
|
| + }
|
| + if (removedIndexesSorted || insertedIndexesSorted) {
|
| + [delegate distantSessionsRemovedAtSortedIndexes:removedIndexesSorted
|
| + insertedAtSortedIndexes:insertedIndexesSorted];
|
| + }
|
| + // Compute and notify the delegate about tabs that were removed or inserted
|
| + // in the sessions that weren't inserted or deleted.
|
| + std::set<TagAndIndex> tagsOfOtherSessions;
|
| + std::set_intersection(
|
| + tagsOfNewSessions.begin(), tagsOfNewSessions.end(),
|
| + tagsOfOldSessions.begin(), tagsOfOldSessions.end(),
|
| + std::inserter(tagsOfOtherSessions, tagsOfOtherSessions.end()));
|
| + for (TagAndIndex const& tagAndIndexOfSession : tagsOfOtherSessions) {
|
| + [delegate distantSessionMayNeedUpdate:tagAndIndexOfSession.tag_];
|
| + }
|
| +}
|
| +
|
| +- (ios_internal::SessionType)typeOfLocalSessionForTabModel:(TabModel*)tabModel {
|
| + DCHECK(tabModel == _mainTabModel || tabModel == _otrTabModel);
|
| + if (tabModel == _otrTabModel)
|
| + return ios_internal::SessionType::OFF_THE_RECORD_SESSION;
|
| + return ios_internal::SessionType::REGULAR_SESSION;
|
| +}
|
| +
|
| +- (TabModelSnapshot*)tabModelSnapshotForSession:
|
| + (ios_internal::SessionType)type {
|
| + switch (type) {
|
| + case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
|
| + return _otrTabModelSnapshot.get();
|
| + case ios_internal::SessionType::REGULAR_SESSION:
|
| + return _mainTabModelSnapshot.get();
|
| + default:
|
| + NOTREACHED();
|
| + return nullptr;
|
| + break;
|
| + }
|
| +}
|
| +
|
| +- (void)setTabModelSnapshot:(std::unique_ptr<TabModelSnapshot>)tabModelSnapshot
|
| + forSession:(ios_internal::SessionType)type {
|
| + switch (type) {
|
| + case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
|
| + _otrTabModelSnapshot = std::move(tabModelSnapshot);
|
| + break;
|
| + case ios_internal::SessionType::REGULAR_SESSION:
|
| + _mainTabModelSnapshot = std::move(tabModelSnapshot);
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +- (void)tabModelChanged:(TabModel*)tabModel {
|
| + ios_internal::SessionType sessionType =
|
| + [self typeOfLocalSessionForTabModel:tabModel];
|
| + [_delegate localSessionMayNeedUpdate:sessionType];
|
| +}
|
| +
|
| +#pragma mark - SyncedSessionsObserver
|
| +
|
| +- (void)reloadSessions {
|
| + [self syncedSessionsChanged];
|
| +}
|
| +
|
| +- (void)onSyncStateChanged {
|
| + [self syncedSessionsChanged];
|
| +}
|
| +
|
| +#pragma mark - TabModelObserver
|
| +
|
| +- (void)tabModel:(TabModel*)model didChangeTab:(Tab*)tab {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +- (void)tabModel:(TabModel*)model
|
| + didInsertTab:(Tab*)tab
|
| + atIndex:(NSUInteger)index
|
| + inForeground:(BOOL)fg {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +- (void)tabModel:(TabModel*)model
|
| + didRemoveTab:(Tab*)tab
|
| + atIndex:(NSUInteger)index {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +- (void)tabModel:(TabModel*)model
|
| + didMoveTab:(Tab*)tab
|
| + fromIndex:(NSUInteger)fromIndex
|
| + toIndex:(NSUInteger)toIndex {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +- (void)tabModel:(TabModel*)model
|
| + didReplaceTab:(Tab*)oldTab
|
| + withTab:(Tab*)newTab
|
| + atIndex:(NSUInteger)index {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +- (void)tabModel:(TabModel*)model
|
| + didChangeTabSnapshot:(Tab*)tab
|
| + withImage:(UIImage*)image {
|
| + [self tabModelChanged:model];
|
| +}
|
| +
|
| +@end
|
|
|