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

Side by Side Diff: ios/chrome/browser/ui/history/tab_history_view_controller.mm

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

Powered by Google App Engine
This is Rietveld 408576698