Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(440)

Unified Diff: ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.mm
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..1819df60fec8fa08248055596010fe43cdd11fc2
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.mm
@@ -0,0 +1,1249 @@
+// 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/ios/weak_nsobject.h"
+#include "base/mac/scoped_nsobject.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"
+#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"
+#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
+#include "ios/chrome/browser/ui/commands/ios_command_ids.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/session_changes.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"
+#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"
+#import "ios/web/web_state/ui/crw_web_controller.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/resource/resource_bundle.h"
+
+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.
+ id<TabSwitcherDelegate> _delegate;
+ // The model selected when the tab switcher was toggled.
+ // weak.
+ TabModel* _onLoadActiveModel;
+ // The view this controller manages.
+ base::scoped_nsobject<TabSwitcherView> _tabSwitcherView;
+ // The list of panels controllers for distant sessions.
+ base::scoped_nsobject<NSMutableArray> _controllersOfDistantSessions;
+ // The panel controllers for the local sessions.
+ base::scoped_nsobject<TabSwitcherPanelController> _onTheRecordSession;
+ base::scoped_nsobject<TabSwitcherPanelController> _offTheRecordSession;
+ // The model storing the state of what is shown by the tab switcher.
+ base::scoped_nsobject<TabSwitcherModel> _tabSwitcherModel;
+ // Stores the current sign-in panel type.
+ TabSwitcherSignInPanelsType _signInPanelType;
+ // Cache for the panel's cells.
+ base::scoped_nsobject<TabSwitcherCache> _cache;
+ // Stores the background color of the window when the tab switcher was
+ // presented.
+ base::scoped_nsobject<UIColor> _initialWindowBackgroundColor;
+ // Indicate whether a previous promo panel header cell should be removed or
+ // added.
+ BOOL _shouldRemovePromoPanelHeaderCell;
+ BOOL _shouldAddPromoPanelHeaderCell;
+}
+
+// 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.
+- (ios_internal::SessionType)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:(ios_internal::SessionType)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 {
+ DCHECK(mainTabModel);
+ DCHECK(otrTabModel);
+ DCHECK(activeTabModel == otrTabModel || activeTabModel == mainTabModel);
+ self = [super initWithNibName:nil bundle:nil];
+ if (self) {
+ _browserState = browserState;
+ _onLoadActiveModel = activeTabModel;
+ _cache.reset([[TabSwitcherCache alloc] init]);
+ [_cache setMainTabModel:mainTabModel otrTabModel:otrTabModel];
+ _tabSwitcherModel.reset([[TabSwitcherModel alloc]
+ initWithBrowserState:browserState
+ delegate:self
+ mainTabModel:mainTabModel
+ otrTabModel:otrTabModel
+ withCache:_cache]);
+ _controllersOfDistantSessions.reset([[NSMutableArray alloc] init]);
+ [self loadTabSwitcherView];
+ _onTheRecordSession.reset([[TabSwitcherPanelController alloc]
+ initWithModel:_tabSwitcherModel
+ forLocalSessionOfType:ios_internal::SessionType::REGULAR_SESSION
+ withCache:_cache
+ browserState:_browserState]);
+ [_onTheRecordSession setDelegate:self];
+ _offTheRecordSession.reset([[TabSwitcherPanelController alloc]
+ initWithModel:_tabSwitcherModel
+ forLocalSessionOfType:ios_internal::SessionType::OFF_THE_RECORD_SESSION
+ withCache:_cache
+ browserState:_browserState]);
+ [_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.reset(
+ [[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 {
+ base::WeakNSObject<TabSwitcherController> weakSelf(self);
+ return @[
+ [UIKeyCommand
+ cr_keyCommandWithInput:@"t"
+ modifierFlags:UIKeyModifierCommand
+ title:l10n_util::GetNSStringWithFixup(
+ IDS_IOS_TOOLS_MENU_NEW_TAB)
+ action:^{
+ base::scoped_nsobject<TabSwitcherController>
+ strongSelf([weakSelf retain]);
+ 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:^{
+ base::scoped_nsobject<TabSwitcherController>
+ strongSelf([weakSelf retain]);
+ 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 {
+ // Stores the current tab's scroll position. Helps determine whether the
+ // current tab snapshot should be updated or not.
+ [_onLoadActiveModel.currentTab recordStateInHistory];
+
+ [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;
+}
+
+- (IBAction)chromeExecuteCommand:(id)sender {
+ int command = [sender tag];
+
+ switch (command) {
+ case IDC_NEW_INCOGNITO_TAB: // fallthrough
+ case IDC_NEW_TAB: {
+ // Ensure that the right mode is showing.
+ NSInteger panelIndex = (command == IDC_NEW_TAB)
+ ? kLocalTabsOnTheRecordPanelIndex
+ : kLocalTabsOffTheRecordPanelIndex;
+ [_tabSwitcherView selectPanelAtIndex:panelIndex];
+
+ const ios_internal::SessionType panelSessionType =
+ (command == IDC_NEW_TAB)
+ ? ios_internal::SessionType::REGULAR_SESSION
+ : ios_internal::SessionType::OFF_THE_RECORD_SESSION;
+
+ TabModel* model = [self tabModelForSessionType:panelSessionType];
+ [self dismissWithNewTabAnimation:GURL(kChromeUINewTabURL)
+ atIndex:NSNotFound
+ transition:ui::PAGE_TRANSITION_TYPED
+ tabModel:model];
+ } break;
+ case IDC_TOGGLE_TAB_SWITCHER:
+ [self tabSwitcherDismissWithCurrentSelectedModel];
+ break;
+ default:
+ [super chromeExecuteCommand:sender];
+ break;
+ }
+}
+
+#pragma mark - Private
+
+- (void)updateWindowBackgroundColor {
+ DCHECK(!_initialWindowBackgroundColor);
+ _initialWindowBackgroundColor.reset(
+ [self.view.window.backgroundColor retain]);
+ self.view.window.backgroundColor = [[MDCPalette greyPalette] tint900];
+}
+
+- (void)restoreWindowBackgroundColor {
+ self.view.window.backgroundColor = _initialWindowBackgroundColor;
+ _initialWindowBackgroundColor.reset();
+}
+
+- (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] autorelease];
+ [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] autorelease];
+ placeholdView.backgroundColor = backgroundColor;
+ return placeholdView;
+ }
+}
+
+- (TabSwitcherTransitionContextContent*)transitionContextContextForTabModel:
+ (TabModel*)tabModel {
+ if ([tabModel isOffTheRecord])
+ return self.transitionContext.incognitoContent;
+ else
+ return self.transitionContext.regularContent;
+}
+
+- (UIImage*)updateScreenshotForCellIfNeeded:(TabSwitcherLocalSessionCell*)cell
+ tabModel:(TabModel*)tabModel {
+ if (cell.screenshot)
+ return cell.screenshot;
+ 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 transitionContextContextForTabModel: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);
+
+ base::scoped_nsobject<UIImageView> tabScreenshotImageView(
+ [[UIImageView alloc] initWithFrame:CGRectZero]);
+
+ base::WeakNSObject<UIImageView> weakTabScreenshotImageView(
+ tabScreenshotImageView.get());
+
+ if (self.transitionContext.initialTabModel != tabModel ||
+ transitionContextContent.initialSelectedTabIndex != selectedTabIndex) {
+ tabScreenshotImageView.get().image =
+ [self updateScreenshotForCellIfNeeded:selectedCell tabModel:tabModel];
+ [selectedTab retrieveSnapshot:^(UIImage* snapshot) {
+ [weakTabScreenshotImageView setImage:snapshot];
+ }];
+ } else {
+ tabScreenshotImageView.get().image =
+ self.transitionContext.tabSnapshotImage;
+ }
+
+ const CGSize tabScreenshotImageSize = tabScreenshotImageView.get().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.
+ base::scoped_nsobject<UIView> placeholderView(
+ [[UIView alloc] initWithFrame:initialTabFrame]);
+ [placeholderView setClipsToBounds:YES];
+ [placeholderView setUserInteractionEnabled:NO];
+
+ tabScreenshotImageView.get().frame = initialTabScreenshotFrame;
+ tabScreenshotImageView.get().contentMode = UIViewContentModeScaleToFill;
+ tabScreenshotImageView.get().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];
+
+ base::scoped_nsobject<UIImageView> toolbarShadowImageView(
+ [[UIImageView alloc] initWithFrame:shadowInitialFrame]);
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ gfx::Image shadow = rb.GetNativeImageNamed(IDR_IOS_TOOLBAR_SHADOW);
+ [toolbarShadowImageView setAutoresizingMask:UIViewAutoresizingNone];
+ [toolbarShadowImageView setImage:shadow.ToUIImage()];
+ [placeholderView addSubview:toolbarShadowImageView];
+
+ [self.view addSubview:placeholderView];
+
+ [selectedCell setHidden:YES];
+ toolbarScreenshotImageView.alpha =
+ (transitionType == TransitionType::TRANSITION_DISMISS) ? 0 : 1.0;
+
+ base::WeakNSObject<TabSwitcherController> weakSelf(self);
+ void (^completionBlock)(BOOL) = ^(BOOL) {
+ base::scoped_nsobject<TabSwitcherController> strongSelf([weakSelf retain]);
+
+ [tabStripPlaceholderView removeFromSuperview];
+ [toolbarScreenshotImageView removeFromSuperview];
+ [selectedCell setHidden:NO];
+ [placeholderView removeFromSuperview];
+
+ if (transitionType == TransitionType::TRANSITION_DISMISS)
+ [strongSelf restoreWindowBackgroundColor];
+ [[[strongSelf delegate] tabSwitcherTransitionToolbarOwner]
+ reparentToolbarController];
+ [[strongSelf view] setUserInteractionEnabled:YES];
+ completion();
+ };
+
+ ProceduralBlock animationBlock = ^{
+ toolbarScreenshotImageView.alpha =
+ transitionType == TransitionType::TRANSITION_DISMISS ? 1.0 : 0;
+ tabStripPlaceholderView.frame = tabStripFinalFrame;
+ toolbarShadowImageView.get().frame = shadowFinalFrame;
+ placeholderView.get().frame = finalTabFrame;
+ toolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
+ finalToolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
+ tabScreenshotImageView.get().frame = finalTabScreenshotFrame;
+ };
+
+ [UIView animateWithDuration:animated ? kTransitionAnimationDuration : 0
+ delay:0
+ options:UIViewAnimationCurveEaseInOut
+ animations:animationBlock
+ completion:completionBlock];
+}
+
+- (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;
+}
+
+- (ios_internal::SessionType)sessionTypeForPanelIndex:(NSInteger)panelIndex {
+ if (panelIndex == kLocalTabsOffTheRecordPanelIndex)
+ return ios_internal::SessionType::OFF_THE_RECORD_SESSION;
+ if (panelIndex == kLocalTabsOnTheRecordPanelIndex)
+ return ios_internal::SessionType::REGULAR_SESSION;
+ return ios_internal::SessionType::DISTANT_SESSION;
+}
+
+- (TabModel*)tabModelForSessionType:(ios_internal::SessionType)sessionType {
+ switch (sessionType) {
+ case ios_internal::SessionType::REGULAR_SESSION:
+ return [_tabSwitcherModel mainTabModel];
+ break;
+ case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
+ return [_tabSwitcherModel otrTabModel];
+ break;
+ case ios_internal::SessionType::DISTANT_SESSION:
+ return nil;
+ break;
+ }
+}
+
+- (TabModel*)currentSelectedModel {
+ const NSInteger currentPanelIndex = [self currentPanelIndex];
+ const ios_internal::SessionType 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.get();
+ if (tabModel == [_tabSwitcherModel otrTabModel])
+ return _offTheRecordSession.get();
+ 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 {
+ web::NavigationManager::WebLoadParams params(URL);
+ params.referrer = web::Referrer();
+ params.transition_type = transition;
+
+ DCHECK(tabModel.browserState);
+
+ base::scoped_nsobject<Tab> tab([[Tab alloc]
+ initWithWindowName:nil
+ opener:nil
+ openedByDOM:NO
+ model:tabModel
+ browserState:tabModel.browserState]);
+ [tab webController].webUsageEnabled = tabModel.webUsageEnabled;
+
+ ProceduralBlock dismissWithNewTab = ^{
+ NSUInteger tabIndex = position;
+ if (position > tabModel.count)
+ tabIndex = tabModel.count;
+ [tabModel insertTab:tab atIndex:tabIndex];
+
+ if (tabModel.tabUsageRecorder)
+ tabModel.tabUsageRecorder->TabCreatedForSelection(tab);
+
+ [[tab webController] loadWithParams:params];
+
+ if (tabModel.webUsageEnabled) {
+ [tab setWebUsageEnabled:tabModel.webUsageEnabled];
+ [[tab webController] triggerPendingLoad];
+ }
+
+ NSDictionary* userInfo = @{
+ kTabModelTabKey : tab,
+ kTabModelOpenInBackgroundKey : @(NO),
+ };
+
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:kTabModelNewTabWillOpenNotification
+ object:self
+ userInfo:userInfo];
+
+ [tabModel setCurrentTab:tab];
+
+ [self
+ tabSwitcherDismissWithModel:tabModel
+ animated:YES
+ withCompletion:^{
+ [self.delegate tabSwitcherDismissTransitionDidEnd:self];
+ }];
+ };
+
+ dismissWithNewTab();
+ 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 insertOrUpdateTabWithURL:GURL()
+ referrer:web::Referrer()
+ transition:ui::PAGE_TRANSITION_TYPED
+ windowName:nil
+ 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 chromeExecuteCommand:[GenericChromeCommand commandWithTag:IDC_NEW_TAB]];
+ 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];
+ base::scoped_nsobject<TabSwitcherPanelController> panelController(
+ [[TabSwitcherPanelController alloc] initWithModel:_tabSwitcherModel
+ forDistantSessionWithTag:tag
+ browserState:_browserState]);
+ [panelController setDelegate:self];
+ [_tabSwitcherView addPanelView:[panelController view]
+ atIndex:index + offset];
+ [_controllersOfDistantSessions insertObject:panelController.get()
+ 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
+ .get()) {
+ DCHECK([panel isKindOfClass:[TabSwitcherPanelController class]]);
+ if ([panel sessionTag] == tag) {
+ [panel updateCollectionViewIfNeeded];
+ return;
+ }
+ }
+}
+
+- (void)localSessionMayNeedUpdate:(ios_internal::SessionType)type {
+ if (type == ios_internal::SessionType::REGULAR_SESSION) {
+ [_onTheRecordSession updateCollectionViewIfNeeded];
+ } else {
+ DCHECK(type == ios_internal::SessionType::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]
+ autorelease];
+ [panelView setOverlayType:PanelOverlayTypeFromSignInPanelsType(panelType)];
+ [_tabSwitcherView addPanelView:panelView atIndex:kSignInPromoPanelIndex];
+ }
+}
+
+- (CGSize)sizeForItemAtIndex:(NSUInteger)index
+ inSession:(ios_internal::SessionType)session {
+ switch (session) {
+ case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
+ return [[_offTheRecordSession view] cellSize];
+ case ios_internal::SessionType::REGULAR_SESSION:
+ return [[_onTheRecordSession view] cellSize];
+ case ios_internal::SessionType::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;
+}
+
+- (SessionCellData*)sessionCellDataAtIndex:(NSUInteger)index {
+ if (index == kLocalTabsOffTheRecordPanelIndex) {
+ // If has incognito tabs return incognito cell data.
+ return [SessionCellData incognitoSessionCellData];
+ } else if (index == kLocalTabsOnTheRecordPanelIndex) {
+ return [SessionCellData openTabSessionCellData];
+ } else {
+ if (![_tabSwitcherModel distantSessionCount]) {
+ // Display promo panel cell if there is no distant sessions.
+ return [SessionCellData otherDevicesSessionCellData];
+ } else {
+ index -= kHeaderDistantSessionIndexOffset;
+
+ sync_sessions::SyncedSession::DeviceType deviceType =
+ sync_sessions::SyncedSession::TYPE_UNSET;
+ NSString* cellTitle = nil;
+
+ if (index < _controllersOfDistantSessions.get().count) {
+ TabSwitcherPanelController* panel =
+ [_controllersOfDistantSessions objectAtIndex:index];
+ const synced_sessions::DistantSession* distantSession =
+ [panel distantSession];
+ deviceType = distantSession->device_type;
+ cellTitle = base::SysUTF8ToNSString(distantSession->name);
+ }
+ ios_internal::SessionCellType cellType;
+ switch (deviceType) {
+ case sync_sessions::SyncedSession::TYPE_PHONE:
+ cellType = ios_internal::kPhoneRemoteSessionCell;
+ break;
+ case sync_sessions::SyncedSession::TYPE_TABLET:
+ cellType = ios_internal::kTabletRemoteSessionCell;
+ break;
+ default:
+ cellType = ios_internal::kLaptopRemoteSessionCell;
+ break;
+ }
+ SessionCellData* sessionData = [[[SessionCellData alloc]
+ initWithSessionCellType:cellType] autorelease];
+ sessionData.title = cellTitle;
+ return sessionData;
+ }
+ }
+}
+
+- (NSInteger)tabSwitcherHeaderViewSelectedPanelIndex {
+ return [_tabSwitcherView currentPanelIndex];
+}
+
+#pragma mark - TabSwitcherViewDelegate
+
+- (void)openNewTabInPanelAtIndex:(NSInteger)panelIndex {
+ CHECK(panelIndex >= 0);
+ DCHECK([self isPanelIndexForLocalSession:panelIndex]);
+ const NSInteger tag = (panelIndex == kLocalTabsOnTheRecordPanelIndex)
+ ? IDC_NEW_TAB
+ : IDC_NEW_INCOGNITO_TAB;
+ if (tag == IDC_NEW_INCOGNITO_TAB) {
+ base::RecordAction(
+ base::UserMetricsAction("MobileTabSwitcherCreateIncognitoTab"));
+ } else {
+ base::RecordAction(
+ base::UserMetricsAction("MobileTabSwitcherCreateNonIncognitoTab"));
+ }
+ // Create and execute command to create the tab.
+ base::scoped_nsobject<GenericChromeCommand> command(
+ [[GenericChromeCommand alloc] initWithTag:tag]);
+ [self chromeExecuteCommand:command];
+}
+
+- (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 ios_internal::SessionType panelSessionType =
+ tabSwitcherPanelController.sessionType;
+ TabModel* tabModel = [self tabModelForSessionType:panelSessionType];
+ [tabModel setCurrentTab:tab];
+ [self.delegate tabSwitcher:self
+ dismissTransitionWillStartWithActiveModel:tabModel];
+ [self tabSwitcherDismissWithModel:tabModel];
+ if (panelSessionType == ios_internal::SessionType::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 ios_internal::SessionType panelSessionType =
+ tabSwitcherPanelController.sessionType;
+ [tab close];
+ if (panelSessionType == ios_internal::SessionType::OFF_THE_RECORD_SESSION) {
+ base::RecordAction(
+ base::UserMetricsAction("MobileTabSwitcherCloseIncognitoTab"));
+ } else {
+ base::RecordAction(
+ base::UserMetricsAction("MobileTabSwitcherCloseNonIncognitoTab"));
+ }
+}
+
+- (void)tabSwitcherPanelControllerDidUpdateOverlayViewVisibility:
+ (TabSwitcherPanelController*)tabSwitcherPanelController {
+ [_tabSwitcherView updateOverlayButtonState];
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698