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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h"
6
7 #include "base/ios/block_types.h"
8 #include "base/ios/weak_nsobject.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/metrics/user_metrics.h"
11 #include "base/metrics/user_metrics_action.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "components/browser_sync/profile_sync_service.h"
14 #include "components/sessions/core/tab_restore_service_helper.h"
15 #include "components/sync/driver/sync_service.h"
16 #include "components/sync_sessions/open_tabs_ui_delegate.h"
17 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
18 #include "ios/chrome/browser/chrome_url_constants.h"
19 #import "ios/chrome/browser/metrics/tab_usage_recorder.h"
20 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
21 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios.h"
22 #include "ios/chrome/browser/sessions/tab_restore_service_delegate_impl_ios_fact ory.h"
23 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
24 #import "ios/chrome/browser/tabs/tab.h"
25 #import "ios/chrome/browser/tabs/tab_model.h"
26 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
27 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
28 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
29 #import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
30 #include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h"
31 #import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_in_sync_off_view.h"
32 #import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_in_sync_on_no_sessio ns_view.h"
33 #import "ios/chrome/browser/ui/ntp/recent_tabs/views/signed_out_view.h"
34 #import "ios/chrome/browser/ui/tab_switcher/session_changes.h"
35 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h"
36 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.h"
37 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h"
38 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
39 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_controller.h"
40 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_overlay_view.h"
41 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_view.h"
42 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_session_cell_data.h"
43 #include "ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_context.h"
44 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_view.h"
45 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
46 #import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
47 #include "ios/chrome/browser/ui/ui_util.h"
48 #include "ios/chrome/grit/ios_strings.h"
49 #include "ios/chrome/grit/ios_theme_resources.h"
50 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
51 #import "ios/web/public/navigation_manager.h"
52 #include "ios/web/public/referrer.h"
53 #import "ios/web/web_state/ui/crw_web_controller.h"
54 #include "ui/base/l10n/l10n_util.h"
55 #include "ui/base/l10n/l10n_util_mac.h"
56 #include "ui/base/resource/resource_bundle.h"
57
58 namespace {
59
60 // Offsets for computing the panels' indexes in the TabSwitcherView.
61 const int kSignInPromoPanelIndex = 2;
62 const int kHeaderDistantSessionIndexOffset = 2;
63 const int kLocalTabsOffTheRecordPanelIndex = 0;
64 const int kLocalTabsOnTheRecordPanelIndex = 1;
65 // The duration of the tab switcher toggle animation.
66 const CGFloat kTransitionAnimationDuration = 0.25;
67 // The height of the browser view controller header.
68 const CGFloat kHeaderHeight = 39;
69
70 enum class TransitionType {
71 TRANSITION_PRESENT,
72 TRANSITION_DISMISS,
73 };
74
75 enum class SnapshotViewOption {
76 SNAPSHOT_VIEW, // Try taking a snapshot view if possible.
77 CLIENT_RENDERING, // Use [UIView renderInContext:] to render a bitmap and set
78 // it as the backing store of a view's layer.
79 };
80
81 } // namespace
82
83 @interface TabSwitcherController ()<TabSwitcherModelDelegate,
84 TabSwitcherViewDelegate,
85 TabSwitcherHeaderViewDelegate,
86 TabSwitcherHeaderViewDataSource,
87 TabSwitcherPanelControllerDelegate> {
88 // weak.
89 ios::ChromeBrowserState* _browserState;
90 // weak.
91 id<TabSwitcherDelegate> _delegate;
92 // The model selected when the tab switcher was toggled.
93 // weak.
94 TabModel* _onLoadActiveModel;
95 // The view this controller manages.
96 base::scoped_nsobject<TabSwitcherView> _tabSwitcherView;
97 // The list of panels controllers for distant sessions.
98 base::scoped_nsobject<NSMutableArray> _controllersOfDistantSessions;
99 // The panel controllers for the local sessions.
100 base::scoped_nsobject<TabSwitcherPanelController> _onTheRecordSession;
101 base::scoped_nsobject<TabSwitcherPanelController> _offTheRecordSession;
102 // The model storing the state of what is shown by the tab switcher.
103 base::scoped_nsobject<TabSwitcherModel> _tabSwitcherModel;
104 // Stores the current sign-in panel type.
105 TabSwitcherSignInPanelsType _signInPanelType;
106 // Cache for the panel's cells.
107 base::scoped_nsobject<TabSwitcherCache> _cache;
108 // Stores the background color of the window when the tab switcher was
109 // presented.
110 base::scoped_nsobject<UIColor> _initialWindowBackgroundColor;
111 // Indicate whether a previous promo panel header cell should be removed or
112 // added.
113 BOOL _shouldRemovePromoPanelHeaderCell;
114 BOOL _shouldAddPromoPanelHeaderCell;
115 }
116
117 // Updates the window background color to the tab switcher's background color.
118 // The original background color can be restored by calling
119 // -restoreWindowBackgroundColor.
120 - (void)updateWindowBackgroundColor;
121
122 // Restores the tab switcher's window background color to the value it had
123 // before presenting it.
124 - (void)restoreWindowBackgroundColor;
125
126 // Performs the tab switcher transition according to the |transitionType| and
127 // call the completion block at the end of the transition. The transition will
128 // be animated according to the |animated| parameter. |completion| block must
129 // not be nil.
130 - (void)performTabSwitcherTransition:(TransitionType)transitionType
131 withModel:(TabModel*)tabModel
132 animated:(BOOL)animated
133 withCompletion:(ProceduralBlock)completion;
134
135 // Returns the index of the currently selected panel.
136 - (NSInteger)currentPanelIndex;
137
138 // Returns the session type of the panel and the given index.
139 - (ios_internal::SessionType)sessionTypeForPanelIndex:(NSInteger)panelIndex;
140
141 // Returns the tab model corresponding to the given session type.
142 // There is no tab model for distant sessions so it returns nil for distant
143 // sessions type.
144 - (TabModel*)tabModelForSessionType:(ios_internal::SessionType)sessionType;
145
146 // Returns the tab model of the currently selected tab.
147 - (TabModel*)currentSelectedModel;
148
149 // Calls tabSwitcherDismissWithModel:animated: with the |animated| parameter
150 // set to YES.
151 - (void)tabSwitcherDismissWithModel:(TabModel*)model;
152
153 // Dismisses the tab switcher using the given tab model. The completion block is
154 // called at the end of the animation. The tab switcher delegate method
155 // -tabSwitcherPresentationTransitionDidEnd: must be called in the completion
156 // block. The dismissal of the tab switcher will be animated if the |animated|
157 // parameter is set to YES.
158 - (void)tabSwitcherDismissWithModel:(TabModel*)model
159 animated:(BOOL)animated
160 withCompletion:(ProceduralBlock)completion;
161
162 // Dismisses the tab switcher using the currently selected tab's tab model.
163 - (void)tabSwitcherDismissWithCurrentSelectedModel;
164
165 // Scrolls the scrollview to show the panel displaying the |selectedTabModel|.
166 - (void)selectPanelForTabModel:(TabModel*)selectedTabModel;
167
168 // Dismisses the tab switcher and create a new tab using the url, position and
169 // transition on the given tab model.
170 - (Tab*)dismissWithNewTabAnimation:(const GURL&)url
171 atIndex:(NSUInteger)position
172 transition:(ui::PageTransition)transition
173 tabModel:(TabModel*)tabModel;
174 // Add a promo panel corresponding to the panel type argument.
175 // Should only be called from inititalizer and signInPanelChangedTo:.
176 - (void)addPromoPanelForSignInPanelType:(TabSwitcherSignInPanelsType)panelType;
177
178 // Updates cells of local panels.
179 - (void)updateLocalPanelsCells;
180
181 @end
182
183 @implementation TabSwitcherController
184
185 @synthesize transitionContext = _transitionContext;
186
187 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
188 mainTabModel:(TabModel*)mainTabModel
189 otrTabModel:(TabModel*)otrTabModel
190 activeTabModel:(TabModel*)activeTabModel {
191 DCHECK(mainTabModel);
192 DCHECK(otrTabModel);
193 DCHECK(activeTabModel == otrTabModel || activeTabModel == mainTabModel);
194 self = [super initWithNibName:nil bundle:nil];
195 if (self) {
196 _browserState = browserState;
197 _onLoadActiveModel = activeTabModel;
198 _cache.reset([[TabSwitcherCache alloc] init]);
199 [_cache setMainTabModel:mainTabModel otrTabModel:otrTabModel];
200 _tabSwitcherModel.reset([[TabSwitcherModel alloc]
201 initWithBrowserState:browserState
202 delegate:self
203 mainTabModel:mainTabModel
204 otrTabModel:otrTabModel
205 withCache:_cache]);
206 _controllersOfDistantSessions.reset([[NSMutableArray alloc] init]);
207 [self loadTabSwitcherView];
208 _onTheRecordSession.reset([[TabSwitcherPanelController alloc]
209 initWithModel:_tabSwitcherModel
210 forLocalSessionOfType:ios_internal::SessionType::REGULAR_SESSION
211 withCache:_cache
212 browserState:_browserState]);
213 [_onTheRecordSession setDelegate:self];
214 _offTheRecordSession.reset([[TabSwitcherPanelController alloc]
215 initWithModel:_tabSwitcherModel
216 forLocalSessionOfType:ios_internal::SessionType::OFF_THE_RECORD_SESSION
217 withCache:_cache
218 browserState:_browserState]);
219 [_offTheRecordSession setDelegate:self];
220 [_tabSwitcherView addPanelView:[_offTheRecordSession view]
221 atIndex:kLocalTabsOffTheRecordPanelIndex];
222 [_tabSwitcherView addPanelView:[_onTheRecordSession view]
223 atIndex:kLocalTabsOnTheRecordPanelIndex];
224 [self addPromoPanelForSignInPanelType:[_tabSwitcherModel signInPanelType]];
225 [[_tabSwitcherView headerView] reloadData];
226 [_tabSwitcherModel syncedSessionsChanged];
227 [self selectPanelForTabModel:_onLoadActiveModel];
228 }
229 return self;
230 }
231
232 #pragma mark - UIViewController
233
234 - (BOOL)prefersStatusBarHidden {
235 return NO;
236 }
237
238 - (UIStatusBarStyle)preferredStatusBarStyle {
239 return UIStatusBarStyleLightContent;
240 }
241
242 - (CGRect)tabSwitcherInitialFrame {
243 return [[UIScreen mainScreen] bounds];
244 }
245
246 - (void)loadTabSwitcherView {
247 DCHECK(![_tabSwitcherView superview]);
248 _tabSwitcherView.reset(
249 [[TabSwitcherView alloc] initWithFrame:[self tabSwitcherInitialFrame]]);
250 [_tabSwitcherView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
251 UIViewAutoresizingFlexibleHeight];
252 [_tabSwitcherView setDelegate:self];
253 [[_tabSwitcherView headerView] setDelegate:self];
254 [[_tabSwitcherView headerView] setDataSource:self];
255 }
256
257 - (void)viewDidLoad {
258 [super viewDidLoad];
259 [self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
260 UIViewAutoresizingFlexibleHeight];
261 [_tabSwitcherView setFrame:self.view.bounds];
262 [self.view addSubview:_tabSwitcherView];
263 }
264
265 #pragma mark - UIResponder
266
267 - (NSArray*)keyCommands {
268 base::WeakNSObject<TabSwitcherController> weakSelf(self);
269 return @[
270 [UIKeyCommand
271 cr_keyCommandWithInput:@"t"
272 modifierFlags:UIKeyModifierCommand
273 title:l10n_util::GetNSStringWithFixup(
274 IDS_IOS_TOOLS_MENU_NEW_TAB)
275 action:^{
276 base::scoped_nsobject<TabSwitcherController>
277 strongSelf([weakSelf retain]);
278 if (!strongSelf)
279 return;
280 if ([strongSelf currentPanelIndex] ==
281 kLocalTabsOffTheRecordPanelIndex) {
282 [strongSelf openNewTabInPanelAtIndex:
283 kLocalTabsOffTheRecordPanelIndex];
284 } else {
285 [strongSelf openNewTabInPanelAtIndex:
286 kLocalTabsOnTheRecordPanelIndex];
287 }
288 }],
289 [UIKeyCommand
290 cr_keyCommandWithInput:@"n"
291 modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
292 title:l10n_util::GetNSStringWithFixup(
293 IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB)
294 action:^{
295 [weakSelf openNewTabInPanelAtIndex:
296 kLocalTabsOffTheRecordPanelIndex];
297 }],
298 [UIKeyCommand
299 cr_keyCommandWithInput:@"t"
300 modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
301 title:l10n_util::GetNSStringWithFixup(
302 IDS_IOS_KEYBOARD_REOPEN_CLOSED_TAB)
303 action:^{
304 [weakSelf reopenClosedTab];
305 }],
306 [UIKeyCommand
307 cr_keyCommandWithInput:@"n"
308 modifierFlags:UIKeyModifierCommand
309 title:nil
310 action:^{
311 base::scoped_nsobject<TabSwitcherController>
312 strongSelf([weakSelf retain]);
313 if (!strongSelf)
314 return;
315 if ([strongSelf currentPanelIndex] ==
316 kLocalTabsOffTheRecordPanelIndex) {
317 [strongSelf openNewTabInPanelAtIndex:
318 kLocalTabsOffTheRecordPanelIndex];
319 } else {
320 [strongSelf openNewTabInPanelAtIndex:
321 kLocalTabsOnTheRecordPanelIndex];
322 }
323 }],
324 ];
325 }
326
327 #pragma mark - TabSwitcher protocol implementation
328
329 - (void)restoreInternalStateWithMainTabModel:(TabModel*)mainModel
330 otrTabModel:(TabModel*)otrModel
331 activeTabModel:(TabModel*)activeModel {
332 _onLoadActiveModel = activeModel;
333 [_cache setMainTabModel:mainModel otrTabModel:otrModel];
334 [_tabSwitcherModel setMainTabModel:mainModel otrTabModel:otrModel];
335 [self selectPanelForTabModel:activeModel];
336 }
337
338 - (void)setOtrTabModel:(TabModel*)otrModel {
339 [_cache setMainTabModel:[_cache mainTabModel] otrTabModel:otrModel];
340 [_tabSwitcherModel setMainTabModel:[_tabSwitcherModel mainTabModel]
341 otrTabModel:otrModel];
342 }
343
344 - (void)showWithSelectedTabAnimation {
345 // Stores the current tab's scroll position. Helps determine whether the
346 // current tab snapshot should be updated or not.
347 [_onLoadActiveModel.currentTab recordStateInHistory];
348
349 [self updateWindowBackgroundColor];
350 [self performTabSwitcherTransition:TransitionType::TRANSITION_PRESENT
351 withModel:_onLoadActiveModel
352 animated:YES
353 withCompletion:^{
354 [self.delegate
355 tabSwitcherPresentationTransitionDidEnd:self];
356 }];
357 }
358
359 - (Tab*)dismissWithNewTabAnimationToModel:(TabModel*)targetModel
360 withURL:(const GURL&)url
361 atIndex:(NSUInteger)position
362 transition:(ui::PageTransition)transition {
363 if (targetModel == [_tabSwitcherModel mainTabModel])
364 [_tabSwitcherView selectPanelAtIndex:kLocalTabsOnTheRecordPanelIndex];
365 else
366 [_tabSwitcherView selectPanelAtIndex:kLocalTabsOffTheRecordPanelIndex];
367 return [self dismissWithNewTabAnimation:url
368 atIndex:position
369 transition:transition
370 tabModel:targetModel];
371 }
372
373 - (void)setDelegate:(id<TabSwitcherDelegate>)delegate {
374 _delegate = delegate;
375 if (delegate == nullptr)
376 [_tabSwitcherModel setMainTabModel:nil otrTabModel:nil];
377 }
378
379 - (id<TabSwitcherDelegate>)delegate {
380 return _delegate;
381 }
382
383 - (IBAction)chromeExecuteCommand:(id)sender {
384 int command = [sender tag];
385
386 switch (command) {
387 case IDC_NEW_INCOGNITO_TAB: // fallthrough
388 case IDC_NEW_TAB: {
389 // Ensure that the right mode is showing.
390 NSInteger panelIndex = (command == IDC_NEW_TAB)
391 ? kLocalTabsOnTheRecordPanelIndex
392 : kLocalTabsOffTheRecordPanelIndex;
393 [_tabSwitcherView selectPanelAtIndex:panelIndex];
394
395 const ios_internal::SessionType panelSessionType =
396 (command == IDC_NEW_TAB)
397 ? ios_internal::SessionType::REGULAR_SESSION
398 : ios_internal::SessionType::OFF_THE_RECORD_SESSION;
399
400 TabModel* model = [self tabModelForSessionType:panelSessionType];
401 [self dismissWithNewTabAnimation:GURL(kChromeUINewTabURL)
402 atIndex:NSNotFound
403 transition:ui::PAGE_TRANSITION_TYPED
404 tabModel:model];
405 } break;
406 case IDC_TOGGLE_TAB_SWITCHER:
407 [self tabSwitcherDismissWithCurrentSelectedModel];
408 break;
409 default:
410 [super chromeExecuteCommand:sender];
411 break;
412 }
413 }
414
415 #pragma mark - Private
416
417 - (void)updateWindowBackgroundColor {
418 DCHECK(!_initialWindowBackgroundColor);
419 _initialWindowBackgroundColor.reset(
420 [self.view.window.backgroundColor retain]);
421 self.view.window.backgroundColor = [[MDCPalette greyPalette] tint900];
422 }
423
424 - (void)restoreWindowBackgroundColor {
425 self.view.window.backgroundColor = _initialWindowBackgroundColor;
426 _initialWindowBackgroundColor.reset();
427 }
428
429 - (UIView*)snapshotViewForView:(UIView*)inputView
430 withModel:(TabModel*)tabModel
431 option:(SnapshotViewOption)option {
432 if (inputView) {
433 if (option == SnapshotViewOption::SNAPSHOT_VIEW) {
434 UIView* view = [inputView snapshotViewAfterScreenUpdates:NO];
435 if (view)
436 return view;
437 }
438 // If the view has just been created, it has not been rendered by Core
439 // Animation and the snapshot view can't be generated. In that case we
440 // trigger a client side rendering of the view and use the rendered image
441 // as the backing store of a view layer.
442 UIGraphicsBeginImageContextWithOptions(inputView.bounds.size,
443 inputView.opaque, 0);
444 [inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
445 UIImage* screenshot = UIGraphicsGetImageFromCurrentImageContext();
446 UIGraphicsEndImageContext();
447 UIView* view = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
448 [view layer].contents = static_cast<id>(screenshot.CGImage);
449 return view;
450 } else {
451 // When the input view is nil, we can't generate a snapshot so a placeholder
452 // is returned.
453 UIColor* backgroundColor = [tabModel isOffTheRecord]
454 ? [[MDCPalette greyPalette] tint700]
455 : [[MDCPalette greyPalette] tint100];
456 UIView* placeholdView =
457 [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
458 placeholdView.backgroundColor = backgroundColor;
459 return placeholdView;
460 }
461 }
462
463 - (TabSwitcherTransitionContextContent*)transitionContextContextForTabModel:
464 (TabModel*)tabModel {
465 if ([tabModel isOffTheRecord])
466 return self.transitionContext.incognitoContent;
467 else
468 return self.transitionContext.regularContent;
469 }
470
471 - (UIImage*)updateScreenshotForCellIfNeeded:(TabSwitcherLocalSessionCell*)cell
472 tabModel:(TabModel*)tabModel {
473 if (cell.screenshot)
474 return cell.screenshot;
475 UIColor* backgroundColor = [tabModel isOffTheRecord]
476 ? [[MDCPalette greyPalette] tint700]
477 : [UIColor whiteColor];
478 CGRect rect = CGRectMake(0, 0, 1, 1);
479 UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
480 [backgroundColor setFill];
481 CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
482 UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
483 UIGraphicsEndImageContext();
484 return image;
485 }
486
487 - (void)performTabSwitcherTransition:(TransitionType)transitionType
488 withModel:(TabModel*)tabModel
489 animated:(BOOL)animated
490 withCompletion:(ProceduralBlock)completion {
491 switch (transitionType) {
492 case TransitionType::TRANSITION_PRESENT:
493 base::RecordAction(base::UserMetricsAction("MobileTabSwitcherPresented"));
494 break;
495 case TransitionType::TRANSITION_DISMISS:
496 base::RecordAction(base::UserMetricsAction("MobileTabSwitcherDismissed"));
497 break;
498 }
499 DCHECK(completion);
500 DCHECK([self transitionContext]);
501 [[self view] setUserInteractionEnabled:NO];
502
503 TabSwitcherTransitionContextContent* transitionContextContent =
504 [self transitionContextContextForTabModel:tabModel];
505 DCHECK(transitionContextContent);
506
507 ToolbarController* toolbarController =
508 [[self.delegate tabSwitcherTransitionToolbarOwner]
509 relinquishedToolbarController];
510 Tab* selectedTab = [tabModel currentTab];
511
512 NSInteger selectedTabIndex = [tabModel indexOfTab:selectedTab];
513 TabSwitcherLocalSessionCell* selectedCell = nil;
514 CGRect selectedCellFrame = CGRectZero;
515 TabSwitcherPanelController* selectedPanel =
516 [self panelControllerForTabModel:tabModel];
517 // Force invalidation and update of the tab switcher view and collection view
518 // layout to take into account any changes in the size of the view controller
519 // while the tab switcher was not shown.
520 if (transitionType == TransitionType::TRANSITION_PRESENT) {
521 [[self view] layoutIfNeeded];
522 [[selectedPanel collectionView].collectionViewLayout invalidateLayout];
523 }
524 if (selectedTabIndex != NSNotFound) {
525 selectedCell =
526 [selectedPanel localSessionCellForTabAtIndex:selectedTabIndex];
527 selectedCellFrame =
528 [selectedPanel localSessionCellFrameForTabAtIndex:selectedTabIndex];
529
530 const CGFloat collectionViewContentOffsetMinY =
531 [selectedPanel collectionView].contentOffset.y;
532 const CGFloat collectionViewContentOffsetMaxY =
533 collectionViewContentOffsetMinY +
534 [selectedPanel collectionView].bounds.size.height;
535 const BOOL isCellTotallyHidden =
536 CGRectGetMaxY(selectedCellFrame) < collectionViewContentOffsetMinY ||
537 CGRectGetMinY(selectedCellFrame) > collectionViewContentOffsetMaxY;
538 if (transitionType == TransitionType::TRANSITION_PRESENT ||
539 isCellTotallyHidden) {
540 [selectedPanel scrollTabIndexToVisible:selectedTabIndex
541 triggerLayout:YES];
542 }
543 } else {
544 CGRect collectionViewFrame = [selectedPanel.collectionView frame];
545 selectedCellFrame = CGRectMake(collectionViewFrame.size.width / 2,
546 collectionViewFrame.size.height / 2, 0, 0);
547 }
548 // Compute initial and final tab frames.
549 const CGFloat initialTabFrameOriginY = kHeaderHeight + StatusBarHeight();
550 CGRect initialTabFrame =
551 CGRectMake(0, initialTabFrameOriginY, self.view.bounds.size.width,
552 self.view.bounds.size.height - initialTabFrameOriginY);
553 CGRect finalTabFrame =
554 [[selectedPanel collectionView] convertRect:selectedCellFrame
555 toView:self.view];
556
557 // Compute initial and final toolbar screenshot frames.
558 const CGRect initialToolbarFrame = toolbarController.view.frame;
559 CGRect initialToolbarScreenshotFrame = CGRectMake(
560 0, 0, initialToolbarFrame.size.width, initialToolbarFrame.size.height);
561
562 const CGFloat cellTopBarHeight = tabSwitcherLocalSessionCellTopBarHeight();
563 CGRect finalToolbarScreenshotFrame =
564 CGRectMake(0, 0, selectedCellFrame.size.width, cellTopBarHeight);
565
566 base::scoped_nsobject<UIImageView> tabScreenshotImageView(
567 [[UIImageView alloc] initWithFrame:CGRectZero]);
568
569 base::WeakNSObject<UIImageView> weakTabScreenshotImageView(
570 tabScreenshotImageView.get());
571
572 if (self.transitionContext.initialTabModel != tabModel ||
573 transitionContextContent.initialSelectedTabIndex != selectedTabIndex) {
574 tabScreenshotImageView.get().image =
575 [self updateScreenshotForCellIfNeeded:selectedCell tabModel:tabModel];
576 [selectedTab retrieveSnapshot:^(UIImage* snapshot) {
577 [weakTabScreenshotImageView setImage:snapshot];
578 }];
579 } else {
580 tabScreenshotImageView.get().image =
581 self.transitionContext.tabSnapshotImage;
582 }
583
584 const CGSize tabScreenshotImageSize = tabScreenshotImageView.get().image.size;
585
586 CGRect initialTabScreenshotFrame = CGRectZero;
587 const CGSize toolbarSize = toolbarController.view.bounds.size;
588 CGSize initialTabTargetSize =
589 CGSizeMake(initialTabFrame.size.width,
590 initialTabFrame.size.height - toolbarSize.height);
591 CGSize revisedTargetSize = CGSizeZero;
592 CalculateProjection(tabScreenshotImageSize, initialTabTargetSize,
593 ProjectionMode::kAspectFill, revisedTargetSize,
594 initialTabScreenshotFrame);
595 initialTabScreenshotFrame.origin.y += toolbarSize.height;
596
597 CGSize finalTabTargetSize =
598 CGSizeMake(selectedCellFrame.size.width,
599 selectedCellFrame.size.height - cellTopBarHeight);
600 CGRect finalTabScreenshotFrame;
601 CalculateProjection(tabScreenshotImageSize, finalTabTargetSize,
602 ProjectionMode::kAspectFill, revisedTargetSize,
603 finalTabScreenshotFrame);
604 finalTabScreenshotFrame.origin.y += cellTopBarHeight;
605
606 TabSwitcherTabStripPlaceholderView* tabStripPlaceholderView =
607 [transitionContextContent generateTabStripPlaceholderView];
608 tabStripPlaceholderView.clipsToBounds = YES;
609 tabStripPlaceholderView.backgroundColor = [UIColor clearColor];
610 [self.view addSubview:tabStripPlaceholderView];
611
612 CGRect tabStripInitialFrame =
613 CGRectMake(0, StatusBarHeight(), self.view.bounds.size.width,
614 tabStripPlaceholderView.frame.size.height);
615 CGRect tabStripFinalFrame =
616 CGRectMake(finalTabFrame.origin.x,
617 finalTabFrame.origin.y - tabStripInitialFrame.size.height,
618 finalTabFrame.size.width, tabStripInitialFrame.size.height);
619 CGRect shadowInitialFrame =
620 CGRectMake(0, CGRectGetMaxY(initialToolbarScreenshotFrame),
621 initialToolbarScreenshotFrame.size.width, 2);
622 CGRect shadowFinalFrame =
623 CGRectMake(0, CGRectGetMaxY(finalToolbarScreenshotFrame),
624 finalToolbarScreenshotFrame.size.width, 2);
625
626 if (transitionType == TransitionType::TRANSITION_DISMISS) {
627 [tabStripPlaceholderView unfoldWithCompletion:nil];
628 std::swap(initialTabFrame, finalTabFrame);
629 std::swap(tabStripInitialFrame, tabStripFinalFrame);
630 std::swap(shadowInitialFrame, shadowFinalFrame);
631 std::swap(initialTabScreenshotFrame, finalTabScreenshotFrame);
632 std::swap(initialToolbarScreenshotFrame, finalToolbarScreenshotFrame);
633 } else {
634 [tabStripPlaceholderView foldWithCompletion:^{
635 [tabStripPlaceholderView removeFromSuperview];
636 }];
637 }
638
639 tabStripPlaceholderView.frame = tabStripInitialFrame;
640
641 // Create and setup placeholder view and subviews.
642 base::scoped_nsobject<UIView> placeholderView(
643 [[UIView alloc] initWithFrame:initialTabFrame]);
644 [placeholderView setClipsToBounds:YES];
645 [placeholderView setUserInteractionEnabled:NO];
646
647 tabScreenshotImageView.get().frame = initialTabScreenshotFrame;
648 tabScreenshotImageView.get().contentMode = UIViewContentModeScaleToFill;
649 tabScreenshotImageView.get().autoresizingMask = UIViewAutoresizingNone;
650 [placeholderView addSubview:tabScreenshotImageView];
651
652 // Try using a snapshot view for dismissal animation because it's faster and
653 // the collection view has already been rendered. Use a client rendering
654 // otherwise.
655 SnapshotViewOption snapshotOption = SnapshotViewOption::SNAPSHOT_VIEW;
656 if (transitionType == TransitionType::TRANSITION_PRESENT)
657 snapshotOption = SnapshotViewOption::CLIENT_RENDERING;
658
659 UIView* finalToolbarScreenshotImageView =
660 [self snapshotViewForView:selectedCell.topBar
661 withModel:tabModel
662 option:snapshotOption];
663 finalToolbarScreenshotImageView.autoresizingMask = UIViewAutoresizingNone;
664 finalToolbarScreenshotImageView.frame = initialToolbarScreenshotFrame;
665 [placeholderView addSubview:finalToolbarScreenshotImageView];
666
667 UIView* toolbarScreenshotImageView =
668 transitionContextContent.toolbarSnapshotView;
669 toolbarScreenshotImageView.autoresizingMask = UIViewAutoresizingNone;
670 toolbarScreenshotImageView.frame = initialToolbarScreenshotFrame;
671 [placeholderView addSubview:toolbarScreenshotImageView];
672
673 base::scoped_nsobject<UIImageView> toolbarShadowImageView(
674 [[UIImageView alloc] initWithFrame:shadowInitialFrame]);
675 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
676 gfx::Image shadow = rb.GetNativeImageNamed(IDR_IOS_TOOLBAR_SHADOW);
677 [toolbarShadowImageView setAutoresizingMask:UIViewAutoresizingNone];
678 [toolbarShadowImageView setImage:shadow.ToUIImage()];
679 [placeholderView addSubview:toolbarShadowImageView];
680
681 [self.view addSubview:placeholderView];
682
683 [selectedCell setHidden:YES];
684 toolbarScreenshotImageView.alpha =
685 (transitionType == TransitionType::TRANSITION_DISMISS) ? 0 : 1.0;
686
687 base::WeakNSObject<TabSwitcherController> weakSelf(self);
688 void (^completionBlock)(BOOL) = ^(BOOL) {
689 base::scoped_nsobject<TabSwitcherController> strongSelf([weakSelf retain]);
690
691 [tabStripPlaceholderView removeFromSuperview];
692 [toolbarScreenshotImageView removeFromSuperview];
693 [selectedCell setHidden:NO];
694 [placeholderView removeFromSuperview];
695
696 if (transitionType == TransitionType::TRANSITION_DISMISS)
697 [strongSelf restoreWindowBackgroundColor];
698 [[[strongSelf delegate] tabSwitcherTransitionToolbarOwner]
699 reparentToolbarController];
700 [[strongSelf view] setUserInteractionEnabled:YES];
701 completion();
702 };
703
704 ProceduralBlock animationBlock = ^{
705 toolbarScreenshotImageView.alpha =
706 transitionType == TransitionType::TRANSITION_DISMISS ? 1.0 : 0;
707 tabStripPlaceholderView.frame = tabStripFinalFrame;
708 toolbarShadowImageView.get().frame = shadowFinalFrame;
709 placeholderView.get().frame = finalTabFrame;
710 toolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
711 finalToolbarScreenshotImageView.frame = finalToolbarScreenshotFrame;
712 tabScreenshotImageView.get().frame = finalTabScreenshotFrame;
713 };
714
715 [UIView animateWithDuration:animated ? kTransitionAnimationDuration : 0
716 delay:0
717 options:UIViewAnimationCurveEaseInOut
718 animations:animationBlock
719 completion:completionBlock];
720 }
721
722 - (void)updateLocalPanelsCells {
723 auto mainTabPanel =
724 [self panelControllerForTabModel:[_tabSwitcherModel mainTabModel]];
725 auto otrTabPanel =
726 [self panelControllerForTabModel:[_tabSwitcherModel otrTabModel]];
727 [mainTabPanel reload];
728 [otrTabPanel reload];
729 }
730
731 - (NSInteger)currentPanelIndex {
732 return [_tabSwitcherView currentPanelIndex];
733 }
734
735 - (int)offsetToDistantSessionPanels {
736 if (_signInPanelType == TabSwitcherSignInPanelsType::NO_PANEL) {
737 return kSignInPromoPanelIndex;
738 }
739 return kSignInPromoPanelIndex + 1;
740 }
741
742 - (ios_internal::SessionType)sessionTypeForPanelIndex:(NSInteger)panelIndex {
743 if (panelIndex == kLocalTabsOffTheRecordPanelIndex)
744 return ios_internal::SessionType::OFF_THE_RECORD_SESSION;
745 if (panelIndex == kLocalTabsOnTheRecordPanelIndex)
746 return ios_internal::SessionType::REGULAR_SESSION;
747 return ios_internal::SessionType::DISTANT_SESSION;
748 }
749
750 - (TabModel*)tabModelForSessionType:(ios_internal::SessionType)sessionType {
751 switch (sessionType) {
752 case ios_internal::SessionType::REGULAR_SESSION:
753 return [_tabSwitcherModel mainTabModel];
754 break;
755 case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
756 return [_tabSwitcherModel otrTabModel];
757 break;
758 case ios_internal::SessionType::DISTANT_SESSION:
759 return nil;
760 break;
761 }
762 }
763
764 - (TabModel*)currentSelectedModel {
765 const NSInteger currentPanelIndex = [self currentPanelIndex];
766 const ios_internal::SessionType sessionType =
767 [self sessionTypeForPanelIndex:currentPanelIndex];
768 TabModel* model = [self tabModelForSessionType:sessionType];
769 if (!model)
770 model = _onLoadActiveModel;
771 return model;
772 }
773
774 - (void)selectPanelForTabModel:(TabModel*)selectedTabModel {
775 DCHECK(selectedTabModel == [_tabSwitcherModel otrTabModel] ||
776 selectedTabModel == [_tabSwitcherModel mainTabModel]);
777 NSInteger selectedPanel =
778 (selectedTabModel == [_tabSwitcherModel otrTabModel])
779 ? kLocalTabsOffTheRecordPanelIndex
780 : kLocalTabsOnTheRecordPanelIndex;
781 [_tabSwitcherView selectPanelAtIndex:selectedPanel];
782 }
783
784 - (TabSwitcherPanelController*)panelControllerForTabModel:(TabModel*)tabModel {
785 DCHECK(tabModel == [_tabSwitcherModel mainTabModel] ||
786 tabModel == [_tabSwitcherModel otrTabModel]);
787 if (tabModel == [_tabSwitcherModel mainTabModel])
788 return _onTheRecordSession.get();
789 if (tabModel == [_tabSwitcherModel otrTabModel])
790 return _offTheRecordSession.get();
791 return nil;
792 }
793
794 - (void)tabSwitcherDismissWithModel:(TabModel*)model {
795 [self tabSwitcherDismissWithModel:model animated:YES];
796 }
797
798 - (void)tabSwitcherDismissWithModel:(TabModel*)model animated:(BOOL)animated {
799 [self tabSwitcherDismissWithModel:model
800 animated:animated
801 withCompletion:^{
802 [self.delegate tabSwitcherDismissTransitionDidEnd:self];
803 }];
804 }
805
806 - (void)tabSwitcherDismissWithModel:(TabModel*)model
807 animated:(BOOL)animated
808 withCompletion:(ProceduralBlock)completion {
809 DCHECK(completion);
810 DCHECK(model);
811 [[self presentedViewController] dismissViewControllerAnimated:NO
812 completion:nil];
813 [self.delegate tabSwitcher:self
814 dismissTransitionWillStartWithActiveModel:model];
815 [self performTabSwitcherTransition:TransitionType::TRANSITION_DISMISS
816 withModel:model
817 animated:animated
818 withCompletion:^{
819 [self.view removeFromSuperview];
820 completion();
821 }];
822 }
823
824 - (void)tabSwitcherDismissWithCurrentSelectedModel {
825 TabModel* model = [self currentSelectedModel];
826 base::RecordAction(base::UserMetricsAction("MobileTabSwitcherClose"));
827 [self tabSwitcherDismissWithModel:model];
828 }
829
830 - (Tab*)dismissWithNewTabAnimation:(const GURL&)URL
831 atIndex:(NSUInteger)position
832 transition:(ui::PageTransition)transition
833 tabModel:(TabModel*)tabModel {
834 web::NavigationManager::WebLoadParams params(URL);
835 params.referrer = web::Referrer();
836 params.transition_type = transition;
837
838 DCHECK(tabModel.browserState);
839
840 base::scoped_nsobject<Tab> tab([[Tab alloc]
841 initWithWindowName:nil
842 opener:nil
843 openedByDOM:NO
844 model:tabModel
845 browserState:tabModel.browserState]);
846 [tab webController].webUsageEnabled = tabModel.webUsageEnabled;
847
848 ProceduralBlock dismissWithNewTab = ^{
849 NSUInteger tabIndex = position;
850 if (position > tabModel.count)
851 tabIndex = tabModel.count;
852 [tabModel insertTab:tab atIndex:tabIndex];
853
854 if (tabModel.tabUsageRecorder)
855 tabModel.tabUsageRecorder->TabCreatedForSelection(tab);
856
857 [[tab webController] loadWithParams:params];
858
859 if (tabModel.webUsageEnabled) {
860 [tab setWebUsageEnabled:tabModel.webUsageEnabled];
861 [[tab webController] triggerPendingLoad];
862 }
863
864 NSDictionary* userInfo = @{
865 kTabModelTabKey : tab,
866 kTabModelOpenInBackgroundKey : @(NO),
867 };
868
869 [[NSNotificationCenter defaultCenter]
870 postNotificationName:kTabModelNewTabWillOpenNotification
871 object:self
872 userInfo:userInfo];
873
874 [tabModel setCurrentTab:tab];
875
876 [self
877 tabSwitcherDismissWithModel:tabModel
878 animated:YES
879 withCompletion:^{
880 [self.delegate tabSwitcherDismissTransitionDidEnd:self];
881 }];
882 };
883
884 dismissWithNewTab();
885 return tab;
886 }
887
888 - (BOOL)isPanelIndexForLocalSession:(NSUInteger)panelIndex {
889 return panelIndex == kLocalTabsOnTheRecordPanelIndex ||
890 panelIndex == kLocalTabsOffTheRecordPanelIndex;
891 }
892
893 - (void)openTabWithContentOfDistantTab:
894 (synced_sessions::DistantTab const*)distantTab {
895 sync_sessions::OpenTabsUIDelegate* openTabs =
896 IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState)
897 ->GetOpenTabsUIDelegate();
898 const sessions::SessionTab* toLoad = nullptr;
899 if (openTabs->GetForeignTab(distantTab->session_tag, distantTab->tab_id,
900 &toLoad)) {
901 TabModel* mainModel = [_tabSwitcherModel mainTabModel];
902 // Disable user interactions until the tab is inserted to prevent multiple
903 // concurrent tab model updates.
904 [_tabSwitcherView setUserInteractionEnabled:NO];
905 Tab* tab = [mainModel insertOrUpdateTabWithURL:GURL()
906 referrer:web::Referrer()
907 transition:ui::PAGE_TRANSITION_TYPED
908 windowName:nil
909 opener:nil
910 openedByDOM:NO
911 atIndex:NSNotFound
912 inBackground:NO];
913 [tab loadSessionTab:toLoad];
914 [mainModel setCurrentTab:tab];
915
916 // Reenable touch events.
917 [_tabSwitcherView setUserInteractionEnabled:YES];
918 [self
919 tabSwitcherDismissWithModel:mainModel
920 animated:YES
921 withCompletion:^{
922 [self.delegate tabSwitcherDismissTransitionDidEnd:self];
923 }];
924 }
925 }
926
927 - (void)removePromoPanelHeaderCellIfNeeded {
928 if (_shouldRemovePromoPanelHeaderCell) {
929 _shouldRemovePromoPanelHeaderCell = NO;
930 [[_tabSwitcherView headerView]
931 removeSessionsAtIndexes:@[ @(kSignInPromoPanelIndex) ]];
932 }
933 }
934
935 - (void)addPromoPanelHeaderCellIfNeeded {
936 if (_shouldAddPromoPanelHeaderCell) {
937 _shouldAddPromoPanelHeaderCell = NO;
938 [[_tabSwitcherView headerView]
939 insertSessionsAtIndexes:@[ @(kSignInPromoPanelIndex) ]];
940 }
941 }
942
943 - (void)reopenClosedTab {
944 sessions::TabRestoreService* const tabRestoreService =
945 IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
946 if (!tabRestoreService || tabRestoreService->entries().empty())
947 return;
948
949 const std::unique_ptr<sessions::TabRestoreService::Entry>& entry =
950 tabRestoreService->entries().front();
951 // Only handle the TAB type.
952 if (entry->type != sessions::TabRestoreService::TAB)
953 return;
954
955 [self chromeExecuteCommand:[GenericChromeCommand commandWithTag:IDC_NEW_TAB]];
956 TabRestoreServiceDelegateImplIOS* const delegate =
957 TabRestoreServiceDelegateImplIOSFactory::GetForBrowserState(
958 _browserState);
959 tabRestoreService->RestoreEntryById(delegate, entry->id,
960 WindowOpenDisposition::CURRENT_TAB);
961 }
962
963 #pragma mark - TabSwitcherModelDelegate
964
965 - (void)distantSessionsRemovedAtSortedIndexes:(NSArray*)removedIndexes
966 insertedAtSortedIndexes:(NSArray*)insertedIndexes {
967 // Update the panels
968 int offset = [self offsetToDistantSessionPanels];
969
970 // Iterate in reverse order so that indexes in |removedIndexes| stays synced
971 // with the indexes in |_controllersOfDistantSessions|.
972 for (NSNumber* objCIndex in [removedIndexes reverseObjectEnumerator]) {
973 int index = [objCIndex intValue];
974 [_tabSwitcherView removePanelViewAtIndex:index + offset];
975 [_controllersOfDistantSessions removeObjectAtIndex:index];
976 }
977
978 for (NSNumber* objCIndex in insertedIndexes) {
979 int index = [objCIndex intValue];
980 std::string tag = [_tabSwitcherModel tagOfDistantSessionAtIndex:index];
981 base::scoped_nsobject<TabSwitcherPanelController> panelController(
982 [[TabSwitcherPanelController alloc] initWithModel:_tabSwitcherModel
983 forDistantSessionWithTag:tag
984 browserState:_browserState]);
985 [panelController setDelegate:self];
986 [_tabSwitcherView addPanelView:[panelController view]
987 atIndex:index + offset];
988 [_controllersOfDistantSessions insertObject:panelController.get()
989 atIndex:index];
990 }
991
992 // Update the header view.
993 // The header view's content is created using the panel's content.
994 [[_tabSwitcherView headerView] reloadData];
995 }
996
997 - (void)distantSessionMayNeedUpdate:(std::string const&)tag {
998 for (TabSwitcherPanelController* panel in _controllersOfDistantSessions
999 .get()) {
1000 DCHECK([panel isKindOfClass:[TabSwitcherPanelController class]]);
1001 if ([panel sessionTag] == tag) {
1002 [panel updateCollectionViewIfNeeded];
1003 return;
1004 }
1005 }
1006 }
1007
1008 - (void)localSessionMayNeedUpdate:(ios_internal::SessionType)type {
1009 if (type == ios_internal::SessionType::REGULAR_SESSION) {
1010 [_onTheRecordSession updateCollectionViewIfNeeded];
1011 } else {
1012 DCHECK(type == ios_internal::SessionType::OFF_THE_RECORD_SESSION);
1013 [_offTheRecordSession updateCollectionViewIfNeeded];
1014 }
1015 }
1016
1017 - (void)signInPanelChangedTo:(TabSwitcherSignInPanelsType)newPanelType {
1018 if (_signInPanelType == newPanelType)
1019 return;
1020
1021 // Remove promo panel that is no longer relevant, if any.
1022 if (_signInPanelType != TabSwitcherSignInPanelsType::NO_PANEL) {
1023 _shouldRemovePromoPanelHeaderCell = YES;
1024 BOOL updateScrollView =
1025 [_tabSwitcherView currentPanelIndex] != kSignInPromoPanelIndex;
1026 [_tabSwitcherView removePanelViewAtIndex:kSignInPromoPanelIndex
1027 updateScrollView:updateScrollView];
1028 } else {
1029 _shouldAddPromoPanelHeaderCell = YES;
1030 }
1031 [self addPromoPanelForSignInPanelType:newPanelType];
1032 }
1033
1034 - (void)addPromoPanelForSignInPanelType:(TabSwitcherSignInPanelsType)panelType {
1035 _signInPanelType = panelType;
1036 if (panelType != TabSwitcherSignInPanelsType::NO_PANEL) {
1037 TabSwitcherPanelOverlayView* panelView =
1038 [[[TabSwitcherPanelOverlayView alloc] initWithFrame:CGRectZero
1039 browserState:_browserState]
1040 autorelease];
1041 [panelView setOverlayType:PanelOverlayTypeFromSignInPanelsType(panelType)];
1042 [_tabSwitcherView addPanelView:panelView atIndex:kSignInPromoPanelIndex];
1043 }
1044 }
1045
1046 - (CGSize)sizeForItemAtIndex:(NSUInteger)index
1047 inSession:(ios_internal::SessionType)session {
1048 switch (session) {
1049 case ios_internal::SessionType::OFF_THE_RECORD_SESSION:
1050 return [[_offTheRecordSession view] cellSize];
1051 case ios_internal::SessionType::REGULAR_SESSION:
1052 return [[_onTheRecordSession view] cellSize];
1053 case ios_internal::SessionType::DISTANT_SESSION:
1054 NOTREACHED();
1055 return {};
1056 }
1057 }
1058
1059 #pragma mark - TabSwitcherHeaderViewDelegate
1060
1061 - (void)tabSwitcherHeaderViewDismiss:(TabSwitcherHeaderView*)view {
1062 [self tabSwitcherDismissWithCurrentSelectedModel];
1063 }
1064
1065 - (void)tabSwitcherHeaderViewDidSelectSessionAtIndex:(NSInteger)index {
1066 switch (index) {
1067 case kLocalTabsOffTheRecordPanelIndex:
1068 base::RecordAction(base::UserMetricsAction(
1069 "MobileTabSwitcherHeaderViewSelectIncognitoPanel"));
1070 break;
1071 case kLocalTabsOnTheRecordPanelIndex:
1072 base::RecordAction(base::UserMetricsAction(
1073 "MobileTabSwitcherHeaderViewSelectNonIncognitoPanel"));
1074 break;
1075 default:
1076 base::RecordAction(base::UserMetricsAction(
1077 "MobileTabSwitcherHeaderViewSelectDistantSessionPanel"));
1078 break;
1079 }
1080 [_tabSwitcherView selectPanelAtIndex:index];
1081 }
1082
1083 #pragma mark - TabSwitcherHeaderViewDataSource
1084
1085 - (NSInteger)tabSwitcherHeaderViewSessionCount {
1086 NSInteger promoPanel = (![_tabSwitcherModel distantSessionCount]) ? 1 : 0;
1087 return [_tabSwitcherModel sessionCount] + promoPanel;
1088 }
1089
1090 - (SessionCellData*)sessionCellDataAtIndex:(NSUInteger)index {
1091 if (index == kLocalTabsOffTheRecordPanelIndex) {
1092 // If has incognito tabs return incognito cell data.
1093 return [SessionCellData incognitoSessionCellData];
1094 } else if (index == kLocalTabsOnTheRecordPanelIndex) {
1095 return [SessionCellData openTabSessionCellData];
1096 } else {
1097 if (![_tabSwitcherModel distantSessionCount]) {
1098 // Display promo panel cell if there is no distant sessions.
1099 return [SessionCellData otherDevicesSessionCellData];
1100 } else {
1101 index -= kHeaderDistantSessionIndexOffset;
1102
1103 sync_sessions::SyncedSession::DeviceType deviceType =
1104 sync_sessions::SyncedSession::TYPE_UNSET;
1105 NSString* cellTitle = nil;
1106
1107 if (index < _controllersOfDistantSessions.get().count) {
1108 TabSwitcherPanelController* panel =
1109 [_controllersOfDistantSessions objectAtIndex:index];
1110 const synced_sessions::DistantSession* distantSession =
1111 [panel distantSession];
1112 deviceType = distantSession->device_type;
1113 cellTitle = base::SysUTF8ToNSString(distantSession->name);
1114 }
1115 ios_internal::SessionCellType cellType;
1116 switch (deviceType) {
1117 case sync_sessions::SyncedSession::TYPE_PHONE:
1118 cellType = ios_internal::kPhoneRemoteSessionCell;
1119 break;
1120 case sync_sessions::SyncedSession::TYPE_TABLET:
1121 cellType = ios_internal::kTabletRemoteSessionCell;
1122 break;
1123 default:
1124 cellType = ios_internal::kLaptopRemoteSessionCell;
1125 break;
1126 }
1127 SessionCellData* sessionData = [[[SessionCellData alloc]
1128 initWithSessionCellType:cellType] autorelease];
1129 sessionData.title = cellTitle;
1130 return sessionData;
1131 }
1132 }
1133 }
1134
1135 - (NSInteger)tabSwitcherHeaderViewSelectedPanelIndex {
1136 return [_tabSwitcherView currentPanelIndex];
1137 }
1138
1139 #pragma mark - TabSwitcherViewDelegate
1140
1141 - (void)openNewTabInPanelAtIndex:(NSInteger)panelIndex {
1142 CHECK(panelIndex >= 0);
1143 DCHECK([self isPanelIndexForLocalSession:panelIndex]);
1144 const NSInteger tag = (panelIndex == kLocalTabsOnTheRecordPanelIndex)
1145 ? IDC_NEW_TAB
1146 : IDC_NEW_INCOGNITO_TAB;
1147 if (tag == IDC_NEW_INCOGNITO_TAB) {
1148 base::RecordAction(
1149 base::UserMetricsAction("MobileTabSwitcherCreateIncognitoTab"));
1150 } else {
1151 base::RecordAction(
1152 base::UserMetricsAction("MobileTabSwitcherCreateNonIncognitoTab"));
1153 }
1154 // Create and execute command to create the tab.
1155 base::scoped_nsobject<GenericChromeCommand> command(
1156 [[GenericChromeCommand alloc] initWithTag:tag]);
1157 [self chromeExecuteCommand:command];
1158 }
1159
1160 - (ios_internal::NewTabButtonStyle)buttonStyleForPanelAtIndex:
1161 (NSInteger)panelIndex {
1162 CHECK(panelIndex >= 0);
1163 switch (panelIndex) {
1164 case kLocalTabsOnTheRecordPanelIndex:
1165 if ([_onTheRecordSession shouldShowNewTabButton]) {
1166 return ios_internal::NewTabButtonStyle::BLUE;
1167 } else {
1168 return ios_internal::NewTabButtonStyle::HIDDEN;
1169 }
1170 case kLocalTabsOffTheRecordPanelIndex:
1171 if ([_offTheRecordSession shouldShowNewTabButton]) {
1172 return ios_internal::NewTabButtonStyle::GRAY;
1173 } else {
1174 return ios_internal::NewTabButtonStyle::HIDDEN;
1175 }
1176 default:
1177 return ios_internal::NewTabButtonStyle::HIDDEN;
1178 }
1179 }
1180
1181 - (BOOL)shouldShowDismissButtonForPanelAtIndex:(NSInteger)panelIndex {
1182 CHECK(panelIndex >= 0);
1183 switch (panelIndex) {
1184 case kLocalTabsOnTheRecordPanelIndex:
1185 return [[_tabSwitcherModel mainTabModel] count] != 0;
1186 case kLocalTabsOffTheRecordPanelIndex:
1187 return [[_tabSwitcherModel otrTabModel] count] != 0;
1188 default:
1189 return [[self currentSelectedModel] count] != 0;
1190 }
1191 }
1192
1193 - (void)tabSwitcherViewDelegateDismissTabSwitcher:(TabSwitcherView*)view {
1194 [self tabSwitcherDismissWithCurrentSelectedModel];
1195 }
1196
1197 #pragma mark - TabSwitcherPanelControllerDelegate
1198
1199 - (void)tabSwitcherPanelController:
1200 (TabSwitcherPanelController*)tabSwitcherPanelController
1201 didSelectDistantTab:(synced_sessions::DistantTab*)tab {
1202 DCHECK(tab);
1203 [self openTabWithContentOfDistantTab:tab];
1204 base::RecordAction(
1205 base::UserMetricsAction("MobileTabSwitcherOpenDistantTab"));
1206 }
1207
1208 - (void)tabSwitcherPanelController:
1209 (TabSwitcherPanelController*)tabSwitcherPanelController
1210 didSelectLocalTab:(Tab*)tab {
1211 DCHECK(tab);
1212 const ios_internal::SessionType panelSessionType =
1213 tabSwitcherPanelController.sessionType;
1214 TabModel* tabModel = [self tabModelForSessionType:panelSessionType];
1215 [tabModel setCurrentTab:tab];
1216 [self.delegate tabSwitcher:self
1217 dismissTransitionWillStartWithActiveModel:tabModel];
1218 [self tabSwitcherDismissWithModel:tabModel];
1219 if (panelSessionType == ios_internal::SessionType::OFF_THE_RECORD_SESSION) {
1220 base::RecordAction(
1221 base::UserMetricsAction("MobileTabSwitcherOpenIncognitoTab"));
1222 } else {
1223 base::RecordAction(
1224 base::UserMetricsAction("MobileTabSwitcherOpenNonIncognitoTab"));
1225 }
1226 }
1227
1228 - (void)tabSwitcherPanelController:
1229 (TabSwitcherPanelController*)tabSwitcherPanelController
1230 didCloseLocalTab:(Tab*)tab {
1231 DCHECK(tab);
1232 const ios_internal::SessionType panelSessionType =
1233 tabSwitcherPanelController.sessionType;
1234 [tab close];
1235 if (panelSessionType == ios_internal::SessionType::OFF_THE_RECORD_SESSION) {
1236 base::RecordAction(
1237 base::UserMetricsAction("MobileTabSwitcherCloseIncognitoTab"));
1238 } else {
1239 base::RecordAction(
1240 base::UserMetricsAction("MobileTabSwitcherCloseNonIncognitoTab"));
1241 }
1242 }
1243
1244 - (void)tabSwitcherPanelControllerDidUpdateOverlayViewVisibility:
1245 (TabSwitcherPanelController*)tabSwitcherPanelController {
1246 [_tabSwitcherView updateOverlayButtonState];
1247 }
1248
1249 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698