blob: 8ebe3924446847744fd5a7576a081fb9bfb16d87 [file] [log] [blame]
// Copyright 2012 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 <objc/runtime.h>
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
#import "ios/chrome/browser/sessions/session_ios.h"
#import "ios/chrome/browser/sessions/session_window_ios.h"
#import "ios/chrome/browser/sessions/test_session_service.h"
#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
#import "ios/chrome/browser/tabs/tab.h"
#import "ios/chrome/browser/tabs/tab_helper_util.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#import "ios/chrome/browser/tabs/tab_model_observer.h"
#import "ios/chrome/browser/tabs/tab_private.h"
#import "ios/chrome/browser/web/chrome_web_client.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_opener.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#import "ios/web/navigation/navigation_manager_impl.h"
#import "ios/web/public/crw_session_storage.h"
#import "ios/web/public/navigation_manager.h"
#include "ios/web/public/referrer.h"
#import "ios/web/public/serializable_user_data_manager.h"
#include "ios/web/public/test/scoped_testing_web_client.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "ios/web/public/web_thread.h"
#import "ios/web/navigation/navigation_manager_impl.h"
#import "ios/web/web_state/web_state_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Defines a TabModelObserver for use in unittests. This class can be used to
// test if an observer method was called or not.
@interface TabModelObserverPong : NSObject<TabModelObserver> {
// TODO(crbug.com/661989): Add tests for the other observer methods.
BOOL tabMovedWasCalled_;
}
@property(nonatomic, assign) BOOL tabMovedWasCalled;
@end
@implementation TabModelObserverPong
@synthesize tabMovedWasCalled = tabMovedWasCalled_;
- (void)tabModel:(TabModel*)model
didMoveTab:(Tab*)tab
fromIndex:(NSUInteger)fromIndex
toIndex:(NSUInteger)toIndex {
tabMovedWasCalled_ = YES;
}
@end
namespace {
const char kURL1[] = "https://www.some.url.com";
const char kURL2[] = "https://www.some.url2.com";
class TabModelTest : public PlatformTest {
public:
TabModelTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())),
web_client_(std::make_unique<ChromeWebClient>()) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
TestChromeBrowserState::Builder test_cbs_builder;
chrome_browser_state_ = test_cbs_builder.Build();
session_window_ = [[SessionWindowIOS alloc] init];
// Create tab model with just a dummy session service so the async state
// saving doesn't trigger unless actually wanted.
SetTabModel(CreateTabModel([[TestSessionService alloc] init], nil));
}
~TabModelTest() override = default;
void TearDown() override {
SetTabModel(nil);
PlatformTest::TearDown();
}
void SetTabModel(TabModel* tab_model) {
if (tab_model_) {
@autoreleasepool {
[tab_model_ browserStateDestroyed];
tab_model_ = nil;
}
}
tab_model_ = tab_model;
}
TabModel* CreateTabModel(SessionServiceIOS* session_service,
SessionWindowIOS* session_window) {
TabModel* tab_model([[TabModel alloc]
initWithSessionWindow:session_window
sessionService:session_service
browserState:chrome_browser_state_.get()]);
[tab_model setWebUsageEnabled:NO];
[tab_model setPrimary:YES];
return tab_model;
}
protected:
// Creates a session window with entries named "restored window 1",
// "restored window 2" and "restored window 3" and the second entry
// marked as selected.
SessionWindowIOS* CreateSessionWindow() {
NSMutableArray<CRWSessionStorage*>* sessions = [NSMutableArray array];
for (int i = 0; i < 3; i++) {
CRWSessionStorage* session_storage = [[CRWSessionStorage alloc] init];
session_storage.lastCommittedItemIndex = -1;
[sessions addObject:session_storage];
}
return [[SessionWindowIOS alloc] initWithSessions:sessions selectedIndex:1];
}
web::TestWebThreadBundle thread_bundle_;
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
web::ScopedTestingWebClient web_client_;
SessionWindowIOS* session_window_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
TabModel* tab_model_;
};
TEST_F(TabModelTest, IsEmpty) {
EXPECT_EQ([tab_model_ count], 0U);
EXPECT_TRUE([tab_model_ isEmpty]);
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
ASSERT_EQ(1U, [tab_model_ count]);
EXPECT_FALSE([tab_model_ isEmpty]);
}
TEST_F(TabModelTest, InsertUrlSingle) {
Tab* tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
ASSERT_EQ(1U, [tab_model_ count]);
EXPECT_NSEQ(tab, [tab_model_ tabAtIndex:0]);
}
TEST_F(TabModelTest, InsertUrlMultiple) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:1
inBackground:NO];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:2]);
}
TEST_F(TabModelTest, AppendUrlSingle) {
Tab* tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
ASSERT_EQ(1U, [tab_model_ count]);
EXPECT_NSEQ(tab, [tab_model_ tabAtIndex:0]);
}
TEST_F(TabModelTest, AppendUrlMultiple) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:2]);
}
TEST_F(TabModelTest, CloseTabAtIndexBeginning) {
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ closeTabAtIndex:0];
ASSERT_EQ(2U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
}
TEST_F(TabModelTest, CloseTabAtIndexMiddle) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ closeTabAtIndex:1];
ASSERT_EQ(2U, [tab_model_ count]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
}
TEST_F(TabModelTest, CloseTabAtIndexLast) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ closeTabAtIndex:2];
ASSERT_EQ(2U, [tab_model_ count]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:1]);
}
TEST_F(TabModelTest, CloseTabAtIndexOnlyOne) {
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ closeTabAtIndex:0];
EXPECT_EQ(0U, [tab_model_ count]);
}
TEST_F(TabModelTest, RestoreSessionOnNTPTest) {
Tab* tab = [tab_model_ insertTabWithURL:GURL(kChromeUINewTabURL)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
web::WebStateImpl* web_state = static_cast<web::WebStateImpl*>(tab.webState);
web_state->GetNavigationManagerImpl().CommitPendingItem();
SessionWindowIOS* window(CreateSessionWindow());
[tab_model_ restoreSessionWindow:window];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ([tab_model_ tabAtIndex:1], [tab_model_ currentTab]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:0]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:1]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:2]);
}
TEST_F(TabModelTest, RestoreSessionOn2NtpTest) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kChromeUINewTabURL)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
web::WebStateImpl* web_state = static_cast<web::WebStateImpl*>(tab0.webState);
web_state->GetNavigationManagerImpl().CommitPendingItem();
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kChromeUINewTabURL)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:1
inBackground:NO];
web_state = static_cast<web::WebStateImpl*>(tab1.webState);
web_state->GetNavigationManagerImpl().CommitPendingItem();
SessionWindowIOS* window(CreateSessionWindow());
[tab_model_ restoreSessionWindow:window];
ASSERT_EQ(5U, [tab_model_ count]);
EXPECT_NSEQ([tab_model_ tabAtIndex:3], [tab_model_ currentTab]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:1]);
EXPECT_NSNE(tab0, [tab_model_ tabAtIndex:2]);
EXPECT_NSNE(tab0, [tab_model_ tabAtIndex:3]);
EXPECT_NSNE(tab0, [tab_model_ tabAtIndex:4]);
EXPECT_NSNE(tab1, [tab_model_ tabAtIndex:2]);
EXPECT_NSNE(tab1, [tab_model_ tabAtIndex:3]);
EXPECT_NSNE(tab1, [tab_model_ tabAtIndex:4]);
}
TEST_F(TabModelTest, RestoreSessionOnAnyTest) {
Tab* tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:0
inBackground:NO];
web::WebStateImpl* web_state = static_cast<web::WebStateImpl*>(tab.webState);
web_state->GetNavigationManagerImpl().CommitPendingItem();
SessionWindowIOS* window(CreateSessionWindow());
[tab_model_ restoreSessionWindow:window];
ASSERT_EQ(4U, [tab_model_ count]);
EXPECT_NSEQ([tab_model_ tabAtIndex:2], [tab_model_ currentTab]);
EXPECT_NSEQ(tab, [tab_model_ tabAtIndex:0]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:1]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:2]);
EXPECT_NSNE(tab, [tab_model_ tabAtIndex:3]);
}
TEST_F(TabModelTest, CloseAllTabs) {
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL2)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ closeAllTabs];
EXPECT_EQ(0U, [tab_model_ count]);
}
TEST_F(TabModelTest, CloseAllTabsWithNoTabs) {
[tab_model_ closeAllTabs];
EXPECT_EQ(0U, [tab_model_ count]);
}
TEST_F(TabModelTest, InsertWithSessionController) {
EXPECT_EQ([tab_model_ count], 0U);
EXPECT_TRUE([tab_model_ isEmpty]);
Tab* new_tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
EXPECT_EQ([tab_model_ count], 1U);
[tab_model_ setCurrentTab:new_tab];
Tab* current_tab = [tab_model_ currentTab];
EXPECT_TRUE(current_tab);
}
TEST_F(TabModelTest, OpenerOfTab) {
// Start off with a couple tabs.
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Create parent tab.
Tab* parent_tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Create child tab.
Tab* child_tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:parent_tab
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Create another unrelated tab.
Tab* another_tab = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Create another child of the first tab.
Tab* child_tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:parent_tab
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
EXPECT_FALSE([tab_model_ openerOfTab:parent_tab]);
EXPECT_FALSE([tab_model_ openerOfTab:another_tab]);
EXPECT_EQ(parent_tab, [tab_model_ openerOfTab:child_tab]);
EXPECT_EQ(parent_tab, [tab_model_ openerOfTab:child_tab2]);
}
TEST_F(TabModelTest, OpenerOfTabEmptyModel) {
EXPECT_FALSE([tab_model_ openerOfTab:nil]);
}
TEST_F(TabModelTest, AddWithOrderController) {
// Create a few tabs with the controller at the front.
Tab* parent = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Add a new tab, it should be added behind the parent.
Tab* child =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:parent], 0U);
EXPECT_EQ([tab_model_ indexOfTab:child], 1U);
// Add another new tab without a parent, should go at the end.
Tab* tab =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:nil
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:tab], [tab_model_ count] - 1);
// Same for a tab that's not opened via a LINK transition.
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:tab2], [tab_model_ count] - 1);
// Add a tab in the background. It should appear behind the opening tab.
Tab* tab3 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:tab
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:tab3], [tab_model_ indexOfTab:tab] + 1);
// Add another background tab behind the one we just opened.
Tab* tab4 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:tab3
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:tab4], [tab_model_ indexOfTab:tab3] + 1);
}
TEST_F(TabModelTest, AddWithOrderControllerAndGrouping) {
// Create a few tabs with the controller at the front.
Tab* parent = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Force the history to update, as it is used to determine grouping.
ASSERT_TRUE([parent navigationManagerImpl]);
[parent navigationManagerImpl]->CommitPendingItem();
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
ASSERT_TRUE(chrome_browser_state_->CreateHistoryService(true));
// Add a new tab, it should be added behind the parent.
Tab* child1 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:parent], 0U);
EXPECT_EQ([tab_model_ indexOfTab:child1], 1U);
// Add a second child tab in the background. It should be added behind the
// first child.
Tab* child2 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:child2], 2U);
// Navigate the parent tab to a new URL. It should not change any ordering.
web::NavigationManager::WebLoadParams parent_params(
GURL("http://www.espn.com"));
parent_params.transition_type = ui::PAGE_TRANSITION_TYPED;
[parent navigationManager]->LoadURLWithParams(parent_params);
ASSERT_TRUE([parent navigationManagerImpl]);
[parent navigationManagerImpl]->CommitPendingItem();
EXPECT_EQ([tab_model_ indexOfTab:parent], 0U);
// Add a new tab. It should be added behind the parent. It should not be added
// after the previous two children.
Tab* child3 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:child3], 1U);
// Add a fourt child tab in the background. It should be added behind the
// third child.
Tab* child4 =
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:TabModelConstants::kTabPositionAutomatically
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:child4], 2U);
// The first two children should have been moved to the right.
EXPECT_EQ([tab_model_ indexOfTab:child1], 3U);
EXPECT_EQ([tab_model_ indexOfTab:child2], 4U);
// Now add a non-owned tab and make sure it is added at the end.
Tab* nonChild = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:nonChild], [tab_model_ count] - 1);
}
TEST_F(TabModelTest, AddWithLinkTransitionAndIndex) {
// Create a few tabs with the controller at the front.
Tab* parent = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Force the history to update, as it is used to determine grouping.
ASSERT_TRUE([parent navigationManagerImpl]);
[parent navigationManagerImpl]->CommitPendingItem();
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
ASSERT_TRUE(chrome_browser_state_->CreateHistoryService(true));
// Add a new tab, it should be added before the parent since the index
// parameter has been specified with a valid value.
Tab* child1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:0
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:parent], 1U);
EXPECT_EQ([tab_model_ indexOfTab:child1], 0U);
// Add a new tab, it should be added at the beginning of the stack because
// the index parameter has been specified with a valid value.
Tab* child2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:0
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:parent], 2U);
EXPECT_EQ([tab_model_ indexOfTab:child1], 1U);
EXPECT_EQ([tab_model_ indexOfTab:child2], 0U);
// Add a new tab, it should be added at position 1 because the index parameter
// has been specified with a valid value.
Tab* child3 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_LINK
opener:parent
openedByDOM:NO
atIndex:1
inBackground:NO];
EXPECT_EQ([tab_model_ indexOfTab:parent], 3U);
EXPECT_EQ([tab_model_ indexOfTab:child1], 2U);
EXPECT_EQ([tab_model_ indexOfTab:child3], 1U);
EXPECT_EQ([tab_model_ indexOfTab:child2], 0U);
}
TEST_F(TabModelTest, MoveTabs) {
Tab* tab0 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab1 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
Tab* tab2 = [tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
// Basic sanity checks before moving on.
ASSERT_EQ(3U, [tab_model_ count]);
ASSERT_NSEQ(tab0, [tab_model_ tabAtIndex:0]);
ASSERT_NSEQ(tab1, [tab_model_ tabAtIndex:1]);
ASSERT_NSEQ(tab2, [tab_model_ tabAtIndex:2]);
// Check that observer methods are called.
TabModelObserverPong* tab_model_observer;
tab_model_observer = [[TabModelObserverPong alloc] init];
[tab_model_ addObserver:tab_model_observer];
// Move a tab from index 1 to index 0 (move tab left by one).
[tab_model_observer setTabMovedWasCalled:NO];
[tab_model_ moveTab:[tab_model_ tabAtIndex:1] toIndex:0];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:2]);
EXPECT_TRUE([tab_model_observer tabMovedWasCalled]);
// Move a tab from index 1 to index 2 (move tab right by one).
[tab_model_observer setTabMovedWasCalled:NO];
[tab_model_ moveTab:[tab_model_ tabAtIndex:1] toIndex:2];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:2]);
EXPECT_TRUE([tab_model_observer tabMovedWasCalled]);
// Move a tab from index 0 to index 2 (move tab right by more than one).
[tab_model_observer setTabMovedWasCalled:NO];
[tab_model_ moveTab:[tab_model_ tabAtIndex:0] toIndex:2];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:2]);
EXPECT_TRUE([tab_model_observer tabMovedWasCalled]);
// Move a tab from index 2 to index 0 (move tab left by more than one).
[tab_model_observer setTabMovedWasCalled:NO];
[tab_model_ moveTab:[tab_model_ tabAtIndex:2] toIndex:0];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:2]);
EXPECT_TRUE([tab_model_observer tabMovedWasCalled]);
// Move a tab from index 2 to index 2 (move tab to the same index).
[tab_model_observer setTabMovedWasCalled:NO];
[tab_model_ moveTab:[tab_model_ tabAtIndex:2] toIndex:2];
ASSERT_EQ(3U, [tab_model_ count]);
EXPECT_NSEQ(tab1, [tab_model_ tabAtIndex:0]);
EXPECT_NSEQ(tab2, [tab_model_ tabAtIndex:1]);
EXPECT_NSEQ(tab0, [tab_model_ tabAtIndex:2]);
EXPECT_FALSE([tab_model_observer tabMovedWasCalled]);
// TabModel asserts that there are no observer when it is deallocated,
// so remove the observer before the end of the method.
[tab_model_ removeObserver:tab_model_observer];
}
TEST_F(TabModelTest, ParentTabModel) {
std::unique_ptr<web::WebState> web_state = web::WebState::Create(
web::WebState::CreateParams(chrome_browser_state_.get()));
AttachTabHelpers(web_state.get(), /*for_prerender=*/false);
Tab* tab = LegacyTabHelper::GetTabForWebState(web_state.get());
EXPECT_NSEQ(nil, [tab parentTabModel]);
[tab_model_ webStateList]->InsertWebState(0, std::move(web_state),
WebStateList::INSERT_FORCE_INDEX,
WebStateOpener());
EXPECT_NSEQ(tab_model_, [tab parentTabModel]);
}
TEST_F(TabModelTest, TabCreatedOnInsertion) {
std::unique_ptr<web::WebState> web_state = web::WebState::Create(
web::WebState::CreateParams(chrome_browser_state_.get()));
EXPECT_NSEQ(nil, LegacyTabHelper::GetTabForWebState(web_state.get()));
web::WebState* web_state_ptr = web_state.get();
[tab_model_ webStateList]->InsertWebState(0, std::move(web_state),
WebStateList::INSERT_FORCE_INDEX,
WebStateOpener());
EXPECT_NSNE(nil, LegacyTabHelper::GetTabForWebState(web_state_ptr));
}
TEST_F(TabModelTest, PersistSelectionChange) {
NSString* stashPath =
base::SysUTF8ToNSString(chrome_browser_state_->GetStatePath().value());
// Reset the TabModel with a custom SessionServiceIOS (to control whether
// data is saved to disk).
TestSessionService* test_session_service = [[TestSessionService alloc] init];
SetTabModel(CreateTabModel(test_session_service, nil));
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:nil
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:[tab_model_ tabAtIndex:0]
openedByDOM:NO
atIndex:[tab_model_ count]
inBackground:NO];
[tab_model_ insertTabWithURL:GURL(kURL1)
referrer:web::Referrer()
transition:ui::PAGE_TRANSITION_TYPED
opener:[tab_model_ tabAtIndex:1]
openedByDOM:NO
atIndex:0
inBackground:NO];
ASSERT_EQ(3U, [tab_model_ count]);
[tab_model_ setCurrentTab:[tab_model_ tabAtIndex:1]];
EXPECT_EQ(nil, [tab_model_ openerOfTab:[tab_model_ tabAtIndex:1]]);
EXPECT_EQ([tab_model_ tabAtIndex:1],
[tab_model_ openerOfTab:[tab_model_ tabAtIndex:2]]);
EXPECT_EQ([tab_model_ tabAtIndex:2],
[tab_model_ openerOfTab:[tab_model_ tabAtIndex:0]]);
// Force state to flush to disk on the main thread so it can be immediately
// tested below.
[test_session_service setPerformIO:YES];
[tab_model_ saveSessionImmediately:YES];
[test_session_service setPerformIO:NO];
NSString* state_path = base::SysUTF8ToNSString(
chrome_browser_state_->GetStatePath().AsUTF8Unsafe());
SessionIOS* session =
[test_session_service loadSessionFromDirectory:state_path];
ASSERT_EQ(1u, session.sessionWindows.count);
SessionWindowIOS* session_window = session.sessionWindows[0];
// Create tab model from saved session.
SetTabModel(CreateTabModel(test_session_service, session_window));
ASSERT_EQ(3u, [tab_model_ count]);
EXPECT_EQ([tab_model_ tabAtIndex:1], [tab_model_ currentTab]);
EXPECT_EQ(nil, [tab_model_ openerOfTab:[tab_model_ tabAtIndex:1]]);
EXPECT_EQ([tab_model_ tabAtIndex:1],
[tab_model_ openerOfTab:[tab_model_ tabAtIndex:2]]);
EXPECT_EQ([tab_model_ tabAtIndex:2],
[tab_model_ openerOfTab:[tab_model_ tabAtIndex:0]]);
// Clean up.
EXPECT_TRUE([[NSFileManager defaultManager] removeItemAtPath:stashPath
error:nullptr]);
}
} // anonymous namespace