chromium/ios/chrome/browser/ui/tab_switcher/web_state_tab_switcher_item.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/tab_switcher/web_state_tab_switcher_item.h"

#import "base/apple/foundation_util.h"
#import "base/memory/weak_ptr.h"
#import "components/favicon/ios/web_favicon_driver.h"
#import "ios/chrome/browser/shared/model/url/url_util.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h"
#import "ios/chrome/browser/tabs/model/tab_title_util.h"
#import "ios/web/public/web_state.h"

namespace {
const CGFloat kSymbolSize = 16;
}

@implementation WebStateTabSwitcherItem {
  // The web state represented by this item.
  base::WeakPtr<web::WebState> _webState;
}

- (instancetype)initWithWebState:(web::WebState*)webState {
  DCHECK(webState);
  self = [super initWithIdentifier:webState->GetUniqueIdentifier()];
  if (self) {
    _webState = webState->GetWeakPtr();
  }
  return self;
}

- (GURL)URL {
  if (!_webState) {
    return GURL();
  }
  return _webState->GetVisibleURL();
}

- (NSString*)title {
  if (!_webState) {
    return nil;
  }
  return tab_util::GetTabTitle(_webState.get());
}

- (BOOL)hidesTitle {
  if (!_webState) {
    return NO;
  }
  return IsUrlNtp(_webState->GetVisibleURL());
}

- (BOOL)showsActivity {
  if (!_webState) {
    return NO;
  }
  return _webState->IsLoading();
}

#pragma mark - Image Fetching

- (void)fetchFavicon:(TabSwitcherImageFetchingCompletionBlock)completion {
  web::WebState* webState = _webState.get();
  if (!webState) {
    completion(self, nil);
    return;
  }

  // NTP tabs have special treatment.
  if (IsUrlNtp(webState->GetVisibleURL())) {
    completion(self, [self NTPFavicon]);
    return;
  }

  // Use the page favicon.
  favicon::FaviconDriver* faviconDriver =
      favicon::WebFaviconDriver::FromWebState(webState);
  // The favicon driver may be null during testing.
  if (faviconDriver) {
    gfx::Image favicon = faviconDriver->GetFavicon();
    if (!favicon.IsEmpty()) {
      completion(self, favicon.ToUIImage());
      return;
    }
  }

  // Otherwise, set a default favicon.
  completion(self, [self defaultFavicon]);
}

- (void)fetchSnapshot:(TabSwitcherImageFetchingCompletionBlock)completion {
  web::WebState* webState = _webState.get();
  if (!webState) {
    completion(self, nil);
    return;
  }

  __weak __typeof(self) weakSelf = self;
  SnapshotTabHelper::FromWebState(webState)->RetrieveColorSnapshot(
      ^(UIImage* snapshot) {
        if (weakSelf) {
          completion(weakSelf, snapshot);
        }
      });
}

#pragma mark - Favicons

- (UIImage*)defaultFavicon {
  UIImageConfiguration* configuration = [UIImageSymbolConfiguration
      configurationWithPointSize:kSymbolSize
                          weight:UIImageSymbolWeightBold
                           scale:UIImageSymbolScaleMedium];
  return DefaultSymbolWithConfiguration(kGlobeAmericasSymbol, configuration);
}

- (UIImage*)NTPFavicon {
  // By default NTP tabs gets no favicon.
  return nil;
}

#pragma mark - NSObject

- (BOOL)isEqual:(id)object {
  if (self == object) {
    return YES;
  }
  if (![object isKindOfClass:[WebStateTabSwitcherItem class]]) {
    return NO;
  }
  WebStateTabSwitcherItem* otherTabStrip =
      base::apple::ObjCCastStrict<WebStateTabSwitcherItem>(object);
  return self.identifier == otherTabStrip.identifier;
}

- (NSUInteger)hash {
  return static_cast<NSUInteger>(self.identifier.identifier());
}

@end