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

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

Issue 2693013005: Updated tab history classes to use NavigationItemLists. (Closed)
Patch Set: move TabHistoryPopupController ivar underscore to prefix, pass items by reference to toolbar Created 3 years, 10 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;
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 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 [attributes setZIndex:0]; 213 [attributes setZIndex:0];
213 } 214 }
214 215
215 return attributes; 216 return attributes;
216 } 217 }
217 218
218 @end 219 @end
219 220
220 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> { 221 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> {
221 MDCInkTouchController* _inkTouchController; 222 MDCInkTouchController* _inkTouchController;
222 NSArray* _partitionedEntries; 223 // A vector of NavigationItemLists where the NavigationItems are separated
223 NSArray* _sessionEntries; 224 // by hostname.
225 std::vector<web::NavigationItemList> _partitionedItems;
224 } 226 }
227
228 // Populates |_partitionedItems| with the NavigationItems in |items| partitioned
229 // by their hosts.
230 - (void)partitionItemsByHost:(const web::NavigationItemList&)items;
231
232 // Returns the NavigationItem corresponding with |indexPath|.
233 - (const web::NavigationItem*)itemAtIndexPath:(NSIndexPath*)indexPath;
234
235 // Removes all NavigationItem pointers from this class. Tapping a cell that
236 // triggers a navigation may delete NavigationItems, so NavigationItem
237 // references should be reset to avoid use-after-free errors.
238 - (void)clearNavigationItems;
239
225 @end 240 @end
226 241
227 @implementation TabHistoryViewController 242 @implementation TabHistoryViewController
228 243
229 - (NSArray*)sessionEntries { 244 - (instancetype)initWithItems:(const web::NavigationItemList&)items {
230 return _sessionEntries; 245 TabHistoryViewControllerLayout* layout =
Eugene But (OOO till 7-30) 2017/02/14 23:08:08 Do you want to move this inside |if ((self =| bloc
kkhorimoto 2017/02/14 23:23:51 It's passed to |initWithCollectionViewLayout|.
246 [[TabHistoryViewControllerLayout alloc] init];
247 if ((self = [super initWithCollectionViewLayout:layout])) {
248 // Populate |_partitionedItems|.
249 [self partitionItemsByHost:items];
Eugene But (OOO till 7-30) 2017/02/14 23:08:08 We should not call instance methods inside init. T
kkhorimoto 2017/02/14 23:23:51 Changed to C function.
250
251 // Set up the UICollectionView.
252 UICollectionView* collectionView = [self collectionView];
253 collectionView.accessibilityLabel = kCollectionViewLabel;
254 collectionView.backgroundColor = [UIColor whiteColor];
255 [collectionView registerClass:[TabHistoryCell class]
256 forCellWithReuseIdentifier:kCellIdentifier];
257 [collectionView registerClass:[TabHistorySectionHeader class]
258 forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
259 withReuseIdentifier:kHeaderIdentifier];
260 [collectionView registerClass:[TabHistorySectionFooter class]
261 forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
262 withReuseIdentifier:kFooterIdentifier];
263
264 // Set up the ink controller.
265 _inkTouchController =
266 [[MDCInkTouchController alloc] initWithView:collectionView];
267 [_inkTouchController setDelegate:self];
268 [_inkTouchController addInkView];
269 }
270 return self;
231 } 271 }
232 272
233 #pragma mark Public Methods 273 #pragma mark Public Methods
234 274
235 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight { 275 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight {
236 DCHECK(suggestedHeight >= kCellHeight); 276 DCHECK(suggestedHeight >= kCellHeight);
237 CGFloat optimalHeight = 0; 277 CGFloat optimalHeight = 0;
238 278
239 for (NSArray* sectionArray in _partitionedEntries) { 279 for (web::NavigationItemList& itemsWithSameHost : _partitionedItems) {
240 NSUInteger sectionItemCount = [sectionArray count]; 280 for (size_t count = 0; count < itemsWithSameHost.size(); ++count) {
241 for (NSUInteger i = 0; i < sectionItemCount; ++i) {
242 CGFloat proposedHeight = optimalHeight + kCellHeight; 281 CGFloat proposedHeight = optimalHeight + kCellHeight;
243 282
244 if (proposedHeight > suggestedHeight) { 283 if (proposedHeight > suggestedHeight) {
245 CGFloat difference = proposedHeight - suggestedHeight; 284 CGFloat difference = proposedHeight - suggestedHeight;
246 if (difference > kCellHeightLastRow) { 285 if (difference > kCellHeightLastRow) {
247 return optimalHeight + kCellHeightLastRow; 286 return optimalHeight + kCellHeightLastRow;
248 } else { 287 } else {
249 return optimalHeight - kCellHeight + kCellHeightLastRow; 288 return optimalHeight - kCellHeight + kCellHeightLastRow;
250 } 289 }
251 } 290 }
252 291
253 optimalHeight = proposedHeight; 292 optimalHeight = proposedHeight;
254 } 293 }
255 294
256 optimalHeight += FooterHeight(); 295 optimalHeight += FooterHeight();
257 } 296 }
258 297
259 // If this point is reached, it means the entire content fits and this last 298 // If this point is reached, it means the entire content fits and this last
260 // section should not include the footer. 299 // section should not include the footer.
261 optimalHeight -= FooterHeight(); 300 optimalHeight -= FooterHeight();
262 301
263 return optimalHeight; 302 return optimalHeight;
264 } 303 }
265 304
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 305 #pragma mark UICollectionViewDelegate
300 306
301 - (void)collectionView:(UICollectionView*)collectionView 307 - (void)collectionView:(UICollectionView*)collectionView
302 didSelectItemAtIndexPath:(NSIndexPath*)indexPath { 308 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
303 UICollectionViewCell* cell = 309 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>(
304 [collectionView cellForItemAtIndexPath:indexPath]; 310 [collectionView cellForItemAtIndexPath:indexPath]);
305 [collectionView chromeExecuteCommand:cell]; 311 [collectionView chromeExecuteCommand:cell];
312 [self clearNavigationItems];
306 } 313 }
307 314
308 #pragma mark UICollectionViewDataSource 315 #pragma mark UICollectionViewDataSource
309 316
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 317 - (NSInteger)collectionView:(UICollectionView*)collectionView
322 numberOfItemsInSection:(NSInteger)section { 318 numberOfItemsInSection:(NSInteger)section {
323 DCHECK(section < (NSInteger)[_partitionedEntries count]); 319 size_t sectionIdx = static_cast<size_t>(section);
Eugene But (OOO till 7-30) 2017/02/14 23:08:08 s/sectionIdx/sectionIndex Objective-C Style Guid
324 return [[_partitionedEntries objectAtIndex:section] count]; 320 DCHECK_LT(sectionIdx, _partitionedItems.size());
321 return _partitionedItems[sectionIdx].size();
325 } 322 }
326 323
327 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView 324 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
328 cellForItemAtIndexPath:(NSIndexPath*)indexPath { 325 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
329 TabHistoryCell* cell = 326 TabHistoryCell* cell =
330 [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier 327 [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier
331 forIndexPath:indexPath]; 328 forIndexPath:indexPath];
332 329 cell.item = [self itemAtIndexPath:indexPath];
333 [cell setEntry:[self entryForIndexPath:indexPath]]; 330 cell.tag = IDC_BACK_FORWARD_IN_TAB_HISTORY;
334 [cell setTag:IDC_BACK_FORWARD_IN_TAB_HISTORY];
335
336 return cell; 331 return cell;
337 } 332 }
338 333
339 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view { 334 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view {
340 return [_partitionedEntries count]; 335 return _partitionedItems.size();
341 } 336 }
342 337
343 - (UICollectionReusableView*)collectionView:(UICollectionView*)view 338 - (UICollectionReusableView*)collectionView:(UICollectionView*)view
344 viewForSupplementaryElementOfKind:(NSString*)kind 339 viewForSupplementaryElementOfKind:(NSString*)kind
345 atIndexPath:(NSIndexPath*)indexPath { 340 atIndexPath:(NSIndexPath*)indexPath {
341 // Return a footer cell if requested.
346 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { 342 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
347 return [view dequeueReusableSupplementaryViewOfKind:kind 343 return [view dequeueReusableSupplementaryViewOfKind:kind
348 withReuseIdentifier:footerIdentifier 344 withReuseIdentifier:kFooterIdentifier
349 forIndexPath:indexPath]; 345 forIndexPath:indexPath];
350 } 346 }
347 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]);
351 348
352 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]); 349 // 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 = 350 TabHistorySectionHeader* header =
357 [view dequeueReusableSupplementaryViewOfKind:kind 351 [view dequeueReusableSupplementaryViewOfKind:kind
358 withReuseIdentifier:headerIdentifier 352 withReuseIdentifier:kHeaderIdentifier
359 forIndexPath:indexPath]; 353 forIndexPath:indexPath];
360
361 UIImage* iconImage = nil; 354 UIImage* iconImage = nil;
362 const gfx::Image& image = navigationItem->GetFavicon().image; 355 const gfx::Image& image =
356 [self itemAtIndexPath:indexPath]->GetFavicon().image;
363 if (!image.IsEmpty()) 357 if (!image.IsEmpty())
364 iconImage = image.ToUIImage(); 358 iconImage = image.ToUIImage();
365 else 359 else
366 iconImage = [UIImage imageNamed:@"default_favicon"]; 360 iconImage = [UIImage imageNamed:@"default_favicon"];
367
368 [[header iconView] setImage:iconImage]; 361 [[header iconView] setImage:iconImage];
369 362
370 return header; 363 return header;
371 } 364 }
372 365
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 366 #pragma mark MDCInkTouchControllerDelegate
414 367
415 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController 368 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController
416 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location { 369 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location {
417 NSIndexPath* indexPath = 370 NSIndexPath* indexPath =
418 [self.collectionView indexPathForItemAtPoint:location]; 371 [self.collectionView indexPathForItemAtPoint:location];
419 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>( 372 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>(
420 [self.collectionView cellForItemAtIndexPath:indexPath]); 373 [self.collectionView cellForItemAtIndexPath:indexPath]);
421 if (!cell) { 374 if (!cell) {
422 return NO; 375 return NO;
423 } 376 }
424 377
425 // Increase the size of the ink view to cover the collection view from edge 378 // Increase the size of the ink view to cover the collection view from edge
426 // to edge. 379 // to edge.
427 CGRect inkViewFrame = [cell frame]; 380 CGRect inkViewFrame = [cell frame];
428 inkViewFrame.origin.x = 0; 381 inkViewFrame.origin.x = 0;
429 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]); 382 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]);
430 [[inkTouchController defaultInkView] setFrame:inkViewFrame]; 383 [[inkTouchController defaultInkView] setFrame:inkViewFrame];
431 return YES; 384 return YES;
432 } 385 }
433 386
387 #pragma mark -
388
389 - (void)partitionItemsByHost:(const web::NavigationItemList&)items {
390 _partitionedItems.clear();
391 // Used to store the previous host when partitioning NavigationItems.
392 std::string previousHost;
393 // The NavigationItemList containing NavigationItems with the same host.
394 web::NavigationItemList itemsWithSameHostname;
395 // Separate the items in |items| by host.
396 for (web::NavigationItem* item : items) {
397 std::string currentHost = item->GetURL().host();
398 if (previousHost.empty())
399 previousHost = currentHost;
400 // TODO: This should use some sort of Top Level Domain matching instead of
401 // explicit host match so that images.googe.com matches shopping.google.com.
402 if (previousHost == currentHost) {
403 itemsWithSameHostname.push_back(item);
404 } else {
405 _partitionedItems.push_back(itemsWithSameHostname);
406 itemsWithSameHostname.clear();
407 previousHost = currentHost;
408 }
409 }
410 // Add the last list contiaining the same host.
411 if (!itemsWithSameHostname.empty())
412 _partitionedItems.push_back(itemsWithSameHostname);
413 }
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698