diff --git a/devutils/clear-ublock-assets.js b/devutils/clear-ublock-assets.js new file mode 100644 index 00000000..90b52698 --- /dev/null +++ b/devutils/clear-ublock-assets.js @@ -0,0 +1,61 @@ +// 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. + +// Program for updating the assets.json file in uB0 to disable all +// outgoing connections before the user is able to consent to them. + +const fs = require('fs'); + +const err = () => { + console.error('usage: node clear-ublock-assets + [c].flat().filter(s => !URL.canParse(s)); + +const breakKey = (obj, key_) => { + const keys = Object.keys(obj); + const idx = keys.indexOf(key_); + + if (idx === -1) { + return; + } + + for (let key of keys.splice(idx)) { + const val = obj[key]; + delete obj[key]; + + if (key === key_) { + key = `^${key}`; + } + + obj[key] = val; + } +} + +const clear = obj => { + for (const filter of Object.values(obj)) { + if (filter.off) { + continue; + } + + filter.contentURL = stripURLs(filter.contentURL); + breakKey(filter, 'cdnURLs'); + breakKey(filter, 'patchURLs'); + } + + return obj; +} + +fs.writeFileSync( + assets_path, + JSON.stringify(clear( + JSON.parse(fs.readFileSync( + assets_path + )) + ), null, '\t') + '\n' +); diff --git a/downloads.ini b/downloads.ini index e61ab4d7..d64e2cd5 100644 --- a/downloads.ini +++ b/downloads.ini @@ -6,3 +6,12 @@ download_filename = chromium-%(_chromium_version)s-lite.tar.xz hash_url = chromium|chromium-%(_chromium_version)s-lite.tar.xz.hashes|https://commondatastorage.googleapis.com/chromium-browser-official/chromium-%(_chromium_version)s-lite.tar.xz.hashes output_path = ./ strip_leading_dirs = chromium-%(_chromium_version)s + +# If you are bumping this, you *NEED* to re-strip the assets.json +# file *every time* by using `devutils/clear-ublock-assets.js`. +[ublock_origin] +version = 1.66.4 +url = https://github.com/imputnet/ublock-origin-crx/releases/download/%(version)s/uBlock0_%(version)s.crx +sha256 = c2809bb4cd2f3a6b9ddfc5e165cba74a200cd80ca691521f73d98bf5ab5abcef +download_filename = ublock-origin-%(version)s.zip +output_path = third_party/ublock diff --git a/patches/extra/brave/fix-component-content-settings-store.patch b/patches/extra/brave/fix-component-content-settings-store.patch new file mode 100644 index 00000000..72c91b3c --- /dev/null +++ b/patches/extra/brave/fix-component-content-settings-store.patch @@ -0,0 +1,58 @@ +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/. + +Copyright (c) 2025, The Brave Authors +Copyright (c) 2025, The Helium Authors + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License Version 3, as described below: + +Copyright (C) 2025 The Helium Authors + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +--- a/extensions/browser/extension_registrar.cc ++++ b/extensions/browser/extension_registrar.cc +@@ -18,6 +18,7 @@ + #include "content/public/browser/browser_context.h" + #include "content/public/browser/browser_thread.h" + #include "content/public/browser/devtools_agent_host.h" ++#include "extensions/browser/api/content_settings/content_settings_service.h" + #include "extensions/browser/blocklist_extension_prefs.h" + #include "extensions/browser/delayed_install_manager.h" + #include "extensions/browser/disable_reason.h" +@@ -534,6 +535,22 @@ void ExtensionRegistrar::AddComponentExt + } + + AddExtension(extension); ++ ++ if (!IsExtensionEnabled(extension->id())) ++ return; ++ ++ // ContentSettingsStore::RegisterExtension is only called for default ++ // components on the first run with a fresh profile. All restarts of the ++ // browser after that do not call it. This causes ContentSettingsStore's ++ // `entries_` to never insert the component ID and then ++ // ContentSettingsStore::GetValueMap always returns nullptr. I don't think ++ // Chromium is affected by this simply because they don't use content settings ++ // from default component extensions. ++ extension_prefs_->OnExtensionInstalled( ++ extension, /*disable_reasons=*/{}, syncer::StringOrdinal(), ++ extensions::kInstallFlagNone, std::string(), {} /* ruleset_checksums */); ++ extensions::ContentSettingsService::Get(browser_context_) ++ ->OnExtensionPrefsLoaded(extension->id(), extension_prefs_); + } + + void ExtensionRegistrar::RemoveComponentExtension( diff --git a/patches/helium/core/add-component-l10n-support.patch b/patches/helium/core/add-component-l10n-support.patch new file mode 100644 index 00000000..1d3641da --- /dev/null +++ b/patches/helium/core/add-component-l10n-support.patch @@ -0,0 +1,238 @@ +--- a/extensions/common/extension_l10n_util.h ++++ b/extensions/common/extension_l10n_util.h +@@ -81,7 +81,8 @@ bool LocalizeManifest(const extensions:: + bool LocalizeExtension(const base::FilePath& extension_path, + base::Value::Dict* manifest, + GzippedMessagesPermission gzip_permission, +- std::string* error); ++ std::string* error, ++ bool is_component = false); + + // Adds locale_name to the extension if it's in chrome_locales, and + // if messages file is present (we don't check content of messages file here). +@@ -129,6 +130,11 @@ extensions::MessageBundle* LoadMessageCa + GzippedMessagesPermission gzip_permission, + std::string* error); + ++extensions::MessageBundle* LoadComponentMessageCatalogs( ++ const base::FilePath& extension_root, ++ const std::string& default_locale, ++ std::string* error); ++ + // Loads message catalogs for all locales to check for validity. Used for + // validating unpacked extensions. + bool ValidateExtensionLocales(const base::FilePath& extension_path, +--- a/extensions/common/extension_l10n_util.cc ++++ b/extensions/common/extension_l10n_util.cc +@@ -18,14 +18,18 @@ + #include "base/files/file_enumerator.h" + #include "base/files/file_util.h" + #include "base/json/json_file_value_serializer.h" ++#include "base/json/json_string_value_serializer.h" + #include "base/json/json_reader.h" + #include "base/logging.h" ++#include "base/memory/ref_counted_memory.h" + #include "base/no_destructor.h" + #include "base/strings/strcat.h" + #include "base/strings/string_util.h" + #include "base/strings/stringprintf.h" + #include "base/strings/utf_string_conversions.h" + #include "base/values.h" ++#include "extensions/browser/component_extension_resource_manager.h" ++#include "extensions/browser/extensions_browser_client.h" + #include "extensions/common/constants.h" + #include "extensions/common/error_utils.h" + #include "extensions/common/extension.h" +@@ -38,6 +42,7 @@ + #include "third_party/icu/source/common/unicode/uloc.h" + #include "third_party/zlib/google/compression_utils.h" + #include "ui/base/l10n/l10n_util.h" ++#include "ui/base/resource/resource_bundle.h" + + namespace errors = extensions::manifest_errors; + namespace keys = extensions::manifest_keys; +@@ -113,6 +118,19 @@ std::optional LoadMes + return dictionary; + } + ++std::optional LoadMessageFile( ++ int resource_id, std::string* error) { ++ const ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); ++ JSONStringValueDeserializer messages_deserializer( ++ base::as_string_view(*rb.LoadDataResourceBytes(resource_id))); ++ ++ if (auto value = messages_deserializer.Deserialize(nullptr, error); value) { ++ return std::move(*value).TakeDict(); ++ } ++ ++ return std::nullopt; ++} ++ + // Localizes manifest value of string type for a given key. + bool LocalizeManifestValue(const std::string& key, + const extensions::MessageBundle& messages, +@@ -360,13 +378,16 @@ bool LocalizeManifest(const extensions:: + bool LocalizeExtension(const base::FilePath& extension_path, + base::Value::Dict* manifest, + GzippedMessagesPermission gzip_permission, +- std::string* error) { ++ std::string* error, ++ bool is_component) { + DCHECK(manifest); + + std::string default_locale = GetDefaultLocaleFromManifest(*manifest, error); + + std::unique_ptr message_bundle( +- extensions::file_util::LoadMessageBundle(extension_path, default_locale, ++ is_component ? extensions::file_util::LoadComponentMessageBundle(extension_path, ++ default_locale, error) ++ : extensions::file_util::LoadMessageBundle(extension_path, default_locale, + gzip_permission, error)); + + if (!message_bundle && !error->empty()) +@@ -504,6 +525,42 @@ extensions::MessageBundle* LoadMessageCa + return extensions::MessageBundle::Create(catalogs, error); + } + ++extensions::MessageBundle* LoadComponentMessageCatalogs( ++ const base::FilePath& extension_root, ++ const std::string& default_locale, ++ std::string* error) { ++ std::vector all_fallback_locales; ++ base::FilePath locale_path(extensions::kLocaleFolder); ++ ++ auto* manager = ++ extensions::ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager(); ++ ++ GetAllFallbackLocales(default_locale, &all_fallback_locales); ++ ++ extensions::MessageBundle::CatalogVector catalogs; ++ for (const auto& fallback_locale : all_fallback_locales) { ++ base::FilePath this_locale_path = ++ locale_path.AppendASCII(fallback_locale) ++ .AppendASCII(extensions::kMessagesFilename); ++ int resource_id = 0; ++ ++ if (!manager->IsComponentExtensionResource( ++ extension_root, this_locale_path, &resource_id)) { ++ continue; ++ } ++ ++ std::optional catalog = LoadMessageFile(resource_id, error); ++ if (!catalog.has_value()) { ++ return nullptr; ++ } ++ ++ catalogs.push_back(std::move(*catalog)); ++ } ++ ++ return extensions::MessageBundle::Create( ++ catalogs, error); ++} ++ + bool ValidateExtensionLocales(const base::FilePath& extension_path, + const base::Value::Dict& manifest, + std::string* error) { +--- a/extensions/browser/l10n_file_util.h ++++ b/extensions/browser/l10n_file_util.h +@@ -47,7 +47,8 @@ LoadMessageBundleSubstitutionMapFromPath + const std::vector& paths, + const ExtensionId& extension_id, + const std::string& default_locale, +- extension_l10n_util::GzippedMessagesPermission gzip_permission); ++ extension_l10n_util::GzippedMessagesPermission gzip_permission, ++ bool is_component = false); + + } // namespace extensions::l10n_file_util + +--- a/extensions/browser/l10n_file_util.cc ++++ b/extensions/browser/l10n_file_util.cc +@@ -38,7 +38,8 @@ LoadMessageBundleSubstitutionMapFromPath + const std::vector& paths, + const ExtensionId& extension_id, + const std::string& default_locale, +- extension_l10n_util::GzippedMessagesPermission gzip_permission) { ++ extension_l10n_util::GzippedMessagesPermission gzip_permission, ++ bool is_component) { + std::unique_ptr return_value = + LoadNonLocalizedMessageBundleSubstitutionMap(extension_id); + +@@ -49,8 +50,13 @@ LoadMessageBundleSubstitutionMapFromPath + + std::string error; + for (const base::FilePath& path : paths) { +- std::unique_ptr bundle(file_util::LoadMessageBundle( +- path, default_locale, gzip_permission, &error)); ++ std::unique_ptr bundle( ++ is_component ++ ? file_util::LoadComponentMessageBundle( ++ path, default_locale, &error) ++ : file_util::LoadMessageBundle( ++ path, default_locale, gzip_permission, &error)); ++ + if (bundle) { + for (const auto& iter : *bundle->dictionary()) { + // |insert| only adds new entries, and does not replace entries in +--- a/extensions/common/file_util.h ++++ b/extensions/common/file_util.h +@@ -162,6 +162,11 @@ MessageBundle* LoadMessageBundle( + extension_l10n_util::GzippedMessagesPermission gzip_permission, + std::string* error); + ++MessageBundle* LoadComponentMessageBundle( ++ const base::FilePath& extension_root, ++ const std::string& default_locale, ++ std::string* error); ++ + // Helper functions for getting paths for files used in content verification. + base::FilePath GetVerifiedContentsPath(const base::FilePath& extension_path); + base::FilePath GetComputedHashesPath(const base::FilePath& extension_path); +--- a/extensions/common/file_util.cc ++++ b/extensions/common/file_util.cc +@@ -593,6 +593,25 @@ MessageBundle* LoadMessageBundle( + return message_bundle; + } + ++MessageBundle* LoadComponentMessageBundle( ++ const base::FilePath& extension_root, ++ const std::string& default_locale, ++ std::string* error) { ++ error->clear(); ++ ++ std::set chrome_locales; ++ extension_l10n_util::GetAllLocales(&chrome_locales); ++ ++ if (default_locale.empty() || !base::Contains(chrome_locales, default_locale)) { ++ *error = l10n_util::GetStringUTF8( ++ IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED); ++ return nullptr; ++ } ++ ++ return extension_l10n_util::LoadComponentMessageCatalogs( ++ extension_root, default_locale, error); ++} ++ + base::FilePath GetVerifiedContentsPath(const base::FilePath& extension_path) { + return extension_path.Append(kMetadataFolder) + .Append(kVerifiedContentsFilename); +--- a/extensions/browser/renderer_startup_helper.cc ++++ b/extensions/browser/renderer_startup_helper.cc +@@ -608,6 +608,18 @@ void RendererStartupHelper::GetMessageBu + paths_to_load.push_back(imported_extension->path()); + } + ++ bool is_component = ++ extension->location() == extensions::mojom::ManifestLocation::kComponent; ++ ++ if (is_component) { ++ auto dictionary_map = l10n_file_util::LoadMessageBundleSubstitutionMapFromPaths( ++ paths_to_load, extension_id, default_locale, ++ extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource, ++ is_component); ++ std::move(callback).Run(ToFlatMap(*dictionary_map)); ++ return; ++ } ++ + // This blocks tab loading. Priority is inherited from the calling context. + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, diff --git a/patches/helium/core/ublock-helium-services.patch b/patches/helium/core/ublock-helium-services.patch new file mode 100644 index 00000000..574099e4 --- /dev/null +++ b/patches/helium/core/ublock-helium-services.patch @@ -0,0 +1,259 @@ +--- a/components/helium_services/helium_services_helpers.cc ++++ b/components/helium_services/helium_services_helpers.cc +@@ -88,6 +88,11 @@ bool ShouldAccessUpdateService(const Pre + prefs.GetBoolean(prefs::kHeliumUpdateFetchingEnabled); + } + ++bool ShouldAccessUBlockAssets(const PrefService& prefs) { ++ return ShouldAccessServices(prefs) && ++ prefs.GetBoolean(prefs::kHeliumUBlockAssetsEnabled); ++} ++ + GURL GetExtensionUpdateURL(const PrefService& prefs) { + if (!ShouldAccessExtensionService(prefs)) { + return GetDummyURL(); +@@ -127,6 +132,14 @@ GURL GetBrowserUpdateURL(const PrefServi + #endif + } + ++GURL GetUBlockAssetsURL(const PrefService& prefs) { ++ if (!ShouldAccessUBlockAssets(prefs)) { ++ return GetDummyURL(); ++ } ++ ++ return GetServicesBaseURL(prefs).Resolve("/ubo/assets.json"); ++} ++ + void ConfigurePrefChangeRegistrarFor(std::string_view pref_name, + PrefChangeRegistrar& registrar, const base::RepeatingClosure& observer) { + 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[] = + COMPONENT_EXPORT(HELIUM) bool ShouldFetchBangs(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) bool ShouldAccessExtensionService(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) bool ShouldAccessUpdateService(const PrefService& prefs); ++COMPONENT_EXPORT(HELIUM) bool ShouldAccessUBlockAssets(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) GURL GetServicesBaseURL(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) GURL GetDummyURL(); + COMPONENT_EXPORT(HELIUM) GURL GetExtensionUpdateURL(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) GURL GetWebstoreSnippetURL(const PrefService& prefs, std::string_view id); + COMPONENT_EXPORT(HELIUM) GURL GetSpellcheckURL(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) GURL GetBrowserUpdateURL(const PrefService& prefs); ++COMPONENT_EXPORT(HELIUM) GURL GetUBlockAssetsURL(const PrefService& prefs); + COMPONENT_EXPORT(HELIUM) std::optional GetValidUserOverridenURL(std::string_view user_url_); + COMPONENT_EXPORT(HELIUM) void ConfigurePrefChangeRegistrarFor(std::string_view pref_name, + 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 + inline constexpr char kHeliumSpellcheckEnabled[] = + "helium.services.spellcheck_files"; + ++inline constexpr char kHeliumUBlockAssetsEnabled[] = ++ "helium.services.ublock_assets"; + } // namespace prefs + + #endif // COMPONENTS_HELIUM_SERVICES_PREF_NAMES_H_ +--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc ++++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc +@@ -364,6 +364,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil + settings_api::PrefType::kBoolean; + (*s_allowlist)[::prefs::kHeliumUpdateFetchingEnabled] = + settings_api::PrefType::kBoolean; ++ (*s_allowlist)[::prefs::kHeliumUBlockAssetsEnabled] = ++ settings_api::PrefType::kBoolean; + (*s_allowlist)[::prefs::kHeliumServicesOrigin] = + settings_api::PrefType::kString; + +--- a/chrome/browser/ui/browser_ui_prefs.cc ++++ b/chrome/browser/ui/browser_ui_prefs.cc +@@ -208,6 +208,7 @@ void RegisterBrowserUserPrefs(user_prefs + registry->RegisterBooleanPref(prefs::kHeliumDidOnboarding, false); + registry->RegisterBooleanPref(prefs::kHeliumServicesConsented, false); + registry->RegisterBooleanPref(prefs::kHeliumUpdateFetchingEnabled, true); ++ registry->RegisterBooleanPref(prefs::kHeliumUBlockAssetsEnabled, true); + } + + registry->RegisterBooleanPref( +--- a/chrome/browser/extensions/extension_service.cc ++++ b/chrome/browser/extensions/extension_service.cc +@@ -33,6 +33,7 @@ + #include "base/strings/utf_string_conversions.h" + #include "base/syslog_logging.h" + #include "base/task/single_thread_task_runner.h" ++#include "base/task/thread_pool.h" + #include "base/threading/thread_restrictions.h" + #include "base/time/time.h" + #include "base/trace_event/trace_event.h" +@@ -72,6 +73,9 @@ + #include "chrome/common/pref_names.h" + #include "chrome/common/url_constants.h" + #include "components/crx_file/id_util.h" ++#include "components/helium_services/extension_ids.h" ++#include "components/helium_services/helium_services_helpers.h" ++#include "components/helium_services/pref_names.h" + #include "components/policy/core/common/policy_pref_names.h" + #include "components/supervised_user/core/browser/supervised_user_preferences.h" + #include "content/public/browser/browser_thread.h" +@@ -199,6 +203,7 @@ ExtensionService::ExtensionService( + ready_(ready), + updater_(ExtensionUpdater::Get(profile)), + component_loader_(ComponentLoader::Get(profile_)), ++ can_access_ublock_assets_(helium::ShouldAccessUBlockAssets(*profile_->GetPrefs())), + error_controller_(error_controller), + external_install_manager_(ExternalInstallManager::Get(profile)), + extension_registrar_delegate_( +@@ -272,6 +277,33 @@ ExtensionService::ExtensionService( + prefs::kExtensionsUIDeveloperMode, + base::BindRepeating(&ExtensionService::OnDeveloperModePrefChanged, + base::Unretained(this))); ++ helium::ConfigurePrefChangeRegistrarFor( ++ prefs::kHeliumUBlockAssetsEnabled, ++ pref_change_registrar_, ++ base::BindRepeating(&ExtensionService::ToggleUBlockExtension, ++ base::Unretained(this))); ++} ++ ++void ExtensionService::ToggleUBlockExtension() { ++ if (!extension_registrar_) { ++ return; ++ } ++ ++ bool has_permission_now = helium::ShouldAccessUBlockAssets(*profile_->GetPrefs()); ++ if (can_access_ublock_assets_ == has_permission_now) { ++ return; ++ } ++ ++ can_access_ublock_assets_ = has_permission_now; ++ ++ for (int seconds : {0, 5}) { ++ content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})->PostDelayedTask( ++ FROM_HERE, ++ base::BindOnce(&ExtensionRegistrar::ReloadExtension, ++ extension_registrar_->GetWeakPtr(), ++ helium::kUBlockOriginComponentId), ++ base::Seconds(seconds)); ++ } + } + + base::WeakPtr ExtensionService::AsWeakPtr() { +--- a/chrome/browser/extensions/extension_service.h ++++ b/chrome/browser/extensions/extension_service.h +@@ -165,6 +165,8 @@ class ExtensionService : public Extensio + // KeyedService two-phase shutdown. + void Shutdown(); + ++ void ToggleUBlockExtension(); ++ + // Performs action based on verdicts received from the Extension Telemetry + // server. Currently, these verdicts are limited to off-store extensions. + void PerformActionBasedOnExtensionTelemetryServiceVerdicts( +@@ -355,6 +357,8 @@ class ExtensionService : public Extensio + // Used in test to ensure the service's shutdown method has been called. + bool is_shut_down_executed_ = false; + ++ bool can_access_ublock_assets_; ++ + // The controller for the UI that alerts the user about any blocklisted + // extensions. Not owned. + raw_ptr error_controller_ = nullptr; +--- a/extensions/browser/api/storage/BUILD.gn ++++ b/extensions/browser/api/storage/BUILD.gn +@@ -60,6 +60,7 @@ source_set("storage") { + ":settings_namespace", + ":settings_observer", + "//base", ++ "//components/helium_services", + "//components/keyed_service/content", + "//components/value_store", + "//content/public/browser", +--- a/extensions/browser/api/storage/storage_frontend.cc ++++ b/extensions/browser/api/storage/storage_frontend.cc +@@ -25,6 +25,8 @@ + #include "base/strings/string_util.h" + #include "base/strings/stringprintf.h" + #include "base/trace_event/trace_event.h" ++#include "chrome/browser/profiles/profile.h" ++#include "components/helium_services/helium_services_helpers.h" + #include "components/value_store/value_store_factory.h" + #include "content/public/browser/browser_context.h" + #include "content/public/browser/browser_thread.h" +@@ -222,6 +224,24 @@ void StorageFrontend::OnReadFinished( + + if (success) { + get_result.data = result.PassSettings(); ++ ++ Profile* profile_ = Profile::FromBrowserContext(browser_context_); ++ ++ if (Manifest::IsUBlockComponent(extension_id) ++ && storage_area == StorageAreaNamespace::kManaged ++ && helium::ShouldAccessUBlockAssets(*profile_->GetPrefs())) { ++ ++ if (!get_result.data->FindDict("adminSettings")) { ++ get_result.data->Set("adminSettings", base::Value::Dict()); ++ } ++ ++ base::Value::Dict* adminSettings = get_result.data->FindDict("adminSettings"); ++ ++ if (!adminSettings->Find("assetsBootstrapLocation")) { ++ adminSettings->Set("assetsBootstrapLocation", ++ base::Value(helium::GetUBlockAssetsURL(*profile_->GetPrefs()).spec())); ++ } ++ } + } + + std::move(callback).Run(std::move(get_result)); +--- a/third_party/ublock/js/assets.js ++++ b/third_party/ublock/js/assets.js +@@ -525,7 +525,7 @@ let assetSourceRegistry = Object.create( + function getAssetSourceRegistry() { + if ( assetSourceRegistryPromise === undefined ) { + assetSourceRegistryPromise = cacheStorage.get( +- 'assetSourceRegistry' ++ 'assetSourceRegistry' + (µb.assetsBootstrapLocation ?? '') + ).then(bin => { + if ( bin instanceof Object ) { + if ( bin.assetSourceRegistry instanceof Object ) { +--- a/chrome/app/settings_strings.grdp ++++ b/chrome/app/settings_strings.grdp +@@ -1957,6 +1957,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. + ++ ++ Allow downloading filter lists for uBlock Origin ++ ++ ++ Helium will fetch fresh filter lists for uBlock Origin. All requests to lists are proxied to protect your privacy. When disabled, default filter lists will be loaded from local storage, which are only updated along with Helium. Optional filter lists will be requested without proxying. ++ + + Use your own instance of Helium services + +--- a/chrome/browser/resources/settings/privacy_page/services_page.html ++++ b/chrome/browser/resources/settings/privacy_page/services_page.html +@@ -36,6 +36,11 @@ + sub-label="$i18n{heliumUpdatesToggleDescription}"> + + ++ ++ + + + +--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc ++++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +@@ -1963,6 +1963,10 @@ void AddPrivacyStrings(content::WebUIDat + IDS_SETTINGS_HELIUM_SERVICES_UPDATE}, + {"heliumUpdatesToggleDescription", + IDS_SETTINGS_HELIUM_SERVICES_UPDATE_DESCRIPTION}, ++ {"heliumUboToggle", ++ IDS_SETTINGS_HELIUM_SERVICES_UBO}, ++ {"heliumUboToggleDescription", ++ IDS_SETTINGS_HELIUM_SERVICES_UBO_DESCRIPTION}, + {"heliumOriginOverride", IDS_SETTINGS_HELIUM_SERVICES_OVERRIDE}, + {"heliumOriginOverrideDescription", + IDS_SETTINGS_HELIUM_SERVICES_OVERRIDE_DESCRIPTION}, diff --git a/patches/helium/core/ublock-install-as-component.patch b/patches/helium/core/ublock-install-as-component.patch new file mode 100644 index 00000000..a9d67646 --- /dev/null +++ b/patches/helium/core/ublock-install-as-component.patch @@ -0,0 +1,111 @@ +--- /dev/null ++++ b/components/helium_services/extension_ids.h +@@ -0,0 +1,18 @@ ++// 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_EXTENSION_IDS_H_ ++#define COMPONENTS_HELIUM_SERVICES_EXTENSION_IDS_H_ ++ ++namespace helium { ++ ++inline constexpr char kUBlockOriginWebstoreId[] = ++ "cjpalhdlnbpafiamejdnhcphjbkeiagm"; ++ ++inline constexpr char kUBlockOriginComponentId[] = ++ "blockjmkbacgjkknlgpkjjiijinjdanf"; ++ ++} // namespace helium ++ ++#endif /* COMPONENTS_HELIUM_SERVICES_EXTENSION_IDS_H_ */ +--- a/chrome/browser/extensions/component_loader.h ++++ b/chrome/browser/extensions/component_loader.h +@@ -215,6 +215,7 @@ class ComponentLoader : public KeyedServ + const std::string& name_string, + const std::string& description_string); + void AddWebStoreApp(); ++ void AddUBlock(); + + #if BUILDFLAG(IS_CHROMEOS) + void AddChromeApp(); +--- a/chrome/browser/extensions/component_loader.cc ++++ b/chrome/browser/extensions/component_loader.cc +@@ -57,6 +57,7 @@ + #include "ui/accessibility/accessibility_features.h" + #include "ui/base/l10n/l10n_util.h" + #include "ui/base/resource/resource_bundle.h" ++#include "third_party/ublock/resources/grit/ublock_resources.h" + + #if BUILDFLAG(IS_CHROMEOS) + #include "ash/constants/ash_features.h" +@@ -429,6 +430,21 @@ void ComponentLoader::AddWebStoreApp() { + l10n_util::GetStringUTF8(IDS_WEBSTORE_APP_DESCRIPTION)); + } + ++void ComponentLoader::AddUBlock() { ++ Add(IDR_UBLOCK_MANIFEST_JSON, base::FilePath(FILE_PATH_LITERAL("ublock"))); ++ ++ using namespace extension_l10n_util; ++ ComponentExtensionInfo& info = component_extensions_.back(); ++ std::string error; ++ bool localized = LocalizeExtension(info.root_directory, &info.manifest, ++ GzippedMessagesPermission::kAllowForTrustedSource, ++ &error, true); ++ ++ if (!localized) { ++ LOG(ERROR) << "Failed to localize uBlock: " << error; ++ } ++} ++ + #if BUILDFLAG(IS_CHROMEOS) + void ComponentLoader::AddChromeApp() { + AddWithNameAndDescription( +@@ -506,6 +522,7 @@ void ComponentLoader::AddDefaultComponen + + if (!skip_session_components) { + AddWebStoreApp(); ++ AddUBlock(); + #if BUILDFLAG(IS_CHROMEOS) + AddChromeApp(); + #endif // BUILDFLAG(IS_CHROMEOS) +--- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc ++++ b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc +@@ -15,8 +15,10 @@ + #include "chrome/common/buildflags.h" + #include "chrome/common/extensions/extension_constants.h" + #include "chrome/grit/browser_resources.h" ++#include "components/helium_services/extension_ids.h" + #include "extensions/common/constants.h" + #include "printing/buildflags/buildflags.h" ++#include "third_party/ublock/resources/grit/ublock_resources.h" + + #if BUILDFLAG(IS_CHROMEOS) + #include "ash/keyboard/ui/grit/keyboard_resources.h" +@@ -31,6 +33,7 @@ bool IsComponentExtensionAllowlisted(con + constexpr auto kAllowed = base::MakeFixedFlatSet({ + extension_misc::kInAppPaymentsSupportAppId, + extension_misc::kPdfExtensionId, ++ helium::kUBlockOriginComponentId, + #if BUILDFLAG(IS_CHROMEOS) + extension_misc::kAssessmentAssistantExtensionId, + extension_misc::kAccessibilityCommonExtensionId, +@@ -86,6 +89,7 @@ bool IsComponentExtensionAllowlisted(int + case IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST_MV3: + case IDR_READING_MODE_GDOCS_HELPER_MANIFEST: + case IDR_WEBSTORE_MANIFEST: ++ case IDR_UBLOCK_MANIFEST_JSON: + + #if BUILDFLAG(IS_CHROMEOS) + // Separate ChromeOS list, as it is quite large. +--- a/ui/base/resource/resource_bundle.cc ++++ b/ui/base/resource/resource_bundle.cc +@@ -768,7 +768,7 @@ base::RefCountedMemory* ResourceBundle:: + + std::string_view data = GetRawDataResourceForScale(resource_id, scale_factor); + if (data.empty()) +- return nullptr; ++ return new base::RefCountedString(); // fixes accessing "web_accessible_resources/empty" in u0 + + if (net::GZipHeader::HasGZipHeader(base::as_byte_span(data)) || + HasBrotliHeader(data)) { diff --git a/patches/helium/core/ublock-migrate-prefs.patch b/patches/helium/core/ublock-migrate-prefs.patch new file mode 100644 index 00000000..c3015f42 --- /dev/null +++ b/patches/helium/core/ublock-migrate-prefs.patch @@ -0,0 +1,129 @@ +--- a/chrome/browser/extensions/chrome_extension_registrar_delegate.cc ++++ b/chrome/browser/extensions/chrome_extension_registrar_delegate.cc +@@ -12,6 +12,7 @@ + #include "base/metrics/histogram_functions.h" + #include "base/metrics/histogram_macros.h" + #include "base/notimplemented.h" ++#include "base/task/thread_pool.h" + #include "chrome/browser/extensions/component_loader.h" + #include "chrome/browser/extensions/corrupted_extension_reinstaller.h" + #include "chrome/browser/extensions/data_deleter.h" +@@ -31,6 +32,9 @@ + #include "chrome/browser/ui/webui/favicon_source.h" + #include "chrome/common/webui_url_constants.h" + #include "components/favicon_base/favicon_url_parser.h" ++#include "components/helium_services/extension_ids.h" ++#include "components/value_store/value_store.h" ++#include "extensions/browser/api/storage/storage_frontend.h" + #include "extensions/browser/delayed_install_manager.h" + #include "extensions/browser/disable_reason.h" + #include "extensions/browser/extension_file_task_runner.h" +@@ -248,7 +252,85 @@ void ChromeExtensionRegistrarDelegate::P + } + } + +- DataDeleter::StartDeleting(profile_, extension.get(), subtask_done_callback); ++ ChromeExtensionRegistrarDelegate::MaybeMigrateUBlockPrefs( ++ extension, base::BindOnce([](Profile* profile, ++ scoped_refptr extension, ++ base::OnceClosure cb) { ++ content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, ++ base::BindOnce(&DataDeleter::StartDeleting, ++ profile, base::RetainedRef(extension), ++ std::move(cb))); ++ }, profile_, extension, std::move(subtask_done_callback))); ++} ++ ++void ChromeExtensionRegistrarDelegate::MaybeMigrateUBlockPrefs( ++ scoped_refptr extension, ++ base::OnceClosure callback) { ++ StorageFrontend* frontend = StorageFrontend::Get(profile_); ++ scoped_refptr component = registry_->GetExtensionById( ++ helium::kUBlockOriginComponentId, ExtensionRegistry::EVERYTHING); ++ ++ if (!frontend || extension->id() != helium::kUBlockOriginWebstoreId || !component) { ++ if (!component) { ++ LOG(ERROR) << "component-based ublock is missing, skipping migration"; ++ } ++ std::move(callback).Run(); ++ return; ++ } ++ ++ std::array namespaces = { settings_namespace::LOCAL, ++ settings_namespace::SYNC }; ++ base::RepeatingClosure barrier_cb = ++ base::BarrierClosure(namespaces.size(), std::move(callback)); ++ ++ for (settings_namespace::Namespace ns : namespaces) { ++ ValueStoreCache* store_cache = frontend->GetValueStoreCache(ns); ++ if (!store_cache) { ++ continue; ++ } ++ ++ GetExtensionFileTaskRunner()->PostTask(FROM_HERE, ++ base::BindOnce( ++ &ValueStoreCache::RunWithValueStoreForExtension, ++ base::Unretained(store_cache), ++ base::BindOnce([] (base::WeakPtr weak_ptr, ++ scoped_refptr component, ++ ValueStoreCache* store_cache, ++ base::RepeatingClosure cb, ++ value_store::ValueStore* old) { ++ store_cache->RunWithValueStoreForExtension( ++ base::BindOnce( ++ &ChromeExtensionRegistrarDelegate::HandleUblockPrefsMigration, ++ weak_ptr, old).Then(std::move(cb)), component); ++ }, weak_factory_.GetWeakPtr(), component, store_cache, barrier_cb), ++ extension)); ++ } ++} ++ ++void ChromeExtensionRegistrarDelegate::HandleUblockPrefsMigration( ++ value_store::ValueStore* old_, value_store::ValueStore* new_) { ++ if (!old_ || !new_) { ++ LOG(ERROR) << "missing old/new"; ++ return; ++ } ++ ++ auto oldValues = old_->Get(); ++ if (!oldValues.status().ok()) { ++ LOG(ERROR) << "failed getting webstore uBlock prefs"; ++ return; ++ } ++ ++ auto getKeysResult = new_->GetKeys(); ++ CHECK(getKeysResult.status().ok()); ++ ++ auto existingKeys = getKeysResult.PassSettings(); ++ for (const auto [ key, value ] : oldValues.PassSettings()) { ++ if (existingKeys.Find(key)) { ++ continue; ++ } ++ ++ new_->Set(value_store::ValueStore::DEFAULTS, key, value); ++ } + } + + void ChromeExtensionRegistrarDelegate::DoLoadExtensionForReload( +--- a/chrome/browser/extensions/chrome_extension_registrar_delegate.h ++++ b/chrome/browser/extensions/chrome_extension_registrar_delegate.h +@@ -7,6 +7,7 @@ + + #include "base/memory/raw_ptr.h" + #include "base/memory/weak_ptr.h" ++#include "components/value_store/value_store.h" + #include "extensions/browser/extension_registrar.h" + #include "extensions/buildflags/buildflags.h" + +@@ -51,6 +52,11 @@ class ChromeExtensionRegistrarDelegate : + void PreUninstallExtension(scoped_refptr extension) override; + void PostUninstallExtension(scoped_refptr extension, + base::OnceClosure done_callback) override; ++ void MaybeMigrateUBlockPrefs(scoped_refptr extension, ++ base::OnceClosure callback); ++ void HandleUblockPrefsMigration(value_store::ValueStore* old_, ++ value_store::ValueStore* new_); ++ + void LoadExtensionForReload(const ExtensionId& extension_id, + const base::FilePath& path) override; + void LoadExtensionForReloadWithQuietFailure( diff --git a/patches/helium/core/ublock-reconfigure-defaults.patch b/patches/helium/core/ublock-reconfigure-defaults.patch new file mode 100644 index 00000000..65f41574 --- /dev/null +++ b/patches/helium/core/ublock-reconfigure-defaults.patch @@ -0,0 +1,225 @@ +--- a/extensions/common/manifest.h ++++ b/extensions/common/manifest.h +@@ -16,6 +16,7 @@ + #include "extensions/common/extension_id.h" + #include "extensions/common/hashed_extension_id.h" + #include "extensions/common/mojom/manifest.mojom-shared.h" ++#include "components/helium_services/extension_ids.h" + + namespace extensions { + struct InstallWarning; +@@ -90,6 +91,10 @@ class Manifest final { + location == mojom::ManifestLocation::kExternalComponent; + } + ++ static inline bool IsUBlockComponent(std::string_view extension_id) { ++ return extension_id == helium::kUBlockOriginComponentId; ++ } ++ + static inline bool IsValidLocation(mojom::ManifestLocation location) { + return location > mojom::ManifestLocation::kInvalidLocation && + location <= mojom::ManifestLocation::kMaxValue; +--- a/chrome/browser/chrome_content_browser_client.cc ++++ b/chrome/browser/chrome_content_browser_client.cc +@@ -6171,7 +6171,8 @@ void AddChromeSchemeFactories( + std::vector allowed_webui_hosts; + // Support for chrome:// scheme if appropriate. + if ((extension->is_extension() || extension->is_platform_app()) && +- Manifest::IsComponentLocation(extension->location())) { ++ Manifest::IsComponentLocation(extension->location()) ++ && !Manifest::IsUBlockComponent(extension->id())) { + // Components of chrome that are implemented as extensions or platform apps + // are allowed to use chrome://resources/ and chrome://theme/ URLs. + allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost); +--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc ++++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc +@@ -156,8 +156,11 @@ void AppInfoSummaryPanel::AddDescription + } + + void AppInfoSummaryPanel::AddDetailsControl(views::View* vertical_stack) { ++ using namespace extensions; ++ + // Component apps have no details. +- if (app_->location() == extensions::mojom::ManifestLocation::kComponent) { ++ if (Manifest::IsComponentLocation(app_->location()) ++ && !Manifest::IsUBlockComponent(app_->id())) { + return; + } + +--- a/chrome/common/extensions/chrome_extensions_client.cc ++++ b/chrome/common/extensions/chrome_extensions_client.cc +@@ -214,8 +214,9 @@ void ChromeExtensionsClient::AddOriginAc + // conservative. Components shouldn't be subject to enterprise policy controls + // or blocking access to the webstore so they get the highest priority + // allowlist entry. +- if (extensions::Manifest::IsComponentLocation(extension.location()) && +- is_extension_active) { ++ if (extensions::Manifest::IsComponentLocation(extension.location()) ++ && !extensions::Manifest::IsUBlockComponent(extension.id()) ++ && is_extension_active) { + origin_patterns->push_back(network::mojom::CorsOriginPattern::New( + content::kChromeUIScheme, chrome::kChromeUIThemeHost, /*port=*/0, + network::mojom::CorsDomainMatchMode::kDisallowSubdomains, +--- a/extensions/common/permissions/permissions_data.cc ++++ b/extensions/common/permissions/permissions_data.cc +@@ -114,7 +114,8 @@ void PermissionsData::SetPolicyDelegate( + bool PermissionsData::CanExecuteScriptEverywhere( + const ExtensionId& extension_id, + mojom::ManifestLocation location) { +- if (location == mojom::ManifestLocation::kComponent) ++ if (location == mojom::ManifestLocation::kComponent ++ && !Manifest::IsUBlockComponent(extension_id)) + return true; + + const ExtensionsClient::ScriptingAllowlist& allowlist = +@@ -479,7 +480,8 @@ bool PermissionsData::CanCaptureVisibleP + // blocked host in a different page and then capture that, but it's better + // than nothing (and policy hosts can set their x-frame options + // accordingly). +- if (location_ != mojom::ManifestLocation::kComponent && ++ if ((location_ != mojom::ManifestLocation::kComponent ++ || Manifest::IsUBlockComponent(extension_id_)) && + IsPolicyBlockedHostUnsafe(origin_url)) { + if (error) + *error = extension_misc::kPolicyBlockedScripting; +@@ -617,7 +619,8 @@ PermissionsData::PageAccess PermissionsD + const URLPatternSet* tab_url_patterns, + std::string* error) const { + runtime_lock_.AssertAcquired(); +- if (location_ != mojom::ManifestLocation::kComponent && ++ if ((location_ != mojom::ManifestLocation::kComponent ++ || Manifest::IsUBlockComponent(extension_id_)) && + IsPolicyBlockedHostUnsafe(document_url)) { + if (error) + *error = extension_misc::kPolicyBlockedScripting; +@@ -629,7 +632,8 @@ PermissionsData::PageAccess PermissionsD + + if (base::FeatureList::IsEnabled( + extensions_features::kExtensionsMenuAccessControl) && +- context_id_ && location_ != mojom::ManifestLocation::kComponent && ++ context_id_ && (location_ != mojom::ManifestLocation::kComponent ++ || Manifest::IsUBlockComponent(extension_id_)) && + !Manifest::IsPolicyLocation(location_)) { + base::AutoLock lock(GetContextPermissionsLock()); + auto& context_permissions = GetContextPermissions(*context_id_); +--- a/chrome/browser/extensions/extension_util.cc ++++ b/chrome/browser/extensions/extension_util.cc +@@ -186,7 +186,8 @@ void SetIsIncognitoEnabled(const std::st + #if !BUILDFLAG(IS_ANDROID) + // TODO(treib,kalman): Should this be Manifest::IsComponentLocation(..)? + // (which also checks for kExternalComponent). +- if (extension->location() == mojom::ManifestLocation::kComponent) { ++ if (extension->location() == mojom::ManifestLocation::kComponent ++ && !Manifest::IsUBlockComponent(extension->id())) { + // This shouldn't be called for component extensions unless it is called + // by sync, for syncable component extensions. + // See http://crbug.com/112290 and associated CLs for the sordid history. +--- a/chrome/browser/extensions/standard_management_policy_provider.cc ++++ b/chrome/browser/extensions/standard_management_policy_provider.cc +@@ -27,6 +27,14 @@ namespace { + bool AdminPolicyIsModifiable(const Extension* source_extension, + const Extension* extension, + std::u16string* error) { ++ bool is_u0 = Manifest::IsUBlockComponent(extension->id()); ++ ++ if (source_extension && Manifest::IsUBlockComponent(source_extension->id())) { ++ return is_u0; ++ } else if (source_extension == nullptr && is_u0) { ++ return true; ++ } ++ + // Component and force installed extensions can enable/disable all other + // extensions including force installed ones (but component are off limits). + const bool component_or_force_installed = +@@ -239,6 +247,11 @@ bool StandardManagementPolicyProvider::M + const Extension* extension, + std::u16string* error) const { + ManagedInstallationMode mode = settings_->GetInstallationMode(extension); ++ ++ if (Manifest::IsComponentLocation(extension->location())) { ++ return true; ++ } ++ + // Disallow removing of recommended extension, to avoid re-install it + // again while policy is reload. But disabling of recommended extension is + // allowed. +--- a/extensions/browser/extension_util.cc ++++ b/extensions/browser/extension_util.cc +@@ -94,7 +94,8 @@ bool IsIncognitoEnabled(const ExtensionI + } + // If this is an existing component extension we always allow it to + // work in incognito mode. +- if (Manifest::IsComponentLocation(extension->location())) { ++ if (Manifest::IsComponentLocation(extension->location()) ++ && !Manifest::IsUBlockComponent(extension->id())) { + return true; + } + if (extension->is_login_screen_extension()) { +--- a/extensions/browser/extension_prefs.cc ++++ b/extensions/browser/extension_prefs.cc +@@ -30,6 +30,7 @@ + #include "build/build_config.h" + #include "components/content_settings/core/common/content_settings.h" + #include "components/crx_file/id_util.h" ++#include "components/helium_services/extension_ids.h" + #include "components/pref_registry/pref_registry_syncable.h" + #include "components/prefs/pref_service.h" + #include "components/supervised_user/core/common/buildflags.h" +@@ -707,11 +708,18 @@ bool ExtensionPrefs::ReadPrefAsBoolean(c + std::string_view pref_key, + bool* out_value) const { + const base::Value::Dict* ext = GetExtensionPref(extension_id); +- if (!ext) { +- return false; ++ std::optional value; ++ ++ if (ext) { ++ value = ext->FindBoolByDottedPath(pref_key); ++ } ++ ++ if (!value.has_value() ++ && extension_id == helium::kUBlockOriginComponentId ++ && pref_key == kPrefIncognitoEnabled) { ++ value = true; + } + +- std::optional value = ext->FindBoolByDottedPath(pref_key); + if (!value) { + return false; + } +@@ -1148,6 +1156,9 @@ void ExtensionPrefs::ClearInapplicableDi + const DisableReasonSet kAllowDisableReasons = { + disable_reason::DISABLE_RELOAD, + disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT, ++ Manifest::IsUBlockComponent(component_extension_id) ++ ? disable_reason::DISABLE_USER_ACTION ++ : disable_reason::DISABLE_RELOAD, + disable_reason::DISABLE_CORRUPTED, disable_reason::DISABLE_REINSTALL}; + + const base::flat_set current_disable_reasons = +@@ -2236,6 +2247,7 @@ void ExtensionPrefs::RegisterProfilePref + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterDictionaryPref(pref_names::kExtensions); + registry->RegisterListPref(pref_names::kPinnedExtensions, ++ base::Value::List().Append(helium::kUBlockOriginComponentId), + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterListPref(pref_names::kDeletedComponentExtensions); + registry->RegisterDictionaryPref(kExtensionsBlocklistUpdate); +@@ -2300,14 +2312,14 @@ bool ExtensionPrefs::GetUserExtensionPre + ExtensionIdContainer* id_container_out) const { + DCHECK(id_container_out->empty()); + +- const base::Value* user_pref_value = prefs_->GetUserPrefValue(pref); +- if (!user_pref_value || !user_pref_value->is_list()) { ++ const base::Value& user_pref_value = prefs_->GetValue(pref); ++ if (!user_pref_value.is_list()) { + return false; + } + + std::insert_iterator insert_iterator( + *id_container_out, id_container_out->end()); +- for (const auto& entry : user_pref_value->GetList()) { ++ for (const auto& entry : user_pref_value.GetList()) { + if (!entry.is_string()) { + NOTREACHED(); + } diff --git a/patches/helium/core/ublock-setup-sources.patch b/patches/helium/core/ublock-setup-sources.patch new file mode 100644 index 00000000..5046cdd6 --- /dev/null +++ b/patches/helium/core/ublock-setup-sources.patch @@ -0,0 +1,350 @@ +--- /dev/null ++++ b/third_party/ublock/README.chromium +@@ -0,0 +1,14 @@ ++Name: uBlock Origin ++Short Name: ublock ++URL: https://github.com/gorhill/uBlock ++Version: 1.66.4 ++License: GPL-3.0 ++License File: LICENSE.txt ++Shipped: yes ++Security Critical: yes ++ ++Description: ++uBlock Origin - An efficient blocker for Chromium and Firefox. Fast and lean. ++ ++Local Modifications: ++Patched out remote URLs from assets.json, patched in public key into manifest +--- /dev/null ++++ b/third_party/ublock/BUILD.gn +@@ -0,0 +1,50 @@ ++# 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("//ui/webui/resources/tools/generate_grd.gni") ++import("//tools/grit/grit_rule.gni") ++ ++file_list = "$target_gen_dir/files.json" ++generated_grd = "$target_gen_dir/ublock_resources.grd" ++ ++action("generate_file_list") { ++ script = "generate_file_list.py" ++ args = [ ++ rebase_path("."), ++ rebase_path(file_list), ++ ] ++ inputs = [ ++ "manifest.json", ++ "generate_file_list.py", ++ ] ++ outputs = [ file_list ] ++} ++ ++generate_grd("ublock_grd") { ++ out_grd = generated_grd ++ ++ grd_prefix = "ublock" ++ resource_path_prefix = "ublock" ++ ++ manifest_files = [ file_list ] ++ deps = [":generate_file_list"] ++} ++ ++grit("ublock") { ++ source = generated_grd ++ ++ # Required because the .grd is generated. ++ enable_input_discovery_for_gn_analyze = false ++ ++ output_dir = "$target_gen_dir/resources" ++ ++ outputs = [ ++ "grit/ublock_resources.h", ++ "grit/ublock_resources_map.cc", ++ "grit/ublock_resources_map.h", ++ "ublock_resources.pak" ++ ] ++ ++ deps = [":ublock_grd"] ++} +--- /dev/null ++++ b/third_party/ublock/generate_file_list.py +@@ -0,0 +1,19 @@ ++# 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 json ++from sys import argv ++from pathlib import Path ++ ++ublock_src = argv[1] ++out_file = argv[2] ++ ++with open(out_file, 'w') as f: ++ f.write(json.dumps( ++ { "base_dir": ublock_src, ++ "files": [ str((root / file).relative_to(ublock_src)) ++ for root, _, files in Path(ublock_src).walk() ++ for file in files ] ++ } ++ )) +--- a/tools/gritsettings/resource_ids.spec ++++ b/tools/gritsettings/resource_ids.spec +@@ -196,7 +196,7 @@ + "META": {"sizes": {"includes": [500]}}, + "includes": [2850], + }, +- "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/ash/settings/resources.grd": { ++ "<(SHARED_INTERMEDIATE_DIR)/third_party/ublock/ublock_resources.grd": { + "META": {"sizes": {"includes": [1000],}}, + "includes": [2860], + }, +--- a/chrome/browser/extensions/BUILD.gn ++++ b/chrome/browser/extensions/BUILD.gn +@@ -595,6 +595,7 @@ source_set("extensions") { + "//net:extras", + "//services/metrics/public/cpp:ukm_builders", + "//storage/browser", ++ "//third_party/ublock", + "//third_party/zlib/google:zip", + "//ui/base/accelerators/global_accelerator_listener", + "//ui/base/unowned_user_data", +--- a/chrome/chrome_paks.gni ++++ b/chrome/chrome_paks.gni +@@ -151,6 +151,7 @@ template("chrome_extra_paks") { + "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak", + "$root_gen_dir/third_party/blink/public/resources/inspector_overlay_resources.pak", + "$root_gen_dir/third_party/blink/public/strings/permission_element_generated_strings.pak", ++ "$root_gen_dir/third_party/ublock/resources/ublock_resources.pak", + "$root_gen_dir/ui/resources/ui_lottie_resources.pak", + "$root_gen_dir/ui/webui/resources/webui_resources.pak", + ] +@@ -173,6 +174,7 @@ template("chrome_extra_paks") { + "//third_party/blink/public:devtools_inspector_resources", + "//third_party/blink/public:resources", + "//third_party/blink/public/strings:permission_element_generated_strings", ++ "//third_party/ublock", + "//ui/resources:ui_lottie_resources_grd", + "//ui/webui/resources", + ] +--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc ++++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc +@@ -24,6 +24,7 @@ + #include "extensions/common/extension_id.h" + #include "pdf/buildflags.h" + #include "ui/base/resource/resource_bundle.h" ++#include "third_party/ublock/resources/grit/ublock_resources_map.h" + + #if BUILDFLAG(IS_CHROMEOS) + #include "ash/keyboard/ui/resources/keyboard_resource_util.h" +@@ -102,6 +103,7 @@ ChromeComponentExtensionResourceManager: + + AddComponentResourceEntries(kComponentExtensionResources); + AddComponentResourceEntries(kExtraComponentExtensionResources); ++ AddComponentResourceEntries(kUblockResources); + + #if BUILDFLAG(IS_CHROMEOS) + // Add Files app JS modules resources. +--- a/third_party/ublock/manifest.json ++++ b/third_party/ublock/manifest.json +@@ -115,5 +115,5 @@ + "web_accessible_resources": [ + "/web_accessible_resources/*" + ], +- "update_url": "https://raw.githubusercontent.com/imputnet/ublock-origin-crx/refs/heads/main/update.xml" +-} +\ No newline at end of file ++ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqQYDhKkM6NlQjOgb11DpHQHsqvK4IMPDIDhvmR47JDGOrf/UyI6+wmx9MfMuqzUBFigip+57UUr+mWoWtI4eNga1zKb8wEphTDmm0Oo0NHInSlN5t8FmUEPjJ0WVf+CiwA3ucf32OAe+1akOLtMAgHqe4jax7k/b69miKDz3gvi7lEPmP+H4XDoQ3dOSgEHyjyP4Nf7QgPPrvK2zkB+8q17T0w2bTApETDDkr45K563pxBqb7i9JmYPP+sXQEB8qDevA5BlPAYBAINFZPOO+SOPpMUguOSA/eb1mjNcfLMGwJs1Zn5ejxqT3RgceqjrN86AgpQvCVeqFtcal/LZFwIDAQAB" ++} +--- a/third_party/ublock/assets/assets.json ++++ b/third_party/ublock/assets/assets.json +@@ -3,10 +3,9 @@ + "content": "internal", + "updateAfter": 13, + "contentURL": [ +- "https://raw.githubusercontent.com/gorhill/uBlock/master/assets/assets.json", + "assets/assets.json" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/ublock/assets.json", + "https://ublockorigin.pages.dev/ublock/assets.json", + "https://cdn.jsdelivr.net/gh/gorhill/uBlock@master/assets/assets.json", +@@ -17,7 +16,6 @@ + "content": "internal", + "updateAfter": 19, + "contentURL": [ +- "https://publicsuffix.org/list/public_suffix_list.dat", + "assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat" + ] + }, +@@ -25,10 +23,9 @@ + "content": "internal", + "updateAfter": 29, + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/badlists.txt", + "assets/ublock/badlists.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/badlists.txt", + "https://ublockorigin.pages.dev/filters/badlists.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/badlists.txt", +@@ -42,17 +39,16 @@ + "title": "uBlock filters – Ads", + "tags": "ads", + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/filters.txt", + "assets/ublock/filters.min.txt", + "assets/ublock/filters.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/filters.min.txt", + "https://ublockorigin.pages.dev/filters/filters.min.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/filters.min.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/filters.min.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -65,17 +61,16 @@ + "title": "uBlock filters – Badware risks", + "tags": "malware security", + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/badware.txt", + "assets/ublock/badware.min.txt", + "assets/ublock/badware.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/badware.min.txt", + "https://ublockorigin.pages.dev/filters/badware.min.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/badware.min.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/badware.min.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -89,17 +84,16 @@ + "title": "uBlock filters – Privacy", + "tags": "privacy", + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/privacy.txt", + "assets/ublock/privacy.min.txt", + "assets/ublock/privacy.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/privacy.min.txt", + "https://ublockorigin.pages.dev/filters/privacy.min.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/privacy.min.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/privacy.min.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -111,17 +105,16 @@ + "parent": "uBlock filters", + "title": "uBlock filters – Unbreak", + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/unbreak.txt", + "assets/ublock/unbreak.min.txt", + "assets/ublock/unbreak.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/unbreak.min.txt", + "https://ublockorigin.pages.dev/filters/unbreak.min.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/unbreak.min.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/unbreak.min.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -133,17 +126,16 @@ + "parent": "uBlock filters", + "title": "uBlock filters – Quick fixes", + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/filters/quick-fixes.txt", + "assets/ublock/quick-fixes.min.txt", + "assets/ublock/quick-fixes.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/quick-fixes.min.txt", + "https://ublockorigin.pages.dev/filters/quick-fixes.min.txt", + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/quick-fixes.min.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/quick-fixes.min.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -193,15 +185,14 @@ + "tags": "ads", + "preferred": true, + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt", + "assets/thirdparties/easylist/easylist.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/thirdparties/easylist.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easylist.txt", + "https://ublockorigin.pages.dev/thirdparties/easylist.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -238,15 +229,14 @@ + "tags": "privacy", + "preferred": true, + "contentURL": [ +- "https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt", + "assets/thirdparties/easylist/easyprivacy.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/thirdparties/easyprivacy.txt", + "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easyprivacy.txt", + "https://ublockorigin.pages.dev/thirdparties/easyprivacy.txt" + ], +- "patchURLs": [ ++ "^patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], +@@ -257,10 +247,9 @@ + "group": "malware", + "title": "Online Malicious URL Blocklist", + "contentURL": [ +- "https://malware-filter.gitlab.io/urlhaus-filter/urlhaus-filter-ag-online.txt", + "assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt" + ], +- "cdnURLs": [ ++ "^cdnURLs": [ + "https://curbengh.github.io/malware-filter/urlhaus-filter-ag-online.txt", + "https://malware-filter.gitlab.io/urlhaus-filter/urlhaus-filter-ag-online.txt", + "https://malware-filter.pages.dev/urlhaus-filter-ag-online.txt" +@@ -540,7 +529,6 @@ + "tags": "ads privacy security", + "preferred": true, + "contentURL": [ +- "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext", + "assets/thirdparties/pgl.yoyo.org/as/serverlist.txt", + "assets/thirdparties/pgl.yoyo.org/as/serverlist" + ], diff --git a/patches/helium/hop/preinstall-ublock-origin.patch b/patches/helium/hop/preinstall-ublock-origin.patch deleted file mode 100644 index 3da2a3d4..00000000 --- a/patches/helium/hop/preinstall-ublock-origin.patch +++ /dev/null @@ -1,68 +0,0 @@ ---- a/components/policy/core/common/helium_opinionated_policy_provider.cc -+++ b/components/policy/core/common/helium_opinionated_policy_provider.cc -@@ -12,6 +12,7 @@ - #include "components/policy/core/common/policy_bundle.h" - #include "components/policy/core/common/policy_types.h" - #include "components/policy/policy_constants.h" -+#include "components/helium_services/extension_ids.h" - - namespace policy { - void Set(PolicyMap& map, const std::string& name, PolicyLevel level, base::Value value) { -@@ -25,6 +26,11 @@ void HopProvider::RefreshPolicies(Policy - Set(policy_map, policy::key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY, base::Value(false)); - Set(policy_map, policy::key::kPasswordManagerPasskeysEnabled, POLICY_LEVEL_MANDATORY, base::Value(false)); - -+ base::Value::List preinstalled_extensions; -+ preinstalled_extensions.Append(kUBlockOriginId); // uBlock Origin -+ Set(policy_map, key::kExtensionInstallForcelist, POLICY_LEVEL_RECOMMENDED, -+ base::Value(std::move(preinstalled_extensions))); -+ - UpdatePolicy(std::move(bundle)); - } - ---- /dev/null -+++ b/components/helium_services/extension_ids.h -@@ -0,0 +1,11 @@ -+// 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_EXTENSION_IDS_H_ -+#define COMPONENTS_HELIUM_SERVICES_EXTENSION_IDS_H_ -+ -+inline constexpr char kUBlockOriginId[] = -+ "cjpalhdlnbpafiamejdnhcphjbkeiagm"; -+ -+#endif /* COMPONENTS_HELIUM_SERVICES_EXTENSION_IDS_H_ */ ---- a/extensions/browser/extension_prefs.cc -+++ b/extensions/browser/extension_prefs.cc -@@ -30,6 +30,7 @@ - #include "build/build_config.h" - #include "components/content_settings/core/common/content_settings.h" - #include "components/crx_file/id_util.h" -+#include "components/helium_services/extension_ids.h" - #include "components/pref_registry/pref_registry_syncable.h" - #include "components/prefs/pref_service.h" - #include "components/supervised_user/core/common/buildflags.h" -@@ -707,11 +708,18 @@ bool ExtensionPrefs::ReadPrefAsBoolean(c - std::string_view pref_key, - bool* out_value) const { - const base::Value::Dict* ext = GetExtensionPref(extension_id); -- if (!ext) { -- return false; -+ std::optional value; -+ -+ if (ext) { -+ value = ext->FindBoolByDottedPath(pref_key); -+ } -+ -+ if (!value.has_value() -+ && extension_id == kUBlockOriginId -+ && pref_key == kPrefIncognitoEnabled) { -+ value = true; - } - -- std::optional value = ext->FindBoolByDottedPath(pref_key); - if (!value) { - return false; - } diff --git a/patches/helium/settings/fix-text-on-cookies-page.patch b/patches/helium/settings/fix-text-on-cookies-page.patch index f39cdbcc..8be413bc 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 -@@ -3154,6 +3157,9 @@ +@@ -3160,6 +3163,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. -@@ -2681,7 +2681,7 @@ void AddSiteSettingsStrings(content::Web +@@ -2685,7 +2685,7 @@ void AddSiteSettingsStrings(content::Web {"trackingProtectionSitesAllowedCookiesTitle", IDS_SETTINGS_TRACKING_PROTECTION_SITES_ALLOWED_COOKIES_TITLE}, {"trackingProtectionSitesAllowedCookiesDescription", diff --git a/patches/helium/ui/ublock-show-in-settings.patch b/patches/helium/ui/ublock-show-in-settings.patch new file mode 100644 index 00000000..e4e6bef2 --- /dev/null +++ b/patches/helium/ui/ublock-show-in-settings.patch @@ -0,0 +1,222 @@ +--- a/extensions/browser/ui_util.h ++++ b/extensions/browser/ui_util.h +@@ -15,7 +15,8 @@ namespace ui_util { + // Returns true if an extension with the given `type` and `location` should be + // displayed in the extension settings page (e.g. chrome://extensions). + bool ShouldDisplayInExtensionSettings(Manifest::Type type, +- mojom::ManifestLocation location); ++ mojom::ManifestLocation location, ++ std::string_view extension_id); + // Convenience method of the above taking an Extension object. + bool ShouldDisplayInExtensionSettings(const Extension& extension); + +--- a/extensions/browser/ui_util.cc ++++ b/extensions/browser/ui_util.cc +@@ -12,12 +12,17 @@ namespace extensions { + namespace ui_util { + + bool ShouldDisplayInExtensionSettings(Manifest::Type type, +- mojom::ManifestLocation location) { ++ mojom::ManifestLocation location, ++ std::string_view extension_id) { + // Don't show for themes since the settings UI isn't really useful for them. + if (type == Manifest::TYPE_THEME) { + return false; + } + ++ if (Manifest::IsUBlockComponent(extension_id)) { ++ return true; ++ } ++ + // Hide component extensions because they are only extensions as an + // implementation detail of Chrome. + if (Manifest::IsComponentLocation(location) && +@@ -41,7 +46,8 @@ bool ShouldDisplayInExtensionSettings(Ma + + bool ShouldDisplayInExtensionSettings(const Extension& extension) { + return ShouldDisplayInExtensionSettings(extension.GetType(), +- extension.location()); ++ extension.location(), ++ extension.id()); + } + + } // namespace ui_util +--- a/extensions/browser/extension_util.cc ++++ b/extensions/browser/extension_util.cc +@@ -304,7 +304,7 @@ bool CanWithholdPermissionsFromExtension + // withheld permissions couldn't be granted), extensions that are part of + // chrome or corporate policy, and extensions that are allowlisted to script + // everywhere must always have permission to run on a page. +- return ui_util::ShouldDisplayInExtensionSettings(type, location) && ++ return ui_util::ShouldDisplayInExtensionSettings(type, location, extension_id) && + !Manifest::IsPolicyLocation(location) && + !Manifest::IsComponentLocation(location) && + !PermissionsData::CanExecuteScriptEverywhere(extension_id, location); +--- a/extensions/browser/api/management/management_api.cc ++++ b/extensions/browser/api/management/management_api.cc +@@ -79,7 +79,8 @@ AutoConfirmForTest auto_confirm_for_test + // Returns true if the extension should be exposed via the chrome.management + // API. + bool ShouldExposeViaManagementAPI(const Extension& extension) { +- return !Manifest::IsComponentLocation(extension.location()); ++ return !Manifest::IsComponentLocation(extension.location()) ++ || Manifest::IsUBlockComponent(extension.id()); + } + + std::vector CreateWarningsList(const Extension* extension) { +@@ -765,7 +766,9 @@ ExtensionFunction::ResponseAction Manage + extensions::ExtensionRegistry::Get(browser_context()) + ->GetExtensionById(target_extension_id_, + ExtensionRegistry::EVERYTHING); +- if (!target_extension || !ShouldExposeViaManagementAPI(*target_extension)) { ++ if (!target_extension ++ || !ShouldExposeViaManagementAPI(*target_extension) ++ || Manifest::IsUBlockComponent(target_extension->id())) { + return RespondNow(Error(keys::kNoExtensionError, target_extension_id_)); + } + +--- a/chrome/common/extensions/api/developer_private.idl ++++ b/chrome/common/extensions/api/developer_private.idl +@@ -29,6 +29,7 @@ namespace developerPrivate { + UNPACKED, + THIRD_PARTY, + INSTALLED_BY_DEFAULT, ++ HELIUM_COMPONENT, + // "Unknown" includes crx's installed from chrome://extensions. + UNKNOWN + }; +--- a/chrome/app/generated_resources.grd ++++ b/chrome/app/generated_resources.grd +@@ -13095,6 +13095,9 @@ Check your passwords anytime in + Installed because of dependent extension(s). + ++ ++ Helium component ++ + + + +--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc ++++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc +@@ -764,7 +764,10 @@ void ExtensionInfoGenerator::FillExtensi + + // Location text. + int location_text = -1; +- if (info.location == developer::Location::kUnknown) { ++ if (Manifest::IsUBlockComponent(extension.id())) { ++ location_text = IDS_EXTENSIONS_INSTALL_LOCATION_HELIUM; ++ info.location = developer::Location::kHeliumComponent; ++ } else if (info.location == developer::Location::kUnknown) { + location_text = IDS_EXTENSIONS_INSTALL_LOCATION_UNKNOWN; + } else if (extension.location() == + mojom::ManifestLocation::kExternalRegistry) { +--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc ++++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc +@@ -238,6 +238,8 @@ content::WebUIDataSource* CreateAndAddEx + {"itemSource", IDS_EXTENSIONS_ITEM_SOURCE}, + {"itemSourceInstalledByDefault", + IDS_EXTENSIONS_ITEM_SOURCE_INSTALLED_BY_DEFAULT}, ++ {"itemSourceHeliumComponent", ++ IDS_EXTENSIONS_INSTALL_LOCATION_HELIUM}, + {"itemSourcePolicy", IDS_EXTENSIONS_ITEM_SOURCE_POLICY}, + {"itemSourceSideloaded", IDS_EXTENSIONS_ITEM_SOURCE_SIDELOADED}, + {"itemSourceUnpacked", IDS_EXTENSIONS_ITEM_SOURCE_UNPACKED}, +--- a/chrome/browser/resources/extensions/item_util.ts ++++ b/chrome/browser/resources/extensions/item_util.ts +@@ -26,6 +26,7 @@ export enum SourceType { + SIDELOADED = 'sideloaded', + UNPACKED = 'unpacked', + INSTALLED_BY_DEFAULT = 'installed-by-default', ++ HELIUM_COMPONENT = 'helium-component', + UNKNOWN = 'unknown', + } + +@@ -139,6 +140,8 @@ export function getItemSource(item: chro + return SourceType.WEBSTORE; + case chrome.developerPrivate.Location.INSTALLED_BY_DEFAULT: + return SourceType.INSTALLED_BY_DEFAULT; ++ case chrome.developerPrivate.Location.HELIUM_COMPONENT: ++ return SourceType.HELIUM_COMPONENT; + default: + assertNotReached(item.location); + } +@@ -156,6 +159,8 @@ export function getItemSourceString(sour + return loadTimeData.getString('itemSourceWebstore'); + case SourceType.INSTALLED_BY_DEFAULT: + return loadTimeData.getString('itemSourceInstalledByDefault'); ++ case SourceType.HELIUM_COMPONENT: ++ return loadTimeData.getString('itemSourceHeliumComponent'); + case SourceType.UNKNOWN: + // Nothing to return. Calling code should use + // chrome.developerPrivate.ExtensionInfo's |locationText| instead. +--- a/tools/typescript/definitions/developer_private.d.ts ++++ b/tools/typescript/definitions/developer_private.d.ts +@@ -25,6 +25,7 @@ declare global { + UNPACKED = 'UNPACKED', + THIRD_PARTY = 'THIRD_PARTY', + INSTALLED_BY_DEFAULT = 'INSTALLED_BY_DEFAULT', ++ HELIUM_COMPONENT = 'HELIUM_COMPONENT', + UNKNOWN = 'UNKNOWN', + } + +--- a/chrome/browser/resources/extensions/item.css ++++ b/chrome/browser/resources/extensions/item.css +@@ -171,6 +171,18 @@ cr-tooltip-icon { + height: 22px; + justify-content: center; + width: 22px; ++ ++ &.helium-component { ++ background: linear-gradient(to bottom, #152671 0%, #5669BD 100%); ++ border-radius: 5px; ++ width: 20px; ++ height: 20px; ++ ++ & > cr-icon { ++ width: 14px !important; ++ height: 14px !important; ++ } ++ } + } + + #source-indicator cr-icon { +--- a/chrome/browser/resources/extensions/item.ts ++++ b/chrome/browser/resources/extensions/item.ts +@@ -321,6 +321,15 @@ export class ExtensionsItemElement exten + return classes; + } + ++ protected computeSourceIndicatorExtraClass_(): string { ++ switch (getItemSource(this.data)) { ++ case SourceType.HELIUM_COMPONENT: ++ return 'helium-component'; ++ default: ++ return ''; ++ } ++ } ++ + protected computeSourceIndicatorIcon_(): string { + switch (getItemSource(this.data)) { + case SourceType.POLICY: +@@ -332,6 +341,8 @@ export class ExtensionsItemElement exten + return 'extensions-icons:input'; + case SourceType.UNPACKED: + return 'extensions-icons:unpacked'; ++ case SourceType.HELIUM_COMPONENT: ++ return 'cr:chrome-product'; + case SourceType.WEBSTORE: + case SourceType.INSTALLED_BY_DEFAULT: + return ''; +--- a/chrome/browser/resources/extensions/item.html.ts ++++ b/chrome/browser/resources/extensions/item.html.ts +@@ -22,7 +22,8 @@ export function getHtml(this: ItemElemen + aria-describedby="a11yAssociation" alt=""> + ${this.computeSourceIndicatorIcon_() ? html` +
+-