blob: 258f2e031d0540855158a5748069651d38c9b22a [file] [log] [blame]
// 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_controller.h"
#include "base/ios/block_types.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/sys_string_conversions.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sessions/core/tab_restore_service_helper.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync_sessions/open_tabs_ui_delegate.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/feature_engagement/tracker_util.h"
#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
#include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_factory.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
#import "ios/chrome/browser/tabs/tab.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#include "ios/chrome/browser/ui/commands/application_commands.h"
#include "ios/chrome/browser/ui/commands/browser_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
#import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
#include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h"
#import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_in_sync_off_view.h"
#import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_in_sync_on_no_sessions_view.h"
#import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_out_view.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_overlay_view.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_view.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_session_cell_data.h"
#include "ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_context.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_view.h"
#import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
#include "ios/chrome/browser/ui/ui_util.h"
#import "ios/chrome/browser/ui/uikit_ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/chrome/grit/ios_theme_resources.h"
#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
#import "ios/web/public/navigation_manager.h"
#include "ios/web/public/referrer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Offsets for computing the panels' indexes in the TabSwitcherView.
const int kSignInPromoPanelIndex = 2;
const int kHeaderDistantSessionIndexOffset = 2;
const int kLocalTabsOffTheRecordPanelIndex = 0;
const int kLocalTabsOnTheRecordPanelIndex = 1;
// The duration of the tab switcher toggle animation.
const CGFloat kTransitionAnimationDuration = 0.25;
// The height of the browser view controller header.
const CGFloat kHeaderHeight = 39;
enum class TransitionType {
TRANSITION_PRESENT,
TRANSITION_DISMISS,
};
enum class SnapshotViewOption {
SNAPSHOT_VIEW, // Try taking a snapshot view if possible.
CLIENT_RENDERING, // Use [UIView renderInContext:] to render a bitmap and set
// it as the backing store of a view's layer.
};
} // namespace
@interface TabSwitcherController ()<TabSwitcherModelDelegate,
TabSwitcherViewDelegate,
TabSwitcherHeaderViewDelegate,
TabSwitcherHeaderViewDataSource,
TabSwitcherPanelControllerDelegate> {
// weak.
ios::ChromeBrowserState* _browserState;
// weak.
__weak id<TabSwitcherDelegate> _delegate;
// The model selected when the tab switcher was toggled.
// weak.
__weak TabModel* _onLoadActiveModel;
// The view this controller manages.
TabSwitcherView* _tabSwitcherView;
// The list of panels controllers for distant sessions.
NSMutableArray* _controllersOfDistantSessions;
// The panel controllers for the local sessions.
TabSwitcherPanelController* _onTheRecordSession;
TabSwitcherPanelController* _offTheRecordSession;
// The model storing the state of what is shown by the tab switcher.
TabSwitcherModel* _tabSwitcherModel;
// Stores the current sign-in panel type.
TabSwitcherSignInPanelsType _signInPanelType;
// Cache for the panel's cells.
TabSwitcherCache* _cache;
// Stores the background color of the window when the tab switcher was
// presented.
UIColor* _initialWindowBackgroundColor;
// Indicate whether a previous promo panel header cell should be removed or
// added.
BOOL _shouldRemovePromoPanelHeaderCell;
BOOL _shouldAddPromoPanelHeaderCell;
// Handles command dispatching.
CommandDispatcher* _dispatcher;
}
// Updates the window background color to the tab switcher's background color.
// The original background color can be restored by calling
// -restoreWindowBackgroundColor.
- (void)updateWindowBackgroundColor;
// Restores the tab switcher's window background color to the value it had
// before presenting it.
- (void)restoreWindowBackgroundColor;
// Performs the tab switcher transition according to the |transitionType| and
// call the completion block at the end of the transition. The transition will
// be animated according to the |animated| parameter. |completion| block must
// not be nil.
- (void)performTabSwitcherTransition:(TransitionType)transitionType
withModel:(TabModel*)tabModel
animated:(BOOL)animated
withCompletion:(ProceduralBlock)completion;
// Returns the index of the currently selected panel.
- (NSInteger)currentPanelIndex;
// Returns the session type of the panel and the given index.
- (TabSwitcherSessionType)sessionTypeForPanelIndex:(NSInteger)panelIndex;
// Returns the tab model corresponding to the given session type.
// There is no tab model for distant sessions so it returns nil for distant
// sessions type.
- (TabModel*)tabModelForSessionType:(TabSwitcherSessionType)sessionType;
// Returns the tab model of the currently selected tab.
- (TabModel*)currentSelectedModel;
// Calls tabSwitcherDismissWithModel:animated: with the |animated| parameter
// set to YES.
- (void)tabSwitcherDismissWithModel:(TabModel*)model;
// Dismisses the tab switcher using the given tab model. The completion block is
// called at the end of the animation. The tab switcher delegate method
// -tabSwitcherPresentationTransitionDidEnd: must be called in the completion
// block. The dismissal of the tab switcher will be animated if the |animated|
// parameter is set to YES.
- (void)tabSwitcherDismissWithModel:(TabModel*)model
animated:(BOOL)animated
withCompletion:(ProceduralBlock)completion;
// Dismisses the tab switcher using the currently selected tab's tab model.
- (void)tabSwitcherDismissWithCurrentSelectedModel;
// Scrolls the scrollview to show the panel displaying the |selectedTabModel|.
- (void)selectPanelForTabModel:(TabModel*)selectedTabModel;
// Dismisses the tab switcher and create a new tab using the url, position and
// transition on the given tab model.
- (Tab*)dismissWithNewTabAnimation:(const GURL&)url
atIndex:(NSUInteger)position
transition:(ui::PageTransition)transition
tabModel:(TabModel*)tabModel;
// Add a promo panel corresponding to the panel type argument.
// Should only be called from inititalizer and signInPanelChangedTo:.
- (void)addPromoPanelForSignInPanelType:(TabSwitcherSignInPanelsType)panelType;
// Updates cells of local panels.
- (void)updateLocalPanelsCells;
@end
@implementation TabSwitcherController
@synthesize transitionContext = _transitionContext;
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
mainTabModel:(TabModel*)mainTabModel
otrTabModel:(TabModel*)otrTabModel
activeTabModel:(TabModel*)activeTabModel
applicationCommandEndpoint:(id<ApplicationCommands>)endpoint {
DCHECK(mainTabModel);
DCHECK(otrTabModel);
DCHECK(activeTabModel == otrTabModel || activeTabModel == mainTabModel);
self = [super initWithNibName:nil bundle:nil];
if (self) {
_browserState = browserState;
_dispatcher = [[CommandDispatcher alloc] init];
[_dispatcher startDispatchingToTarget:self
forProtocol:@protocol(BrowserCommands)];
[_dispatcher startDispatchingToTarget:endpoint
forProtocol:@protocol(ApplicationCommands)];
// self.dispatcher shouldn't be used in this init method, so duplicate the
// typecast to pass dispatcher into child objects.
id<ApplicationCommands, BrowserCommands> passableDispatcher =
static_cast<id<ApplicationCommands, BrowserCommands>>(_dispatcher);
_onLoadActiveModel = activeTabModel;
_cache = [[TabSwitcherCache alloc] init];
[_cache setMainTabModel:mainTabModel otrTabModel:otrTabModel];
_tabSwitcherModel =
[[TabSwitcherModel alloc] initWithBrowserState:browserState
delegate:self
mainTabModel:mainTabModel
otrTabModel:otrTabModel
withCache:_cache];
_controllersOfDistantSessions = [[NSMutableArray alloc] init];
[self loadTabSwitcherView];
_onTheRecordSession = [[TabSwitcherPanelController alloc]
initWithModel:_tabSwitcherModel
forLocalSessionOfType:TabSwitcherSessionType::REGULAR_SESSION
withCache:_cache
browserState:_browserState
dispatcher:passableDispatcher];
[_onTheRecordSession setDelegate:self];
_offTheRecordSession = [[TabSwitcherPanelController alloc]
initWithModel:_tabSwitcherModel
forLocalSessionOfType:TabSwitcherSessionType::OFF_THE_RECORD_SESSION
withCache:_cache
browserState:_browserState
dispatcher:passableDispatcher];
[_offTheRecordSession setDelegate:self];
[_tabSwitcherView addPanelView:[_offTheRecordSession view]
atIndex:kLocalTabsOffTheRecordPanelIndex];
[_tabSwitcherView addPanelView:[_onTheRecordSession view]
atIndex:kLocalTabsOnTheRecordPanelIndex];
[self addPromoPanelForSignInPanelType:[_tabSwitcherModel signInPanelType]];
[[_tabSwitcherView headerView] reloadData];
[_tabSwitcherModel syncedSessionsChanged];
[self selectPanelForTabModel:_onLoadActiveModel];
}
return self;
}
#pragma mark - UIViewController
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
- (CGRect)tabSwitcherInitialFrame {
return [[UIScreen mainScreen] bounds];
}
- (void)loadTabSwitcherView {
DCHECK(![_tabSwitcherView superview]);
_tabSwitcherView =
[[TabSwitcherView alloc] initWithFrame:[self tabSwitcherInitialFrame]];
[_tabSwitcherView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight];
[_tabSwitcherView setDelegate:self];
[[_tabSwitcherView headerView] setDelegate:self];
[[_tabSwitcherView headerView] setDataSource:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight];
[_tabSwitcherView setFrame:self.view.bounds];
[self.view addSubview:_tabSwitcherView];
}
#pragma mark - UIResponder
- (NSArray*)keyCommands {
__weak TabSwitcherController* weakSelf = self;
return @[
[UIKeyCommand
cr_keyCommandWithInput:@"t"
modifierFlags:UIKeyModifierCommand
title:l10n_util::GetNSStringWithFixup(
IDS_IOS_TOOLS_MENU_NEW_TAB)
action:^{
TabSwitcherController* strongSelf = weakSelf;
if (!strongSelf)
return;
if ([strongSelf currentPanelIndex] ==
kLocalTabsOffTheRecordPanelIndex) {
[strongSelf openNewTabInPanelAtIndex:
kLocalTabsOffTheRecordPanelIndex];
} else {
[strongSelf openNewTabInPanelAtIndex:
kLocalTabsOnTheRecordPanelIndex];
}
}],
[UIKeyCommand
cr_keyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
title:l10n_util::GetNSStringWithFixup(
IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB)
action:^{
[weakSelf openNewTabInPanelAtIndex:
kLocalTabsOffTheRecordPanelIndex];
}],
[UIKeyCommand
cr_keyCommandWithInput:@"t"
modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
title:l10n_util::GetNSStringWithFixup(
IDS_IOS_KEYBOARD_REOPEN_CLOSED_TAB)
action:^{
[weakSelf reopenClosedTab];
}],
[UIKeyCommand
cr_keyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand
title:nil
action:^{
TabSwitcherController* strongSelf = weakSelf;
if (!strongSelf)
return;
if ([strongSelf currentPanelIndex] ==
kLocalTabsOffTheRecordPanelIndex) {
[strongSelf openNewTabInPanelAtIndex:
kLocalTabsOffTheRecordPanelIndex];
} else {
[strongSelf openNewTabInPanelAtIndex:
kLocalTabsOnTheRecordPanelIndex];
}
}],
];
}
#pragma mark - TabSwitcher protocol implementation
- (void)restoreInternalStateWithMainTabModel:(TabModel*)mainModel
otrTabModel:(TabModel*)otrModel
activeTabModel:(TabModel*)activeModel {
_onLoadActiveModel = activeModel;
[_cache setMainTabModel:mainModel otrTabModel:otrModel];
[_tabSwitcherModel setMainTabModel:mainModel otrTabModel:otrModel];
[self selectPanelForTabModel:activeModel];
}
- (void)setOtrTabModel:(TabModel*)otrModel {
[_cache setMainTabModel:[_cache mainTabModel] otrTabModel:otrModel];
[_tabSwitcherModel setMainTabModel:[_tabSwitcherModel mainTabModel]
otrTabModel:otrModel];
}
- (void)showWithSelectedTabAnimation {
[self updateWindowBackgroundColor];
[self performTabSwitcherTransition:TransitionType::TRANSITION_PRESENT
withModel:_onLoadActiveModel
animated:YES
withCompletion:^{
[self.delegate
tabSwitcherPresentationTransitionDidEnd:self];
}];
}
- (Tab*)dismissWithNewTabAnimationToModel:(TabModel*)targetModel
withURL:(const GURL&)url
atIndex:(NSUInteger)position
transition:(ui::PageTransition)transition {
if (targetModel == [_tabSwitcherModel mainTabModel])
[_tabSwitcherView selectPanelAtIndex:kLocalTabsOnTheRecordPanelIndex];
else
[_tabSwitcherView selectPanelAtIndex:kLocalTabsOffTheRecordPanelIndex];
return [self dismissWithNewTabAnimation:url
atIndex:position
transition:transition
tabModel:targetModel];
}
- (void)setDelegate:(id<TabSwitcherDelegate>)delegate {
_delegate = delegate;
if (delegate == nullptr)
[_tabSwitcherModel setMainTabModel:nil otrTabModel:nil];
}
- (id<TabSwitcherDelegate>)delegate {
return _delegate;
}
- (id<ApplicationCommands, BrowserCommands>)dispatcher {
return static_cast<id<ApplicationCommands, BrowserCommands>>(_dispatcher);
}
#pragma mark - BrowserCommands
- (void)openNewTab:(OpenNewTabCommand*)command {
// Ensure that the right mode is showing.
NSInteger panelIndex = command.incognito ? kLocalTabsOffTheRecordPanelIndex
: kLocalTabsOnTheRecordPanelIndex;
[_tabSwitcherView selectPanelAtIndex:panelIndex];
const TabSwitcherSessionType panelSessionType =
(command.incognito) ? TabSwitcherSessionType::OFF_THE_RECORD_SESSION
: TabSwitcherSessionType::REGULAR_SESSION;
TabModel* model = [self tabModelForSessionType:panelSessionType];
// Either send or don't send the "New Tab Opened" or "Incognito Tab Opened" to
// the feature_engageament::Tracker based on |command.userInitiated| and
// |command.incognito|.
feature_engagement::NotifyNewTabEventForCommand(model.browserState, command);
[self dismissWithNewTabAnimation:GURL(kChromeUINewTabURL)
atIndex:NSNotFound
transition:ui::PAGE_TRANSITION_TYPED
tabModel:model];
}
#pragma mark - Private
- (void)updateWindowBackgroundColor {
DCHECK(!_initialWindowBackgroundColor);
_initialWindowBackgroundColor = self.view.window.backgroundColor;
self.view.window.backgroundColor = [[MDCPalette greyPalette] tint900];
}
- (void)restoreWindowBackgroundColor {
self.view.window.backgroundColor = _initialWindowBackgroundColor;
_initialWindowBackgroundColor = nil;
}
- (UIView*)snapshotViewForView:(UIView*)inputView
withModel:(TabModel*)tabModel
option:(SnapshotViewOption)option {
if (inputView) {
if (option == SnapshotViewOption::SNAPSHOT_VIEW) {
UIView* view = [inputView snapshotViewAfterScreenUpdates:NO];
if (view)
return view;
}
// If the view has just been created, it has not been rendered by Core
// Animation and the snapshot view can't be generated. In that case we
// trigger a client side rendering of the view and use the rendered image
// as the backing store of a view layer.
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size,
inputView.opaque, 0);
[inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
[view layer].contents = static_cast<id>(screenshot.CGImage);
return view;
} else {
// When the input view is nil, we can't generate a snapshot so a placeholder
// is returned.
UIColor* backgroundColor = [tabModel isOffTheRecord]
? [[MDCPalette greyPalette] tint700]
: [[MDCPalette greyPalette] tint100];
UIView* placeholdView = [[UIView alloc] initWithFrame:CGRectZero];
placeholdView.backgroundColor = backgroundColor;
return placeholdView;
}
}
- (TabSwitcherTransitionContextContent*)transitionContextContentForTabModel:
(TabModel*)tabModel {
if ([tabModel isOffTheRecord])
return self.transitionContext.incognitoContent;
else
return self.transitionContext.regularContent;
}
- (UIImage*)updateScreenshotForCellIfNeeded:(TabSwitcherLocalSessionCell*)cell
tabModel:(TabModel*)tabModel {
if (cell.snapshot)
return cell.snapshot;
UIColor* backgroundColor = [tabModel isOffTheRecord]
? [[MDCPalette greyPalette] tint700]
: [UIColor whiteColor];
CGRect rect = CGRectMake(0, 0, 1, 1);
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
[backgroundColor setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (void)performTabSwitcherTransition:(TransitionType)transitionType
withModel:(TabModel*)tabModel
animated:(BOOL)animated
withCompletion:(ProceduralBlock)completion {
switch (transitionType) {
case TransitionType::TRANSITION_PRESENT:
base::RecordAction(base::UserMetricsAction("MobileTabSwitcherPresented"));
break;
case TransitionType::TRANSITION_DISMISS:
base::RecordAction(base::UserMetricsAction("MobileTabSwitcherDismissed"));
break;
}
DCHECK(completion);
DCHECK([self transitionContext]);
[[self view] setUserInteractionEnabled:NO];
TabSwitcherTransitionContextContent* transitionContextContent =
[self transitionContextContentForTabModel:tabModel];
DCHECK(transitionContextContent);
ToolbarController* toolbarController =
[[self.delegate tabSwitcherTransitionToolbarOwner]
relinquishedToolbarController];
Tab* selectedTab = [tabModel currentTab];
NSInteger selectedTabIndex = [tabModel indexOfTab:selectedTab];
TabSwitcherLocalSessionCell* selectedCell = nil;
CGRect selectedCellFrame = CGRectZero;
TabSwitcherPanelController* selectedPanel =
[self panelControllerForTabModel:tabModel];
// Force invalidation and update of the tab switcher view and collection view
// layout to take into account any changes in the size of the view controller
// while the tab switcher was not shown.
if (transitionType == TransitionType::TRANSITION_PRESENT) {
[[self view] layoutIfNeeded];
[[selectedPanel collectionView].collectionViewLayout invalidateLayout];
}
if (selectedTabIndex != NSNotFound) {
selectedCell =
[selectedPanel localSessionCellForTabAtIndex:selectedTabIndex];
selectedCellFrame =
[selectedPanel localSessionCellFrameForTabAtIndex:selectedTabIndex];
const CGFloat collectionViewContentOffsetMinY =
[selectedPanel collectionView].contentOffset.y;
const CGFloat collectionViewContentOffsetMaxY =
collectionViewContentOffsetMinY +
[selectedPanel collectionView].bounds.size.height;
const BOOL isCellTotallyHidden =
CGRectGetMaxY(selectedCellFrame) < collectionViewContentOffsetMinY ||
CGRectGetMinY(selectedCellFrame) > collectionViewContentOffsetMaxY;
if (transitionType == TransitionType::TRANSITION_PRESENT ||
isCellTotallyHidden) {
[selectedPanel scrollTabIndexToVisible:selectedTabIndex
triggerLayout:YES];
}
} else {
CGRect collectionViewFrame = [selectedPanel.collectionView frame];
selectedCellFrame = CGRectMake(collectionViewFrame.size.width / 2,
collectionViewFrame.size.height / 2, 0, 0);
}
// Compute initial and final tab frames.
const CGFloat initialTabFrameOriginY = kHeaderHeight + StatusBarHeight();
CGRect initialTabFrame =
CGRectMake(0, initialTabFrameOriginY, self.view.bounds.size.width,
self.view.bounds.size.height - initialTabFrameOriginY);
CGRect finalTabFrame =
[[selectedPanel collectionView] convertRect:selectedCellFrame
toView:self.view];
// Compute initial and final toolbar screenshot frames.
const CGRect initialToolbarFrame = toolbarController.view.frame;
CGRect initialToolbarScreenshotFrame = CGRectMake(
0, 0, initialToolbarFrame.size.width, initialToolbarFrame.size.height);
const CGFloat cellTopBarHeight = tabSwitcherLocalSessionCellTopBarHeight();
CGRect finalToolbarScreenshotFrame =
CGRectMake(0, 0, selectedCellFrame.size.width, cellTopBarHeight);
UIImageView* tabScreenshotImageView =
[[UIImageView alloc] initWithFrame:CGRectZero];
__weak UIImageView* weakTabScreenshotImageView = tabScreenshotImageView;
if ([self initialTabModelAndTabIDMatchesTabModel:tabModel
tabID:selectedTab.tabId]) {
tabScreenshotImageView.image = self.transitionContext.tabSnapshotImage;
} else {
// If transitioning to a different tab than the one animated in
// from, a new snapshot should be generated instead of using the transition
// context
tabScreenshotImageView.image =
[self updateScreenshotForCellIfNeeded:selectedCell tabModel:tabModel];
[selectedTab retrieveSnapshot:^(UIImage* snapshot) {
[weakTabScreenshotImageView setImage:snapshot];
}];
}
const CGSize tabScreenshotImageSize = tabScreenshotImageView.image.size;
CGRect initialTabScreenshotFrame = CGRectZero;
const CGSize toolbarSize = toolbarController.view.bounds.size;
CGSize initialTabTargetSize =
CGSizeMake(initialTabFrame.size.width,
initialTabFrame.size.height - toolbarSize.height);
CGSize revisedTargetSize = CGSizeZero;
CalculateProjection(tabScreenshotImageSize, initialTabTargetSize,
ProjectionMode::kAspectFill, revisedTargetSize,
initialTabScreenshotFrame);
initialTabScreenshotFrame.origin.y += toolbarSize.height;
CGSize finalTabTargetSize =
CGSizeMake(selectedCellFrame.size.width,
selectedCellFrame.size.height - cellTopBarHeight);
CGRect finalTabScreenshotFrame;
CalculateProjection(tabScreenshotImageSize, finalTabTargetSize,
ProjectionMode::kAspectFill, revisedTargetSize,
finalTabScreenshotFrame);
finalTabScreenshotFrame.origin.y += cellTopBarHeight;
TabSwitcherTabStripPlaceholderView* tabStripPlaceholderView =
[transitionContextContent generateTabStripPlaceholderView];
tabStripPlaceholderView.clipsToBounds = YES;
tabStripPlaceholderView.backgroundColor = [UIColor clearColor];
[self.view addSubview:tabStripPlaceholderView];
CGRect tabStripInitialFrame =
CGRectMake(0, StatusBarHeight(), self.view.bounds.size.width,
tabStripPlaceholderView.frame.size.height);
CGRect tabStripFinalFrame =
CGRectMake(finalTabFrame.origin.x,
finalTabFrame.origin.y - tabStripInitialFrame.size.height,
finalTabFrame.size.width, tabStripInitialFrame.size.height);
CGRect shadowInitialFrame =
CGRectMake(0, CGRectGetMaxY(initialToolbarScreenshotFrame),
initialToolbarScreenshotFrame.size.width, 2);
CGRect shadowFinalFrame =
CGRectMake(0, CGRectGetMaxY(finalToolbarScreenshotFrame),
finalToolbarScreenshotFrame.size.width, 2);
if (transitionType == TransitionType::TRANSITION_DISMISS) {
[tabStripPlaceholderView unfoldWithCompletion:nil];
std::swap(initialTabFrame, finalTabFrame);
std::swap(tabStripInitialFrame, tabStripFinalFrame);
std::swap(shadowInitialFrame, shadowFinalFrame);
std::swap(initialTabScreenshotFrame, finalTabScreenshotFrame);
std::swap(initialToolbarScreenshotFrame, finalToolbarScreenshotFrame);
} else {
[tabStripPlaceholderView foldWithCompletion:^{
[tabStripPlaceholderView removeFromSuperview];
}];
}
tabStripPlaceholderView.frame = tabStripInitialFrame;
// Create and setup placeholder view and subviews.
UIView* placeholderView = [[UIView alloc] initWithFrame:initialTabFrame];
[placeholderView setClipsToBounds:YES];
[placeholderView setUserInteractionEnabled:NO];
tabScreenshotImageView.frame = initialTabScreenshotFrame;
tabScreenshotImageView.contentMode = UIViewContentModeScaleToFill;
tabScreenshotImageView.autoresizingMask = UIViewAutoresizingNone;
[placeholderView addSubview:tabScreenshotImageView];
// Try using a snapshot view for dismissal animation because it's faster and
// the collection view has already been rendered. Use a client rendering
// otherwise.
SnapshotViewOption snapshotOption = SnapshotViewOption::SNAPSHOT_VIEW;
if (transitionType == TransitionType::TRANSITION_PRESENT)
snapshotOption = SnapshotViewOption::CLIENT_RENDERING;
UIView* finalToolbarScreenshotImageView =
[self snapshotViewForView:selectedCell.topBar
withModel:tabModel
option:snapshotOption];
finalToolbarScreenshotImageView.autoresizingMask = UIViewAutoresizingNone;
finalToolbarScreenshotImageView.frame = initialToolbarScreenshotFrame;
[placeholderView addSubview:finalToolbarScreenshotImageView];
UIView* toolbarScreenshotImageView =
transitionContextContent.toolbarSnapshotView;
toolbarScreenshotImageView.autoresizingMask = UIViewAutoresizingNone;
toolbarScreenshotImageView.frame = initialToolbarScreenshotFrame;
[placeholderView addSubview:toolbarScreenshotImageView];
UIImageView* toolbarShadowImageView =
[[UIImageView alloc] initWithFrame:shadowInitialFrame];
[toolbarShadowImageView setAutoresizingMask:UIViewAutoresizingNone];
[toolbarShadowImageView setImage:NativeImage(IDR_IOS_TOOLBAR_SHADOW)];
[placeholderView addSubview:toolbarShadowImageView];
[self.view addSubview:placeholderView];
[selectedCell setHidden:YES];
toolbarScreenshotImageView.alpha =
(transitionType == TransitionType::TRANSITION_DISMISS) ? 0 : 1.0;
__weak TabSwitcherController* weakSelf = self;
void (^completionBlock)(BOOL) = ^(BOOL finished) {
TabSwitcherController* strongSelf = weakSelf;
[tabStripPlaceholderView removeFromSuperview];
[toolbarScreenshotImageView removeFromSuperview];
[selectedCell setHidden:NO];
[placeholderView removeFromSuperview];
if (transitionType == TransitionType::TRANSITION_DISMISS) {
[strongSelf restoreWindowBackgroundColor];
if (finished) {
[strongSelf setTransitionContext:nil];
}
}
[[[strongSelf delegate] tabSwitcherTransitionToolbarOwner]
reparentToolbarController];
[[strongSelf view] setUserInteractionEnabled:YES];
completion();
};
ProceduralBlock animationBlock = ^{
toolbarScreenshotImageView.alpha =
transitionType == TransitionType::TRANSITION_DISMISS ? 1.0 : 0;
tabStripPlaceholderView.frame = tabStripFinalFrame;
toolbarShadowImageView.frame = shadowFinalFrame;
placeholderView.frame = finalTabFrame;
toolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
finalToolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
tabScreenshotImageView.frame = finalTabScreenshotFrame;
};
[UIView animateWithDuration:animated ? kTransitionAnimationDuration : 0
delay:0
options:UIViewAnimationCurveEaseInOut
animations:animationBlock
completion:completionBlock];
}
- (BOOL)initialTabModelAndTabIDMatchesTabModel:(nonnull TabModel*)tabModel
tabID:(nonnull NSString*)tabID {
TabModel* initialTabModel = self.transitionContext.initialTabModel;
NSString* initialTabID =
[self transitionContextContentForTabModel:initialTabModel].initialTabID;
return initialTabModel == tabModel && [initialTabID isEqualToString:tabID];
}
- (void)updateLocalPanelsCells {
auto mainTabPanel =
[self panelControllerForTabModel:[_tabSwitcherModel mainTabModel]];
auto otrTabPanel =
[self panelControllerForTabModel:[_tabSwitcherModel otrTabModel]];
[mainTabPanel reload];
[otrTabPanel reload];
}
- (NSInteger)currentPanelIndex {
return [_tabSwitcherView currentPanelIndex];
}
- (int)offsetToDistantSessionPanels {
if (_signInPanelType == TabSwitcherSignInPanelsType::NO_PANEL) {
return kSignInPromoPanelIndex;
}
return kSignInPromoPanelIndex + 1;
}
- (TabSwitcherSessionType)sessionTypeForPanelIndex:(NSInteger)panelIndex {
if (panelIndex == kLocalTabsOffTheRecordPanelIndex)
return TabSwitcherSessionType::OFF_THE_RECORD_SESSION;
if (panelIndex == kLocalTabsOnTheRecordPanelIndex)
return TabSwitcherSessionType::REGULAR_SESSION;
return TabSwitcherSessionType::DISTANT_SESSION;
}
- (TabModel*)tabModelForSessionType:(TabSwitcherSessionType)sessionType {
switch (sessionType) {
case TabSwitcherSessionType::REGULAR_SESSION:
return [_tabSwitcherModel mainTabModel];
break;
case TabSwitcherSessionType::OFF_THE_RECORD_SESSION:
return [_tabSwitcherModel otrTabModel];
break;
case TabSwitcherSessionType::DISTANT_SESSION:
return nil;
break;
}
}
- (TabModel*)currentSelectedModel {
const NSInteger currentPanelIndex = [self currentPanelIndex];
const TabSwitcherSessionType sessionType =
[self sessionTypeForPanelIndex:currentPanelIndex];
TabModel* model = [self tabModelForSessionType:sessionType];
if (!model)
model = _onLoadActiveModel;
return model;
}
- (void)selectPanelForTabModel:(TabModel*)selectedTabModel {
DCHECK(selectedTabModel == [_tabSwitcherModel otrTabModel] ||
selectedTabModel == [_tabSwitcherModel mainTabModel]);
NSInteger selectedPanel =
(selectedTabModel == [_tabSwitcherModel otrTabModel])
? kLocalTabsOffTheRecordPanelIndex
: kLocalTabsOnTheRecordPanelIndex;
[_tabSwitcherView selectPanelAtIndex:selectedPanel];
}
- (TabSwitcherPanelController*)panelControllerForTabModel:(TabModel*)tabModel {
DCHECK(tabModel == [_tabSwitcherModel mainTabModel] ||
tabModel == [_tabSwitcherModel otrTabModel]);
if (tabModel == [_tabSwitcherModel mainTabModel])
return _onTheRecordSession;
if (tabModel == [_tabSwitcherModel otrTabModel])
return _offTheRecordSession;
return nil;
}
- (void)tabSwitcherDismissWithModel:(TabModel*)model {
[self tabSwitcherDismissWithModel:model animated:YES];
}
- (void)tabSwitcherDismissWithModel:(TabModel*)model animated:(BOOL)animated {
[self tabSwitcherDismissWithModel:model
animated:animated
withCompletion:^{
[self.delegate tabSwitcherDismissTransitionDidEnd:self];
}];
}
- (void)tabSwitcherDismissWithModel:(TabModel*)model
animated:(BOOL)animated
withCompletion:(ProceduralBlock)completion {
DCHECK(completion);
DCHECK(model);
[[self presentedViewController] dismissViewControllerAnimated:NO
completion:nil];
[self.delegate tabSwitcher:self
dismissTransitionWillStartWithActiveModel:model];
[self performTabSwitcherTransition:TransitionType::TRANSITION_DISMISS
withModel:model
animated:animated
withCompletion:^{
[self.view removeFromSuperview];
completion();
}];
}
- (void)tabSwitcherDismissWithCurrentSelectedModel {
TabModel* model = [self currentSelectedModel];
base::RecordAction(base::UserMetricsAction("MobileTabSwitcherClose"));
[self tabSwitcherDismissWithModel:model];
}
- (Tab*)dismissWithNewTabAnimation:(const GURL&)URL
atIndex:(NSUInteger)position
transition:(ui::PageTransition)transition
tabModel:(TabModel*)tabModel {
NSUInteger tabIndex = position;
if (position > tabModel.count)
tabIndex = tabModel.count;
web::NavigationManager::WebLoadParams loadParams(URL);
loadParams.transition_type = transition;
Tab* tab = [tabModel insertTabWithLoadParams:loadParams
opener:nil
openedByDOM:NO
atIndex:tabIndex
inBackground:NO];
[self tabSwitcherDismissWithModel:tabModel
animated:YES
withCompletion:^{
[self.delegate tabSwitcherDismissTransitionDidEnd:self];
}];
return tab;
}
- (BOOL)isPanelIndexForLocalSession:(NSUInteger)panelIndex {
return panelIndex == kLocalTabsOnTheRecordPanelIndex ||
panelIndex == kLocalTabsOffTheRecordPanelIndex;
}
- (void)openTabWithContentOfDistantTab:
(synced_sessions::DistantTab const*)distantTab {
sync_sessions::OpenTabsUIDelegate* openTabs =
IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState)
->GetOpenTabsUIDelegate();
const sessions::SessionTab* toLoad = nullptr;
if (openTabs->GetForeignTab(distantTab->session_tag, distantTab->tab_id,
&toLoad)) {
TabModel* mainModel = [_tabSwitcherModel mainTabModel];
// Disable user interactions until the tab is inserted to prevent multiple
// concurrent tab model updates.
[_tabSwitcherView setUserInteractionEnabled:NO];
Tab* tab = [mainModel insertTabWithURL:GURL()
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:NSNotFound
inBackground:NO];
[tab loadSessionTab:toLoad];
[mainModel setCurrentTab:tab];
// Reenable touch events.
[_tabSwitcherView setUserInteractionEnabled:YES];
[self
tabSwitcherDismissWithModel:mainModel
animated:YES
withCompletion:^{
[self.delegate tabSwitcherDismissTransitionDidEnd:self];
}];
}
}
- (void)removePromoPanelHeaderCellIfNeeded {
if (_shouldRemovePromoPanelHeaderCell) {
_shouldRemovePromoPanelHeaderCell = NO;
[[_tabSwitcherView headerView]
removeSessionsAtIndexes:@[ @(kSignInPromoPanelIndex) ]];
}
}
- (void)addPromoPanelHeaderCellIfNeeded {
if (_shouldAddPromoPanelHeaderCell) {
_shouldAddPromoPanelHeaderCell = NO;
[[_tabSwitcherView headerView]
insertSessionsAtIndexes:@[ @(kSignInPromoPanelIndex) ]];
}
}
- (void)reopenClosedTab {
sessions::TabRestoreService* const tabRestoreService =
IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
if (!tabRestoreService || tabRestoreService->entries().empty())
return;
const std::unique_ptr<sessions::TabRestoreService::Entry>& entry =
tabRestoreService->entries().front();
// Only handle the TAB type.
if (entry->type != sessions::TabRestoreService::TAB)
return;
[self.dispatcher openNewTab:[OpenNewTabCommand command]];
TabRestoreServiceDelegateImplIOS* const delegate =
TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
_browserState);
tabRestoreService->RestoreEntryById(delegate, entry->id,
WindowOpenDisposition::CURRENT_TAB);
}
#pragma mark - TabSwitcherModelDelegate
- (void)distantSessionsRemovedAtSortedIndexes:(NSArray*)removedIndexes
insertedAtSortedIndexes:(NSArray*)insertedIndexes {
// Update the panels
int offset = [self offsetToDistantSessionPanels];
// Iterate in reverse order so that indexes in |removedIndexes| stays synced
// with the indexes in |_controllersOfDistantSessions|.
for (NSNumber* objCIndex in [removedIndexes reverseObjectEnumerator]) {
int index = [objCIndex intValue];
[_tabSwitcherView removePanelViewAtIndex:index + offset];
[_controllersOfDistantSessions removeObjectAtIndex:index];
}
for (NSNumber* objCIndex in insertedIndexes) {
int index = [objCIndex intValue];
std::string tag = [_tabSwitcherModel tagOfDistantSessionAtIndex:index];
TabSwitcherPanelController* panelController =
[[TabSwitcherPanelController alloc] initWithModel:_tabSwitcherModel
forDistantSessionWithTag:tag
browserState:_browserState
dispatcher:self.dispatcher];
[panelController setDelegate:self];
[_tabSwitcherView addPanelView:[panelController view]
atIndex:index + offset];
[_controllersOfDistantSessions insertObject:panelController atIndex:index];
}
// Update the header view.
// The header view's content is created using the panel's content.
[[_tabSwitcherView headerView] reloadData];
}
- (void)distantSessionMayNeedUpdate:(std::string const&)tag {
for (TabSwitcherPanelController* panel in _controllersOfDistantSessions) {
DCHECK([panel isKindOfClass:[TabSwitcherPanelController class]]);
if ([panel sessionTag] == tag) {
[panel updateCollectionViewIfNeeded];
return;
}
}
}
- (void)localSessionMayNeedUpdate:(TabSwitcherSessionType)type {
if (type == TabSwitcherSessionType::REGULAR_SESSION) {
[_onTheRecordSession updateCollectionViewIfNeeded];
} else {
DCHECK(type == TabSwitcherSessionType::OFF_THE_RECORD_SESSION);
[_offTheRecordSession updateCollectionViewIfNeeded];
}
}
- (void)signInPanelChangedTo:(TabSwitcherSignInPanelsType)newPanelType {
if (_signInPanelType == newPanelType)
return;
// Remove promo panel that is no longer relevant, if any.
if (_signInPanelType != TabSwitcherSignInPanelsType::NO_PANEL) {
_shouldRemovePromoPanelHeaderCell = YES;
BOOL updateScrollView =
[_tabSwitcherView currentPanelIndex] != kSignInPromoPanelIndex;
[_tabSwitcherView removePanelViewAtIndex:kSignInPromoPanelIndex
updateScrollView:updateScrollView];
} else {
_shouldAddPromoPanelHeaderCell = YES;
}
[self addPromoPanelForSignInPanelType:newPanelType];
}
- (void)addPromoPanelForSignInPanelType:(TabSwitcherSignInPanelsType)panelType {
_signInPanelType = panelType;
if (panelType != TabSwitcherSignInPanelsType::NO_PANEL) {
TabSwitcherPanelOverlayView* panelView =
[[TabSwitcherPanelOverlayView alloc] initWithFrame:CGRectZero
browserState:_browserState
dispatcher:self.dispatcher];
[panelView setOverlayType:PanelOverlayTypeFromSignInPanelsType(panelType)];
[_tabSwitcherView addPanelView:panelView atIndex:kSignInPromoPanelIndex];
}
}
- (CGSize)sizeForItemAtIndex:(NSUInteger)index
inSession:(TabSwitcherSessionType)session {
switch (session) {
case TabSwitcherSessionType::OFF_THE_RECORD_SESSION:
return [[_offTheRecordSession view] cellSize];
case TabSwitcherSessionType::REGULAR_SESSION:
return [[_onTheRecordSession view] cellSize];
case TabSwitcherSessionType::DISTANT_SESSION:
NOTREACHED();
return {};
}
}
#pragma mark - TabSwitcherHeaderViewDelegate
- (void)tabSwitcherHeaderViewDismiss:(TabSwitcherHeaderView*)view {
[self tabSwitcherDismissWithCurrentSelectedModel];
}
- (void)tabSwitcherHeaderViewDidSelectSessionAtIndex:(NSInteger)index {
switch (index) {
case kLocalTabsOffTheRecordPanelIndex:
base::RecordAction(base::UserMetricsAction(
"MobileTabSwitcherHeaderViewSelectIncognitoPanel"));
break;
case kLocalTabsOnTheRecordPanelIndex:
base::RecordAction(base::UserMetricsAction(
"MobileTabSwitcherHeaderViewSelectNonIncognitoPanel"));
break;
default:
base::RecordAction(base::UserMetricsAction(
"MobileTabSwitcherHeaderViewSelectDistantSessionPanel"));
break;
}
[_tabSwitcherView selectPanelAtIndex:index];
}
#pragma mark - TabSwitcherHeaderViewDataSource
- (NSInteger)tabSwitcherHeaderViewSessionCount {
NSInteger promoPanel = (![_tabSwitcherModel distantSessionCount]) ? 1 : 0;
return [_tabSwitcherModel sessionCount] + promoPanel;
}
- (TabSwitcherSessionCellData*)sessionCellDataAtIndex:(NSUInteger)index {
if (index == kLocalTabsOffTheRecordPanelIndex) {
// If has incognito tabs return incognito cell data.
return [TabSwitcherSessionCellData incognitoSessionCellData];
} else if (index == kLocalTabsOnTheRecordPanelIndex) {
return [TabSwitcherSessionCellData openTabSessionCellData];
} else {
if (![_tabSwitcherModel distantSessionCount]) {
// Display promo panel cell if there is no distant sessions.
return [TabSwitcherSessionCellData otherDevicesSessionCellData];
} else {
index -= kHeaderDistantSessionIndexOffset;
sync_sessions::SyncedSession::DeviceType deviceType =
sync_sessions::SyncedSession::TYPE_UNSET;
NSString* cellTitle = nil;
if (index < _controllersOfDistantSessions.count) {
TabSwitcherPanelController* panel =
[_controllersOfDistantSessions objectAtIndex:index];
const synced_sessions::DistantSession* distantSession =
[panel distantSession];
deviceType = distantSession->device_type;
cellTitle = base::SysUTF8ToNSString(distantSession->name);
}
TabSwitcherSessionCellType cellType;
switch (deviceType) {
case sync_sessions::SyncedSession::TYPE_PHONE:
cellType = kPhoneRemoteSessionCell;
break;
case sync_sessions::SyncedSession::TYPE_TABLET:
cellType = kTabletRemoteSessionCell;
break;
default:
cellType = kLaptopRemoteSessionCell;
break;
}
TabSwitcherSessionCellData* sessionData =
[[TabSwitcherSessionCellData alloc] initWithSessionCellType:cellType];
sessionData.title = cellTitle;
return sessionData;
}
}
}
- (NSInteger)tabSwitcherHeaderViewSelectedPanelIndex {
return [_tabSwitcherView currentPanelIndex];
}
#pragma mark - TabSwitcherViewDelegate
- (void)openNewTabInPanelAtIndex:(NSInteger)panelIndex {
CHECK(panelIndex >= 0);
DCHECK([self isPanelIndexForLocalSession:panelIndex]);
BOOL incognito = !(panelIndex == kLocalTabsOnTheRecordPanelIndex);
if (incognito) {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherCreateIncognitoTab"));
} else {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherCreateNonIncognitoTab"));
}
// Create and execute command to create the tab.
[self.dispatcher
openNewTab:[OpenNewTabCommand commandWithIncognito:incognito]];
}
- (ios_internal::NewTabButtonStyle)buttonStyleForPanelAtIndex:
(NSInteger)panelIndex {
CHECK(panelIndex >= 0);
switch (panelIndex) {
case kLocalTabsOnTheRecordPanelIndex:
if ([_onTheRecordSession shouldShowNewTabButton]) {
return ios_internal::NewTabButtonStyle::BLUE;
} else {
return ios_internal::NewTabButtonStyle::HIDDEN;
}
case kLocalTabsOffTheRecordPanelIndex:
if ([_offTheRecordSession shouldShowNewTabButton]) {
return ios_internal::NewTabButtonStyle::GRAY;
} else {
return ios_internal::NewTabButtonStyle::HIDDEN;
}
default:
return ios_internal::NewTabButtonStyle::HIDDEN;
}
}
- (BOOL)shouldShowDismissButtonForPanelAtIndex:(NSInteger)panelIndex {
CHECK(panelIndex >= 0);
switch (panelIndex) {
case kLocalTabsOnTheRecordPanelIndex:
return [[_tabSwitcherModel mainTabModel] count] != 0;
case kLocalTabsOffTheRecordPanelIndex:
return [[_tabSwitcherModel otrTabModel] count] != 0;
default:
return [[self currentSelectedModel] count] != 0;
}
}
- (void)tabSwitcherViewDelegateDismissTabSwitcher:(TabSwitcherView*)view {
[self tabSwitcherDismissWithCurrentSelectedModel];
}
#pragma mark - TabSwitcherPanelControllerDelegate
- (void)tabSwitcherPanelController:
(TabSwitcherPanelController*)tabSwitcherPanelController
didSelectDistantTab:(synced_sessions::DistantTab*)tab {
DCHECK(tab);
[self openTabWithContentOfDistantTab:tab];
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherOpenDistantTab"));
}
- (void)tabSwitcherPanelController:
(TabSwitcherPanelController*)tabSwitcherPanelController
didSelectLocalTab:(Tab*)tab {
DCHECK(tab);
const TabSwitcherSessionType panelSessionType =
tabSwitcherPanelController.sessionType;
TabModel* tabModel = [self tabModelForSessionType:panelSessionType];
[tabModel setCurrentTab:tab];
[self.delegate tabSwitcher:self
dismissTransitionWillStartWithActiveModel:tabModel];
[self tabSwitcherDismissWithModel:tabModel];
if (panelSessionType == TabSwitcherSessionType::OFF_THE_RECORD_SESSION) {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherOpenIncognitoTab"));
} else {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherOpenNonIncognitoTab"));
}
}
- (void)tabSwitcherPanelController:
(TabSwitcherPanelController*)tabSwitcherPanelController
didCloseLocalTab:(Tab*)tab {
DCHECK(tab);
const TabSwitcherSessionType panelSessionType =
tabSwitcherPanelController.sessionType;
TabModel* tabModel =
panelSessionType == TabSwitcherSessionType::OFF_THE_RECORD_SESSION
? [_tabSwitcherModel otrTabModel]
: [_tabSwitcherModel mainTabModel];
DCHECK_NE(NSNotFound, static_cast<NSInteger>([tabModel indexOfTab:tab]));
[tabModel closeTab:tab];
if (panelSessionType == TabSwitcherSessionType::OFF_THE_RECORD_SESSION) {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherCloseIncognitoTab"));
} else {
base::RecordAction(
base::UserMetricsAction("MobileTabSwitcherCloseNonIncognitoTab"));
}
}
- (void)tabSwitcherPanelControllerDidUpdateOverlayViewVisibility:
(TabSwitcherPanelController*)tabSwitcherPanelController {
[_tabSwitcherView updateOverlayButtonState];
}
@end