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 #include "chrome/browser/ui/tabs/existing_window_sub_menu_model.h"
6
7 #include "base/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "build/build_config.h"
10 #include "chrome/browser/ui/browser_commands.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/test/base/browser_with_test_window_test.h"
13 #include "chrome/test/base/test_browser_window.h"
14 #include "ui/gfx/text_elider.h"
15
16 namespace {
17
18 class ExistingWindowSubMenuModelTest : public BrowserWithTestWindowTest {
19 public:
ExistingWindowSubMenuModelTest()20 ExistingWindowSubMenuModelTest() : BrowserWithTestWindowTest() {}
21
22 protected:
23 std::unique_ptr<Browser> CreateTestBrowser(bool incognito, bool popup);
24 void AddTabWithTitle(Browser* browser, std::string title);
25
26 static void CheckBrowserTitle(const base::string16& title,
27 const std::string& expected_title,
28 const int expected_num_tabs);
29 };
30
CreateTestBrowser(bool incognito,bool popup)31 std::unique_ptr<Browser> ExistingWindowSubMenuModelTest::CreateTestBrowser(
32 bool incognito,
33 bool popup) {
34 TestBrowserWindow* window = new TestBrowserWindow;
35 new TestBrowserWindowOwner(window);
36 Profile* profile = incognito ? browser()->profile()->GetPrimaryOTRProfile()
37 : browser()->profile();
38 Browser::Type type = popup ? Browser::TYPE_POPUP : Browser::TYPE_NORMAL;
39
40 std::unique_ptr<Browser> browser =
41 CreateBrowser(profile, type, false, window);
42 BrowserList::SetLastActive(browser.get());
43 return browser;
44 }
45
AddTabWithTitle(Browser * browser,std::string title)46 void ExistingWindowSubMenuModelTest::AddTabWithTitle(Browser* browser,
47 std::string title) {
48 AddTab(browser, GURL("about:blank"));
49 NavigateAndCommitActiveTabWithTitle(browser, GURL("about:blank"),
50 base::ASCIIToUTF16(title));
51 }
52
53 // static
CheckBrowserTitle(const base::string16 & title,const std::string & expected_title,const int expected_num_tabs)54 void ExistingWindowSubMenuModelTest::CheckBrowserTitle(
55 const base::string16& title,
56 const std::string& expected_title,
57 const int expected_num_tabs) {
58 const base::string16 expected_title16 = base::ASCIIToUTF16(expected_title);
59
60 // Check the suffix, which should always show if there are multiple tabs.
61 if (expected_num_tabs > 1) {
62 std::ostringstream oss;
63 oss << " and " << expected_num_tabs - 1;
64 oss << ((expected_num_tabs == 2) ? " other tab" : " other tabs");
65 const base::string16 expected_suffix16 = base::ASCIIToUTF16(oss.str());
66
67 // Not case sensitive, since MacOS uses title case.
68 EXPECT_TRUE(base::EndsWith(title, expected_suffix16,
69 base::CompareCase::INSENSITIVE_ASCII));
70 }
71
72 // Either the title contains the whole tab title, or it was elided.
73 if (!base::StartsWith(title, expected_title16,
74 base::CompareCase::SENSITIVE)) {
75 // Check that the title before being elided matches the tab title.
76 std::vector<base::string16> tokens =
77 SplitString(title, gfx::kEllipsisUTF16, base::KEEP_WHITESPACE,
78 base::SPLIT_WANT_NONEMPTY);
79 EXPECT_TRUE(base::StartsWith(expected_title16, tokens[0],
80 base::CompareCase::SENSITIVE));
81
82 // Title should always have at least a few characters.
83 EXPECT_GE(tokens[0].size(), 3ull);
84 }
85 }
86
87 // Ensure that the move to existing window menu only appears when another window
88 // of the current profile exists.
TEST_F(ExistingWindowSubMenuModelTest,ShouldShowSubmenu)89 TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenu) {
90 // Shouldn't show menu for one window.
91 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
92
93 // Add another browser, and make sure we do show the menu now.
94 std::unique_ptr<Browser> browser_2(CreateTestBrowser(false, false));
95 ASSERT_TRUE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
96
97 // Close the window, so the menu does not show anymore.
98 BrowserList::RemoveBrowser(browser_2.get());
99 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
100 }
101
102 // Ensure we only show the menu on incognito when at least one other incognito
103 // window exists.
TEST_F(ExistingWindowSubMenuModelTest,ShouldShowSubmenuIncognito)104 TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenuIncognito) {
105 // Shouldn't show menu for one window.
106 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
107 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(
108 profile()->GetPrimaryOTRProfile()));
109
110 // Create an incognito browser. We shouldn't show the menu, because we only
111 // move tabs between windows of the same profile.
112 std::unique_ptr<Browser> incognito_browser_1(CreateTestBrowser(true, false));
113 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
114 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(
115 profile()->GetPrimaryOTRProfile()));
116
117 // Add another incognito browser, and make sure we do show the menu now.
118 std::unique_ptr<Browser> incognito_browser_2(CreateTestBrowser(true, false));
119 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
120 ASSERT_TRUE(ExistingWindowSubMenuModel::ShouldShowSubmenu(
121 profile()->GetPrimaryOTRProfile()));
122 }
123
124 // Ensure we don't show the menu on a popup window.
TEST_F(ExistingWindowSubMenuModelTest,ShouldShowSubmenuPopup)125 TEST_F(ExistingWindowSubMenuModelTest, ShouldShowSubmenuPopup) {
126 // Popup windows aren't counted when determining whether to show the menu.
127 std::unique_ptr<Browser> browser_2(CreateTestBrowser(false, true));
128 ASSERT_FALSE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
129
130 // Add another tabbed window, make sure the menu shows.
131 std::unique_ptr<Browser> browser_3(CreateTestBrowser(false, false));
132 ASSERT_TRUE(ExistingWindowSubMenuModel::ShouldShowSubmenu(profile()));
133 }
134
135 // Validate that windows appear in MRU order and with the expected labels.
TEST_F(ExistingWindowSubMenuModelTest,BuildSubmenuOrder)136 TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuOrder) {
137 // Add some browsers.
138 BrowserList::SetLastActive(browser());
139 std::unique_ptr<Browser> browser_2(CreateTestBrowser(false, false));
140 std::unique_ptr<Browser> browser_3(CreateTestBrowser(false, false));
141 std::unique_ptr<Browser> browser_4(CreateTestBrowser(false, false));
142
143 // Add tabs.
144 constexpr char kLongTabTitleExample[] =
145 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas "
146 "porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies,"
147 "purus lectus malesuada libero, sit amet commodo magna eros quis urna.";
148
149 AddTabWithTitle(browser(), "Browser 1");
150 AddTabWithTitle(browser_2.get(), kLongTabTitleExample);
151 AddTabWithTitle(browser_3.get(), "Browser 3 Tab 1");
152 AddTabWithTitle(browser_3.get(), "Browser 3 Tab 2");
153 AddTabWithTitle(browser_4.get(), "Browser 4 Tab 1");
154 AddTabWithTitle(browser_4.get(), "Browser 4 Tab 2");
155 AddTabWithTitle(browser_4.get(), kLongTabTitleExample);
156
157 // Create menu from browser 1.
158 ExistingWindowSubMenuModel menu1(nullptr, browser()->tab_strip_model(), 0);
159 ASSERT_EQ(5, menu1.GetItemCount());
160 CheckBrowserTitle(menu1.GetLabelAt(2), kLongTabTitleExample, 3);
161 CheckBrowserTitle(menu1.GetLabelAt(3), "Browser 3 Tab 2", 2);
162 CheckBrowserTitle(menu1.GetLabelAt(4), kLongTabTitleExample, 1);
163
164 // Create menu from browser 2.
165 ExistingWindowSubMenuModel menu2(nullptr, browser_2->tab_strip_model(), 0);
166 ASSERT_EQ(5, menu2.GetItemCount());
167 CheckBrowserTitle(menu2.GetLabelAt(2), kLongTabTitleExample, 3);
168 CheckBrowserTitle(menu2.GetLabelAt(3), "Browser 3 Tab 2", 2);
169 CheckBrowserTitle(menu2.GetLabelAt(4), "Browser 1", 1);
170
171 // Rearrange the MRU and re-test.
172 BrowserList::SetLastActive(browser());
173 BrowserList::SetLastActive(browser_2.get());
174
175 ExistingWindowSubMenuModel menu3(nullptr, browser_3->tab_strip_model(), 0);
176 ASSERT_EQ(5, menu3.GetItemCount());
177 CheckBrowserTitle(menu3.GetLabelAt(2), kLongTabTitleExample, 1);
178 CheckBrowserTitle(menu3.GetLabelAt(3), "Browser 1", 1);
179 CheckBrowserTitle(menu3.GetLabelAt(4), kLongTabTitleExample, 3);
180
181 // Clean up.
182 chrome::CloseTab(browser_2.get());
183 chrome::CloseTab(browser_3.get());
184 chrome::CloseTab(browser_3.get());
185 chrome::CloseTab(browser_4.get());
186 chrome::CloseTab(browser_4.get());
187 chrome::CloseTab(browser_4.get());
188 }
189
190 // Ensure that normal browsers and incognito browsers have their own lists.
TEST_F(ExistingWindowSubMenuModelTest,BuildSubmenuIncognito)191 TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuIncognito) {
192 // Add some browsers.
193 BrowserList::SetLastActive(browser());
194 std::unique_ptr<Browser> browser_2(CreateTestBrowser(false, false));
195 std::unique_ptr<Browser> browser_3(CreateTestBrowser(false, false));
196 std::unique_ptr<Browser> incognito_browser_1(CreateTestBrowser(true, false));
197 std::unique_ptr<Browser> incognito_browser_2(CreateTestBrowser(true, false));
198
199 AddTabWithTitle(browser(), "Browser 1");
200 AddTabWithTitle(browser_2.get(), "Browser 2");
201 AddTabWithTitle(browser_3.get(), "Browser 3");
202 AddTabWithTitle(incognito_browser_1.get(), "Incognito Browser 1");
203 AddTabWithTitle(incognito_browser_2.get(), "Incognito Browser 2");
204
205 const base::string16 kBrowser2ExpectedTitle = base::WideToUTF16(L"Browser 2");
206 const base::string16 kBrowser3ExpectedTitle = base::WideToUTF16(L"Browser 3");
207 const base::string16 kIncognitoBrowser2ExpectedTitle =
208 base::WideToUTF16(L"Incognito Browser 2");
209
210 // Test that a non-incognito browser only shows non-incognito windows.
211 ExistingWindowSubMenuModel menu(nullptr, browser()->tab_strip_model(), 0);
212 ASSERT_EQ(4, menu.GetItemCount());
213 ASSERT_EQ(kBrowser3ExpectedTitle, menu.GetLabelAt(2));
214 ASSERT_EQ(kBrowser2ExpectedTitle, menu.GetLabelAt(3));
215
216 // Test that a incognito browser only shows incognito windows.
217 ExistingWindowSubMenuModel menu_incognito(
218 nullptr, incognito_browser_1->tab_strip_model(), 0);
219 ASSERT_EQ(3, menu_incognito.GetItemCount());
220 ASSERT_EQ(kIncognitoBrowser2ExpectedTitle, menu_incognito.GetLabelAt(2));
221
222 // Clean up.
223 chrome::CloseTab(browser_2.get());
224 chrome::CloseTab(browser_3.get());
225 chrome::CloseTab(incognito_browser_1.get());
226 chrome::CloseTab(incognito_browser_2.get());
227 }
228
229 // Ensure that popups don't appear in the list of existing windows.
TEST_F(ExistingWindowSubMenuModelTest,BuildSubmenuPopups)230 TEST_F(ExistingWindowSubMenuModelTest, BuildSubmenuPopups) {
231 // Add some browsers.
232 BrowserList::SetLastActive(browser());
233 std::unique_ptr<Browser> browser_2(CreateTestBrowser(false, false));
234 std::unique_ptr<Browser> browser_3(CreateTestBrowser(false, false));
235 std::unique_ptr<Browser> popup_browser_1(CreateTestBrowser(false, true));
236 std::unique_ptr<Browser> popup_browser_2(CreateTestBrowser(false, true));
237
238 AddTabWithTitle(browser(), "Browser 1");
239 AddTabWithTitle(browser_2.get(), "Browser 2");
240 AddTabWithTitle(browser_3.get(), "Browser 3");
241
242 const base::string16 kBrowser2ExpectedTitle = base::WideToUTF16(L"Browser 2");
243 const base::string16 kBrowser3ExpectedTitle = base::WideToUTF16(L"Browser 3");
244
245 // Test that popups do not show.
246 ExistingWindowSubMenuModel menu(nullptr, browser()->tab_strip_model(), 0);
247 ASSERT_EQ(4, menu.GetItemCount());
248 ASSERT_EQ(kBrowser3ExpectedTitle, menu.GetLabelAt(2));
249 ASSERT_EQ(kBrowser2ExpectedTitle, menu.GetLabelAt(3));
250
251 // Clean up.
252 chrome::CloseTab(browser_2.get());
253 chrome::CloseTab(browser_3.get());
254 chrome::CloseTab(popup_browser_1.get());
255 chrome::CloseTab(popup_browser_2.get());
256 }
257
258 } // namespace
259