Commit 4112fcf1 authored by John Lee's avatar John Lee Committed by Commit Bot

WebUI Tab Strip: Send events when tabs are added or removed into groups

This CL updates the TabStripUIHandler to send events ot the WebUI for
when a tab's group state is changed. This CL also sets up the first
unit test for the handler.

This CL does not add the front-end changes, or the events for changes
to a tab group's visuals or indexes.

Bug: 1027373
Change-Id: I7c72dbbd189a308c8565c9ecd29e76007fe60c93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1999225
Commit-Queue: John Lee <johntlee@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#731733}
parent 43735879
...@@ -51,6 +51,7 @@ export const TabAlertState = { ...@@ -51,6 +51,7 @@ export const TabAlertState = {
* blocked: boolean, * blocked: boolean,
* crashed: boolean, * crashed: boolean,
* favIconUrl: (string|undefined), * favIconUrl: (string|undefined),
* groupId: (string|undefined),
* id: number, * id: number,
* index: number, * index: number,
* isDefaultFavicon: boolean, * isDefaultFavicon: boolean,
......
...@@ -15,11 +15,14 @@ ...@@ -15,11 +15,14 @@
#include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_group.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_renderer_data.h" #include "chrome/browser/ui/tabs/tab_renderer_data.h"
#include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h" #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_metrics.h" #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_metrics.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/tab_groups/tab_group_id.h"
#include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkStream.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/simple_menu_model.h" #include "ui/base/models/simple_menu_model.h"
...@@ -212,6 +215,45 @@ void TabStripUIHandler::OnJavascriptAllowed() { ...@@ -212,6 +215,45 @@ void TabStripUIHandler::OnJavascriptAllowed() {
} }
// TabStripModelObserver: // TabStripModelObserver:
void TabStripUIHandler::OnTabGroupChanged(const TabGroupChange& change) {
switch (change.type) {
case TabGroupChange::kCreated:
case TabGroupChange::kContentsChanged: {
// TabGroupChange::kCreated events are unnecessary as the front-end will
// assume a group was created if there is a tab-group-state-changed event
// with a new group ID. TabGroupChange::kContentsChanged events are
// handled by TabGroupStateChanged.
break;
}
case TabGroupChange::kVisualsChanged: {
// TODO(johntlee): Send visuals to front-end.
break;
}
case TabGroupChange::kClosed: {
FireWebUIListener("tab-group-closed",
base::Value(change.group.ToString()));
break;
}
}
}
void TabStripUIHandler::TabGroupedStateChanged(
base::Optional<tab_groups::TabGroupId> group,
int index) {
int tab_id = extensions::ExtensionTabUtil::GetTabId(
browser_->tab_strip_model()->GetWebContentsAt(index));
if (group.has_value()) {
FireWebUIListener("tab-group-state-changed", base::Value(tab_id),
base::Value(index),
base::Value(group.value().ToString()));
} else {
FireWebUIListener("tab-group-state-changed", base::Value(tab_id),
base::Value(index));
}
}
void TabStripUIHandler::OnTabStripModelChanged( void TabStripUIHandler::OnTabStripModelChanged(
TabStripModel* tab_strip_model, TabStripModel* tab_strip_model,
const TabStripModelChange& change, const TabStripModelChange& change,
...@@ -342,6 +384,12 @@ base::DictionaryValue TabStripUIHandler::GetTabData( ...@@ -342,6 +384,12 @@ base::DictionaryValue TabStripUIHandler::GetTabData(
tab_data.SetInteger("id", extensions::ExtensionTabUtil::GetTabId(contents)); tab_data.SetInteger("id", extensions::ExtensionTabUtil::GetTabId(contents));
tab_data.SetInteger("index", index); tab_data.SetInteger("index", index);
const base::Optional<tab_groups::TabGroupId> group_id =
browser_->tab_strip_model()->GetTabGroupForTab(index);
if (group_id.has_value()) {
tab_data.SetString("groupId", group_id.value().ToString());
}
TabRendererData tab_renderer_data = TabRendererData tab_renderer_data =
TabRendererData::FromTabInModel(browser_->tab_strip_model(), index); TabRendererData::FromTabInModel(browser_->tab_strip_model(), index);
tab_data.SetBoolean("pinned", tab_renderer_data.pinned); tab_data.SetBoolean("pinned", tab_renderer_data.pinned);
......
...@@ -26,6 +26,9 @@ class TabStripUIHandler : public content::WebUIMessageHandler, ...@@ -26,6 +26,9 @@ class TabStripUIHandler : public content::WebUIMessageHandler,
void NotifyReceivedKeyboardFocus(); void NotifyReceivedKeyboardFocus();
// TabStripModelObserver: // TabStripModelObserver:
void OnTabGroupChanged(const TabGroupChange& change) override;
void TabGroupedStateChanged(base::Optional<tab_groups::TabGroupId> group,
int index) override;
void OnTabStripModelChanged( void OnTabStripModelChanged(
TabStripModel* tab_strip_model, TabStripModel* tab_strip_model,
const TabStripModelChange& change, const TabStripModelChange& change,
......
// Copyright (c) 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.
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_handler.h"
#include <memory>
#include "base/macros.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "components/tab_groups/tab_group_id.h"
#include "content/public/browser/web_ui.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/default_theme_provider.h"
#include "ui/base/theme_provider.h"
#include "ui/gfx/geometry/point.h"
namespace {
class TestTabStripUIHandler : public TabStripUIHandler {
public:
explicit TestTabStripUIHandler(content::WebUI* web_ui,
Browser* browser,
TabStripUIEmbedder* embedder)
: TabStripUIHandler(browser, embedder) {
set_web_ui(web_ui);
}
};
class MockTabStripUIEmbedder : public TabStripUIEmbedder {
public:
MockTabStripUIEmbedder() {}
MOCK_CONST_METHOD0(GetAcceleratorProvider, const ui::AcceleratorProvider*());
MOCK_METHOD0(CloseContainer, void());
MOCK_METHOD2(ShowContextMenuAtPoint,
void(gfx::Point, std::unique_ptr<ui::MenuModel>));
MOCK_METHOD0(GetLayout, TabStripUILayout());
MOCK_METHOD0(GetThemeProvider, const ui::ThemeProvider*());
};
} // namespace
class TabStripUIHandlerTest : public BrowserWithTestWindowTest {
public:
TabStripUIHandlerTest() : web_ui_(new content::TestWebUI) {}
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
handler_ = std::make_unique<TestTabStripUIHandler>(web_ui(), browser(),
&mock_embedder_);
handler()->AllowJavascriptForTesting();
web_ui()->ClearTrackedCalls();
}
TabStripUIHandler* handler() { return handler_.get(); }
content::TestWebUI* web_ui() { return web_ui_.get(); }
protected:
::testing::NiceMock<MockTabStripUIEmbedder> mock_embedder_;
private:
std::unique_ptr<content::TestWebUI> web_ui_;
std::unique_ptr<TestTabStripUIHandler> handler_;
DISALLOW_COPY_AND_ASSIGN(TabStripUIHandlerTest);
};
TEST_F(TabStripUIHandlerTest, GroupClosedEvent) {
AddTab(browser(), GURL("http://foo"));
tab_groups::TabGroupId expected_group_id =
browser()->tab_strip_model()->AddToNewGroup({0});
browser()->tab_strip_model()->RemoveFromGroup({0});
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
std::string event_name;
ASSERT_TRUE(data.arg1()->GetAsString(&event_name));
EXPECT_EQ("tab-group-closed", event_name);
std::string actual_group_id;
ASSERT_TRUE(data.arg2()->GetAsString(&actual_group_id));
EXPECT_EQ(expected_group_id.ToString(), actual_group_id);
}
TEST_F(TabStripUIHandlerTest, GroupStateChangedEvents) {
AddTab(browser(), GURL("http://foo/1"));
AddTab(browser(), GURL("http://foo/2"));
// Add one of the tabs to a group to test for a tab-group-state-changed event.
tab_groups::TabGroupId expected_group_id =
browser()->tab_strip_model()->AddToNewGroup({0, 1});
const content::TestWebUI::CallData& grouped_data =
*web_ui()->call_data().back();
EXPECT_EQ("cr.webUIListenerCallback", grouped_data.function_name());
std::string event_name;
ASSERT_TRUE(grouped_data.arg1()->GetAsString(&event_name));
EXPECT_EQ("tab-group-state-changed", event_name);
int expected_tab_id = extensions::ExtensionTabUtil::GetTabId(
browser()->tab_strip_model()->GetWebContentsAt(1));
int actual_tab_id;
ASSERT_TRUE(grouped_data.arg2()->GetAsInteger(&actual_tab_id));
EXPECT_EQ(expected_tab_id, actual_tab_id);
int index;
ASSERT_TRUE(grouped_data.arg3()->GetAsInteger(&index));
EXPECT_EQ(1, index);
std::string actual_group_id;
ASSERT_TRUE(grouped_data.arg4()->GetAsString(&actual_group_id));
EXPECT_EQ(expected_group_id.ToString(), actual_group_id);
// Remove the tab from the group to test for a tab-group-state-changed event.
browser()->tab_strip_model()->RemoveFromGroup({1});
const content::TestWebUI::CallData& ungrouped_data =
*web_ui()->call_data().back();
EXPECT_EQ("cr.webUIListenerCallback", ungrouped_data.function_name());
ASSERT_TRUE(ungrouped_data.arg1()->GetAsString(&event_name));
EXPECT_EQ("tab-group-state-changed", event_name);
ASSERT_TRUE(ungrouped_data.arg2()->GetAsInteger(&actual_tab_id));
EXPECT_EQ(expected_tab_id, actual_tab_id);
ASSERT_TRUE(ungrouped_data.arg3()->GetAsInteger(&index));
EXPECT_EQ(1, index);
EXPECT_EQ(nullptr, ungrouped_data.arg4());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment