From 820ff6653c15472ed15ee7a2ccee02539fffa6b9 Mon Sep 17 00:00:00 2001 From: Jay Harris Date: Thu, 16 Jan 2025 15:50:27 +1300 Subject: [PATCH] Working TabInformer --- browser/sources.gni | 1 + browser/tab_informer/BUILD.gn | 8 + browser/tab_informer/tab_informer.cc | 68 ++++++ browser/tab_informer/tab_informer.h | 36 +++ .../tab_informer_service_factory.cc | 2 + .../tab_informer_service_factory.h | 7 + browser/ui/BUILD.gn | 3 +- browser/ui/ai_chat/BUILD.gn | 23 -- .../ui/ai_chat/tab_informer_browsertest.cc | 211 ------------------ .../browser/ui/tabs/public/tab_features.h | 17 +- .../chrome/browser/ui/tabs/tab_features.cc | 4 + components/ai_chat/core/common/mojom/BUILD.gn | 4 +- .../ai_chat/resources/page/api/index.ts | 2 + components/tab_informer/browser/BUILD.gn | 1 + components/tab_informer/browser/DEPS | 3 + .../browser/tab_informer_service.cc | 29 +-- .../browser/tab_informer_service.h | 5 +- .../browser/tab_informer_service_unittest.cc | 146 ++++++++++++ components/tab_informer/common/BUILD.gn | 4 +- test/BUILD.gn | 2 +- 20 files changed, 303 insertions(+), 273 deletions(-) create mode 100644 browser/tab_informer/tab_informer.cc create mode 100644 browser/tab_informer/tab_informer.h delete mode 100644 browser/ui/ai_chat/tab_informer_browsertest.cc create mode 100644 components/tab_informer/browser/DEPS diff --git a/browser/sources.gni b/browser/sources.gni index d0c67b924f6a..e2ab279a4804 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -135,6 +135,7 @@ brave_chrome_browser_deps = [ "//brave/browser/ntp_background", "//brave/browser/skus", "//brave/browser/sync", + "//brave/browser/tab_informer", "//brave/browser/themes", "//brave/browser/ui", "//brave/browser/ui/webui/ads_internals", diff --git a/browser/tab_informer/BUILD.gn b/browser/tab_informer/BUILD.gn index 847efa771234..4b767e78cc34 100644 --- a/browser/tab_informer/BUILD.gn +++ b/browser/tab_informer/BUILD.gn @@ -1,11 +1,19 @@ +# Copyright (c) 2025 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + static_library("tab_informer") { sources = [ + "tab_informer.cc", + "tab_informer.h", "tab_informer_service_factory.cc", "tab_informer_service_factory.h", ] deps = [ "//brave/components/tab_informer/browser", + "//chrome/browser/ui/tabs:tab_model", "//components/keyed_service/content", "//content/public/browser", ] diff --git a/browser/tab_informer/tab_informer.cc b/browser/tab_informer/tab_informer.cc new file mode 100644 index 000000000000..0a0c0c7af052 --- /dev/null +++ b/browser/tab_informer/tab_informer.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "brave/browser/tab_informer/tab_informer.h" + +#include + +#include "base/strings/utf_string_conversions.h" +#include "brave/browser/tab_informer/tab_informer_service_factory.h" +#include "brave/components/tab_informer/browser/tab_informer_service.h" +#include "brave/components/tab_informer/common/tab_informer.mojom.h" +#include "chrome/browser/ui/tabs/tab_model.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents_observer.h" + +namespace tab_informer { + +namespace { + +mojom::TabPtr FromWebContents(content::WebContents* web_contents) { + auto tab = mojom::Tab::New(); + tab->content_id = + web_contents->GetController().GetVisibleEntry()->GetUniqueID(); + tab->title = base::UTF16ToUTF8(web_contents->GetTitle()); + tab->url = web_contents->GetLastCommittedURL(); + return tab; +} + +} // namespace + +TabInformer::TabInformer(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + tab_id_(tabs::TabModel::GetFromContents(web_contents)->GetTabHandle()) {} + +TabInformer::~TabInformer() { + auto* service = TabInformerServiceFactory::GetForBrowserContext( + web_contents()->GetBrowserContext()); + if (!service) { + return; + } + + service->UpdateTab(tab_id_, nullptr); +} + +void TabInformer::TitleWasSet(content::NavigationEntry* entry) { + UpdateTab(); +} + +void TabInformer::PrimaryPageChanged(content::Page& page) { + UpdateTab(); +} + +void TabInformer::UpdateTab() { + auto* service = TabInformerServiceFactory::GetForBrowserContext( + web_contents()->GetBrowserContext()); + if (!service) { + return; + } + + auto tab = FromWebContents(web_contents()); + tab->id = tab_id_; + service->UpdateTab(tab_id_, std::move(tab)); +} + +} // namespace tab_informer diff --git a/browser/tab_informer/tab_informer.h b/browser/tab_informer/tab_informer.h new file mode 100644 index 000000000000..11332427e66b --- /dev/null +++ b/browser/tab_informer/tab_informer.h @@ -0,0 +1,36 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_BROWSER_TAB_INFORMER_TAB_INFORMER_H_ +#define BRAVE_BROWSER_TAB_INFORMER_TAB_INFORMER_H_ + +#include "content/public/browser/web_contents_observer.h" + +namespace content { +class NavigationEntry; +} + +namespace tab_informer { + +class TabInformer : public content::WebContentsObserver { + public: + explicit TabInformer(content::WebContents* contents); + ~TabInformer() override; + + TabInformer(const TabInformer&) = delete; + TabInformer& operator=(const TabInformer&) = delete; + + void PrimaryPageChanged(content::Page& page) override; + void TitleWasSet(content::NavigationEntry* entry) override; + + private: + int32_t tab_id_ = 0; + + void UpdateTab(); +}; + +} // namespace tab_informer + +#endif // BRAVE_BROWSER_TAB_INFORMER_TAB_INFORMER_H_ diff --git a/browser/tab_informer/tab_informer_service_factory.cc b/browser/tab_informer/tab_informer_service_factory.cc index 2c127565c170..088e1ef3be9b 100644 --- a/browser/tab_informer/tab_informer_service_factory.cc +++ b/browser/tab_informer/tab_informer_service_factory.cc @@ -5,6 +5,8 @@ #include "brave/browser/tab_informer/tab_informer_service_factory.h" +#include + #include "brave/components/tab_informer/browser/tab_informer_service.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" diff --git a/browser/tab_informer/tab_informer_service_factory.h b/browser/tab_informer/tab_informer_service_factory.h index 8d4d736af673..e16afa83adf6 100644 --- a/browser/tab_informer/tab_informer_service_factory.h +++ b/browser/tab_informer/tab_informer_service_factory.h @@ -1,6 +1,13 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + #ifndef BRAVE_BROWSER_TAB_INFORMER_TAB_INFORMER_SERVICE_FACTORY_H_ #define BRAVE_BROWSER_TAB_INFORMER_TAB_INFORMER_SERVICE_FACTORY_H_ +#include + #include "base/no_destructor.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index ae9082ce6e0a..8c389a600768 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -50,12 +50,13 @@ source_set("ui") { deps = [ "//brave/browser/ai_chat", - "//brave/browser/tab_informer", "//brave/browser/brave_adaptive_captcha", "//brave/browser/brave_ads", "//brave/browser/brave_rewards", "//brave/browser/skus", + "//brave/browser/tab_informer", "//brave/components/brave_shields/core/browser", + "//brave/components/tab_informer/browser", "//brave/components/webcompat/core/common", "//chrome/browser/ui:browser_navigator_params_headers", ] diff --git a/browser/ui/ai_chat/BUILD.gn b/browser/ui/ai_chat/BUILD.gn index 429b16e73a3a..08fe8f927b1c 100644 --- a/browser/ui/ai_chat/BUILD.gn +++ b/browser/ui/ai_chat/BUILD.gn @@ -35,26 +35,3 @@ source_set("unit_tests") { "//testing/gtest:gtest", ] } - -source_set("browser_tests") { - testonly = true - - sources = [] - - if (!is_android) { - sources += [ "tab_informer_browsertest.cc" ] - } - - defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] - - deps = [ - "//base/test:test_support", - "//brave/components/ai_chat/content/browser", - "//brave/components/ai_chat/core/browser", - "//brave/components/ai_chat/core/common", - "//brave/components/ai_chat/core/common/mojom", - "//chrome/browser/ui", - "//chrome/test:test_support", - "//testing/gtest", - ] -} diff --git a/browser/ui/ai_chat/tab_informer_browsertest.cc b/browser/ui/ai_chat/tab_informer_browsertest.cc deleted file mode 100644 index ab51d99668e2..000000000000 --- a/browser/ui/ai_chat/tab_informer_browsertest.cc +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2025 The Brave Authors. All rights reserved. -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "brave/browser/ui/ai_chat/tab_informer.h" - -#include -#include - -#include "base/functional/callback_forward.h" -#include "base/run_loop.h" -#include "base/test/bind.h" -#include "brave/components/ai_chat/core/common/mojom/tab_informer.mojom-forward.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_tabstrip.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "content/public/test/browser_test.h" -#include "mojo/public/cpp/bindings/pending_remote.h" -#include "mojo/public/cpp/bindings/self_owned_receiver.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace ai_chat { - -namespace { - -using Predicate = - base::RepeatingCallback&)>; - -class UpdateTracker : public mojom::TabListener { - public: - UpdateTracker() = default; - ~UpdateTracker() override = default; - - const std::vector& GetLastWindows() const { - CHECK(last_windows_.has_value()); - return last_windows_.value(); - } - - void WaitFor(Predicate predicate) { - if (last_windows_ && predicate.Run(last_windows_.value())) { - return; - } - - base::RunLoop waiter; - on_change_ = base::BindLambdaForTesting([&]() { - if (!last_windows_) { - return; - } - if (predicate.Run(last_windows_.value())) { - waiter.Quit(); - on_change_.Reset(); - } - }); - - waiter.Run(); - } - - private: - void TabsChanged(std::vector windows) override { - last_windows_ = std::move(windows); - - if (!on_change_.is_null()) { - on_change_.Run(); - } - } - - base::RepeatingClosure on_change_; - std::optional> last_windows_; - std::unique_ptr run_loop_; -}; - -} // namespace - -class TabInformerBrowserTest : public InProcessBrowserTest { - public: - TabInformerBrowserTest() = default; - ~TabInformerBrowserTest() override = default; - - void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); - - mojo::PendingReceiver receiver; - tab_informer_ = std::make_unique( - std::move(receiver), - browser()->tab_strip_model()->GetActiveWebContents()); - - auto listener = std::make_unique(); - tracker_ = listener.get(); - - mojo::PendingRemote pending_remote; - mojo::MakeSelfOwnedReceiver( - std::move(listener), pending_remote.InitWithNewPipeAndPassReceiver()); - tab_informer_->AddListener(std::move(pending_remote)); - } - - void TearDownOnMainThread() override { - tracker_ = nullptr; - tab_informer_.reset(); - - InProcessBrowserTest::SetUpOnMainThread(); - } - - const std::vector& GetLastWindows() const { - return tracker_->GetLastWindows(); - } - - void WaitFor(Predicate predicate) { tracker_->WaitFor(std::move(predicate)); } - - protected: - void AppendTab(std::string url) { - chrome::AddTabAt(browser(), GURL(url), -1, false); - } - - std::unique_ptr tab_informer_; - raw_ptr tracker_; -}; - -IN_PROC_BROWSER_TEST_F(TabInformerBrowserTest, GetsInitialTabs) { - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 1; - })); - - const auto& windows = GetLastWindows(); - - // Should be have no tabs because we exclude the owner tab - EXPECT_EQ(windows[0]->tabs.size(), 0u); -} - -IN_PROC_BROWSER_TEST_F(TabInformerBrowserTest, TabsChange) { - AppendTab("https://google.com"); - AppendTab("https://brave.com"); - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 1 && windows.at(0)->tabs.size() == 2; - })); - - { - const auto& windows = GetLastWindows(); - EXPECT_EQ(windows[0]->tabs[0]->url, GURL("https://google.com")); - EXPECT_EQ(windows[0]->tabs[1]->url, GURL("https://brave.com")); - } - - // Close all tabs but the one we're bound to - chrome::CloseOtherTabs(browser()); - - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 1 && windows.at(0)->tabs.size() == 0; - })); - - { - const auto& updated_windows = GetLastWindows(); - EXPECT_EQ(updated_windows[0]->tabs.size(), 0u); - } -} - -IN_PROC_BROWSER_TEST_F(TabInformerBrowserTest, MultipleWindows) { - AppendTab("https://topos.nz"); - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 1 && windows.at(0)->tabs.size() == 1; - })); - - { - const auto& windows = GetLastWindows(); - EXPECT_EQ(windows[0]->tabs[0]->url, GURL("https://topos.nz")); - } - - chrome::NewWindow(browser()); - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 2 && windows.at(0)->tabs.size() == 1 && - windows.at(1)->tabs.size() == 0; - })); - - AppendTab("https://brave.com"); - AppendTab("https://readr.nz"); - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - return windows.size() == 2 && windows.at(0)->tabs.size() == 3 && - windows.at(1)->tabs.size() == 0; - })); - - chrome::MoveTabsToNewWindow(browser(), {2, 3}); - WaitFor(base::BindLambdaForTesting( - [](const std::vector& windows) { - LOG(ERROR) << "Window 0: " << windows.at(0)->tabs.size() - << ", Window 1: " << windows.at(1)->tabs.size(); - return windows.size() == 3 && windows.at(0)->tabs.size() == 1 && - windows.at(1)->tabs.size() == 0 && - windows.at(2)->tabs.size() == 2; - })); - - { - const auto& windows = GetLastWindows(); - - // Just topos.nz - first tab is bound to the TabInformer - EXPECT_EQ(windows[0]->tabs[0]->url, GURL("https://topos.nz")); - - // readr.nz, brave.com - EXPECT_EQ(windows[2]->tabs[1]->url, GURL("https://readr.nz")); - EXPECT_EQ(windows[2]->tabs[0]->url, GURL("https://brave.com")); - } -} - -} // namespace ai_chat diff --git a/chromium_src/chrome/browser/ui/tabs/public/tab_features.h b/chromium_src/chrome/browser/ui/tabs/public/tab_features.h index f6e6a39aad69..fab7f8ff2af6 100644 --- a/chromium_src/chrome/browser/ui/tabs/public/tab_features.h +++ b/chromium_src/chrome/browser/ui/tabs/public/tab_features.h @@ -8,9 +8,20 @@ #include "base/callback_list.h" -#define Init(...) \ - Init_ChromiumImpl(__VA_ARGS__); \ - virtual void Init(__VA_ARGS__) +namespace tab_informer { +class TabInformer; +} + +#define Init(...) \ + Init_ChromiumImpl(__VA_ARGS__); \ + virtual void Init(__VA_ARGS__); \ + \ + tab_informer::TabInformer* tab_informer() { \ + return tab_informer_.get(); \ + } \ + \ + private: \ + std::unique_ptr tab_informer_ #include "src/chrome/browser/ui/tabs/public/tab_features.h" // IWYU pragma: export #undef Init diff --git a/chromium_src/chrome/browser/ui/tabs/tab_features.cc b/chromium_src/chrome/browser/ui/tabs/tab_features.cc index 28cfff0d23bc..a82087f636d5 100644 --- a/chromium_src/chrome/browser/ui/tabs/tab_features.cc +++ b/chromium_src/chrome/browser/ui/tabs/tab_features.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/tabs/public/tab_features.h" +#include "brave/browser/tab_informer/tab_informer.h" #include "brave/browser/ui/side_panel/brave_side_panel_utils.h" #define Init Init_ChromiumImpl @@ -19,6 +20,9 @@ void TabFeatures::Init(TabInterface& tab, Profile* profile) { CHECK(side_panel_registry_.get()); brave::RegisterContextualSidePanel(side_panel_registry_.get(), tab.GetContents()); + + tab_informer_ = + std::make_unique(tab.GetContents()); } } // namespace tabs diff --git a/components/ai_chat/core/common/mojom/BUILD.gn b/components/ai_chat/core/common/mojom/BUILD.gn index 3b0f2bf90bda..8234ca6f30fe 100644 --- a/components/ai_chat/core/common/mojom/BUILD.gn +++ b/components/ai_chat/core/common/mojom/BUILD.gn @@ -25,7 +25,5 @@ mojom_component("mojom") { "//url/mojom:url_mojom_gurl", ] - public_deps = [ - "//brave/components/tab_informer/common:mojom", - ] + public_deps = [ "//brave/components/tab_informer/common:mojom" ] } diff --git a/components/ai_chat/resources/page/api/index.ts b/components/ai_chat/resources/page/api/index.ts index a56697bbd67b..16946bf0055a 100644 --- a/components/ai_chat/resources/page/api/index.ts +++ b/components/ai_chat/resources/page/api/index.ts @@ -98,6 +98,8 @@ class PageAPI extends API { this.setPartialState({ tabs }) + + console.log('tabs', tabs) }) } diff --git a/components/tab_informer/browser/BUILD.gn b/components/tab_informer/browser/BUILD.gn index 32edb6f819e7..f42a34e30e05 100644 --- a/components/tab_informer/browser/BUILD.gn +++ b/components/tab_informer/browser/BUILD.gn @@ -27,6 +27,7 @@ source_set("unit_tests") { sources = [ "tab_informer_service_unittest.cc" ] deps = [ ":browser", + "//base/test:test_support", "//testing/gtest", ] } diff --git a/components/tab_informer/browser/DEPS b/components/tab_informer/browser/DEPS new file mode 100644 index 000000000000..fe7bb13183f7 --- /dev/null +++ b/components/tab_informer/browser/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+content/public", +] diff --git a/components/tab_informer/browser/tab_informer_service.cc b/components/tab_informer/browser/tab_informer_service.cc index 6242f3044988..f0651acc1ffe 100644 --- a/components/tab_informer/browser/tab_informer_service.cc +++ b/components/tab_informer/browser/tab_informer_service.cc @@ -9,46 +9,27 @@ #include #include -#include "base/containers/contains.h" -#include "base/strings/utf_string_conversions.h" -#include "brave/components/ai_chat/core/common/constants.h" #include "brave/components/tab_informer/common/tab_informer.mojom.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" -#include "chrome/browser/ui/browser_list.h" -#include "chrome/browser/ui/tabs/tab_model.h" -#include "content/public/browser/navigation_entry.h" -#include "content/public/browser/web_contents.h" -#include "mojo/public/cpp/bindings/receiver.h" namespace tab_informer { TabInformerService::TabInformerService() = default; TabInformerService::~TabInformerService() = default; -content::WebContents* TabInformerService::GetFromTab( - const mojom::TabPtr& mojom_tab) { - const tabs::TabHandle handle = tabs::TabHandle(mojom_tab->id); - tabs::TabInterface* const tab = handle.Get(); - if (!tab) { - return nullptr; - } - return tab->GetContents(); -} - void TabInformerService::Bind( mojo::PendingReceiver receiver) { receivers_.Add(this, std::move(receiver)); } -void TabInformerService::UpdateTab(int tab_id, mojom::TabPtr tab) { +void TabInformerService::UpdateTab(int32_t tab_id, mojom::TabPtr tab) { auto it = base::ranges::find_if( tabs_, [tab_id](const auto& tab) { return tab->id == tab_id; }); // New tab we haven't heard about - if (it == tabs_.end() && tab) { - tabs_.push_back(std::move(tab)); + if (it == tabs_.end()) { + if (tab) { + tabs_.push_back(std::move(tab)); + } } else if (!tab) { // Removal of an existing tab tabs_.erase(it); diff --git a/components/tab_informer/browser/tab_informer_service.h b/components/tab_informer/browser/tab_informer_service.h index af3e74dc36fa..84f8c1821372 100644 --- a/components/tab_informer/browser/tab_informer_service.h +++ b/components/tab_informer/browser/tab_informer_service.h @@ -10,7 +10,6 @@ #include "base/memory/raw_ptr.h" #include "brave/components/tab_informer/common/tab_informer.mojom.h" -#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/web_contents.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -26,11 +25,9 @@ class COMPONENT_EXPORT(TAB_INFORMER_BROWSER) TabInformerService TabInformerService(); ~TabInformerService() override; - static content::WebContents* GetFromTab(const mojom::TabPtr& tab); - // Updates the tab with the given |tab_id|. If |tab| is nullptr the tab will // be removed. - void UpdateTab(int tab_id, mojom::TabPtr tab); + void UpdateTab(int32_t tab_id, mojom::TabPtr tab); void Bind(mojo::PendingReceiver receiver); diff --git a/components/tab_informer/browser/tab_informer_service_unittest.cc b/components/tab_informer/browser/tab_informer_service_unittest.cc index e69de29bb2d1..0c5576222b97 100644 --- a/components/tab_informer/browser/tab_informer_service_unittest.cc +++ b/components/tab_informer/browser/tab_informer_service_unittest.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "brave/components/tab_informer/browser/tab_informer_service.h" + +#include +#include +#include + +#include "base/task/current_thread.h" +#include "base/test/task_environment.h" +#include "brave/components/tab_informer/common/tab_informer.mojom-forward.h" +#include "brave/components/tab_informer/common/tab_informer.mojom.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "testing/gtest/include/gtest/gtest.h" +namespace tab_informer { + +namespace { +class TabListener : public mojom::TabListener { + public: + TabListener() = default; + ~TabListener() override = default; + + TabListener(const TabListener&) = delete; + TabListener& operator=(const TabListener&) = delete; + + TabListener(TabListener&&) = default; + TabListener& operator=(TabListener&&) = default; + + // mojom::TabListener + void TabsChanged(std::vector tabs) override { + this->last_tabs_ = std::move(tabs); + } + + const std::vector& last_tabs() const { return last_tabs_; } + + private: + std::vector last_tabs_; +}; +} // namespace + +class TabInformerServiceTest : public testing::Test { + public: + TabInformerServiceTest() { + mojo::PendingRemote pending_remote; + auto listener = std::make_unique(); + listener_ = listener.get(); + + auto receiver = mojo::MakeSelfOwnedReceiver( + std::move(listener), pending_remote.InitWithNewPipeAndPassReceiver()); + service_.AddListener(std::move(pending_remote)); + } + ~TabInformerServiceTest() override = default; + + const std::vector& last_tabs() const { + return listener_->last_tabs(); + } + + void UpdateTab(mojom::TabPtr tab) { + service_.UpdateTab(tab->id, std::move(tab)); + } + + void DeleteTab(int id) { service_.UpdateTab(id, nullptr); } + + private: + base::test::SingleThreadTaskEnvironment task_environment_; + + TabInformerService service_; + raw_ptr listener_; +}; + +TEST_F(TabInformerServiceTest, TabsCanBeAdded) { + mojom::TabPtr tab1 = mojom::Tab::New(); + tab1->title = "One"; + tab1->url = GURL("https://one.com"); + tab1->id = 1; + UpdateTab(std::move(tab1)); + + mojom::TabPtr tab2 = mojom::Tab::New(); + tab2->title = "Two"; + tab2->url = GURL("https://two.com"); + tab2->id = 2; + UpdateTab(std::move(tab2)); + + base::test::RunUntil([&]() { return last_tabs().size() == 2; }); + + EXPECT_EQ(last_tabs()[0]->title, "One"); + EXPECT_EQ(last_tabs()[0]->url, GURL("https://one.com")); + EXPECT_EQ(last_tabs()[0]->id, 1); + + EXPECT_EQ(last_tabs()[1]->title, "Two"); + EXPECT_EQ(last_tabs()[1]->url, GURL("https://two.com")); + EXPECT_EQ(last_tabs()[1]->id, 2); +} + +TEST_F(TabInformerServiceTest, TabsCanBeRemoved) { + mojom::TabPtr tab1 = mojom::Tab::New(); + tab1->title = "One"; + tab1->url = GURL("https://one.com"); + tab1->id = 1; + UpdateTab(std::move(tab1)); + + mojom::TabPtr tab2 = mojom::Tab::New(); + tab2->title = "Two"; + tab2->url = GURL("https://two.com"); + tab2->id = 2; + UpdateTab(std::move(tab2)); + + DeleteTab(1); + + base::test::RunUntil([&]() { return last_tabs().size() == 1; }); + EXPECT_EQ(last_tabs()[0]->title, "Two"); + EXPECT_EQ(last_tabs()[0]->url, GURL("https://two.com")); + EXPECT_EQ(last_tabs()[0]->id, 2); +} + +TEST_F(TabInformerServiceTest, TabsCanBeUpdated) { + mojom::TabPtr tab1 = mojom::Tab::New(); + tab1->title = "One"; + tab1->url = GURL("https://one.com"); + tab1->id = 1; + UpdateTab(tab1->Clone()); + + mojom::TabPtr tab2 = mojom::Tab::New(); + tab2->title = "Two"; + tab2->url = GURL("https://two.com"); + tab2->id = 2; + UpdateTab(std::move(tab2)); + + tab1->title = "One Updated"; + UpdateTab(tab1->Clone()); + + base::test::RunUntil([&]() { return last_tabs().size() == 2; }); + + EXPECT_EQ(last_tabs()[0]->title, "One Updated"); + EXPECT_EQ(last_tabs()[0]->url, GURL("https://one.com")); + EXPECT_EQ(last_tabs()[0]->id, 1); + + EXPECT_EQ(last_tabs()[1]->title, "Two"); + EXPECT_EQ(last_tabs()[1]->url, GURL("https://two.com")); + EXPECT_EQ(last_tabs()[1]->id, 2); +} + +} // namespace tab_informer diff --git a/components/tab_informer/common/BUILD.gn b/components/tab_informer/common/BUILD.gn index 18f22d15778e..e64cc40d8826 100644 --- a/components/tab_informer/common/BUILD.gn +++ b/components/tab_informer/common/BUILD.gn @@ -13,9 +13,7 @@ mojom_component("mojom") { generate_legacy_js_bindings = true webui_module_path = "/" - sources = [ - "tab_informer.mojom", - ] + sources = [ "tab_informer.mojom" ] public_deps = [ "//mojo/public/mojom/base", diff --git a/test/BUILD.gn b/test/BUILD.gn index 972431215b12..fbf2d965953f 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -260,6 +260,7 @@ test("brave_unit_tests") { "//brave/components/skus/renderer:unit_tests", "//brave/components/sync/engine:unit_tests", "//brave/components/sync/service:unit_tests", + "//brave/components/tab_informer/browser:unit_tests", "//brave/components/time_period_storage", "//brave/components/tor/buildflags", "//brave/components/url_sanitizer/browser:unittests", @@ -866,7 +867,6 @@ test("brave_browser_tests") { "//brave/browser/test:browser_tests", "//brave/browser/themes", "//brave/browser/ui:browser_tests", - "//brave/browser/ui/ai_chat:browser_tests", "//brave/browser/ui/tabs/test:browser_tests", "//brave/browser/ui/views/tabs:browser_tests", "//brave/browser/ui/webui:browser_tests",