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" | |
16 #include "ios/web/public/favicon_status.h" | 15 #include "ios/web/public/favicon_status.h" |
17 #include "ios/web/public/navigation_item.h" | 16 #include "ios/web/public/navigation_item.h" |
18 #include "ui/gfx/image/image.h" | 17 #include "ui/gfx/image/image.h" |
19 | 18 |
20 #if !defined(__has_feature) || !__has_feature(objc_arc) | 19 #if !defined(__has_feature) || !__has_feature(objc_arc) |
21 #error "This file requires ARC support." | 20 #error "This file requires ARC support." |
22 #endif | 21 #endif |
23 | 22 |
24 namespace { | 23 namespace { |
25 | 24 |
26 // Visible percentage of the last visible row on the Tools menu if the | 25 // Visible percentage of the last visible row on the Tools menu if the |
27 // Tools menu is scrollable. | 26 // Tools menu is scrollable. |
28 const CGFloat kLastRowVisiblePercentage = 0.6; | 27 const CGFloat kLastRowVisiblePercentage = 0.6; |
29 // Reuse identifier for cells. | 28 // Reuse identifier for cells. |
30 NSString* cellIdentifier = @"TabHistoryCell"; | 29 NSString* const kCellIdentifier = @"TabHistoryCell"; |
31 NSString* footerIdentifier = @"Footer"; | 30 NSString* const kFooterIdentifier = @"Footer"; |
32 NSString* headerIdentifier = @"Header"; | 31 NSString* const kHeaderIdentifier = @"Header"; |
| 32 // The collection view's a11y label. |
| 33 NSString* const kCollectionViewLabel = @"Tab History"; |
33 // Height of rows. | 34 // Height of rows. |
34 const CGFloat kCellHeight = 48.0; | 35 const CGFloat kCellHeight = 48.0; |
35 // Fraction height for partially visible row. | 36 // Fraction height for partially visible row. |
36 const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage; | 37 const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage; |
37 // Width and leading for the header view. | 38 // Width and leading for the header view. |
38 const CGFloat kHeaderLeadingInset = 0; | 39 const CGFloat kHeaderLeadingInset = 0; |
39 const CGFloat kHeaderWidth = 48; | 40 const CGFloat kHeaderWidth = 48; |
40 // Leading and trailing insets for cell items. | 41 // Leading and trailing insets for cell items. |
41 const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth; | 42 const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth; |
42 const CGFloat kCellTrailingInset = 16; | 43 const CGFloat kCellTrailingInset = 16; |
(...skipping 21 matching lines...) Expand all Loading... |
64 DCHECK(offsets.at([path section]).size()); | 65 DCHECK(offsets.at([path section]).size()); |
65 | 66 |
66 return offsets.at([path section]).at(0); | 67 return offsets.at([path section]).at(0); |
67 } | 68 } |
68 | 69 |
69 // Height for the footer view. | 70 // Height for the footer view. |
70 NS_INLINE CGFloat FooterHeight() { | 71 NS_INLINE CGFloat FooterHeight() { |
71 return 1.0 / [[UIScreen mainScreen] scale]; | 72 return 1.0 / [[UIScreen mainScreen] scale]; |
72 } | 73 } |
73 | 74 |
| 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 |
74 } // namespace | 105 } // namespace |
75 | 106 |
76 @interface TabHistoryViewControllerLayout : UICollectionViewLayout | 107 @interface TabHistoryViewControllerLayout : UICollectionViewLayout |
77 @end | 108 @end |
78 | 109 |
79 @implementation TabHistoryViewControllerLayout { | 110 @implementation TabHistoryViewControllerLayout { |
80 ItemOffsetVector _itemYOffsets; | 111 ItemOffsetVector _itemYOffsets; |
81 CGFloat _containerCalculatedHeight; | 112 CGFloat _containerCalculatedHeight; |
82 CGFloat _containerWidth; | 113 CGFloat _containerWidth; |
83 CGFloat _cellItemWidth; | 114 CGFloat _cellItemWidth; |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 [attributes setZIndex:0]; | 243 [attributes setZIndex:0]; |
213 } | 244 } |
214 | 245 |
215 return attributes; | 246 return attributes; |
216 } | 247 } |
217 | 248 |
218 @end | 249 @end |
219 | 250 |
220 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> { | 251 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> { |
221 MDCInkTouchController* _inkTouchController; | 252 MDCInkTouchController* _inkTouchController; |
222 NSArray* _partitionedEntries; | 253 // A vector of NavigationItemLists where the NavigationItems are separated |
223 NSArray* _sessionEntries; | 254 // by hostname. |
| 255 std::vector<web::NavigationItemList> _partitionedItems; |
224 } | 256 } |
| 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 |
225 @end | 266 @end |
226 | 267 |
227 @implementation TabHistoryViewController | 268 @implementation TabHistoryViewController |
228 | 269 |
229 - (NSArray*)sessionEntries { | 270 - (instancetype)initWithItems:(const web::NavigationItemList&)items { |
230 return _sessionEntries; | 271 TabHistoryViewControllerLayout* layout = |
| 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; |
231 } | 297 } |
232 | 298 |
233 #pragma mark Public Methods | 299 #pragma mark Public Methods |
234 | 300 |
235 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight { | 301 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight { |
236 DCHECK(suggestedHeight >= kCellHeight); | 302 DCHECK(suggestedHeight >= kCellHeight); |
237 CGFloat optimalHeight = 0; | 303 CGFloat optimalHeight = 0; |
238 | 304 |
239 for (NSArray* sectionArray in _partitionedEntries) { | 305 for (web::NavigationItemList& itemsWithSameHost : _partitionedItems) { |
240 NSUInteger sectionItemCount = [sectionArray count]; | 306 for (size_t count = 0; count < itemsWithSameHost.size(); ++count) { |
241 for (NSUInteger i = 0; i < sectionItemCount; ++i) { | |
242 CGFloat proposedHeight = optimalHeight + kCellHeight; | 307 CGFloat proposedHeight = optimalHeight + kCellHeight; |
243 | 308 |
244 if (proposedHeight > suggestedHeight) { | 309 if (proposedHeight > suggestedHeight) { |
245 CGFloat difference = proposedHeight - suggestedHeight; | 310 CGFloat difference = proposedHeight - suggestedHeight; |
246 if (difference > kCellHeightLastRow) { | 311 if (difference > kCellHeightLastRow) { |
247 return optimalHeight + kCellHeightLastRow; | 312 return optimalHeight + kCellHeightLastRow; |
248 } else { | 313 } else { |
249 return optimalHeight - kCellHeight + kCellHeightLastRow; | 314 return optimalHeight - kCellHeight + kCellHeightLastRow; |
250 } | 315 } |
251 } | 316 } |
252 | 317 |
253 optimalHeight = proposedHeight; | 318 optimalHeight = proposedHeight; |
254 } | 319 } |
255 | 320 |
256 optimalHeight += FooterHeight(); | 321 optimalHeight += FooterHeight(); |
257 } | 322 } |
258 | 323 |
259 // If this point is reached, it means the entire content fits and this last | 324 // If this point is reached, it means the entire content fits and this last |
260 // section should not include the footer. | 325 // section should not include the footer. |
261 optimalHeight -= FooterHeight(); | 326 optimalHeight -= FooterHeight(); |
262 | 327 |
263 return optimalHeight; | 328 return optimalHeight; |
264 } | 329 } |
265 | 330 |
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 | |
299 #pragma mark UICollectionViewDelegate | 331 #pragma mark UICollectionViewDelegate |
300 | 332 |
301 - (void)collectionView:(UICollectionView*)collectionView | 333 - (void)collectionView:(UICollectionView*)collectionView |
302 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { | 334 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
303 UICollectionViewCell* cell = | 335 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( |
304 [collectionView cellForItemAtIndexPath:indexPath]; | 336 [collectionView cellForItemAtIndexPath:indexPath]); |
305 [collectionView chromeExecuteCommand:cell]; | 337 [collectionView chromeExecuteCommand:cell]; |
| 338 [self clearNavigationItems]; |
306 } | 339 } |
307 | 340 |
308 #pragma mark UICollectionViewDataSource | 341 #pragma mark UICollectionViewDataSource |
309 | 342 |
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 | |
321 - (NSInteger)collectionView:(UICollectionView*)collectionView | 343 - (NSInteger)collectionView:(UICollectionView*)collectionView |
322 numberOfItemsInSection:(NSInteger)section { | 344 numberOfItemsInSection:(NSInteger)section { |
323 DCHECK(section < (NSInteger)[_partitionedEntries count]); | 345 size_t sectionIdx = static_cast<size_t>(section); |
324 return [[_partitionedEntries objectAtIndex:section] count]; | 346 DCHECK_LT(sectionIdx, _partitionedItems.size()); |
| 347 return _partitionedItems[sectionIdx].size(); |
325 } | 348 } |
326 | 349 |
327 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView | 350 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
328 cellForItemAtIndexPath:(NSIndexPath*)indexPath { | 351 cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
329 TabHistoryCell* cell = | 352 TabHistoryCell* cell = |
330 [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier | 353 [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier |
331 forIndexPath:indexPath]; | 354 forIndexPath:indexPath]; |
332 | 355 cell.item = [self itemAtIndexPath:indexPath]; |
333 [cell setEntry:[self entryForIndexPath:indexPath]]; | 356 cell.tag = IDC_BACK_FORWARD_IN_TAB_HISTORY; |
334 [cell setTag:IDC_BACK_FORWARD_IN_TAB_HISTORY]; | |
335 | |
336 return cell; | 357 return cell; |
337 } | 358 } |
338 | 359 |
339 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view { | 360 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view { |
340 return [_partitionedEntries count]; | 361 return _partitionedItems.size(); |
341 } | 362 } |
342 | 363 |
343 - (UICollectionReusableView*)collectionView:(UICollectionView*)view | 364 - (UICollectionReusableView*)collectionView:(UICollectionView*)view |
344 viewForSupplementaryElementOfKind:(NSString*)kind | 365 viewForSupplementaryElementOfKind:(NSString*)kind |
345 atIndexPath:(NSIndexPath*)indexPath { | 366 atIndexPath:(NSIndexPath*)indexPath { |
| 367 // Return a footer cell if requested. |
346 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { | 368 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { |
347 return [view dequeueReusableSupplementaryViewOfKind:kind | 369 return [view dequeueReusableSupplementaryViewOfKind:kind |
348 withReuseIdentifier:footerIdentifier | 370 withReuseIdentifier:kFooterIdentifier |
349 forIndexPath:indexPath]; | 371 forIndexPath:indexPath]; |
350 } | 372 } |
| 373 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]); |
351 | 374 |
352 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]); | 375 // Dequeue a header cell and populate its favicon image. |
353 CRWSessionEntry* sessionEntry = [self entryForIndexPath:indexPath]; | |
354 web::NavigationItem* navigationItem = [sessionEntry navigationItem]; | |
355 | |
356 TabHistorySectionHeader* header = | 376 TabHistorySectionHeader* header = |
357 [view dequeueReusableSupplementaryViewOfKind:kind | 377 [view dequeueReusableSupplementaryViewOfKind:kind |
358 withReuseIdentifier:headerIdentifier | 378 withReuseIdentifier:kHeaderIdentifier |
359 forIndexPath:indexPath]; | 379 forIndexPath:indexPath]; |
360 | |
361 UIImage* iconImage = nil; | 380 UIImage* iconImage = nil; |
362 const gfx::Image& image = navigationItem->GetFavicon().image; | 381 const gfx::Image& image = |
| 382 [self itemAtIndexPath:indexPath]->GetFavicon().image; |
363 if (!image.IsEmpty()) | 383 if (!image.IsEmpty()) |
364 iconImage = image.ToUIImage(); | 384 iconImage = image.ToUIImage(); |
365 else | 385 else |
366 iconImage = [UIImage imageNamed:@"default_favicon"]; | 386 iconImage = [UIImage imageNamed:@"default_favicon"]; |
367 | |
368 [[header iconView] setImage:iconImage]; | 387 [[header iconView] setImage:iconImage]; |
369 | 388 |
370 return header; | 389 return header; |
371 } | 390 } |
372 | 391 |
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 | |
413 #pragma mark MDCInkTouchControllerDelegate | 392 #pragma mark MDCInkTouchControllerDelegate |
414 | 393 |
415 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController | 394 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController |
416 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location { | 395 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location { |
417 NSIndexPath* indexPath = | 396 NSIndexPath* indexPath = |
418 [self.collectionView indexPathForItemAtPoint:location]; | 397 [self.collectionView indexPathForItemAtPoint:location]; |
419 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( | 398 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( |
420 [self.collectionView cellForItemAtIndexPath:indexPath]); | 399 [self.collectionView cellForItemAtIndexPath:indexPath]); |
421 if (!cell) { | 400 if (!cell) { |
422 return NO; | 401 return NO; |
423 } | 402 } |
424 | 403 |
425 // Increase the size of the ink view to cover the collection view from edge | 404 // Increase the size of the ink view to cover the collection view from edge |
426 // to edge. | 405 // to edge. |
427 CGRect inkViewFrame = [cell frame]; | 406 CGRect inkViewFrame = [cell frame]; |
428 inkViewFrame.origin.x = 0; | 407 inkViewFrame.origin.x = 0; |
429 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]); | 408 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]); |
430 [[inkTouchController defaultInkView] setFrame:inkViewFrame]; | 409 [[inkTouchController defaultInkView] setFrame:inkViewFrame]; |
431 return YES; | 410 return YES; |
432 } | 411 } |
433 | 412 |
| 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 |
434 @end | 431 @end |
OLD | NEW |