OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/ui/history/tab_history_view_controller.h" | 5 #import "ios/chrome/browser/ui/history/tab_history_view_controller.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/mac/foundation_util.h" | 8 #include "base/mac/foundation_util.h" |
9 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
10 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" | 10 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" |
11 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" | 11 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
12 #import "ios/chrome/browser/ui/history/tab_history_cell.h" | 12 #import "ios/chrome/browser/ui/history/tab_history_cell.h" |
13 #include "ios/chrome/browser/ui/rtl_geometry.h" | 13 #include "ios/chrome/browser/ui/rtl_geometry.h" |
14 #import "ios/third_party/material_components_ios/src/components/Ink/src/Material
Ink.h" | 14 #import "ios/third_party/material_components_ios/src/components/Ink/src/Material
Ink.h" |
| 15 #import "ios/web/navigation/crw_session_entry.h" |
15 #include "ios/web/public/favicon_status.h" | 16 #include "ios/web/public/favicon_status.h" |
16 #include "ios/web/public/navigation_item.h" | 17 #include "ios/web/public/navigation_item.h" |
17 #include "ui/gfx/image/image.h" | 18 #include "ui/gfx/image/image.h" |
18 | 19 |
19 #if !defined(__has_feature) || !__has_feature(objc_arc) | 20 #if !defined(__has_feature) || !__has_feature(objc_arc) |
20 #error "This file requires ARC support." | 21 #error "This file requires ARC support." |
21 #endif | 22 #endif |
22 | 23 |
23 namespace { | 24 namespace { |
24 | 25 |
25 // Visible percentage of the last visible row on the Tools menu if the | 26 // Visible percentage of the last visible row on the Tools menu if the |
26 // Tools menu is scrollable. | 27 // Tools menu is scrollable. |
27 const CGFloat kLastRowVisiblePercentage = 0.6; | 28 const CGFloat kLastRowVisiblePercentage = 0.6; |
28 // Reuse identifier for cells. | 29 // Reuse identifier for cells. |
29 NSString* const kCellIdentifier = @"TabHistoryCell"; | 30 NSString* cellIdentifier = @"TabHistoryCell"; |
30 NSString* const kFooterIdentifier = @"Footer"; | 31 NSString* footerIdentifier = @"Footer"; |
31 NSString* const kHeaderIdentifier = @"Header"; | 32 NSString* headerIdentifier = @"Header"; |
32 // The collection view's a11y label. | |
33 NSString* const kCollectionViewLabel = @"Tab History"; | |
34 // Height of rows. | 33 // Height of rows. |
35 const CGFloat kCellHeight = 48.0; | 34 const CGFloat kCellHeight = 48.0; |
36 // Fraction height for partially visible row. | 35 // Fraction height for partially visible row. |
37 const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage; | 36 const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage; |
38 // Width and leading for the header view. | 37 // Width and leading for the header view. |
39 const CGFloat kHeaderLeadingInset = 0; | 38 const CGFloat kHeaderLeadingInset = 0; |
40 const CGFloat kHeaderWidth = 48; | 39 const CGFloat kHeaderWidth = 48; |
41 // Leading and trailing insets for cell items. | 40 // Leading and trailing insets for cell items. |
42 const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth; | 41 const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth; |
43 const CGFloat kCellTrailingInset = 16; | 42 const CGFloat kCellTrailingInset = 16; |
(...skipping 21 matching lines...) Expand all Loading... |
65 DCHECK(offsets.at([path section]).size()); | 64 DCHECK(offsets.at([path section]).size()); |
66 | 65 |
67 return offsets.at([path section]).at(0); | 66 return offsets.at([path section]).at(0); |
68 } | 67 } |
69 | 68 |
70 // Height for the footer view. | 69 // Height for the footer view. |
71 NS_INLINE CGFloat FooterHeight() { | 70 NS_INLINE CGFloat FooterHeight() { |
72 return 1.0 / [[UIScreen mainScreen] scale]; | 71 return 1.0 / [[UIScreen mainScreen] scale]; |
73 } | 72 } |
74 | 73 |
75 // Returns a vector of of NavigationItemLists where the NavigationItems in | |
76 // |items| are separated by host. | |
77 NS_INLINE std::vector<web::NavigationItemList> PartitionItemsByHost( | |
78 const web::NavigationItemList& items) { | |
79 std::vector<web::NavigationItemList> partitionedItems; | |
80 // Used to store the previous host when partitioning NavigationItems. | |
81 std::string previousHost; | |
82 // The NavigationItemList containing NavigationItems with the same host. | |
83 web::NavigationItemList itemsWithSameHostname; | |
84 // Separate the items in |items| by host. | |
85 for (web::NavigationItem* item : items) { | |
86 std::string currentHost = item->GetURL().host(); | |
87 if (previousHost.empty()) | |
88 previousHost = currentHost; | |
89 // TODO: This should use some sort of Top Level Domain matching instead of | |
90 // explicit host match so that images.googe.com matches shopping.google.com. | |
91 if (previousHost == currentHost) { | |
92 itemsWithSameHostname.push_back(item); | |
93 } else { | |
94 partitionedItems.push_back(itemsWithSameHostname); | |
95 itemsWithSameHostname = web::NavigationItemList(1, item); | |
96 previousHost = currentHost; | |
97 } | |
98 } | |
99 // Add the last list contiaining the same host. | |
100 if (!itemsWithSameHostname.empty()) | |
101 partitionedItems.push_back(itemsWithSameHostname); | |
102 return partitionedItems; | |
103 } | |
104 | |
105 } // namespace | 74 } // namespace |
106 | 75 |
107 @interface TabHistoryViewControllerLayout : UICollectionViewLayout | 76 @interface TabHistoryViewControllerLayout : UICollectionViewLayout |
108 @end | 77 @end |
109 | 78 |
110 @implementation TabHistoryViewControllerLayout { | 79 @implementation TabHistoryViewControllerLayout { |
111 ItemOffsetVector _itemYOffsets; | 80 ItemOffsetVector _itemYOffsets; |
112 CGFloat _containerCalculatedHeight; | 81 CGFloat _containerCalculatedHeight; |
113 CGFloat _containerWidth; | 82 CGFloat _containerWidth; |
114 CGFloat _cellItemWidth; | 83 CGFloat _cellItemWidth; |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 [attributes setZIndex:0]; | 212 [attributes setZIndex:0]; |
244 } | 213 } |
245 | 214 |
246 return attributes; | 215 return attributes; |
247 } | 216 } |
248 | 217 |
249 @end | 218 @end |
250 | 219 |
251 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> { | 220 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> { |
252 MDCInkTouchController* _inkTouchController; | 221 MDCInkTouchController* _inkTouchController; |
253 // A vector of NavigationItemLists where the NavigationItems are separated | 222 NSArray* _partitionedEntries; |
254 // by hostname. | 223 NSArray* _sessionEntries; |
255 std::vector<web::NavigationItemList> _partitionedItems; | |
256 } | 224 } |
257 | |
258 // Returns the NavigationItem corresponding with |indexPath|. | |
259 - (const web::NavigationItem*)itemAtIndexPath:(NSIndexPath*)indexPath; | |
260 | |
261 // Removes all NavigationItem pointers from this class. Tapping a cell that | |
262 // triggers a navigation may delete NavigationItems, so NavigationItem | |
263 // references should be reset to avoid use-after-free errors. | |
264 - (void)clearNavigationItems; | |
265 | |
266 @end | 225 @end |
267 | 226 |
268 @implementation TabHistoryViewController | 227 @implementation TabHistoryViewController |
269 | 228 |
270 - (instancetype)initWithItems:(const web::NavigationItemList&)items { | 229 - (NSArray*)sessionEntries { |
271 TabHistoryViewControllerLayout* layout = | 230 return _sessionEntries; |
272 [[TabHistoryViewControllerLayout alloc] init]; | |
273 if ((self = [super initWithCollectionViewLayout:layout])) { | |
274 // Populate |_partitionedItems|. | |
275 _partitionedItems = PartitionItemsByHost(items); | |
276 | |
277 // Set up the UICollectionView. | |
278 UICollectionView* collectionView = [self collectionView]; | |
279 collectionView.accessibilityLabel = kCollectionViewLabel; | |
280 collectionView.backgroundColor = [UIColor whiteColor]; | |
281 [collectionView registerClass:[TabHistoryCell class] | |
282 forCellWithReuseIdentifier:kCellIdentifier]; | |
283 [collectionView registerClass:[TabHistorySectionHeader class] | |
284 forSupplementaryViewOfKind:UICollectionElementKindSectionHeader | |
285 withReuseIdentifier:kHeaderIdentifier]; | |
286 [collectionView registerClass:[TabHistorySectionFooter class] | |
287 forSupplementaryViewOfKind:UICollectionElementKindSectionFooter | |
288 withReuseIdentifier:kFooterIdentifier]; | |
289 | |
290 // Set up the ink controller. | |
291 _inkTouchController = | |
292 [[MDCInkTouchController alloc] initWithView:collectionView]; | |
293 [_inkTouchController setDelegate:self]; | |
294 [_inkTouchController addInkView]; | |
295 } | |
296 return self; | |
297 } | 231 } |
298 | 232 |
299 #pragma mark Public Methods | 233 #pragma mark Public Methods |
300 | 234 |
301 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight { | 235 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight { |
302 DCHECK(suggestedHeight >= kCellHeight); | 236 DCHECK(suggestedHeight >= kCellHeight); |
303 CGFloat optimalHeight = 0; | 237 CGFloat optimalHeight = 0; |
304 | 238 |
305 for (web::NavigationItemList& itemsWithSameHost : _partitionedItems) { | 239 for (NSArray* sectionArray in _partitionedEntries) { |
306 for (size_t count = 0; count < itemsWithSameHost.size(); ++count) { | 240 NSUInteger sectionItemCount = [sectionArray count]; |
| 241 for (NSUInteger i = 0; i < sectionItemCount; ++i) { |
307 CGFloat proposedHeight = optimalHeight + kCellHeight; | 242 CGFloat proposedHeight = optimalHeight + kCellHeight; |
308 | 243 |
309 if (proposedHeight > suggestedHeight) { | 244 if (proposedHeight > suggestedHeight) { |
310 CGFloat difference = proposedHeight - suggestedHeight; | 245 CGFloat difference = proposedHeight - suggestedHeight; |
311 if (difference > kCellHeightLastRow) { | 246 if (difference > kCellHeightLastRow) { |
312 return optimalHeight + kCellHeightLastRow; | 247 return optimalHeight + kCellHeightLastRow; |
313 } else { | 248 } else { |
314 return optimalHeight - kCellHeight + kCellHeightLastRow; | 249 return optimalHeight - kCellHeight + kCellHeightLastRow; |
315 } | 250 } |
316 } | 251 } |
317 | 252 |
318 optimalHeight = proposedHeight; | 253 optimalHeight = proposedHeight; |
319 } | 254 } |
320 | 255 |
321 optimalHeight += FooterHeight(); | 256 optimalHeight += FooterHeight(); |
322 } | 257 } |
323 | 258 |
324 // If this point is reached, it means the entire content fits and this last | 259 // If this point is reached, it means the entire content fits and this last |
325 // section should not include the footer. | 260 // section should not include the footer. |
326 optimalHeight -= FooterHeight(); | 261 optimalHeight -= FooterHeight(); |
327 | 262 |
328 return optimalHeight; | 263 return optimalHeight; |
329 } | 264 } |
330 | 265 |
| 266 - (instancetype)init { |
| 267 TabHistoryViewControllerLayout* layout = |
| 268 [[TabHistoryViewControllerLayout alloc] init]; |
| 269 |
| 270 return [self initWithCollectionViewLayout:layout]; |
| 271 } |
| 272 |
| 273 - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout*)layout { |
| 274 self = [super initWithCollectionViewLayout:layout]; |
| 275 if (self) { |
| 276 UICollectionView* collectionView = [self collectionView]; |
| 277 [collectionView setBackgroundColor:[UIColor whiteColor]]; |
| 278 |
| 279 [collectionView registerClass:[TabHistoryCell class] |
| 280 forCellWithReuseIdentifier:cellIdentifier]; |
| 281 |
| 282 [collectionView registerClass:[TabHistorySectionHeader class] |
| 283 forSupplementaryViewOfKind:UICollectionElementKindSectionHeader |
| 284 withReuseIdentifier:headerIdentifier]; |
| 285 |
| 286 [collectionView registerClass:[TabHistorySectionFooter class] |
| 287 forSupplementaryViewOfKind:UICollectionElementKindSectionFooter |
| 288 withReuseIdentifier:footerIdentifier]; |
| 289 |
| 290 _inkTouchController = |
| 291 [[MDCInkTouchController alloc] initWithView:collectionView]; |
| 292 [_inkTouchController setDelegate:self]; |
| 293 [_inkTouchController addInkView]; |
| 294 } |
| 295 |
| 296 return self; |
| 297 } |
| 298 |
331 #pragma mark UICollectionViewDelegate | 299 #pragma mark UICollectionViewDelegate |
332 | 300 |
333 - (void)collectionView:(UICollectionView*)collectionView | 301 - (void)collectionView:(UICollectionView*)collectionView |
334 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { | 302 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
335 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( | 303 UICollectionViewCell* cell = |
336 [collectionView cellForItemAtIndexPath:indexPath]); | 304 [collectionView cellForItemAtIndexPath:indexPath]; |
337 [collectionView chromeExecuteCommand:cell]; | 305 [collectionView chromeExecuteCommand:cell]; |
338 [self clearNavigationItems]; | |
339 } | 306 } |
340 | 307 |
341 #pragma mark UICollectionViewDataSource | 308 #pragma mark UICollectionViewDataSource |
342 | 309 |
| 310 - (CRWSessionEntry*)entryForIndexPath:(NSIndexPath*)indexPath { |
| 311 NSInteger section = [indexPath section]; |
| 312 NSInteger item = [indexPath item]; |
| 313 |
| 314 DCHECK(section < (NSInteger)[_partitionedEntries count]); |
| 315 DCHECK(item < (NSInteger)[[_partitionedEntries objectAtIndex:section] count]); |
| 316 NSArray* sectionedArray = [_partitionedEntries objectAtIndex:section]; |
| 317 |
| 318 return [sectionedArray objectAtIndex:item]; |
| 319 } |
| 320 |
343 - (NSInteger)collectionView:(UICollectionView*)collectionView | 321 - (NSInteger)collectionView:(UICollectionView*)collectionView |
344 numberOfItemsInSection:(NSInteger)section { | 322 numberOfItemsInSection:(NSInteger)section { |
345 size_t sectionIdx = static_cast<size_t>(section); | 323 DCHECK(section < (NSInteger)[_partitionedEntries count]); |
346 DCHECK_LT(sectionIdx, _partitionedItems.size()); | 324 return [[_partitionedEntries objectAtIndex:section] count]; |
347 return _partitionedItems[sectionIdx].size(); | |
348 } | 325 } |
349 | 326 |
350 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView | 327 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
351 cellForItemAtIndexPath:(NSIndexPath*)indexPath { | 328 cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
352 TabHistoryCell* cell = | 329 TabHistoryCell* cell = |
353 [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier | 330 [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier |
354 forIndexPath:indexPath]; | 331 forIndexPath:indexPath]; |
355 cell.item = [self itemAtIndexPath:indexPath]; | 332 |
356 cell.tag = IDC_BACK_FORWARD_IN_TAB_HISTORY; | 333 [cell setEntry:[self entryForIndexPath:indexPath]]; |
| 334 [cell setTag:IDC_BACK_FORWARD_IN_TAB_HISTORY]; |
| 335 |
357 return cell; | 336 return cell; |
358 } | 337 } |
359 | 338 |
360 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view { | 339 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view { |
361 return _partitionedItems.size(); | 340 return [_partitionedEntries count]; |
362 } | 341 } |
363 | 342 |
364 - (UICollectionReusableView*)collectionView:(UICollectionView*)view | 343 - (UICollectionReusableView*)collectionView:(UICollectionView*)view |
365 viewForSupplementaryElementOfKind:(NSString*)kind | 344 viewForSupplementaryElementOfKind:(NSString*)kind |
366 atIndexPath:(NSIndexPath*)indexPath { | 345 atIndexPath:(NSIndexPath*)indexPath { |
367 // Return a footer cell if requested. | |
368 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { | 346 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { |
369 return [view dequeueReusableSupplementaryViewOfKind:kind | 347 return [view dequeueReusableSupplementaryViewOfKind:kind |
370 withReuseIdentifier:kFooterIdentifier | 348 withReuseIdentifier:footerIdentifier |
371 forIndexPath:indexPath]; | 349 forIndexPath:indexPath]; |
372 } | 350 } |
| 351 |
373 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]); | 352 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]); |
| 353 CRWSessionEntry* sessionEntry = [self entryForIndexPath:indexPath]; |
| 354 web::NavigationItem* navigationItem = [sessionEntry navigationItem]; |
374 | 355 |
375 // Dequeue a header cell and populate its favicon image. | |
376 TabHistorySectionHeader* header = | 356 TabHistorySectionHeader* header = |
377 [view dequeueReusableSupplementaryViewOfKind:kind | 357 [view dequeueReusableSupplementaryViewOfKind:kind |
378 withReuseIdentifier:kHeaderIdentifier | 358 withReuseIdentifier:headerIdentifier |
379 forIndexPath:indexPath]; | 359 forIndexPath:indexPath]; |
| 360 |
380 UIImage* iconImage = nil; | 361 UIImage* iconImage = nil; |
381 const gfx::Image& image = | 362 const gfx::Image& image = navigationItem->GetFavicon().image; |
382 [self itemAtIndexPath:indexPath]->GetFavicon().image; | |
383 if (!image.IsEmpty()) | 363 if (!image.IsEmpty()) |
384 iconImage = image.ToUIImage(); | 364 iconImage = image.ToUIImage(); |
385 else | 365 else |
386 iconImage = [UIImage imageNamed:@"default_favicon"]; | 366 iconImage = [UIImage imageNamed:@"default_favicon"]; |
| 367 |
387 [[header iconView] setImage:iconImage]; | 368 [[header iconView] setImage:iconImage]; |
388 | 369 |
389 return header; | 370 return header; |
390 } | 371 } |
391 | 372 |
| 373 - (void)setSessionEntries:(NSArray*)sessionEntries { |
| 374 _sessionEntries = sessionEntries; |
| 375 |
| 376 std::string previousHost; |
| 377 |
| 378 NSMutableArray* sectionArray = [NSMutableArray array]; |
| 379 NSMutableArray* partitionedEntries = [NSMutableArray array]; |
| 380 |
| 381 NSInteger numberOfEntries = [_sessionEntries count]; |
| 382 for (NSInteger index = 0; index < numberOfEntries; ++index) { |
| 383 CRWSessionEntry* sessionEntry = [_sessionEntries objectAtIndex:index]; |
| 384 web::NavigationItem* navigationItem = [sessionEntry navigationItem]; |
| 385 |
| 386 std::string currentHost; |
| 387 if (navigationItem) |
| 388 currentHost = navigationItem->GetURL().host(); |
| 389 |
| 390 if (previousHost.empty()) |
| 391 previousHost = currentHost; |
| 392 |
| 393 // TODO: This should use some sort of Top Level Domain matching instead of |
| 394 // explicit host match so that images.googe.com matches shopping.google.com. |
| 395 if (previousHost == currentHost) { |
| 396 [sectionArray addObject:sessionEntry]; |
| 397 } else { |
| 398 [partitionedEntries addObject:sectionArray]; |
| 399 sectionArray = [NSMutableArray arrayWithObject:sessionEntry]; |
| 400 previousHost = currentHost; |
| 401 } |
| 402 } |
| 403 |
| 404 if ([sectionArray count]) |
| 405 [partitionedEntries addObject:sectionArray]; |
| 406 |
| 407 if (![partitionedEntries count]) |
| 408 partitionedEntries = nil; |
| 409 |
| 410 _partitionedEntries = partitionedEntries; |
| 411 } |
| 412 |
392 #pragma mark MDCInkTouchControllerDelegate | 413 #pragma mark MDCInkTouchControllerDelegate |
393 | 414 |
394 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController | 415 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController |
395 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location { | 416 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location { |
396 NSIndexPath* indexPath = | 417 NSIndexPath* indexPath = |
397 [self.collectionView indexPathForItemAtPoint:location]; | 418 [self.collectionView indexPathForItemAtPoint:location]; |
398 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( | 419 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( |
399 [self.collectionView cellForItemAtIndexPath:indexPath]); | 420 [self.collectionView cellForItemAtIndexPath:indexPath]); |
400 if (!cell) { | 421 if (!cell) { |
401 return NO; | 422 return NO; |
402 } | 423 } |
403 | 424 |
404 // Increase the size of the ink view to cover the collection view from edge | 425 // Increase the size of the ink view to cover the collection view from edge |
405 // to edge. | 426 // to edge. |
406 CGRect inkViewFrame = [cell frame]; | 427 CGRect inkViewFrame = [cell frame]; |
407 inkViewFrame.origin.x = 0; | 428 inkViewFrame.origin.x = 0; |
408 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]); | 429 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]); |
409 [[inkTouchController defaultInkView] setFrame:inkViewFrame]; | 430 [[inkTouchController defaultInkView] setFrame:inkViewFrame]; |
410 return YES; | 431 return YES; |
411 } | 432 } |
412 | 433 |
413 #pragma mark - | |
414 | |
415 - (const web::NavigationItem*)itemAtIndexPath:(NSIndexPath*)indexPath { | |
416 size_t section = static_cast<size_t>([indexPath section]); | |
417 size_t item = static_cast<size_t>([indexPath item]); | |
418 DCHECK_LT(section, _partitionedItems.size()); | |
419 DCHECK_LT(item, _partitionedItems[section].size()); | |
420 return _partitionedItems[section][item]; | |
421 } | |
422 | |
423 - (void)clearNavigationItems { | |
424 _partitionedItems.clear(); | |
425 for (UICollectionViewCell* cell in self.collectionView.visibleCells) { | |
426 TabHistoryCell* historyCell = base::mac::ObjCCast<TabHistoryCell>(cell); | |
427 historyCell.item = nullptr; | |
428 } | |
429 } | |
430 | |
431 @end | 434 @end |
OLD | NEW |