++
++
++
$i18n{heliumSchemaUpdatedTitle}
++
++
++ $i18n{heliumGotIt}
++
++ $i18n{heliumSchemaIgnore}
++
++
++
++
+
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/services_page.ts
-@@ -0,0 +1,67 @@
+@@ -0,0 +1,97 @@
+// Copyright 2025 The Helium Authors
+// You can use, redistribute, and/or modify this source code under
+// the terms of the GPL-3.0 license that can be found in the LICENSE file.
+
-+import {HelpBubbleMixin} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
-+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
-+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-+import {getTemplate} from './services_page.html.js';
++import {HelpBubbleMixin} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
+import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
++import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
++import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
++
++import type {HeliumServicesChangeHandler} from './changelog_proxy.js';
++import {HeliumServicesChangeHandlerImpl} from './changelog_proxy.js';
++import {getTemplate} from './services_page.html.js';
+
+const SettingsHeliumServicesPageElementBase =
+ HelpBubbleMixin(I18nMixin(PrefsMixin(PolymerElement)));
+
-+export class SettingsHeliumServicesPageElement
-+ extends SettingsHeliumServicesPageElementBase
-+{
++export class SettingsHeliumServicesPageElement extends
++ SettingsHeliumServicesPageElementBase {
++ private changeProxy_: HeliumServicesChangeHandler =
++ HeliumServicesChangeHandlerImpl.getInstance();
++
+ static get is() {
+ return 'settings-helium-services-page';
+ }
@@ -230,9 +291,15 @@
+ }
+
+ static get properties() {
-+ return {};
++ return {
++ ignoreChecked_: Boolean,
++ changes_: Array,
++ };
+ }
+
++ declare private changes_: string[];
++ declare private ignoreChecked_: boolean;
++
+ private toOrigin(thing: string) {
+ try {
+ return new URL(thing).origin;
@@ -241,6 +308,25 @@
+ }
+ }
+
++ private updateChangelogState(changes: string[]) {
++ this.changes_ = changes.flatMap(ch => ch.split('\n'));
++ }
++
++ private requestChangelog() {
++ this.changeProxy_.requestChangelog().then(
++ this.updateChangelogState.bind(this));
++ }
++
++ private onAcknowledgedChangelog_() {
++ this.changeProxy_.acknowledgeChanges(this.ignoreChecked_);
++ this.requestChangelog();
++ }
++
++ override ready() {
++ super.ready();
++ this.requestChangelog();
++ }
++
+ protected onOriginOverrideKeydown_(event: KeyboardEvent) {
+ const target = event.target as CrInputElement;
+
@@ -299,11 +385,13 @@
#include "components/policy/core/common/policy_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
-@@ -204,6 +205,11 @@ void RegisterBrowserUserPrefs(user_prefs
+@@ -204,6 +205,13 @@ void RegisterBrowserUserPrefs(user_prefs
false);
#endif
+ {
++ registry->RegisterIntegerPref(prefs::kHeliumSchemaVersion, 0);
++ registry->RegisterBooleanPref(prefs::kHeliumDisableSchemaAlerts, false);
+ registry->RegisterBooleanPref(prefs::kHeliumServicesEnabled, true);
+ registry->RegisterStringPref(prefs::kHeliumServicesOrigin, "");
+ }
@@ -336,7 +424,7 @@
settings_api::PrefType::kBoolean;
--- /dev/null
+++ b/components/helium_services/BUILD.gn
-@@ -0,0 +1,20 @@
+@@ -0,0 +1,22 @@
+# Copyright 2025 The Helium Authors
+# You can use, redistribute, and/or modify this source code under
+# the terms of the GPL-3.0 license that can be found in the LICENSE file.
@@ -346,6 +434,8 @@
+ "helium_services_helpers.cc",
+ "helium_services_helpers.h",
+ "pref_names.h",
++ "schema.cc",
++ "schema.h",
+ ]
+
+ public_deps = [
@@ -359,7 +449,7 @@
+}
--- /dev/null
+++ b/components/helium_services/helium_services_helpers.cc
-@@ -0,0 +1,66 @@
+@@ -0,0 +1,67 @@
+// Copyright 2025 The Helium Authors
+// You can use, redistribute, and/or modify this source code under
+// the terms of the GPL-3.0 license that can be found in the LICENSE file.
@@ -422,13 +512,14 @@
+ PrefChangeRegistrar& registrar, const base::RepeatingClosure& observer) {
+ registrar.Add(prefs::kHeliumServicesEnabled, observer);
+ registrar.Add(prefs::kHeliumServicesOrigin, observer);
++ registrar.Add(prefs::kHeliumSchemaVersion, observer);
+ registrar.Add(pref_name, observer);
+}
+
+} // namespace helium
--- /dev/null
+++ b/components/helium_services/helium_services_helpers.h
-@@ -0,0 +1,28 @@
+@@ -0,0 +1,30 @@
+// Copyright 2025 The Helium Authors
+// You can use, redistribute, and/or modify this source code under
+// the terms of the GPL-3.0 license that can be found in the LICENSE file.
@@ -449,6 +540,8 @@
+const char kHeliumDummyOrigin[] =
+ "https://helium-services-are-disabled.qjz9zk";
+
++bool ShouldAccessServices(const PrefService& prefs);
++
+COMPONENT_EXPORT(HELIUM) GURL GetServicesBaseURL(const PrefService& prefs);
+COMPONENT_EXPORT(HELIUM) GURL GetDummyURL();
+COMPONENT_EXPORT(HELIUM) std::optional GetValidUserOverridenURL(std::string_view user_url_);
@@ -459,7 +552,7 @@
+#endif // COMPONENTS_HELIUM_SERVICES_HELIUM_SERVICES_HELPERS_H_
--- /dev/null
+++ b/components/helium_services/pref_names.h
-@@ -0,0 +1,18 @@
+@@ -0,0 +1,24 @@
+// Copyright 2025 The Helium Authors
+// You can use, redistribute, and/or modify this source code under
+// the terms of the GPL-3.0 license that can be found in the LICENSE file.
@@ -469,6 +562,12 @@
+
+namespace prefs {
+
++inline constexpr char kHeliumSchemaVersion[] =
++ "helium.services.schema_version";
++
++inline constexpr char kHeliumDisableSchemaAlerts[] =
++ "helium.services.disable_schema_alerts";
++
+inline constexpr char kHeliumServicesEnabled[] =
+ "helium.services.enabled";
+
@@ -478,3 +577,248 @@
+} // namespace prefs
+
+#endif // COMPONENTS_HELIUM_SERVICES_PREF_NAMES_H_
+--- /dev/null
++++ b/chrome/browser/resources/settings/privacy_page/changelog_proxy.ts
+@@ -0,0 +1,40 @@
++// Copyright 2025 The Helium Authors
++// You can use, redistribute, and/or modify this source code under
++// the terms of the GPL-3.0 license that can be found in the LICENSE file.
++
++/**
++ * @fileoverview A helper object used from the "Helium services" section
++ * to request a changelog (if one exists).
++ */
++
++// clang-format off
++import {sendWithPromise} from 'chrome://resources/js/cr.js';
++// clang-format on
++
++export interface HeliumServicesChangeHandler {
++ // Get the pending changes to Helium services to announce
++ // to the user.
++ requestChangelog(): Promise;
++ acknowledgeChanges(ignoreAllChangelogs: boolean): void;
++}
++
++export class HeliumServicesChangeHandlerImpl implements
++ HeliumServicesChangeHandler {
++ requestChangelog() {
++ return sendWithPromise('getServicesSchemaChangelog');
++ }
++
++ acknowledgeChanges(ignoreAllChangelogs: boolean) {
++ chrome.send('updateServicesSchemaVersion', [ignoreAllChangelogs]);
++ }
++
++ static getInstance(): HeliumServicesChangeHandler {
++ return instance || (instance = new HeliumServicesChangeHandlerImpl());
++ }
++
++ static setInstance(obj: HeliumServicesChangeHandler) {
++ instance = obj;
++ }
++}
++
++let instance: HeliumServicesChangeHandler|null = null;
+--- /dev/null
++++ b/chrome/browser/ui/webui/settings/services_schema_handler.h
+@@ -0,0 +1,36 @@
++// Copyright 2025 The Helium Authors
++// You can use, redistribute, and/or modify this source code under
++// the terms of the GPL-3.0 license that can be found in the LICENSE file.
++
++#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SERVICES_SCHEMA_HANDLER_H_
++#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SERVICES_SCHEMA_HANDLER_H_
++
++#include
++#include
++
++#include "base/memory/raw_ptr.h"
++#include "base/values.h"
++#include "content/public/browser/web_ui_message_handler.h"
++
++class Profile;
++class PrefService;
++
++class HeliumServicesSchemaHandler : public content::WebUIMessageHandler {
++ public:
++ explicit HeliumServicesSchemaHandler(Profile* profile);
++ HeliumServicesSchemaHandler(const HeliumServicesSchemaHandler&) = delete;
++ HeliumServicesSchemaHandler& operator=(const HeliumServicesSchemaHandler&) =
++ delete;
++ ~HeliumServicesSchemaHandler() override;
++
++ // SettingsPageUIHandler:
++ void RegisterMessages() override;
++
++ private:
++ void HandleChangelogRequest(const base::Value::List&);
++ void HandleChangelogAcknowledgment(const base::Value::List&);
++
++ raw_ptr pref_service_ = nullptr;
++};
++
++#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SERVICES_SCHEMA_HANDLER_H_
+--- /dev/null
++++ b/components/helium_services/schema.cc
+@@ -0,0 +1,36 @@
++// Copyright 2025 The Helium Authors
++// You can use, redistribute, and/or modify this source code under
++// the terms of the GPL-3.0 license that can be found in the LICENSE file.
++
++#include "components/helium_services/schema.h"
++
++#include "components/helium_services/helium_services_helpers.h"
++#include "components/helium_services/pref_names.h"
++#include "components/prefs/pref_service.h"
++
++namespace helium {
++
++bool HasAcceptedSchema(const PrefService& prefs, int version) {
++ return prefs.GetBoolean(prefs::kHeliumDisableSchemaAlerts) ||
++ prefs.GetInteger(prefs::kHeliumSchemaVersion) >= version;
++}
++
++void AcceptCurrentSchema(PrefService& prefs) {
++ prefs.SetInteger(prefs::kHeliumSchemaVersion, kHeliumCurrentSchemaVersion);
++}
++
++bool ShouldShowSchemaNotification(const PrefService& prefs) {
++ return ShouldAccessServices(prefs) &&
++ !HasAcceptedSchema(prefs, kHeliumCurrentSchemaVersion);
++}
++
++ServicesChangelog& GetChangelog() {
++ static constexpr auto kHeliumSchemaChangelog =
++ base::MakeFixedFlatMap({
++ {1, "TBA ____________________"}
++ });
++
++ return kHeliumSchemaChangelog;
++}
++
++} // namespace helium
+--- /dev/null
++++ b/components/helium_services/schema.h
+@@ -0,0 +1,30 @@
++// Copyright 2025 The Helium Authors
++// You can use, redistribute, and/or modify this source code under
++// the terms of the GPL-3.0 license that can be found in the LICENSE file.
++
++#ifndef COMPONENTS_HELIUM_SERVICES_SCHEMA_H_
++#define COMPONENTS_HELIUM_SERVICES_SCHEMA_H_
++
++#include "base/component_export.h"
++#include "base/containers/fixed_flat_map.h"
++#include "components/prefs/pref_service.h"
++
++namespace helium {
++
++inline constexpr int kHeliumCurrentSchemaVersion = 1;
++
++using ServicesChangelog = const base::
++ fixed_flat_map;
++
++#define EX COMPONENT_EXPORT(HELIUM)
++
++EX bool HasAcceptedSchema(const PrefService& prefs, int version);
++EX void AcceptCurrentSchema(PrefService& prefs);
++EX bool ShouldShowSchemaNotification(const PrefService& prefs);
++EX ServicesChangelog& GetChangelog();
++
++#undef EX
++
++} // namespace helium
++
++#endif // COMPONENTS_HELIUM_SERVICES_SCHEMA_H_
+--- a/chrome/browser/ui/BUILD.gn
++++ b/chrome/browser/ui/BUILD.gn
+@@ -1296,6 +1296,8 @@ static_library("ui") {
+ "webui/settings/saved_info_handler.h",
+ "webui/settings/search_engines_handler.cc",
+ "webui/settings/search_engines_handler.h",
++ "webui/settings/services_schema_handler.cc",
++ "webui/settings/services_schema_handler.h",
+ "webui/settings/settings_clear_browsing_data_handler.cc",
+ "webui/settings/settings_clear_browsing_data_handler.h",
+ "webui/settings/settings_localized_strings_privacy_sandbox_provider.cc",
+--- /dev/null
++++ b/chrome/browser/ui/webui/settings/services_schema_handler.cc
+@@ -0,0 +1,58 @@
++// Copyright 2025 The Helium Authors
++// You can use, redistribute, and/or modify this source code under
++// the terms of the GPL-3.0 license that can be found in the LICENSE file.
++
++#include "chrome/browser/ui/webui/settings/services_schema_handler.h"
++
++#include "base/functional/bind.h"
++#include "base/values.h"
++#include "chrome/browser/profiles/profile.h"
++#include "components/helium_services/pref_names.h"
++#include "components/helium_services/schema.h"
++#include "components/prefs/pref_service.h"
++
++HeliumServicesSchemaHandler::HeliumServicesSchemaHandler(Profile* profile)
++ : pref_service_(profile->GetPrefs()) {}
++
++HeliumServicesSchemaHandler::~HeliumServicesSchemaHandler() = default;
++
++void HeliumServicesSchemaHandler::RegisterMessages() {
++ web_ui()->RegisterMessageCallback(
++ "getServicesSchemaChangelog",
++ base::BindRepeating(&HeliumServicesSchemaHandler::HandleChangelogRequest,
++ base::Unretained(this)));
++ web_ui()->RegisterMessageCallback(
++ "updateServicesSchemaVersion",
++ base::BindRepeating(
++ &HeliumServicesSchemaHandler::HandleChangelogAcknowledgment,
++ base::Unretained(this)));
++}
++
++void HeliumServicesSchemaHandler::HandleChangelogRequest(
++ const base::Value::List& args) {
++ CHECK_EQ(1U, args.size());
++ const auto& callback_id = args[0].GetString();
++ base::Value::List out;
++
++ if (helium::ShouldShowSchemaNotification(*pref_service_)) {
++ int user_version = pref_service_->GetInteger(prefs::kHeliumSchemaVersion);
++ auto& changelog = helium::GetChangelog();
++
++ for (auto it = changelog.upper_bound(user_version); it != changelog.end();
++ ++it) {
++ out.Append(it->second);
++ }
++ }
++
++ AllowJavascript();
++ ResolveJavascriptCallback(base::Value(callback_id), std::move(out));
++}
++
++void HeliumServicesSchemaHandler::HandleChangelogAcknowledgment(
++ const base::Value::List& args) {
++ CHECK_EQ(1U, args.size());
++ bool ignore_alerts = args[0].GetBool();
++
++ helium::AcceptCurrentSchema(*pref_service_);
++ pref_service_->SetBoolean(prefs::kHeliumDisableSchemaAlerts, ignore_alerts);
++}
+--- a/chrome/browser/ui/webui/settings/settings_ui.cc
++++ b/chrome/browser/ui/webui/settings/settings_ui.cc
+@@ -76,6 +76,7 @@
+ #include "chrome/browser/ui/webui/settings/safety_hub_handler.h"
+ #include "chrome/browser/ui/webui/settings/saved_info_handler.h"
+ #include "chrome/browser/ui/webui/settings/search_engines_handler.h"
++#include "chrome/browser/ui/webui/settings/services_schema_handler.h"
+ #include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
+ #include "chrome/browser/ui/webui/settings/settings_localized_strings_provider.h"
+ #include "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h"
+@@ -252,6 +253,8 @@ SettingsUI::SettingsUI(content::WebUI* w
+ AddSettingsPageUIHandler(std::make_unique(profile));
+ AddSettingsPageUIHandler(std::make_unique());
+ AddSettingsPageUIHandler(std::make_unique(profile));
++ AddSettingsPageUIHandler(
++ std::make_unique(profile));
+ AddSettingsPageUIHandler(std::make_unique());
+ AddSettingsPageUIHandler(std::make_unique(profile));
+ AddSettingsPageUIHandler(std::make_unique(web_ui));
diff --git a/patches/helium/core/services-schema-nag.patch b/patches/helium/core/services-schema-nag.patch
new file mode 100644
index 00000000..8e37f3c5
--- /dev/null
+++ b/patches/helium/core/services-schema-nag.patch
@@ -0,0 +1,232 @@
+--- a/chrome/app/chromium_strings.grd
++++ b/chrome/app/chromium_strings.grd
+@@ -956,6 +956,10 @@ Chromium is unable to recover your setti
+ Customize and control Chromium. Update is available.
+
+
++
++ Helium services have been updated. Please review the changes.
++
++
+
+ Customize and control Chromium. Something needs your attention - click for details.
+
+@@ -1620,6 +1624,11 @@ Chromium is unable to recover your setti
+ Chromium couldn't update to the latest version, so you're missing out on new features and security fixes.
+
+
++
++
++ Review Helium services updates
++
++
+
+
+ Update Chromium
+--- a/chrome/app/generated_resources.grd
++++ b/chrome/app/generated_resources.grd
+@@ -13186,6 +13186,9 @@ Check your passwords anytime in
+ Update
+
++
++ Services updated
++
+
+
+ Finish update
+--- a/chrome/app/chrome_command_ids.h
++++ b/chrome/app/chrome_command_ids.h
+@@ -301,6 +301,7 @@
+ #define IDC_SHOW_COMMENTS_SIDE_PANEL 40297
+ #define IDC_RECENT_TABS_SEE_DEVICE_TABS 40298
+ #define IDC_SHOW_AI_MODE_OMNIBOX_BUTTON 40299
++#define IDC_HELIUM_SERVICES_OPEN 40300
+
+ // Spell-check
+ // Insert any additional suggestions before _LAST; these have to be consecutive.
+--- a/chrome/browser/ui/chrome_pages.h
++++ b/chrome/browser/ui/chrome_pages.h
+@@ -138,6 +138,7 @@ void ShowSafeBrowsingEnhancedProtectionW
+ safe_browsing::SafeBrowsingSettingReferralMethod referral_method);
+ void ShowImportDialog(Browser* browser);
+ void ShowAboutChrome(Browser* browser);
++void ShowHeliumServices(Browser* browser);
+ void ShowSearchEngineSettings(Browser* browser);
+ void ShowWebStore(Browser* browser, std::string_view utm_source_value);
+ void ShowPrivacySandboxSettings(Browser* browser);
+--- a/chrome/browser/ui/chrome_pages.cc
++++ b/chrome/browser/ui/chrome_pages.cc
+@@ -615,6 +615,11 @@ void ShowAboutChrome(Browser* browser) {
+ ShowSingletonTabIgnorePathOverwriteNTP(browser, GURL(kChromeUIHelpURL));
+ }
+
++void ShowHeliumServices(Browser* browser) {
++ ShowSingletonTabIgnorePathOverwriteNTP(
++ browser, GURL("chrome://settings/privacy/services"));
++}
++
+ void ShowSearchEngineSettings(Browser* browser) {
+ base::RecordAction(UserMetricsAction("EditSearchEngines"));
+ ShowSettingsSubPage(browser, kSearchEnginesSubPage);
+--- a/chrome/browser/ui/browser_command_controller.cc
++++ b/chrome/browser/ui/browser_command_controller.cc
+@@ -1067,6 +1067,9 @@ bool BrowserCommandController::ExecuteCo
+ case IDC_UPGRADE_DIALOG:
+ OpenUpdateChromeDialog(browser_);
+ break;
++ case IDC_HELIUM_SERVICES_OPEN:
++ ShowHeliumServices(browser_->GetBrowserForOpeningWebUi());
++ break;
+ case IDC_OPEN_SAFETY_HUB:
+ ShowSettingsSubPage(browser_->GetBrowserForOpeningWebUi(),
+ chrome::kSafetyHubSubPage);
+@@ -1631,6 +1634,7 @@ void BrowserCommandController::InitComma
+
+ // These are always enabled; the menu determines their menu item visibility.
+ command_updater_.UpdateCommandEnabled(IDC_UPGRADE_DIALOG, true);
++ command_updater_.UpdateCommandEnabled(IDC_HELIUM_SERVICES_OPEN, true);
+ command_updater_.UpdateCommandEnabled(IDC_SET_BROWSER_AS_DEFAULT, true);
+
+ // Safety Hub commands.
+--- a/chrome/browser/ui/toolbar/app_menu_model.h
++++ b/chrome/browser/ui/toolbar/app_menu_model.h
+@@ -281,6 +281,9 @@ class AppMenuModel : public ui::SimpleMe
+ // boolean indicating whether any menu items were added.
+ bool AddDefaultBrowserMenuItems();
+
++ // Adds a nag to review Helium services permission changes
++ bool AddHeliumSchemaItem();
++
+ // Adds the Safety Hub menu notifications to the menu. Returns a boolean
+ // indicating whether any menu items were added.
+ [[nodiscard]] bool AddSafetyHubMenuItem();
+--- a/chrome/browser/ui/toolbar/app_menu_model.cc
++++ b/chrome/browser/ui/toolbar/app_menu_model.cc
+@@ -104,6 +104,7 @@
+ #include "components/dom_distiller/core/url_utils.h"
+ #include "components/feature_engagement/public/event_constants.h"
+ #include "components/feature_engagement/public/feature_constants.h"
++#include "components/helium_services/schema.h"
+ #include "components/lens/lens_features.h"
+ #include "components/omnibox/browser/vector_icons.h"
+ #include "components/password_manager/content/common/web_ui_constants.h"
+@@ -2062,7 +2063,8 @@ bool AppMenuModel::AddGlobalErrorMenuIte
+ // how update the menu if new errors are added.
+ const GlobalErrorService::GlobalErrorList& errors =
+ GlobalErrorServiceFactory::GetForProfile(browser_->profile())->errors();
+- bool menu_items_added = false;
++ bool menu_items_added = AddHeliumSchemaItem();
++
+ for (GlobalError* error : errors) {
+ DCHECK(error);
+ if (error->HasMenuItem()) {
+@@ -2126,6 +2128,22 @@ bool AppMenuModel::AddSafetyHubMenuItem(
+ return true;
+ }
+
++bool AppMenuModel::AddHeliumSchemaItem() {
++ if (!helium::ShouldShowSchemaNotification(
++ *browser()->profile()->GetPrefs())) {
++ return false;
++ }
++
++ const auto update_icon = ui::ImageModel::FromVectorIcon(
++ omnibox::kProductChromeRefreshIcon, ui::kColorMenuIcon, kDefaultIconSize);
++ AddItemWithIcon(
++ IDC_HELIUM_SERVICES_OPEN,
++ l10n_util::GetStringUTF16(IDS_HELIUM_SERVICES_SCHEMA_MENU_ITEM),
++ update_icon);
++
++ return true;
++}
++
+ #if BUILDFLAG(IS_CHROMEOS)
+ void AppMenuModel::UpdateSettingsItemState() {
+ bool is_disabled =
+--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
++++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
+@@ -8,11 +8,15 @@
+ #include "build/build_config.h"
+ #include "chrome/app/vector_icons/vector_icons.h"
+ #include "chrome/browser/defaults.h"
++#include "chrome/browser/profiles/profile.h"
+ #include "chrome/browser/ui/global_error/global_error.h"
+ #include "chrome/browser/ui/global_error/global_error_service_factory.h"
+ #include "chrome/browser/ui/ui_features.h"
+ #include "chrome/browser/upgrade_detector/upgrade_detector.h"
+ #include "chrome/common/channel_info.h"
++#include "components/helium_services/pref_names.h"
++#include "components/helium_services/schema.h"
++#include "components/prefs/pref_change_registrar.h"
+ #include "components/version_info/channel.h"
+ #include "ui/gfx/paint_vector_icon.h"
+
+@@ -109,6 +113,14 @@ AppMenuIconController::AppMenuIconContro
+ GlobalErrorServiceFactory::GetForProfile(profile_));
+
+ upgrade_detector_->AddObserver(this);
++
++ if (auto* prefs = profile->GetPrefs()) {
++ registrar_.Init(prefs);
++ registrar_.Add(
++ prefs::kHeliumSchemaVersion,
++ base::BindRepeating(&AppMenuIconController::OnGlobalErrorsChanged,
++ base::Unretained(this)));
++ }
+ }
+
+ AppMenuIconController::~AppMenuIconController() {
+@@ -141,6 +153,12 @@ AppMenuIconController::GetTypeAndSeverit
+ ->GetHighestSeverityGlobalErrorWithAppMenuItem()) {
+ return {IconType::kGlobalError, SeverityFromError(error)};
+ }
++
++ if (auto* prefs = profile_->GetPrefs()) {
++ if (helium::ShouldShowSchemaNotification(*prefs)) {
++ return {IconType::kHeliumServicesUpdate, Severity::kLow};
++ }
++ }
+ #endif
+
+ return {IconType::kNone, Severity::kNone};
+--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.h
++++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.h
+@@ -15,6 +15,7 @@
+ #include "chrome/browser/ui/global_error/global_error_observer.h"
+ #include "chrome/browser/ui/global_error/global_error_service.h"
+ #include "chrome/browser/upgrade_detector/upgrade_observer.h"
++#include "components/prefs/pref_change_registrar.h"
+ #include "third_party/skia/include/core/SkColor.h"
+ #include "ui/base/models/image_model.h"
+
+@@ -29,6 +30,7 @@ class AppMenuIconController : public Glo
+ enum class IconType {
+ kNone,
+ kUpgradeNotification,
++ kHeliumServicesUpdate,
+ kGlobalError,
+ };
+ enum class Severity {
+@@ -93,6 +95,7 @@ class AppMenuIconController : public Glo
+ const raw_ptr delegate_;
+ base::ScopedObservation
+ global_error_observation_{this};
++ PrefChangeRegistrar registrar_;
+ };
+
+ #endif // CHROME_BROWSER_UI_TOOLBAR_APP_MENU_ICON_CONTROLLER_H_
+--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
++++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+@@ -214,6 +214,11 @@ void BrowserAppMenuButton::UpdateTextAnd
+ #else
+ text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_UPDATE);
+ #endif
++ } else if (type_and_severity_.type ==
++ AppMenuIconController::IconType::kHeliumServicesUpdate) {
++ tooltip_message_id = IDS_APPMENU_TOOLTIP_HELIUM_SERVICES_UPDATE;
++ text =
++ l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_HELIUM_SERVICES_UPDATE);
+ } else {
+ tooltip_message_id = IDS_APPMENU_TOOLTIP_ALERT;
+ const int text_id =
diff --git a/patches/helium/core/ublock-helium-services.patch b/patches/helium/core/ublock-helium-services.patch
index 80a21a0e..fa316857 100644
--- a/patches/helium/core/ublock-helium-services.patch
+++ b/patches/helium/core/ublock-helium-services.patch
@@ -29,7 +29,7 @@
registrar.Add(prefs::kHeliumServicesEnabled, observer);
--- a/components/helium_services/helium_services_helpers.h
+++ b/components/helium_services/helium_services_helpers.h
-@@ -21,12 +21,14 @@ const char kHeliumDummyOrigin[] =
+@@ -23,12 +23,14 @@ bool ShouldAccessServices(const PrefServ
COMPONENT_EXPORT(HELIUM) bool ShouldFetchBangs(const PrefService& prefs);
COMPONENT_EXPORT(HELIUM) bool ShouldAccessExtensionService(const PrefService& prefs);
COMPONENT_EXPORT(HELIUM) bool ShouldAccessUpdateService(const PrefService& prefs);
@@ -46,7 +46,7 @@
PrefChangeRegistrar& registrar, const base::RepeatingClosure& observer);
--- a/components/helium_services/pref_names.h
+++ b/components/helium_services/pref_names.h
-@@ -33,6 +33,8 @@ inline constexpr char kHeliumUpdateFetch
+@@ -39,6 +39,8 @@ inline constexpr char kHeliumUpdateFetch
inline constexpr char kHeliumSpellcheckEnabled[] =
"helium.services.spellcheck_files";
@@ -68,7 +68,7 @@
(*s_allowlist)[::prefs::kHeliumServicesConsented] =
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
-@@ -218,6 +218,7 @@ void RegisterBrowserUserPrefs(user_prefs
+@@ -220,6 +220,7 @@ void RegisterBrowserUserPrefs(user_prefs
registry->RegisterBooleanPref(prefs::kHeliumDidOnboarding, false);
registry->RegisterBooleanPref(prefs::kHeliumServicesConsented, false);
registry->RegisterBooleanPref(prefs::kHeliumUpdateFetchingEnabled, true);
@@ -217,7 +217,7 @@
if ( bin.assetSourceRegistry instanceof Object ) {
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
-@@ -2002,6 +2002,12 @@
+@@ -2008,6 +2008,12 @@
Helium will automatically download and install browser updates as they become available. We recommend keeping this setting enabled to ensure you get the latest security patches and features.
@@ -232,7 +232,7 @@
--- a/chrome/browser/resources/settings/privacy_page/services_page.html
+++ b/chrome/browser/resources/settings/privacy_page/services_page.html
-@@ -69,6 +69,11 @@
+@@ -108,6 +108,11 @@
sub-label="$i18n{heliumUpdatesToggleDescription}">
@@ -246,7 +246,7 @@
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
-@@ -2139,6 +2139,10 @@ void AddPrivacyStrings(content::WebUIDat
+@@ -2142,6 +2142,10 @@ void AddPrivacyStrings(content::WebUIDat
IDS_SETTINGS_HELIUM_SERVICES_UPDATE},
{"heliumUpdatesToggleDescription",
IDS_SETTINGS_HELIUM_SERVICES_UPDATE_DESCRIPTION},
diff --git a/patches/helium/settings/fix-text-on-cookies-page.patch b/patches/helium/settings/fix-text-on-cookies-page.patch
index 171b9ed0..19df5071 100644
--- a/patches/helium/settings/fix-text-on-cookies-page.patch
+++ b/patches/helium/settings/fix-text-on-cookies-page.patch
@@ -10,7 +10,7 @@
Learn more about Do Not Track
-@@ -3207,6 +3210,9 @@
+@@ -3213,6 +3216,9 @@
Affects the sites listed here. Inserting “[*.]” before a domain name creates an exception for the entire domain. For example, adding “[*.]google.com” means that third-party cookies can also be active for mail.google.com, because it’s part of google.com.
@@ -31,7 +31,7 @@
{"doNotTrackDialogLearnMoreA11yLabel",
IDS_SETTINGS_ENABLE_DO_NOT_TRACK_DIALOG_LEARN_MORE_ACCESSIBILITY_LABEL},
// TODO(crbug.com/40122957): This string is no longer used. Remove.
-@@ -2829,7 +2829,7 @@ void AddSiteSettingsStrings(content::Web
+@@ -2832,7 +2832,7 @@ void AddSiteSettingsStrings(content::Web
{"trackingProtectionSitesAllowedCookiesTitle",
IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_TITLE},
{"trackingProtectionSitesAllowedCookiesDescription",
diff --git a/patches/helium/ui/app-menu-button.patch b/patches/helium/ui/app-menu-button.patch
index adc5ee8f..fe480374 100644
--- a/patches/helium/ui/app-menu-button.patch
+++ b/patches/helium/ui/app-menu-button.patch
@@ -52,10 +52,10 @@
-#else
text = l10n_util::GetStringUTF16(IDS_APP_MENU_BUTTON_UPDATE);
-#endif
- } else {
- tooltip_message_id = IDS_APPMENU_TOOLTIP_ALERT;
- const int text_id =
-@@ -228,37 +199,19 @@ void BrowserAppMenuButton::UpdateTextAnd
+ } else if (type_and_severity_.type ==
+ AppMenuIconController::IconType::kHeliumServicesUpdate) {
+ tooltip_message_id = IDS_APPMENU_TOOLTIP_HELIUM_SERVICES_UPDATE;
+@@ -233,37 +204,19 @@ void BrowserAppMenuButton::UpdateTextAnd
}
bool BrowserAppMenuButton::ShouldPaintBorder() const {
diff --git a/patches/helium/ui/app-menu-model.patch b/patches/helium/ui/app-menu-model.patch
index 795666a4..4fc09522 100644
--- a/patches/helium/ui/app-menu-model.patch
+++ b/patches/helium/ui/app-menu-model.patch
@@ -1,6 +1,6 @@
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
-@@ -739,9 +739,6 @@ SaveAndShareSubMenuModel::SaveAndShareSu
+@@ -740,9 +740,6 @@ SaveAndShareSubMenuModel::SaveAndShareSu
if (!sharing_hub::SharingIsDisabledByPolicy(browser->profile())) {
AddItemWithStringIdAndVectorIcon(
this, IDC_COPY_URL, IDS_APP_MENU_COPY_LINK, kLinkChromeRefreshIcon);
@@ -10,7 +10,7 @@
AddItemWithStringIdAndVectorIcon(this, IDC_QRCODE_GENERATOR,
IDS_APP_MENU_CREATE_QR_CODE,
kQrCodeChromeRefreshIcon);
-@@ -885,14 +882,6 @@ void ToolsMenuModel::Build(Browser* brow
+@@ -886,14 +883,6 @@ void ToolsMenuModel::Build(Browser* brow
AddItemWithStringIdAndVectorIcon(this, IDC_NAME_WINDOW, IDS_NAME_WINDOW,
kNameWindowIcon);
@@ -25,7 +25,7 @@
if (base::FeatureList::IsEnabled(contextual_tasks::kContextualTasks)) {
AddItemWithStringIdAndVectorIcon(
this, IDC_SHOW_CONTEXTUAL_TASKS_SIDE_PANEL,
-@@ -901,15 +890,6 @@ void ToolsMenuModel::Build(Browser* brow
+@@ -902,15 +891,6 @@ void ToolsMenuModel::Build(Browser* brow
AddSeparator(ui::NORMAL_SEPARATOR);
@@ -41,7 +41,7 @@
AddItemWithStringIdAndVectorIcon(this, IDC_PERFORMANCE, IDS_SHOW_PERFORMANCE,
kPerformanceIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_PERFORMANCE).value(),
-@@ -1760,22 +1740,8 @@ void AppMenuModel::Build() {
+@@ -1761,22 +1741,8 @@ void AppMenuModel::Build() {
// Build (and, by extension, Init) should only be called once.
DCHECK_EQ(0u, GetItemCount());
@@ -66,7 +66,7 @@
AddSeparator(ui::NORMAL_SEPARATOR);
}
-@@ -1812,43 +1778,6 @@ void AppMenuModel::Build() {
+@@ -1813,43 +1779,6 @@ void AppMenuModel::Build() {
AddSeparator(ui::NORMAL_SEPARATOR);
@@ -110,7 +110,7 @@
if (!browser_->profile()->IsOffTheRecord()) {
auto recent_tabs_sub_menu =
std::make_unique
(provider_, browser_);
-@@ -1870,7 +1799,7 @@ void AppMenuModel::Build() {
+@@ -1871,7 +1800,7 @@ void AppMenuModel::Build() {
std::make_unique(this, browser_);
AddSubMenuWithStringIdAndVectorIcon(
@@ -119,7 +119,7 @@
bookmark_sub_menu_model_.get(), kBookmarksListsMenuIcon);
SetElementIdentifierAt(GetIndexOfCommandId(IDC_BOOKMARKS_MENU).value(),
kBookmarksMenuItem);
-@@ -1887,23 +1816,10 @@ void AppMenuModel::Build() {
+@@ -1888,23 +1817,10 @@ void AppMenuModel::Build() {
kTabGroupsMenuItem);
}
@@ -147,7 +147,7 @@
AddItemWithStringIdAndVectorIcon(this, IDC_CLEAR_BROWSING_DATA,
IDS_CLEAR_BROWSING_DATA,
-@@ -1950,9 +1866,6 @@ void AppMenuModel::Build() {
+@@ -1951,9 +1867,6 @@ void AppMenuModel::Build() {
lens::features::kLensOverlay));
}
@@ -157,7 +157,7 @@
CreateFindAndEditSubMenu();
sub_menus_.push_back(
-@@ -2001,6 +1914,12 @@ void AppMenuModel::Build() {
+@@ -2002,6 +1915,12 @@ void AppMenuModel::Build() {
#endif
#endif
@@ -170,7 +170,7 @@
AddItemWithStringIdAndVectorIcon(this, IDC_OPTIONS, IDS_SETTINGS,
kSettingsMenuIcon);
-@@ -2096,34 +2015,21 @@ bool AppMenuModel::AddDefaultBrowserMenu
+@@ -2098,34 +2017,21 @@ bool AppMenuModel::AddDefaultBrowserMenu
return false;
}
@@ -217,12 +217,12 @@
+ return false;
}
- #if BUILDFLAG(IS_CHROMEOS)
+ bool AppMenuModel::AddHeliumSchemaItem() {
--- a/chrome/browser/ui/toolbar/app_menu_model.h
+++ b/chrome/browser/ui/toolbar/app_menu_model.h
-@@ -281,6 +281,9 @@ class AppMenuModel : public ui::SimpleMe
- // boolean indicating whether any menu items were added.
- bool AddDefaultBrowserMenuItems();
+@@ -284,6 +284,9 @@ class AppMenuModel : public ui::SimpleMe
+ // Adds a nag to review Helium services permission changes
+ bool AddHeliumSchemaItem();
+ // Adds a nag to relaunch the browser after an update
+ bool AddUpgradeMenuItem();
diff --git a/patches/helium/ui/ublock-show-in-settings.patch b/patches/helium/ui/ublock-show-in-settings.patch
index 935cedbc..5405c5a9 100644
--- a/patches/helium/ui/ublock-show-in-settings.patch
+++ b/patches/helium/ui/ublock-show-in-settings.patch
@@ -87,7 +87,7 @@
};
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
-@@ -13438,6 +13438,9 @@ Check your passwords anytime in
Installed because of dependent extension(s).
diff --git a/patches/series b/patches/series
index a855cb4f..5facae40 100644
--- a/patches/series
+++ b/patches/series
@@ -118,6 +118,7 @@ helium/core/add-zen-importer.patch
helium/core/disable-unsupported-importers.patch
helium/core/fix-building-without-safebrowsing.patch
helium/core/services-prefs.patch
+helium/core/services-schema-nag.patch
helium/core/override-chrome-protocol.patch
helium/core/onboarding-page.patch
helium/core/change-chromium-branding.patch