diff --git a/browser/brave_profile_prefs.cc b/browser/brave_profile_prefs.cc index ebf145ef798..a8df8a1eca4 100644 --- a/browser/brave_profile_prefs.cc +++ b/browser/brave_profile_prefs.cc @@ -351,6 +351,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(kNewTabPageShowStats, true); registry->RegisterBooleanPref(kNewTabPageShowRewards, true); registry->RegisterBooleanPref(kNewTabPageShowBraveTalk, true); + registry->RegisterBooleanPref(kBraveTalkDisabledByPolicy, false); registry->RegisterBooleanPref(kNewTabPageHideAllWidgets, false); #if BUILDFLAG(ENABLE_BRAVE_VPN) diff --git a/browser/policy/DEPS b/browser/policy/DEPS new file mode 100644 index 00000000000..528d5e35fac --- /dev/null +++ b/browser/policy/DEPS @@ -0,0 +1,5 @@ +specific_include_rules = { + "brave_talk_policy_unittest.cc": [ + "+components/sync_preferences", + ], +} diff --git a/browser/policy/brave_simple_policy_map.h b/browser/policy/brave_simple_policy_map.h index f4538ac1c52..c3824605c0b 100644 --- a/browser/policy/brave_simple_policy_map.h +++ b/browser/policy/brave_simple_policy_map.h @@ -63,6 +63,8 @@ inline constexpr PolicyToPreferenceMapEntry kBraveSimplePolicyMap[] = { #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) {policy::key::kBraveNewsDisabled, brave_news::prefs::kBraveNewsDisabledByPolicy, base::Value::Type::BOOLEAN}, + {policy::key::kBraveTalkDisabled, kBraveTalkDisabledByPolicy, + base::Value::Type::BOOLEAN}, #endif #if BUILDFLAG(DEPRECATE_IPFS) {policy::key::kIPFSEnabled, ipfs::prefs::kIPFSEnabledByPolicy, diff --git a/browser/policy/brave_talk_policy_unittest.cc b/browser/policy/brave_talk_policy_unittest.cc new file mode 100644 index 00000000000..dfc43e4e5bf --- /dev/null +++ b/browser/policy/brave_talk_policy_unittest.cc @@ -0,0 +1,67 @@ +// 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/constants/pref_names.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "testing/gtest/include/gtest/gtest.h" + +class BraveTalkPolicyTest : public testing::Test { + public: + BraveTalkPolicyTest() { + // Register the Brave Talk policy preference + pref_service_.registry()->RegisterBooleanPref(kBraveTalkDisabledByPolicy, + false); + } + + protected: + void SetBraveTalkDisabledByPolicy(bool value) { + pref_service_.SetManagedPref(kBraveTalkDisabledByPolicy, + base::Value(value)); + } + + sync_preferences::TestingPrefServiceSyncable pref_service_; +}; + +TEST_F(BraveTalkPolicyTest, PolicyDisablesBraveTalk) { + // Initially, policy should not be disabled + EXPECT_FALSE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); + EXPECT_FALSE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); + + // Set policy to disable Brave Talk + SetBraveTalkDisabledByPolicy(true); + + // Test that the policy preference is set correctly + EXPECT_TRUE(pref_service_.FindPreference(kBraveTalkDisabledByPolicy)); + EXPECT_TRUE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); + EXPECT_TRUE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); +} + +TEST_F(BraveTalkPolicyTest, PolicyEnablesBraveTalk) { + // Set policy to explicitly enable Brave Talk + SetBraveTalkDisabledByPolicy(false); + + // Test that the policy preference is set correctly + EXPECT_TRUE(pref_service_.FindPreference(kBraveTalkDisabledByPolicy)); + EXPECT_TRUE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); + EXPECT_FALSE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); +} + +TEST_F(BraveTalkPolicyTest, PolicyChangesAreReflected) { + // Start with policy disabled + SetBraveTalkDisabledByPolicy(false); + EXPECT_FALSE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); + EXPECT_TRUE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); + + // Change policy to enabled + SetBraveTalkDisabledByPolicy(true); + EXPECT_TRUE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); + EXPECT_TRUE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); +} + +TEST_F(BraveTalkPolicyTest, DefaultValueWhenNotManaged) { + // When not managed by policy, should be false by default + EXPECT_FALSE(pref_service_.GetBoolean(kBraveTalkDisabledByPolicy)); + EXPECT_FALSE(pref_service_.IsManagedPreference(kBraveTalkDisabledByPolicy)); +} diff --git a/browser/resources/settings/brave_appearance_page/toolbar.ts b/browser/resources/settings/brave_appearance_page/toolbar.ts index 146937944ff..9dc53a9ea7c 100644 --- a/browser/resources/settings/brave_appearance_page/toolbar.ts +++ b/browser/resources/settings/brave_appearance_page/toolbar.ts @@ -65,7 +65,7 @@ class SettingsBraveAppearanceToolbarElement extends SettingsBraveAppearanceToolb if (!elemToHighlight) { return; } - + const elem = this.shadowRoot?.querySelector(elemToHighlight) if (!elem) { return diff --git a/browser/ui/brave_browser_command_controller.cc b/browser/ui/brave_browser_command_controller.cc index 0a9433e0924..f047fa522db 100644 --- a/browser/ui/brave_browser_command_controller.cc +++ b/browser/ui/brave_browser_command_controller.cc @@ -31,6 +31,7 @@ #include "brave/components/brave_wayback_machine/buildflags/buildflags.h" #include "brave/components/commander/common/buildflags/buildflags.h" #include "brave/components/commands/common/features.h" +#include "brave/components/constants/pref_names.h" #include "brave/components/playlist/common/buildflags/buildflags.h" #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "chrome/app/chrome_command_ids.h" @@ -308,7 +309,9 @@ void BraveBrowserCommandController::InitBraveCommandState() { IDC_CONFIGURE_SHORTCUTS, base::FeatureList::IsEnabled(commands::features::kBraveCommands)); - UpdateCommandEnabled(IDC_SHOW_BRAVE_TALK, true); + UpdateCommandEnabled( + IDC_SHOW_BRAVE_TALK, + !browser_->profile()->GetPrefs()->GetBoolean(kBraveTalkDisabledByPolicy)); UpdateCommandEnabled(IDC_TOGGLE_SHIELDS, true); UpdateCommandEnabled(IDC_TOGGLE_JAVASCRIPT, true); diff --git a/browser/ui/webui/new_tab_page/brave_new_tab_message_handler.cc b/browser/ui/webui/new_tab_page/brave_new_tab_message_handler.cc index 486ebe6815e..1c5c3c1b6ed 100644 --- a/browser/ui/webui/new_tab_page/brave_new_tab_message_handler.cc +++ b/browser/ui/webui/new_tab_page/brave_new_tab_message_handler.cc @@ -92,6 +92,8 @@ base::Value::Dict GetPreferencesDictionary(PrefService* prefs) { prefs->GetBoolean(brave_news::prefs::kBraveNewsDisabledByPolicy)); pref_data.Set("hideAllWidgets", prefs->GetBoolean(kNewTabPageHideAllWidgets)); pref_data.Set("showBraveTalk", prefs->GetBoolean(kNewTabPageShowBraveTalk)); + pref_data.Set("isBraveTalkDisabledByPolicy", + prefs->GetBoolean(kBraveTalkDisabledByPolicy)); #if BUILDFLAG(ENABLE_BRAVE_VPN) pref_data.Set("showBraveVPN", prefs->GetBoolean(kNewTabPageShowBraveVPN)); #endif @@ -312,6 +314,10 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() { kNewTabPageShowBraveTalk, base::BindRepeating(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); + pref_change_registrar_.Add( + kBraveTalkDisabledByPolicy, + base::BindRepeating(&BraveNewTabMessageHandler::OnPreferencesChanged, + base::Unretained(this))); #if BUILDFLAG(ENABLE_BRAVE_VPN) pref_change_registrar_.Add( kNewTabPageShowBraveVPN, diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc index 6d507783ab9..26f22a3c0f2 100644 --- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc +++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc @@ -17,6 +17,7 @@ #include "brave/components/brave_shields/core/common/features.h" #include "brave/components/brave_vpn/common/buildflags/buildflags.h" #include "brave/components/brave_wallet/browser/pref_names.h" +#include "brave/components/constants/pref_names.h" #include "brave/components/constants/url_constants.h" #include "brave/components/constants/webui_url_constants.h" #include "brave/components/containers/buildflags/buildflags.h" @@ -1133,6 +1134,10 @@ void BraveAddLocalizedStrings(content::WebUIDataSource* html_source, profile->GetPrefs()->GetBoolean( brave_news::prefs::kBraveNewsDisabledByPolicy)); + html_source->AddBoolean( + "braveTalkDisabledByPolicy", + profile->GetPrefs()->GetBoolean(kBraveTalkDisabledByPolicy)); + if (base::FeatureList::IsEnabled( net::features::kBraveFirstPartyEphemeralStorage)) { const webui::LocalizedString kSessionOnlyToEphemeralStrings[] = { diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index 461f1bee946..3bcd7968cb6 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -449,6 +449,7 @@ class NewTabPage extends React.Component { showRewards, showBraveTalk, showBraveVPN, + isBraveTalkDisabledByPolicy } = this.props.newTabData const lookup: { [p: string]: { display: boolean, render: any } } = { @@ -461,7 +462,8 @@ class NewTabPage extends React.Component { render: this.renderBraveVPNWidget }, 'braveTalk': { - display: braveTalkSupported && showBraveTalk, + display: braveTalkSupported && showBraveTalk && + !isBraveTalkDisabledByPolicy, render: this.renderBraveTalkWidget.bind(this) } } @@ -495,11 +497,13 @@ class NewTabPage extends React.Component { showRewards, showBraveTalk, showBraveVPN, - hideAllWidgets + hideAllWidgets, + isBraveTalkDisabledByPolicy } = this.props.newTabData return hideAllWidgets || [ braveRewardsSupported && showRewards, - braveTalkSupported && showBraveTalk, + braveTalkSupported && showBraveTalk && + !isBraveTalkDisabledByPolicy, this.braveVPNSupported && showBraveVPN, ].every((widget: boolean) => !widget) } @@ -608,9 +612,14 @@ class NewTabPage extends React.Component { renderBraveTalkWidget (showContent: boolean, position: number) { const { newTabData } = this.props - const { showBraveTalk, textDirection, braveTalkSupported } = newTabData + const { + showBraveTalk, + textDirection, + braveTalkSupported, + isBraveTalkDisabledByPolicy + } = newTabData - if (!showBraveTalk || !braveTalkSupported) { + if (!showBraveTalk || !braveTalkSupported || isBraveTalkDisabledByPolicy) { return null } diff --git a/components/brave_new_tab_ui/containers/newTab/settings.tsx b/components/brave_new_tab_ui/containers/newTab/settings.tsx index 03789430e22..154809d8af2 100644 --- a/components/brave_new_tab_ui/containers/newTab/settings.tsx +++ b/components/brave_new_tab_ui/containers/newTab/settings.tsx @@ -153,7 +153,7 @@ export default function Settings(props: Props) { - {allowedTabTypes.map(tabType => ( + {allowedTabTypes.map(tabType => changeTab(tabType)} > {getLocale(tabTranslationKeys[tabType])} - - ))} + )} diff --git a/components/brave_new_tab_ui/containers/newTab/settings/cards.tsx b/components/brave_new_tab_ui/containers/newTab/settings/cards.tsx index 091191eac0f..3c1941ac783 100644 --- a/components/brave_new_tab_ui/containers/newTab/settings/cards.tsx +++ b/components/brave_new_tab_ui/containers/newTab/settings/cards.tsx @@ -63,9 +63,13 @@ const ToggleButton = ({ on, toggleFunc, float }: { on: boolean, toggleFunc: any, function CardSettings({ toggleShowBraveTalk, showBraveTalk, braveTalkSupported, toggleShowRewards, showRewards, braveRewardsSupported, toggleCards, cardsHidden }: Props) { const [showBraveVPN, saveShowBraveVPN] = useNewTabPref('showBraveVPN') + const [isBraveTalkDisabledByPolicy] = useNewTabPref( + 'isBraveTalkDisabledByPolicy' + ) return - {braveTalkSupported && + {braveTalkSupported && !isBraveTalkDisabledByPolicy && + diff --git a/components/brave_new_tab_ui/storage/new_tab_storage.ts b/components/brave_new_tab_ui/storage/new_tab_storage.ts index bb5cf3a550f..da795cab96d 100644 --- a/components/brave_new_tab_ui/storage/new_tab_storage.ts +++ b/components/brave_new_tab_ui/storage/new_tab_storage.ts @@ -39,6 +39,7 @@ export const defaultState: NewTab.State = { isBrandedWallpaperNotificationDismissed: true, isBraveNewsOptedIn: false, isBraveNewsDisabledByPolicy: false, + isBraveTalkDisabledByPolicy: false, showEmptyPage: false, braveRewardsSupported: false, braveTalkSupported: false, @@ -115,14 +116,16 @@ export const replaceStackWidgets = (state: NewTab.State) => { showRewards, showBraveTalk, braveRewardsSupported, - braveTalkSupported + braveTalkSupported, + isBraveTalkDisabledByPolicy } = state const displayLookup: { [p: string]: { display: boolean } } = { 'rewards': { display: braveRewardsSupported && showRewards }, 'braveTalk': { - display: braveTalkSupported && showBraveTalk + display: braveTalkSupported && showBraveTalk && + !isBraveTalkDisabledByPolicy } } for (const key in displayLookup) { diff --git a/components/constants/pref_names.h b/components/constants/pref_names.h index 55a6b57ace4..976f841d460 100644 --- a/components/constants/pref_names.h +++ b/components/constants/pref_names.h @@ -73,6 +73,9 @@ inline constexpr char kNewTabPageShowRewards[] = "brave.new_tab_page.show_rewards"; inline constexpr char kNewTabPageShowBraveTalk[] = "brave.new_tab_page.show_together"; +// Used to enable/disable Brave Talk via a policy. +inline constexpr char kBraveTalkDisabledByPolicy[] = + "brave.talk.disabled_by_policy"; inline constexpr char kNewTabPageShowBraveVPN[] = "brave.new_tab_page.show_brave_vpn"; inline constexpr char kNewTabPageHideAllWidgets[] = diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index d5a51127741..f5374318577 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -131,6 +131,7 @@ declare namespace NewTab { hideAllWidgets: boolean isBraveNewsOptedIn: boolean isBraveNewsDisabledByPolicy: boolean + isBraveTalkDisabledByPolicy: boolean isBrandedWallpaperNotificationDismissed: boolean } diff --git a/components/policy/resources/templates/policy_definitions/BraveSoftware/BraveTalkDisabled.yaml b/components/policy/resources/templates/policy_definitions/BraveSoftware/BraveTalkDisabled.yaml new file mode 100644 index 00000000000..5b5cf7ff24f --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/BraveSoftware/BraveTalkDisabled.yaml @@ -0,0 +1,36 @@ +caption: Disable Brave Talk +default: null +desc: |- + Disable Brave Talk in Brave. + + Brave Talk is a feature that allows users to start private video calls with friends and colleagues directly from the browser. + + If this policy is set to true, Brave Talk will always be disabled and all related UI elements will be hidden. + + If this policy is set to false, Brave Talk will always be enabled. + + If you set this policy, users cannot change or override it. + + If this policy is left unset, Brave Talk will be enabled by default (subject to user preferences). +example_value: true +features: + can_be_mandatory: true + can_be_recommended: false + dynamic_refresh: false + per_profile: true +items: +- caption: Enable Brave Talk + value: false +- caption: Disable Brave Talk + value: true +- caption: Allow the user to decide + value: null +owners: +- bbondy@brave.com +- clifton@brave.com +schema: + type: boolean +supported_on: +- chrome.*:105- +tags: [] +type: main \ No newline at end of file diff --git a/components/policy/resources/templates/policy_definitions/brave_policies.gni b/components/policy/resources/templates/policy_definitions/brave_policies.gni index 894389486e4..73c672f4e41 100644 --- a/components/policy/resources/templates/policy_definitions/brave_policies.gni +++ b/components/policy/resources/templates/policy_definitions/brave_policies.gni @@ -11,6 +11,7 @@ _brave_policies = [ "BraveSoftware/BraveShieldsDisabledForUrls.yaml", "BraveSoftware/BraveShieldsEnabledForUrls.yaml", "BraveSoftware/BraveSyncUrl.yaml", + "BraveSoftware/BraveTalkDisabled.yaml", "BraveSoftware/BraveVPNDisabled.yaml", "BraveSoftware/BraveWalletDisabled.yaml", "BraveSoftware/IPFSEnabled.yaml", diff --git a/components/sidebar/browser/BUILD.gn b/components/sidebar/browser/BUILD.gn index 1c0cf30bc97..efadfd311fc 100644 --- a/components/sidebar/browser/BUILD.gn +++ b/components/sidebar/browser/BUILD.gn @@ -49,6 +49,7 @@ source_set("unit_tests") { "//base", "//base/test:test_support", "//brave/components/ai_chat/core/common", + "//brave/components/constants:constants", "//brave/components/sidebar/browser", "//components/prefs", "//components/prefs:test_support", diff --git a/components/sidebar/browser/DEPS b/components/sidebar/browser/DEPS index 9790ceb36cc..80a0aa9bda0 100644 --- a/components/sidebar/browser/DEPS +++ b/components/sidebar/browser/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+brave/components/constants", "+components/grit/brave_components_strings.h", "+components/keyed_service", "+components/prefs", diff --git a/components/sidebar/browser/sidebar_service.cc b/components/sidebar/browser/sidebar_service.cc index 58eaa48a012..15e90513813 100644 --- a/components/sidebar/browser/sidebar_service.cc +++ b/components/sidebar/browser/sidebar_service.cc @@ -23,6 +23,7 @@ #include "base/values.h" #include "brave/components/ai_chat/core/browser/utils.h" #include "brave/components/brave_wallet/common/common_utils.h" +#include "brave/components/constants/pref_names.h" #include "brave/components/constants/webui_url_constants.h" #include "brave/components/l10n/common/locale_util.h" #include "brave/components/playlist/common/buildflags/buildflags.h" @@ -593,12 +594,15 @@ SidebarItem SidebarService::GetBuiltInItemForType( SidebarItem::BuiltInItemType type) const { switch (type) { case SidebarItem::BuiltInItemType::kBraveTalk: - return SidebarItem::Create( - GURL(kBraveTalkURL), - l10n_util::GetStringUTF16(IDS_SIDEBAR_BRAVE_TALK_ITEM_TITLE), - SidebarItem::Type::kTypeBuiltIn, - SidebarItem::BuiltInItemType::kBraveTalk, - /* open_in_panel = */ false); + if (!prefs_->GetBoolean(kBraveTalkDisabledByPolicy)) { + return SidebarItem::Create( + GURL(kBraveTalkURL), + l10n_util::GetStringUTF16(IDS_SIDEBAR_BRAVE_TALK_ITEM_TITLE), + SidebarItem::Type::kTypeBuiltIn, + SidebarItem::BuiltInItemType::kBraveTalk, + /* open_in_panel = */ false); + } + return SidebarItem(); case SidebarItem::BuiltInItemType::kWallet: { if (brave_wallet::IsAllowed(prefs_)) { return SidebarItem::Create( diff --git a/components/sidebar/browser/sidebar_service_unittest.cc b/components/sidebar/browser/sidebar_service_unittest.cc index 21aeb1eb8c9..f87b28cc25d 100644 --- a/components/sidebar/browser/sidebar_service_unittest.cc +++ b/components/sidebar/browser/sidebar_service_unittest.cc @@ -17,11 +17,13 @@ #include "base/test/scoped_feature_list.h" #include "base/test/values_test_util.h" #include "brave/components/ai_chat/core/common/features.h" +#include "brave/components/constants/pref_names.h" #include "brave/components/playlist/common/buildflags/buildflags.h" #include "brave/components/sidebar/browser/constants.h" #include "brave/components/sidebar/browser/pref_names.h" #include "brave/components/sidebar/browser/sidebar_item.h" #include "brave/components/sidebar/browser/sidebar_p3a.h" +#include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -203,6 +205,8 @@ class SidebarServiceTest : public testing::Test { void SetUp() override { SidebarService::RegisterProfilePrefs( prefs_.registry(), SidebarService::ShowSidebarOption::kShowAlways); + // Register the Brave Talk policy preference that SidebarService now checks + prefs_.registry()->RegisterBooleanPref(kBraveTalkDisabledByPolicy, false); } void TearDown() override { ResetService(); } diff --git a/test/BUILD.gn b/test/BUILD.gn index 02edde29f32..3fa0d58e8ee 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -113,6 +113,7 @@ test("brave_unit_tests") { "//brave/browser/brave_content_browser_client_unittest.cc", "//brave/browser/browsing_data/brave_browsing_data_remover_delegate_unittest.cc", "//brave/browser/download/brave_download_item_model_unittest.cc", + "//brave/browser/policy/brave_talk_policy_unittest.cc", "//brave/browser/profiles/profile_util_unittest.cc", "//brave/chromium_src/chrome/browser/favicon/favicon_utils_unittest.cc", "//brave/chromium_src/chrome/browser/history/history_utils_unittest.cc",