1// Copyright 2020 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.h" 6 7#import "components/favicon/ios/web_favicon_driver.h" 8#import "ios/chrome/browser/browser_state/chrome_browser_state.h" 9#import "ios/chrome/browser/chrome_url_util.h" 10#import "ios/chrome/browser/tabs/tab_title_util.h" 11#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h" 12#import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h" 13#import "ios/chrome/browser/web/tab_id_tab_helper.h" 14#import "ios/chrome/browser/web_state_list/all_web_state_observation_forwarder.h" 15#import "ios/chrome/browser/web_state_list/web_state_list.h" 16#import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h" 17#import "ios/web/public/web_state.h" 18#import "ios/web/public/web_state_observer_bridge.h" 19#import "ui/gfx/image/image.h" 20 21#if !defined(__has_feature) || !__has_feature(objc_arc) 22#error "This file requires ARC support." 23#endif 24 25namespace { 26// Constructs a GridItem from a |web_state|. 27GridItem* CreateItem(web::WebState* web_state) { 28 TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state); 29 GridItem* item = [[GridItem alloc] initWithIdentifier:tab_helper->tab_id()]; 30 // chrome://newtab (NTP) tabs have no title. 31 if (IsURLNtp(web_state->GetVisibleURL())) { 32 item.hidesTitle = YES; 33 } 34 item.title = tab_util::GetTabTitle(web_state); 35 return item; 36} 37 38// Constructs an array of GridItems from a |web_state_list|. 39NSArray* CreateItems(WebStateList* web_state_list) { 40 NSMutableArray* items = [[NSMutableArray alloc] init]; 41 for (int i = 0; i < web_state_list->count(); i++) { 42 web::WebState* web_state = web_state_list->GetWebStateAt(i); 43 [items addObject:CreateItem(web_state)]; 44 } 45 return [items copy]; 46} 47 48// Returns the ID of the active tab in |web_state_list|. 49NSString* GetActiveTabId(WebStateList* web_state_list) { 50 if (!web_state_list) 51 return nil; 52 53 web::WebState* web_state = web_state_list->GetActiveWebState(); 54 if (!web_state) 55 return nil; 56 TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state); 57 return tab_helper->tab_id(); 58} 59 60// Returns the WebState with |identifier| in |web_state_list|. Returns |nullptr| 61// if not found. 62web::WebState* GetWebStateWithId(WebStateList* web_state_list, 63 NSString* identifier) { 64 for (int i = 0; i < web_state_list->count(); i++) { 65 web::WebState* web_state = web_state_list->GetWebStateAt(i); 66 TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state); 67 if ([identifier isEqualToString:tab_helper->tab_id()]) 68 return web_state; 69 } 70 return nullptr; 71} 72 73} // namespace 74 75@interface TabStripMediator () <CRWWebStateObserver, WebStateListObserving> { 76 // Bridge C++ WebStateListObserver methods to this TabStripController. 77 std::unique_ptr<WebStateListObserverBridge> _webStateListObserver; 78 // Bridge C++ WebStateObserver methods to this TabStripController. 79 std::unique_ptr<web::WebStateObserverBridge> _webStateObserver; 80 // Forward observer methods for all WebStates in the WebStateList monitored 81 // by the TabStripMediator. 82 std::unique_ptr<AllWebStateObservationForwarder> 83 _allWebStateObservationForwarder; 84} 85 86// The consumer for this object. 87@property(nonatomic, weak) id<TabStripConsumer> consumer; 88 89@end 90 91@implementation TabStripMediator 92 93- (instancetype)initWithConsumer:(id<TabStripConsumer>)consumer { 94 if (self = [super init]) { 95 _consumer = consumer; 96 } 97 return self; 98} 99 100- (void)disconnect { 101 if (_webStateList) { 102 _allWebStateObservationForwarder.reset(); 103 _webStateList->RemoveObserver(_webStateListObserver.get()); 104 _webStateListObserver = nullptr; 105 _webStateList = nullptr; 106 } 107} 108 109#pragma mark - Public properties 110 111- (void)setWebStateList:(WebStateList*)webStateList { 112 if (_webStateList) { 113 _allWebStateObservationForwarder.reset(); 114 _webStateList->RemoveObserver(_webStateListObserver.get()); 115 } 116 117 _webStateList = webStateList; 118 119 if (_webStateList) { 120 DCHECK_GE(_webStateList->count(), 0); 121 _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self); 122 _webStateList->AddObserver(_webStateListObserver.get()); 123 124 _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self); 125 // Observe all webStates of this |_webStateList|. 126 _allWebStateObservationForwarder = 127 std::make_unique<AllWebStateObservationForwarder>( 128 _webStateList, _webStateObserver.get()); 129 } 130 [self populateConsumerItems]; 131} 132 133#pragma mark - WebStateListObserving 134 135- (void)webStateList:(WebStateList*)webStateList 136 didDetachWebState:(web::WebState*)webState 137 atIndex:(int)atIndex { 138 [self populateConsumerItems]; 139} 140 141- (void)webStateList:(WebStateList*)webStateList 142 didInsertWebState:(web::WebState*)webState 143 atIndex:(int)index 144 activating:(BOOL)activating { 145 [self populateConsumerItems]; 146} 147 148#pragma mark - TabFaviconDataSource 149 150- (void)faviconForIdentifier:(NSString*)identifier 151 completion:(void (^)(UIImage*))completion { 152 web::WebState* webState = GetWebStateWithId(_webStateList, identifier); 153 if (!webState) { 154 return; 155 } 156 // NTP tabs get no favicon. 157 if (IsURLNtp(webState->GetVisibleURL())) { 158 return; 159 } 160 UIImage* defaultFavicon = 161 webState->GetBrowserState()->IsOffTheRecord() 162 ? [UIImage imageNamed:@"default_world_favicon_incognito"] 163 : [UIImage imageNamed:@"default_world_favicon_regular"]; 164 completion(defaultFavicon); 165 166 favicon::FaviconDriver* faviconDriver = 167 favicon::WebFaviconDriver::FromWebState(webState); 168 if (faviconDriver) { 169 gfx::Image favicon = faviconDriver->GetFavicon(); 170 if (!favicon.IsEmpty()) 171 completion(favicon.ToUIImage()); 172 } 173} 174 175#pragma mark - Private 176 177// Calls |-populateItems:selectedItemID:| on the consumer. 178- (void)populateConsumerItems { 179 if (!self.webStateList) 180 return; 181 if (self.webStateList->count() > 0) { 182 [self.consumer populateItems:CreateItems(self.webStateList) 183 selectedItemID:GetActiveTabId(self.webStateList)]; 184 } 185} 186 187#pragma mark - CRWWebStateObserver 188 189- (void)webStateDidChangeTitle:(web::WebState*)webState { 190 // Assumption: the ID of the webState didn't change as a result of this load. 191 TabIdTabHelper* tabHelper = TabIdTabHelper::FromWebState(webState); 192 NSString* itemID = tabHelper->tab_id(); 193 [self.consumer replaceItemID:itemID withItem:CreateItem(webState)]; 194} 195 196@end 197