blob: 531de47a8a405ca8045ec418e60a2ee73775842d [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/tab_strip/tab_strip_mediator.h"
#import "components/favicon/ios/web_favicon_driver.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/chrome_url_constants.h"
#import "ios/chrome/browser/chrome_url_util.h"
#import "ios/chrome/browser/tabs/tab_title_util.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
#import "ios/chrome/browser/web/tab_id_tab_helper.h"
#import "ios/chrome/browser/web_state_list/all_web_state_observation_forwarder.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
#import "ios/chrome/browser/web_state_list/web_state_opener.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/web_state.h"
#import "ios/web/public/web_state_observer_bridge.h"
#import "ui/gfx/image/image.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Constructs a TabSwitcherItem from a |web_state|.
TabSwitcherItem* CreateItem(web::WebState* web_state) {
TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
TabSwitcherItem* item =
[[TabSwitcherItem alloc] initWithIdentifier:tab_helper->tab_id()];
// chrome://newtab (NTP) tabs have no title.
if (IsURLNtp(web_state->GetVisibleURL())) {
item.hidesTitle = YES;
}
item.title = tab_util::GetTabTitle(web_state);
return item;
}
// Constructs an array of TabSwitcherItems from a |web_state_list|.
NSArray* CreateItems(WebStateList* web_state_list) {
NSMutableArray* items = [[NSMutableArray alloc] init];
for (int i = 0; i < web_state_list->count(); i++) {
web::WebState* web_state = web_state_list->GetWebStateAt(i);
[items addObject:CreateItem(web_state)];
}
return [items copy];
}
// Returns the ID of the active tab in |web_state_list|.
NSString* GetActiveTabId(WebStateList* web_state_list) {
if (!web_state_list)
return nil;
web::WebState* web_state = web_state_list->GetActiveWebState();
if (!web_state)
return nil;
TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
return tab_helper->tab_id();
}
// Returns the WebState with |identifier| in |web_state_list|. Returns |nullptr|
// if not found.
web::WebState* GetWebStateWithId(WebStateList* web_state_list,
NSString* identifier) {
for (int i = 0; i < web_state_list->count(); i++) {
web::WebState* web_state = web_state_list->GetWebStateAt(i);
TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
if ([identifier isEqualToString:tab_helper->tab_id()])
return web_state;
}
return nullptr;
}
// Returns the index of the tab with |identifier| in |web_state_list|. Returns
// -1 if not found.
int GetIndexOfTabWithId(WebStateList* web_state_list, NSString* identifier) {
for (int i = 0; i < web_state_list->count(); i++) {
web::WebState* web_state = web_state_list->GetWebStateAt(i);
TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
if ([identifier isEqualToString:tab_helper->tab_id()])
return i;
}
return -1;
}
} // namespace
@interface TabStripMediator () <CRWWebStateObserver, WebStateListObserving> {
// Bridge C++ WebStateListObserver methods to this TabStripController.
std::unique_ptr<WebStateListObserverBridge> _webStateListObserver;
// Bridge C++ WebStateObserver methods to this TabStripController.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
// Forward observer methods for all WebStates in the WebStateList monitored
// by the TabStripMediator.
std::unique_ptr<AllWebStateObservationForwarder>
_allWebStateObservationForwarder;
}
// The consumer for this object.
@property(nonatomic, weak) id<TabStripConsumer> consumer;
@end
@implementation TabStripMediator
- (instancetype)initWithConsumer:(id<TabStripConsumer>)consumer {
if (self = [super init]) {
_consumer = consumer;
}
return self;
}
- (void)disconnect {
if (_webStateList) {
_allWebStateObservationForwarder.reset();
_webStateList->RemoveObserver(_webStateListObserver.get());
_webStateListObserver = nullptr;
_webStateList = nullptr;
}
}
#pragma mark - Public properties
- (void)setWebStateList:(WebStateList*)webStateList {
if (_webStateList) {
_allWebStateObservationForwarder.reset();
_webStateList->RemoveObserver(_webStateListObserver.get());
}
_webStateList = webStateList;
if (_webStateList) {
DCHECK_GE(_webStateList->count(), 0);
_webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
_webStateList->AddObserver(_webStateListObserver.get());
_webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
// Observe all webStates of this |_webStateList|.
_allWebStateObservationForwarder =
std::make_unique<AllWebStateObservationForwarder>(
_webStateList, _webStateObserver.get());
}
[self populateConsumerItems];
}
#pragma mark - WebStateListObserving
- (void)webStateList:(WebStateList*)webStateList
didDetachWebState:(web::WebState*)webState
atIndex:(int)atIndex {
[self populateConsumerItems];
}
- (void)webStateList:(WebStateList*)webStateList
didInsertWebState:(web::WebState*)webState
atIndex:(int)index
activating:(BOOL)activating {
[self populateConsumerItems];
}
#pragma mark - TabFaviconDataSource
- (void)faviconForIdentifier:(NSString*)identifier
completion:(void (^)(UIImage*))completion {
web::WebState* webState = GetWebStateWithId(_webStateList, identifier);
if (!webState) {
return;
}
// NTP tabs get no favicon.
if (IsURLNtp(webState->GetVisibleURL())) {
return;
}
UIImage* defaultFavicon =
webState->GetBrowserState()->IsOffTheRecord()
? [UIImage imageNamed:@"default_world_favicon_incognito"]
: [UIImage imageNamed:@"default_world_favicon_regular"];
completion(defaultFavicon);
favicon::FaviconDriver* faviconDriver =
favicon::WebFaviconDriver::FromWebState(webState);
if (faviconDriver) {
gfx::Image favicon = faviconDriver->GetFavicon();
if (!favicon.IsEmpty())
completion(favicon.ToUIImage());
}
}
#pragma mark - TabStripConsumerDelegate
- (void)addNewItem {
if (!self.webStateList)
return;
web::WebState::CreateParams params(
self.webStateList->GetActiveWebState()->GetBrowserState());
std::unique_ptr<web::WebState> webState = web::WebState::Create(params);
GURL url(kChromeUINewTabURL);
web::NavigationManager::WebLoadParams loadParams(url);
loadParams.transition_type = ui::PAGE_TRANSITION_TYPED;
webState->GetNavigationManager()->LoadURLWithParams(loadParams);
self.webStateList->InsertWebState(
base::checked_cast<int>(self.webStateList->count()), std::move(webState),
(WebStateList::INSERT_FORCE_INDEX | WebStateList::INSERT_ACTIVATE),
WebStateOpener());
}
- (void)selectTab:(int)index {
if (!self.webStateList)
return;
_webStateList->ActivateWebStateAt(index);
}
- (void)closeItemWithID:(NSString*)itemID {
int index = GetIndexOfTabWithId(self.webStateList, itemID);
if (index >= 0)
self.webStateList->CloseWebStateAt(index, WebStateList::CLOSE_USER_ACTION);
}
#pragma mark - Private
// Calls |-populateItems:selectedItemID:| on the consumer.
- (void)populateConsumerItems {
if (!self.webStateList)
return;
if (self.webStateList->count() > 0) {
[self.consumer populateItems:CreateItems(self.webStateList)
selectedItemID:GetActiveTabId(self.webStateList)];
}
}
#pragma mark - CRWWebStateObserver
- (void)webStateDidChangeTitle:(web::WebState*)webState {
// Assumption: the ID of the webState didn't change as a result of this load.
TabIdTabHelper* tabHelper = TabIdTabHelper::FromWebState(webState);
NSString* itemID = tabHelper->tab_id();
[self.consumer replaceItemID:itemID withItem:CreateItem(webState)];
}
@end