1 // Copyright (c) 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/webui/tab_strip/tab_strip_ui_handler.h"
6
7 #include <memory>
8
9 #include "base/macros.h"
10 #include "base/optional.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "chrome/browser/extensions/extension_tab_util.h"
14 #include "chrome/browser/ui/tabs/tab_group_model.h"
15 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
16 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
17 #include "chrome/test/base/browser_with_test_window_test.h"
18 #include "chrome/test/base/testing_profile_manager.h"
19 #include "components/tab_groups/tab_group_color.h"
20 #include "components/tab_groups/tab_group_id.h"
21 #include "components/tab_groups/tab_group_visual_data.h"
22 #include "content/public/browser/web_ui.h"
23 #include "content/public/test/test_web_ui.h"
24 #include "content/public/test/web_contents_tester.h"
25 #include "ui/base/accelerators/accelerator.h"
26 #include "ui/base/theme_provider.h"
27 #include "ui/gfx/color_utils.h"
28 #include "ui/gfx/geometry/point.h"
29
30 namespace {
31
32 class TestTabStripUIHandler : public TabStripUIHandler {
33 public:
TestTabStripUIHandler(content::WebUI * web_ui,Browser * browser,TabStripUIEmbedder * embedder)34 explicit TestTabStripUIHandler(content::WebUI* web_ui,
35 Browser* browser,
36 TabStripUIEmbedder* embedder)
37 : TabStripUIHandler(browser, embedder) {
38 set_web_ui(web_ui);
39 }
40 };
41
42 class StubTabStripUIEmbedder : public TabStripUIEmbedder {
43 public:
GetAcceleratorProvider() const44 const ui::AcceleratorProvider* GetAcceleratorProvider() const override {
45 return nullptr;
46 }
CloseContainer()47 void CloseContainer() override {}
ShowContextMenuAtPoint(gfx::Point point,std::unique_ptr<ui::MenuModel> menu_model)48 void ShowContextMenuAtPoint(
49 gfx::Point point,
50 std::unique_ptr<ui::MenuModel> menu_model) override {}
ShowEditDialogForGroupAtPoint(gfx::Point point,gfx::Rect rect,tab_groups::TabGroupId group_id)51 void ShowEditDialogForGroupAtPoint(gfx::Point point,
52 gfx::Rect rect,
53 tab_groups::TabGroupId group_id) override {
54 }
GetLayout()55 TabStripUILayout GetLayout() override { return TabStripUILayout(); }
GetColor(int id) const56 SkColor GetColor(int id) const override { return SK_ColorWHITE; }
57 };
58
59 } // namespace
60
61 class TabStripUIHandlerTest : public BrowserWithTestWindowTest {
62 public:
63 TabStripUIHandlerTest() = default;
64
SetUp()65 void SetUp() override {
66 BrowserWithTestWindowTest::SetUp();
67 handler_ = std::make_unique<TestTabStripUIHandler>(web_ui(), browser(),
68 &stub_embedder_);
69 handler()->AllowJavascriptForTesting();
70 web_ui()->ClearTrackedCalls();
71 }
72
handler()73 TabStripUIHandler* handler() { return handler_.get(); }
web_ui()74 content::TestWebUI* web_ui() { return &web_ui_; }
75
ExpectVisualDataDictionary(const tab_groups::TabGroupVisualData visual_data,const base::DictionaryValue * visual_data_dict)76 void ExpectVisualDataDictionary(
77 const tab_groups::TabGroupVisualData visual_data,
78 const base::DictionaryValue* visual_data_dict) {
79 std::string group_title;
80 ASSERT_TRUE(visual_data_dict->GetString("title", &group_title));
81 EXPECT_EQ(base::UTF16ToASCII(visual_data.title()), group_title);
82
83 std::string group_color;
84 ASSERT_TRUE(visual_data_dict->GetString("color", &group_color));
85 EXPECT_EQ(color_utils::SkColorToRgbString(SK_ColorWHITE), group_color);
86 }
87
88 private:
89 StubTabStripUIEmbedder stub_embedder_;
90 content::TestWebUI web_ui_;
91 std::unique_ptr<TestTabStripUIHandler> handler_;
92 };
93
TEST_F(TabStripUIHandlerTest,GroupClosedEvent)94 TEST_F(TabStripUIHandlerTest, GroupClosedEvent) {
95 AddTab(browser(), GURL("http://foo"));
96 tab_groups::TabGroupId expected_group_id =
97 browser()->tab_strip_model()->AddToNewGroup({0});
98 browser()->tab_strip_model()->RemoveFromGroup({0});
99
100 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
101 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
102 EXPECT_EQ("tab-group-closed", call_data.arg1()->GetString());
103 EXPECT_EQ(expected_group_id.ToString(), call_data.arg2()->GetString());
104 }
105
TEST_F(TabStripUIHandlerTest,GroupStateChangedEvents)106 TEST_F(TabStripUIHandlerTest, GroupStateChangedEvents) {
107 AddTab(browser(), GURL("http://foo/1"));
108 AddTab(browser(), GURL("http://foo/2"));
109
110 // Add one of the tabs to a group to test for a tab-group-state-changed event.
111 tab_groups::TabGroupId expected_group_id =
112 browser()->tab_strip_model()->AddToNewGroup({0, 1});
113 int expected_tab_id = extensions::ExtensionTabUtil::GetTabId(
114 browser()->tab_strip_model()->GetWebContentsAt(1));
115
116 const content::TestWebUI::CallData& grouped_call_data =
117 *web_ui()->call_data().back();
118 EXPECT_EQ("cr.webUIListenerCallback", grouped_call_data.function_name());
119 EXPECT_EQ("tab-group-state-changed", grouped_call_data.arg1()->GetString());
120 EXPECT_EQ(expected_tab_id, grouped_call_data.arg2()->GetInt());
121 EXPECT_EQ(1, grouped_call_data.arg3()->GetInt());
122 EXPECT_EQ(expected_group_id.ToString(),
123 grouped_call_data.arg4()->GetString());
124
125 // Remove the tab from the group to test for a tab-group-state-changed event.
126 browser()->tab_strip_model()->RemoveFromGroup({1});
127 const content::TestWebUI::CallData& ungrouped_call_data =
128 *web_ui()->call_data().back();
129 EXPECT_EQ("cr.webUIListenerCallback", ungrouped_call_data.function_name());
130 EXPECT_EQ("tab-group-state-changed", ungrouped_call_data.arg1()->GetString());
131 EXPECT_EQ(expected_tab_id, ungrouped_call_data.arg2()->GetInt());
132 EXPECT_EQ(1, ungrouped_call_data.arg3()->GetInt());
133 EXPECT_EQ(nullptr, ungrouped_call_data.arg4());
134 }
135
TEST_F(TabStripUIHandlerTest,GetGroupVisualData)136 TEST_F(TabStripUIHandlerTest, GetGroupVisualData) {
137 AddTab(browser(), GURL("http://foo/1"));
138 AddTab(browser(), GURL("http://foo/2"));
139 tab_groups::TabGroupId group1 =
140 browser()->tab_strip_model()->AddToNewGroup({0});
141 const tab_groups::TabGroupVisualData group1_visuals(
142 base::ASCIIToUTF16("Group 1"), tab_groups::TabGroupColorId::kGreen);
143 browser()
144 ->tab_strip_model()
145 ->group_model()
146 ->GetTabGroup(group1)
147 ->SetVisualData(group1_visuals);
148 tab_groups::TabGroupId group2 =
149 browser()->tab_strip_model()->AddToNewGroup({1});
150 const tab_groups::TabGroupVisualData group2_visuals(
151 base::ASCIIToUTF16("Group 2"), tab_groups::TabGroupColorId::kCyan);
152 browser()
153 ->tab_strip_model()
154 ->group_model()
155 ->GetTabGroup(group2)
156 ->SetVisualData(group2_visuals);
157
158 base::ListValue args;
159 args.AppendString("callback-id");
160 handler()->HandleGetGroupVisualData(&args);
161
162 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
163 EXPECT_EQ("cr.webUIResponse", call_data.function_name());
164 EXPECT_EQ("callback-id", call_data.arg1()->GetString());
165 EXPECT_TRUE(call_data.arg2()->GetBool());
166
167 const base::DictionaryValue* returned_data;
168 ASSERT_TRUE(call_data.arg3()->GetAsDictionary(&returned_data));
169
170 const base::DictionaryValue* group1_dict;
171 ASSERT_TRUE(returned_data->GetDictionary(group1.ToString(), &group1_dict));
172 ExpectVisualDataDictionary(group1_visuals, group1_dict);
173
174 const base::DictionaryValue* group2_dict;
175 ASSERT_TRUE(returned_data->GetDictionary(group2.ToString(), &group2_dict));
176 ExpectVisualDataDictionary(group2_visuals, group2_dict);
177 }
178
TEST_F(TabStripUIHandlerTest,GroupVisualDataChangedEvent)179 TEST_F(TabStripUIHandlerTest, GroupVisualDataChangedEvent) {
180 AddTab(browser(), GURL("http://foo"));
181 tab_groups::TabGroupId expected_group_id =
182 browser()->tab_strip_model()->AddToNewGroup({0});
183 const tab_groups::TabGroupVisualData new_visual_data(
184 base::ASCIIToUTF16("My new title"), tab_groups::TabGroupColorId::kGreen);
185 browser()
186 ->tab_strip_model()
187 ->group_model()
188 ->GetTabGroup(expected_group_id)
189 ->SetVisualData(new_visual_data);
190
191 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
192 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
193 EXPECT_EQ("tab-group-visuals-changed", call_data.arg1()->GetString());
194 EXPECT_EQ(expected_group_id.ToString(), call_data.arg2()->GetString());
195
196 const base::DictionaryValue* visual_data;
197 ASSERT_TRUE(call_data.arg3()->GetAsDictionary(&visual_data));
198 ExpectVisualDataDictionary(new_visual_data, visual_data);
199 }
200
TEST_F(TabStripUIHandlerTest,GroupTab)201 TEST_F(TabStripUIHandlerTest, GroupTab) {
202 // Add a tab inside of a group.
203 AddTab(browser(), GURL("http://foo"));
204 tab_groups::TabGroupId group_id =
205 browser()->tab_strip_model()->AddToNewGroup({0});
206
207 // Add another tab, and try to group it.
208 AddTab(browser(), GURL("http://foo"));
209 base::ListValue args;
210 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
211 browser()->tab_strip_model()->GetWebContentsAt(0)));
212 args.AppendString(group_id.ToString());
213 handler()->HandleGroupTab(&args);
214
215 ASSERT_EQ(group_id, browser()->tab_strip_model()->GetTabGroupForTab(0));
216 }
217
TEST_F(TabStripUIHandlerTest,MoveGroup)218 TEST_F(TabStripUIHandlerTest, MoveGroup) {
219 AddTab(browser(), GURL("http://foo/1"));
220 AddTab(browser(), GURL("http://foo/2"));
221 tab_groups::TabGroupId group_id =
222 browser()->tab_strip_model()->AddToNewGroup({0});
223 web_ui()->ClearTrackedCalls();
224
225 // Move the group to index 1.
226 int new_index = 1;
227 base::ListValue args;
228 args.AppendString(group_id.ToString());
229 args.AppendInteger(new_index);
230 handler()->HandleMoveGroup(&args);
231
232 std::vector<int> tabs_in_group = browser()
233 ->tab_strip_model()
234 ->group_model()
235 ->GetTabGroup(group_id)
236 ->ListTabs();
237 ASSERT_EQ(new_index, tabs_in_group.front());
238 ASSERT_EQ(new_index, tabs_in_group.back());
239
240 EXPECT_EQ(1U, web_ui()->call_data().size());
241 const content::TestWebUI::CallData& call_data =
242 *web_ui()->call_data().front();
243 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
244 EXPECT_EQ("tab-group-moved", call_data.arg1()->GetString());
245 EXPECT_EQ(group_id.ToString(), call_data.arg2()->GetString());
246 }
247
TEST_F(TabStripUIHandlerTest,MoveGroupAcrossWindows)248 TEST_F(TabStripUIHandlerTest, MoveGroupAcrossWindows) {
249 AddTab(browser(), GURL("http://foo"));
250
251 // Create a new window with the same profile, and add a group to it.
252 std::unique_ptr<BrowserWindow> new_window(CreateBrowserWindow());
253 std::unique_ptr<Browser> new_browser =
254 CreateBrowser(profile(), browser()->type(), false, new_window.get());
255 AddTab(new_browser.get(), GURL("http://foo"));
256 AddTab(new_browser.get(), GURL("http://foo"));
257 tab_groups::TabGroupId group_id =
258 new_browser.get()->tab_strip_model()->AddToNewGroup({0, 1});
259
260 // Create some visual data to make sure it gets transferred.
261 const tab_groups::TabGroupVisualData visual_data(
262 base::ASCIIToUTF16("My group"), tab_groups::TabGroupColorId::kGreen);
263 new_browser.get()
264 ->tab_strip_model()
265 ->group_model()
266 ->GetTabGroup(group_id)
267 ->SetVisualData(visual_data);
268
269 content::WebContents* moved_contents1 =
270 new_browser.get()->tab_strip_model()->GetWebContentsAt(0);
271 content::WebContents* moved_contents2 =
272 new_browser.get()->tab_strip_model()->GetWebContentsAt(1);
273 web_ui()->ClearTrackedCalls();
274
275 int new_index = -1;
276 base::ListValue args;
277 args.AppendString(group_id.ToString());
278 args.AppendInteger(new_index);
279 handler()->HandleMoveGroup(&args);
280
281 ASSERT_EQ(0U, new_browser.get()
282 ->tab_strip_model()
283 ->group_model()
284 ->ListTabGroups()
285 .size());
286 ASSERT_EQ(moved_contents1, browser()->tab_strip_model()->GetWebContentsAt(1));
287 ASSERT_EQ(moved_contents2, browser()->tab_strip_model()->GetWebContentsAt(2));
288
289 base::Optional<tab_groups::TabGroupId> new_group_id =
290 browser()->tab_strip_model()->GetTabGroupForTab(1);
291 ASSERT_TRUE(new_group_id.has_value());
292 ASSERT_EQ(browser()->tab_strip_model()->GetTabGroupForTab(1),
293 browser()->tab_strip_model()->GetTabGroupForTab(2));
294
295 const tab_groups::TabGroupVisualData* new_visual_data =
296 browser()
297 ->tab_strip_model()
298 ->group_model()
299 ->GetTabGroup(new_group_id.value())
300 ->visual_data();
301 ASSERT_EQ(visual_data.title(), new_visual_data->title());
302 ASSERT_EQ(visual_data.color(), new_visual_data->color());
303 }
304
TEST_F(TabStripUIHandlerTest,MoveGroupAcrossProfiles)305 TEST_F(TabStripUIHandlerTest, MoveGroupAcrossProfiles) {
306 AddTab(browser(), GURL("http://foo"));
307
308 TestingProfile* different_profile =
309 profile_manager()->CreateTestingProfile("different_profile");
310 std::unique_ptr<BrowserWindow> new_window(CreateBrowserWindow());
311 std::unique_ptr<Browser> new_browser = CreateBrowser(
312 different_profile, browser()->type(), false, new_window.get());
313 AddTab(new_browser.get(), GURL("http://foo"));
314 tab_groups::TabGroupId group_id =
315 new_browser.get()->tab_strip_model()->AddToNewGroup({0});
316
317 int new_index = -1;
318 base::ListValue args;
319 args.AppendString(group_id.ToString());
320 args.AppendInteger(new_index);
321 handler()->HandleMoveGroup(&args);
322
323 ASSERT_TRUE(
324 new_browser.get()->tab_strip_model()->group_model()->ContainsTabGroup(
325 group_id));
326
327 // Close all tabs before destructing.
328 new_browser.get()->tab_strip_model()->CloseAllTabs();
329 }
330
TEST_F(TabStripUIHandlerTest,MoveTab)331 TEST_F(TabStripUIHandlerTest, MoveTab) {
332 AddTab(browser(), GURL("http://foo"));
333 AddTab(browser(), GURL("http://foo"));
334
335 content::WebContents* contents_prev_at_0 =
336 browser()->tab_strip_model()->GetWebContentsAt(0);
337 content::WebContents* contents_prev_at_1 =
338 browser()->tab_strip_model()->GetWebContentsAt(1);
339
340 // Move tab at index 0 to index 1.
341 base::ListValue args;
342 args.AppendInteger(
343 extensions::ExtensionTabUtil::GetTabId(contents_prev_at_0));
344 args.AppendInteger(1);
345 handler()->HandleMoveTab(&args);
346
347 ASSERT_EQ(1, browser()->tab_strip_model()->GetIndexOfWebContents(
348 contents_prev_at_0));
349 ASSERT_EQ(0, browser()->tab_strip_model()->GetIndexOfWebContents(
350 contents_prev_at_1));
351 }
352
TEST_F(TabStripUIHandlerTest,MoveTabAcrossProfiles)353 TEST_F(TabStripUIHandlerTest, MoveTabAcrossProfiles) {
354 AddTab(browser(), GURL("http://foo"));
355
356 TestingProfile* different_profile =
357 profile_manager()->CreateTestingProfile("different_profile");
358 std::unique_ptr<BrowserWindow> new_window(CreateBrowserWindow());
359 std::unique_ptr<Browser> new_browser = CreateBrowser(
360 different_profile, browser()->type(), false, new_window.get());
361 AddTab(new_browser.get(), GURL("http://foo"));
362
363 base::ListValue args;
364 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
365 new_browser->tab_strip_model()->GetWebContentsAt(0)));
366 args.AppendInteger(1);
367 handler()->HandleMoveTab(&args);
368
369 ASSERT_FALSE(browser()->tab_strip_model()->ContainsIndex(1));
370
371 // Close all tabs before destructing.
372 new_browser.get()->tab_strip_model()->CloseAllTabs();
373 }
374
TEST_F(TabStripUIHandlerTest,MoveTabAcrossWindows)375 TEST_F(TabStripUIHandlerTest, MoveTabAcrossWindows) {
376 AddTab(browser(), GURL("http://foo"));
377
378 std::unique_ptr<BrowserWindow> new_window(CreateBrowserWindow());
379 std::unique_ptr<Browser> new_browser =
380 CreateBrowser(profile(), browser()->type(), false, new_window.get());
381 AddTab(new_browser.get(), GURL("http://foo"));
382 content::WebContents* moved_contents =
383 new_browser.get()->tab_strip_model()->GetWebContentsAt(0);
384
385 base::ListValue args;
386 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
387 new_browser->tab_strip_model()->GetWebContentsAt(0)));
388 args.AppendInteger(1);
389 handler()->HandleMoveTab(&args);
390
391 ASSERT_EQ(moved_contents, browser()->tab_strip_model()->GetWebContentsAt(1));
392
393 // Close all tabs before destructing.
394 new_browser.get()->tab_strip_model()->CloseAllTabs();
395 }
396
TEST_F(TabStripUIHandlerTest,TabCreated)397 TEST_F(TabStripUIHandlerTest, TabCreated) {
398 AddTab(browser(), GURL("http://foo"));
399
400 const content::TestWebUI::CallData& call_data =
401 *web_ui()->call_data().front();
402 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
403 EXPECT_EQ("tab-created", call_data.arg1()->GetString());
404
405 const base::DictionaryValue* tab_data;
406 ASSERT_TRUE(call_data.arg2()->GetAsDictionary(&tab_data));
407
408 int tab_id;
409 ASSERT_TRUE(tab_data->GetInteger("id", &tab_id));
410 ASSERT_EQ(extensions::ExtensionTabUtil::GetTabId(
411 browser()->tab_strip_model()->GetWebContentsAt(0)),
412 tab_id);
413
414 bool is_active;
415 ASSERT_TRUE(tab_data->GetBoolean("active", &is_active));
416 ASSERT_TRUE(is_active);
417
418 int tab_index;
419 ASSERT_TRUE(tab_data->GetInteger("index", &tab_index));
420 ASSERT_EQ(0, tab_index);
421 }
422
TEST_F(TabStripUIHandlerTest,TabRemoved)423 TEST_F(TabStripUIHandlerTest, TabRemoved) {
424 // Two tabs so the browser does not close when a tab is closed.
425 AddTab(browser(), GURL("http://foo"));
426 AddTab(browser(), GURL("http://foo"));
427 int expected_tab_id = extensions::ExtensionTabUtil::GetTabId(
428 browser()->tab_strip_model()->GetWebContentsAt(0));
429
430 web_ui()->ClearTrackedCalls();
431 browser()->tab_strip_model()->GetWebContentsAt(0)->Close();
432
433 const content::TestWebUI::CallData& call_data =
434 *web_ui()->call_data().front();
435 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
436 EXPECT_EQ("tab-removed", call_data.arg1()->GetString());
437 EXPECT_EQ(expected_tab_id, call_data.arg2()->GetInt());
438 }
439
TEST_F(TabStripUIHandlerTest,TabMoved)440 TEST_F(TabStripUIHandlerTest, TabMoved) {
441 AddTab(browser(), GURL("http://foo"));
442 AddTab(browser(), GURL("http://foo"));
443
444 int from_index = 0;
445 int expected_to_index = 1;
446 int expected_tab_id = extensions::ExtensionTabUtil::GetTabId(
447 browser()->tab_strip_model()->GetWebContentsAt(from_index));
448
449 browser()->tab_strip_model()->MoveWebContentsAt(from_index, expected_to_index,
450 false);
451
452 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
453 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
454 EXPECT_EQ("tab-moved", call_data.arg1()->GetString());
455 EXPECT_EQ(expected_tab_id, call_data.arg2()->GetInt());
456 EXPECT_EQ(expected_to_index, call_data.arg3()->GetInt());
457 EXPECT_EQ(false, call_data.arg4()->GetBool());
458 }
459
TEST_F(TabStripUIHandlerTest,TabMovedAndPinned)460 TEST_F(TabStripUIHandlerTest, TabMovedAndPinned) {
461 AddTab(browser(), GURL("http://foo"));
462 AddTab(browser(), GURL("http://foo"));
463 web_ui()->ClearTrackedCalls();
464
465 int from_index = 1;
466 int expected_to_index = 0;
467 int expected_tab_id = extensions::ExtensionTabUtil::GetTabId(
468 browser()->tab_strip_model()->GetWebContentsAt(from_index));
469
470 browser()->tab_strip_model()->SetTabPinned(from_index, true);
471
472 const content::TestWebUI::CallData& moved_event =
473 *web_ui()->call_data().front();
474 EXPECT_EQ("cr.webUIListenerCallback", moved_event.function_name());
475 EXPECT_EQ("tab-moved", moved_event.arg1()->GetString());
476 EXPECT_EQ(expected_tab_id, moved_event.arg2()->GetInt());
477 EXPECT_EQ(expected_to_index, moved_event.arg3()->GetInt());
478 EXPECT_EQ(true, moved_event.arg4()->GetBool());
479
480 const content::TestWebUI::CallData& updated_event =
481 *web_ui()->call_data().back();
482 EXPECT_EQ("cr.webUIListenerCallback", updated_event.function_name());
483 EXPECT_EQ("tab-updated", updated_event.arg1()->GetString());
484 const base::DictionaryValue* updated_data;
485 ASSERT_TRUE(updated_event.arg2()->GetAsDictionary(&updated_data));
486 bool pinned;
487 ASSERT_TRUE(updated_data->GetBoolean("pinned", &pinned));
488 ASSERT_TRUE(pinned);
489 }
490
TEST_F(TabStripUIHandlerTest,TabReplaced)491 TEST_F(TabStripUIHandlerTest, TabReplaced) {
492 AddTab(browser(), GURL("http://foo"));
493 int expected_previous_id = extensions::ExtensionTabUtil::GetTabId(
494 browser()->tab_strip_model()->GetWebContentsAt(0));
495
496 web_ui()->ClearTrackedCalls();
497 browser()->tab_strip_model()->ReplaceWebContentsAt(
498 0, content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
499 int expected_new_id = extensions::ExtensionTabUtil::GetTabId(
500 browser()->tab_strip_model()->GetWebContentsAt(0));
501
502 const content::TestWebUI::CallData& call_data =
503 *web_ui()->call_data().front();
504 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
505 EXPECT_EQ("tab-replaced", call_data.arg1()->GetString());
506 ASSERT_EQ(expected_previous_id, call_data.arg2()->GetInt());
507 ASSERT_EQ(expected_new_id, call_data.arg3()->GetInt());
508 }
509
TEST_F(TabStripUIHandlerTest,TabActivated)510 TEST_F(TabStripUIHandlerTest, TabActivated) {
511 AddTab(browser(), GURL("http://foo"));
512 AddTab(browser(), GURL("http://foo"));
513 AddTab(browser(), GURL("http://foo"));
514
515 web_ui()->ClearTrackedCalls();
516 browser()->tab_strip_model()->ActivateTabAt(1);
517
518 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
519 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
520 EXPECT_EQ("tab-active-changed", call_data.arg1()->GetString());
521 EXPECT_EQ(extensions::ExtensionTabUtil::GetTabId(
522 browser()->tab_strip_model()->GetWebContentsAt(1)),
523 call_data.arg2()->GetInt());
524 }
525
TEST_F(TabStripUIHandlerTest,UngroupTab)526 TEST_F(TabStripUIHandlerTest, UngroupTab) {
527 // Add a tab inside of a group.
528 AddTab(browser(), GURL("http://foo"));
529 browser()->tab_strip_model()->AddToNewGroup({0});
530
531 // Add another tab at index 1, and try to group it.
532 base::ListValue args;
533 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
534 browser()->tab_strip_model()->GetWebContentsAt(0)));
535 handler()->HandleUngroupTab(&args);
536
537 ASSERT_FALSE(browser()->tab_strip_model()->GetTabGroupForTab(0).has_value());
538 }
539
TEST_F(TabStripUIHandlerTest,CloseTab)540 TEST_F(TabStripUIHandlerTest, CloseTab) {
541 AddTab(browser(), GURL("http://foo"));
542 AddTab(browser(), GURL("http://bar"));
543
544 base::ListValue args;
545 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
546 browser()->tab_strip_model()->GetWebContentsAt(0)));
547 args.AppendBoolean(false); // If the tab is closed by swipe.
548 handler()->HandleCloseTab(&args);
549
550 ASSERT_EQ(1, browser()->tab_strip_model()->GetTabCount());
551 }
552
TEST_F(TabStripUIHandlerTest,RemoveTabIfInvalidContextMenu)553 TEST_F(TabStripUIHandlerTest, RemoveTabIfInvalidContextMenu) {
554 AddTab(browser(), GURL("http://foo"));
555
556 std::unique_ptr<BrowserWindow> new_window(CreateBrowserWindow());
557 std::unique_ptr<Browser> new_browser =
558 CreateBrowser(profile(), browser()->type(), false, new_window.get());
559 AddTab(new_browser.get(), GURL("http://bar"));
560
561 web_ui()->ClearTrackedCalls();
562
563 base::ListValue args;
564 args.AppendInteger(extensions::ExtensionTabUtil::GetTabId(
565 new_browser->tab_strip_model()->GetWebContentsAt(0)));
566 args.AppendDouble(50);
567 args.AppendDouble(100);
568 handler()->HandleShowTabContextMenu(&args);
569
570 const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
571 EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
572 EXPECT_EQ("tab-removed", call_data.arg1()->GetString());
573 EXPECT_EQ(extensions::ExtensionTabUtil::GetTabId(
574 new_browser->tab_strip_model()->GetWebContentsAt(0)),
575 call_data.arg2()->GetInt());
576
577 // Close all tabs before destructing.
578 new_browser.get()->tab_strip_model()->CloseAllTabs();
579 }
580