mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
patches & fixes for 0.31.1 release (#219)
* chore: new browseros-server binaries * bugfix: was writing chromium_patches/ in wrong location * patch: ntp footer disabled by default * patch: browseros alpha flag * patch: add log for port saving * chore: increment PATCH * feat: use packages/browseros as root_dir properly in context.py
This commit is contained in:
@@ -1,126 +0,0 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_extension_constants.h b/chrome/browser/extensions/browseros_extension_constants.h
|
||||
new file mode 100644
|
||||
index 0000000000000..5a3b518b224a7
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_extension_constants.h
|
||||
@@ -0,0 +1,120 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#ifndef CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTENSION_CONSTANTS_H_
|
||||
+#define CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTENSION_CONSTANTS_H_
|
||||
+
|
||||
+#include <cstddef>
|
||||
+#include <optional>
|
||||
+#include <string>
|
||||
+#include <vector>
|
||||
+
|
||||
+namespace extensions {
|
||||
+namespace browseros {
|
||||
+
|
||||
+// AI Agent Extension ID
|
||||
+inline constexpr char kAISidePanelExtensionId[] =
|
||||
+ "djhdjhlnljbjgejbndockeedocneiaei";
|
||||
+
|
||||
+// Agent V2 Extension ID
|
||||
+inline constexpr char kAgentV2ExtensionId[] =
|
||||
+ "bflpfmnmnokmjhmgnolecpppdbdophmk";
|
||||
+
|
||||
+// BrowserOS extension config URLs
|
||||
+inline constexpr char kBrowserOSConfigUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/extensions.json";
|
||||
+inline constexpr char kBrowserOSAlphaConfigUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/extensions.alpha.json";
|
||||
+
|
||||
+// Bug Reporter Extension ID
|
||||
+inline constexpr char kBugReporterExtensionId[] =
|
||||
+ "adlpneommgkgeanpaekgoaolcpncohkf";
|
||||
+
|
||||
+// Controller Extension ID
|
||||
+inline constexpr char kControllerExtensionId[] =
|
||||
+ "nlnihljpboknmfagkikhkdblbedophja";
|
||||
+
|
||||
+// BrowserOS CDN update manifest URL
|
||||
+// Used for extensions installed from local .crx files that don't have
|
||||
+// an update_url in their manifest
|
||||
+inline constexpr char kBrowserOSUpdateUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/update-manifest.xml";
|
||||
+
|
||||
+struct BrowserOSExtensionInfo {
|
||||
+ const char* id;
|
||||
+ const char* display_name;
|
||||
+ bool is_pinned;
|
||||
+ bool is_labelled;
|
||||
+};
|
||||
+
|
||||
+inline constexpr BrowserOSExtensionInfo kBrowserOSExtensions[] = {
|
||||
+ {kAISidePanelExtensionId, "BrowserOS", true, true},
|
||||
+ {kBugReporterExtensionId, "BrowserOS/bug-reporter", true, false},
|
||||
+ {kControllerExtensionId, "BrowserOS/controller", false, false},
|
||||
+ {kAgentV2ExtensionId, "BrowserOS", true, true},
|
||||
+};
|
||||
+
|
||||
+// Allowlist of BrowserOS extension IDs that are permitted to be installed.
|
||||
+// Only extensions with these IDs will be loaded from the config.
|
||||
+inline constexpr const char* kAllowedExtensions[] = {
|
||||
+ kBrowserOSExtensions[0].id,
|
||||
+ kBrowserOSExtensions[1].id,
|
||||
+ kBrowserOSExtensions[2].id,
|
||||
+ kBrowserOSExtensions[3].id,
|
||||
+};
|
||||
+
|
||||
+inline constexpr size_t kBrowserOSExtensionsCount =
|
||||
+ sizeof(kBrowserOSExtensions) / sizeof(kBrowserOSExtensions[0]);
|
||||
+
|
||||
+inline const BrowserOSExtensionInfo* FindBrowserOSExtensionInfo(
|
||||
+ const std::string& extension_id) {
|
||||
+ for (const auto& info : kBrowserOSExtensions) {
|
||||
+ if (extension_id == info.id)
|
||||
+ return &info;
|
||||
+ }
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+// Check if an extension is a BrowserOS extension
|
||||
+inline bool IsBrowserOSExtension(const std::string& extension_id) {
|
||||
+ return FindBrowserOSExtensionInfo(extension_id) != nullptr;
|
||||
+}
|
||||
+
|
||||
+inline bool IsBrowserOSPinnedExtension(const std::string& extension_id) {
|
||||
+ const BrowserOSExtensionInfo* info =
|
||||
+ FindBrowserOSExtensionInfo(extension_id);
|
||||
+ return info && info->is_pinned;
|
||||
+}
|
||||
+
|
||||
+inline bool IsBrowserOSLabelledExtension(const std::string& extension_id) {
|
||||
+ const BrowserOSExtensionInfo* info =
|
||||
+ FindBrowserOSExtensionInfo(extension_id);
|
||||
+ return info && info->is_labelled;
|
||||
+}
|
||||
+
|
||||
+// Get all BrowserOS extension IDs
|
||||
+inline std::vector<std::string> GetBrowserOSExtensionIds() {
|
||||
+ std::vector<std::string> ids;
|
||||
+ ids.reserve(kBrowserOSExtensionsCount);
|
||||
+ for (const auto& info : kBrowserOSExtensions)
|
||||
+ ids.push_back(info.id);
|
||||
+ return ids;
|
||||
+}
|
||||
+
|
||||
+// Get display name for BrowserOS extensions in omnibox
|
||||
+// Returns the display name if extension_id is a BrowserOS extension,
|
||||
+// otherwise returns std::nullopt
|
||||
+inline std::optional<std::string> GetExtensionDisplayName(
|
||||
+ const std::string& extension_id) {
|
||||
+ if (const BrowserOSExtensionInfo* info =
|
||||
+ FindBrowserOSExtensionInfo(extension_id)) {
|
||||
+ return info->display_name;
|
||||
+ }
|
||||
+ return std::nullopt;
|
||||
+}
|
||||
+
|
||||
+} // namespace browseros
|
||||
+} // namespace extensions
|
||||
+
|
||||
+#endif // CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTENSION_CONSTANTS_H_
|
||||
@@ -1,763 +0,0 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_external_loader.cc b/chrome/browser/extensions/browseros_external_loader.cc
|
||||
new file mode 100644
|
||||
index 0000000000000..8f01037dfcb90
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_external_loader.cc
|
||||
@@ -0,0 +1,757 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "chrome/browser/extensions/browseros_external_loader.h"
|
||||
+
|
||||
+#include <memory>
|
||||
+#include <utility>
|
||||
+
|
||||
+#include "base/feature_list.h"
|
||||
+#include "base/files/file_util.h"
|
||||
+#include "base/functional/bind.h"
|
||||
+#include "base/json/json_reader.h"
|
||||
+#include "base/logging.h"
|
||||
+#include "base/memory/ptr_util.h"
|
||||
+#include "base/strings/string_util.h"
|
||||
+#include "base/task/thread_pool.h"
|
||||
+#include "base/task/single_thread_task_runner.h"
|
||||
+#include "base/values.h"
|
||||
+#include "chrome/browser/browser_features.h"
|
||||
+#include "chrome/browser/browser_process.h"
|
||||
+#include "chrome/browser/extensions/browseros_extension_constants.h"
|
||||
+#include "chrome/browser/extensions/extension_service.h"
|
||||
+#include "chrome/browser/extensions/external_provider_impl.h"
|
||||
+#include "chrome/browser/extensions/updater/extension_updater.h"
|
||||
+#include "chrome/browser/profiles/profile.h"
|
||||
+#include "components/metrics/browseros_metrics/browseros_metrics.h"
|
||||
+#include "content/public/browser/browser_context.h"
|
||||
+#include "content/public/browser/storage_partition.h"
|
||||
+#include "extensions/browser/disable_reason.h"
|
||||
+#include "extensions/browser/extension_prefs.h"
|
||||
+#include "extensions/browser/extension_registrar.h"
|
||||
+#include "extensions/browser/extension_registry.h"
|
||||
+#include "extensions/browser/extension_system.h"
|
||||
+#include "extensions/browser/pending_extension_manager.h"
|
||||
+#include "extensions/common/extension.h"
|
||||
+#include "extensions/common/mojom/manifest.mojom-shared.h"
|
||||
+#include "net/base/load_flags.h"
|
||||
+#include "net/traffic_annotation/network_traffic_annotation.h"
|
||||
+#include "services/network/public/cpp/resource_request.h"
|
||||
+#include "services/network/public/cpp/simple_url_loader.h"
|
||||
+#include "services/network/public/mojom/url_response_head.mojom.h"
|
||||
+
|
||||
+namespace extensions {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+// Interval for periodic maintenance
|
||||
+constexpr base::TimeDelta kPeriodicMaintenanceInterval = base::Minutes(15);
|
||||
+
|
||||
+// Network traffic annotation for the extension configuration fetch.
|
||||
+constexpr net::NetworkTrafficAnnotationTag kBrowserOSExtensionsFetchTrafficAnnotation =
|
||||
+ net::DefineNetworkTrafficAnnotation("browseros_extensions_fetch", R"(
|
||||
+ semantics {
|
||||
+ sender: "BrowserOS External Extension Loader"
|
||||
+ description:
|
||||
+ "Fetches a JSON configuration file that specifies which extensions "
|
||||
+ "should be installed for BrowserOS users at startup."
|
||||
+ trigger:
|
||||
+ "Triggered during browser startup when BrowserOS mode is enabled."
|
||||
+ data:
|
||||
+ "No user data is sent. Only a GET request to fetch the configuration."
|
||||
+ destination: OTHER
|
||||
+ destination_other:
|
||||
+ "The BrowserOS configuration server specified by the config URL."
|
||||
+ }
|
||||
+ policy {
|
||||
+ cookies_allowed: NO
|
||||
+ setting:
|
||||
+ "This feature can be controlled via command-line flags or "
|
||||
+ "enterprise policies."
|
||||
+ policy_exception_justification:
|
||||
+ "Not implemented yet. This is a new feature for BrowserOS."
|
||||
+ })");
|
||||
+
|
||||
+// Example JSON format:
|
||||
+// {
|
||||
+// "extensions": {
|
||||
+// "extension_id_1": {
|
||||
+// "external_update_url": "https://example.com/extension1/updates.xml"
|
||||
+// },
|
||||
+// "extension_id_2": {
|
||||
+// "external_crx": "https://example.com/extension2.crx",
|
||||
+// "external_version": "1.0"
|
||||
+// }
|
||||
+// }
|
||||
+// }
|
||||
+
|
||||
+// Determines if a BrowserOS extension should be enabled based on the
|
||||
+// kBrowserOsAlphaFeatures flag state.
|
||||
+// - kAgentV2ExtensionId: enabled only when flag is ON
|
||||
+// - kAISidePanelExtensionId: enabled only when flag is OFF
|
||||
+// - All other extensions: always enabled
|
||||
+bool ShouldExtensionBeEnabled(const std::string& extension_id) {
|
||||
+ bool alpha_features_enabled =
|
||||
+ base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures);
|
||||
+
|
||||
+ if (extension_id == browseros::kAgentV2ExtensionId) {
|
||||
+ return alpha_features_enabled;
|
||||
+ }
|
||||
+
|
||||
+ if (extension_id == browseros::kAISidePanelExtensionId) {
|
||||
+ return !alpha_features_enabled;
|
||||
+ }
|
||||
+
|
||||
+ // All other BrowserOS extensions are always enabled
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+// Returns the appropriate config URL based on the alpha features flag.
|
||||
+const char* GetConfigUrl() {
|
||||
+ if (base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures)) {
|
||||
+ return browseros::kBrowserOSAlphaConfigUrl;
|
||||
+ }
|
||||
+ return browseros::kBrowserOSConfigUrl;
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+BrowserOSExternalLoader::BrowserOSExternalLoader(Profile* profile)
|
||||
+ : profile_(profile) {
|
||||
+ // Select config URL based on alpha features flag
|
||||
+ config_url_ = GURL(GetConfigUrl());
|
||||
+
|
||||
+ // Add known BrowserOS extension IDs
|
||||
+ for (const char* extension_id : browseros::kAllowedExtensions) {
|
||||
+ browseros_extension_ids_.insert(extension_id);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+BrowserOSExternalLoader::~BrowserOSExternalLoader() = default;
|
||||
+
|
||||
+void BrowserOSExternalLoader::StartLoading() {
|
||||
+ LOG(INFO) << "BrowserOS external extension loader starting...";
|
||||
+
|
||||
+ if (!config_file_for_testing_.empty()) {
|
||||
+ LoadFromFile();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!config_url_.is_valid()) {
|
||||
+ LOG(ERROR) << "Invalid BrowserOS extensions config URL";
|
||||
+ LoadFinished(base::Value::Dict());
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "Fetching BrowserOS extensions from: " << config_url_.spec();
|
||||
+
|
||||
+ // Create the URL loader factory
|
||||
+ url_loader_factory_ = profile_->GetDefaultStoragePartition()
|
||||
+ ->GetURLLoaderFactoryForBrowserProcess();
|
||||
+
|
||||
+ // Create the resource request
|
||||
+ auto resource_request = std::make_unique<network::ResourceRequest>();
|
||||
+ resource_request->url = config_url_;
|
||||
+ resource_request->method = "GET";
|
||||
+ resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
|
||||
+
|
||||
+ // Create the URL loader
|
||||
+ url_loader_ = network::SimpleURLLoader::Create(
|
||||
+ std::move(resource_request), kBrowserOSExtensionsFetchTrafficAnnotation);
|
||||
+
|
||||
+ // Start the download
|
||||
+ url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
|
||||
+ url_loader_factory_.get(),
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::OnURLFetchComplete,
|
||||
+ weak_ptr_factory_.GetWeakPtr()));
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::OnURLFetchComplete(
|
||||
+ std::unique_ptr<std::string> response_body) {
|
||||
+ if (!response_body) {
|
||||
+ LOG(ERROR) << "Failed to fetch BrowserOS extensions config from "
|
||||
+ << config_url_.spec();
|
||||
+ LoadFinished(base::Value::Dict());
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ParseConfiguration(*response_body);
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::ParseConfiguration(
|
||||
+ const std::string& json_content) {
|
||||
+ std::optional<base::Value> parsed_json = base::JSONReader::Read(json_content);
|
||||
+
|
||||
+ if (!parsed_json || !parsed_json->is_dict()) {
|
||||
+ LOG(ERROR) << "Failed to parse BrowserOS extensions config JSON";
|
||||
+ LoadFinished(base::Value::Dict());
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const base::Value::Dict* extensions_dict =
|
||||
+ parsed_json->GetDict().FindDict("extensions");
|
||||
+
|
||||
+ if (!extensions_dict) {
|
||||
+ LOG(ERROR) << "No 'extensions' key found in BrowserOS config";
|
||||
+ LoadFinished(base::Value::Dict());
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Create the prefs dictionary in the format expected by ExternalProviderImpl
|
||||
+ base::Value::Dict prefs;
|
||||
+
|
||||
+ for (const auto [extension_id, extension_config] : *extensions_dict) {
|
||||
+ if (!extension_config.is_dict()) {
|
||||
+ LOG(WARNING) << "Invalid config for extension " << extension_id;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ const base::Value::Dict& config_dict = extension_config.GetDict();
|
||||
+ base::Value::Dict extension_prefs;
|
||||
+
|
||||
+ // Copy supported fields
|
||||
+ if (const std::string* update_url =
|
||||
+ config_dict.FindString(ExternalProviderImpl::kExternalUpdateUrl)) {
|
||||
+ extension_prefs.Set(ExternalProviderImpl::kExternalUpdateUrl, *update_url);
|
||||
+ }
|
||||
+
|
||||
+ if (const std::string* crx_path =
|
||||
+ config_dict.FindString(ExternalProviderImpl::kExternalCrx)) {
|
||||
+ extension_prefs.Set(ExternalProviderImpl::kExternalCrx, *crx_path);
|
||||
+ }
|
||||
+
|
||||
+ if (const std::string* version =
|
||||
+ config_dict.FindString(ExternalProviderImpl::kExternalVersion)) {
|
||||
+ extension_prefs.Set(ExternalProviderImpl::kExternalVersion, *version);
|
||||
+ }
|
||||
+
|
||||
+ // Add other supported fields as needed
|
||||
+ std::optional<bool> keep_if_present =
|
||||
+ config_dict.FindBool(ExternalProviderImpl::kKeepIfPresent);
|
||||
+ if (keep_if_present.has_value()) {
|
||||
+ extension_prefs.Set(ExternalProviderImpl::kKeepIfPresent,
|
||||
+ keep_if_present.value());
|
||||
+ }
|
||||
+
|
||||
+ if (!extension_prefs.empty()) {
|
||||
+ prefs.Set(extension_id, std::move(extension_prefs));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "Loaded " << prefs.size() << " extensions from BrowserOS config";
|
||||
+
|
||||
+ // Track the extension IDs we're managing
|
||||
+ for (const auto [extension_id, _] : prefs) {
|
||||
+ browseros_extension_ids_.insert(extension_id);
|
||||
+ }
|
||||
+
|
||||
+ // Store the initial config for comparison
|
||||
+ if (!extensions_dict->empty()) {
|
||||
+ last_config_ = extensions_dict->Clone();
|
||||
+ }
|
||||
+
|
||||
+ // Pass the prefs to the external provider system
|
||||
+ LoadFinished(std::move(prefs));
|
||||
+
|
||||
+ // Immediately trigger high-priority installation of all BrowserOS extensions
|
||||
+ // This ensures they get installed right away instead of waiting for Chrome's
|
||||
+ // default external extension installation process
|
||||
+ if (!browseros_extension_ids_.empty()) {
|
||||
+ LOG(INFO) << "browseros: Triggering immediate high-priority installation for "
|
||||
+ << browseros_extension_ids_.size() << " BrowserOS extensions";
|
||||
+
|
||||
+ // Use a delayed task to ensure the extension system is fully initialized
|
||||
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::TriggerImmediateInstallation,
|
||||
+ weak_ptr_factory_.GetWeakPtr()),
|
||||
+ base::Seconds(2)); // Small delay to ensure extension system is ready
|
||||
+ }
|
||||
+
|
||||
+ // Start periodic checking after initial load
|
||||
+ StartPeriodicCheck();
|
||||
+
|
||||
+ // Log initial extension state at startup
|
||||
+ CheckAndLogExtensionState("startup");
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::StartPeriodicCheck() {
|
||||
+ LOG(INFO) << "browseros: Starting periodic maintenance (every "
|
||||
+ << kPeriodicMaintenanceInterval.InMinutes() << " minutes)";
|
||||
+
|
||||
+ // Schedule the periodic maintenance
|
||||
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::PeriodicMaintenance,
|
||||
+ weak_ptr_factory_.GetWeakPtr()),
|
||||
+ kPeriodicMaintenanceInterval);
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::PeriodicMaintenance() {
|
||||
+ LOG(INFO) << "browseros: Running periodic maintenance";
|
||||
+
|
||||
+ if (!profile_) {
|
||||
+ // Schedule next check even if profile isn't ready
|
||||
+ StartPeriodicCheck();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // 1. Check for and reinstall any uninstalled BrowserOS extensions
|
||||
+ ReinstallUninstalledExtensions();
|
||||
+
|
||||
+ // 2. Re-enable any disabled BrowserOS extensions (respects flag state)
|
||||
+ ReenableDisabledExtensions();
|
||||
+
|
||||
+ // 3. Enforce correct enabled/disabled state based on alpha features flag
|
||||
+ EnforceExtensionStateBasedOnFlag();
|
||||
+
|
||||
+ // 4. Fetch latest config and check for changes
|
||||
+ FetchAndCheckConfig();
|
||||
+
|
||||
+ // 5. Force immediate update check for all BrowserOS extensions
|
||||
+ ForceUpdateCheck();
|
||||
+
|
||||
+ // 6. Log extension state after all maintenance attempts
|
||||
+ CheckAndLogExtensionState("periodic_maintenance");
|
||||
+
|
||||
+ // Schedule the next maintenance
|
||||
+ StartPeriodicCheck();
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::ReinstallUninstalledExtensions() {
|
||||
+ ExtensionService* service = ExtensionSystem::Get(profile_)->extension_service();
|
||||
+ if (!service) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ PendingExtensionManager* pending_manager = PendingExtensionManager::Get(profile_);
|
||||
+
|
||||
+ if (!registry || !pending_manager || last_config_.empty()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ // Check if extension exists (installed or disabled)
|
||||
+ if (registry->GetInstalledExtension(extension_id)) {
|
||||
+ continue; // Extension is installed, skip to next
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Extension " << extension_id
|
||||
+ << " was uninstalled, attempting to reinstall";
|
||||
+
|
||||
+ // Find the extension's configuration
|
||||
+ const base::Value::Dict* extension_config = last_config_.FindDict(extension_id);
|
||||
+ if (!extension_config) {
|
||||
+ LOG(WARNING) << "browseros: No config found for " << extension_id;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Get the update URL from config
|
||||
+ const std::string* update_url =
|
||||
+ extension_config->FindString(ExternalProviderImpl::kExternalUpdateUrl);
|
||||
+ if (!update_url) {
|
||||
+ LOG(WARNING) << "browseros: No update URL found for " << extension_id;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Validate and add to pending extensions
|
||||
+ GURL update_gurl(*update_url);
|
||||
+ if (!update_gurl.is_valid()) {
|
||||
+ LOG(WARNING) << "browseros: Invalid update URL for " << extension_id;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Add as pending extension for installation
|
||||
+ pending_manager->AddFromExternalUpdateUrl(
|
||||
+ extension_id,
|
||||
+ std::string(), // No install param
|
||||
+ update_gurl,
|
||||
+ mojom::ManifestLocation::kExternalComponent,
|
||||
+ Extension::WAS_INSTALLED_BY_DEFAULT,
|
||||
+ false);
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Added " << extension_id
|
||||
+ << " to pending extensions for reinstall";
|
||||
+
|
||||
+ // Trigger immediate installation
|
||||
+ ExtensionUpdater* updater = ExtensionUpdater::Get(profile_);
|
||||
+ if (updater) {
|
||||
+ ExtensionUpdater::CheckParams params;
|
||||
+ params.ids = {extension_id};
|
||||
+ params.install_immediately = true;
|
||||
+ params.fetch_priority = DownloadFetchPriority::kForeground;
|
||||
+ updater->CheckNow(std::move(params));
|
||||
+ LOG(INFO) << "browseros: Triggered immediate install for " << extension_id;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::ReenableDisabledExtensions() {
|
||||
+ ExtensionService* service = ExtensionSystem::Get(profile_)->extension_service();
|
||||
+ if (!service) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
|
||||
+
|
||||
+ if (!registry || !prefs) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ // Check if extension is disabled
|
||||
+ if (!registry->disabled_extensions().Contains(extension_id)) {
|
||||
+ continue; // Extension is not disabled, skip to next
|
||||
+ }
|
||||
+
|
||||
+ // Only re-enable if this extension should be enabled based on flag state
|
||||
+ if (!ShouldExtensionBeEnabled(extension_id)) {
|
||||
+ continue; // Extension should stay disabled based on flag
|
||||
+ }
|
||||
+
|
||||
+ // Re-enable BrowserOS extensions regardless of disable reason
|
||||
+ auto* registrar = extensions::ExtensionRegistrar::Get(profile_);
|
||||
+ if (!registrar) {
|
||||
+ LOG(WARNING) << "browseros: Cannot re-enable " << extension_id
|
||||
+ << " because ExtensionRegistrar is unavailable";
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Re-enabling extension " << extension_id;
|
||||
+ registrar->EnableExtension(extension_id);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::EnforceExtensionStateBasedOnFlag() {
|
||||
+ if (!profile_) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
|
||||
+ auto* registrar = extensions::ExtensionRegistrar::Get(profile_);
|
||||
+
|
||||
+ if (!registry || !prefs || !registrar) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ bool alpha_features_enabled =
|
||||
+ base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures);
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Enforcing extension state (alpha_features="
|
||||
+ << (alpha_features_enabled ? "ON" : "OFF") << ")";
|
||||
+
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ bool should_be_enabled = ShouldExtensionBeEnabled(extension_id);
|
||||
+ bool is_enabled = registry->enabled_extensions().Contains(extension_id);
|
||||
+ bool is_disabled = registry->disabled_extensions().Contains(extension_id);
|
||||
+
|
||||
+ // Skip if extension is not installed yet
|
||||
+ if (!is_enabled && !is_disabled) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (should_be_enabled && is_disabled) {
|
||||
+ // Extension should be enabled but is disabled - enable it
|
||||
+ LOG(INFO) << "browseros: Enabling extension " << extension_id
|
||||
+ << " (flag state requires it to be enabled)";
|
||||
+ registrar->EnableExtension(extension_id);
|
||||
+ } else if (!should_be_enabled && is_enabled) {
|
||||
+ // Extension should be disabled but is enabled - disable it
|
||||
+ LOG(INFO) << "browseros: Disabling extension " << extension_id
|
||||
+ << " (flag state requires it to be disabled)";
|
||||
+ registrar->DisableExtension(extension_id,
|
||||
+ {disable_reason::DISABLE_USER_ACTION});
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::FetchAndCheckConfig() {
|
||||
+ LOG(INFO) << "browseros: Fetching latest config to check for changes";
|
||||
+
|
||||
+ if (config_file_for_testing_.empty() && config_url_.is_valid()) {
|
||||
+ // Fetch from URL
|
||||
+ if (!url_loader_factory_) {
|
||||
+ url_loader_factory_ = profile_->GetDefaultStoragePartition()
|
||||
+ ->GetURLLoaderFactoryForBrowserProcess();
|
||||
+ }
|
||||
+
|
||||
+ auto resource_request = std::make_unique<network::ResourceRequest>();
|
||||
+ resource_request->url = config_url_;
|
||||
+ resource_request->method = "GET";
|
||||
+ resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
|
||||
+
|
||||
+ auto config_check_loader = network::SimpleURLLoader::Create(
|
||||
+ std::move(resource_request), kBrowserOSExtensionsFetchTrafficAnnotation);
|
||||
+
|
||||
+ // Store the loader to keep it alive during the request
|
||||
+ auto* loader_ptr = config_check_loader.get();
|
||||
+ loader_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
|
||||
+ url_loader_factory_.get(),
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::OnConfigCheckComplete,
|
||||
+ weak_ptr_factory_.GetWeakPtr(),
|
||||
+ std::move(config_check_loader)));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::OnConfigCheckComplete(
|
||||
+ std::unique_ptr<network::SimpleURLLoader> loader,
|
||||
+ std::unique_ptr<std::string> response_body) {
|
||||
+ if (!response_body) {
|
||||
+ LOG(WARNING) << "browseros: Failed to fetch config for update check";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ std::optional<base::Value> parsed_json = base::JSONReader::Read(*response_body);
|
||||
+ if (!parsed_json || !parsed_json->is_dict()) {
|
||||
+ LOG(WARNING) << "browseros: Invalid config JSON during update check";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const base::Value::Dict* extensions_dict =
|
||||
+ parsed_json->GetDict().FindDict("extensions");
|
||||
+ if (!extensions_dict) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // Check if config has changed
|
||||
+ bool config_changed = false;
|
||||
+ if (last_config_.empty()) {
|
||||
+ config_changed = true; // First time
|
||||
+ } else {
|
||||
+ // Compare with last config
|
||||
+ for (const auto [extension_id, new_config] : *extensions_dict) {
|
||||
+ const base::Value::Dict* old_config = last_config_.FindDict(extension_id);
|
||||
+ if (!old_config || *old_config != new_config.GetDict()) {
|
||||
+ config_changed = true;
|
||||
+ LOG(INFO) << "browseros: Config changed for extension " << extension_id;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check for removed extensions
|
||||
+ for (const auto [extension_id, _] : last_config_) {
|
||||
+ if (!extensions_dict->contains(extension_id)) {
|
||||
+ config_changed = true;
|
||||
+ LOG(INFO) << "browseros: Extension " << extension_id << " removed from config";
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (config_changed) {
|
||||
+ LOG(INFO) << "browseros: Config has changed, reloading extensions";
|
||||
+
|
||||
+ // Store the new config
|
||||
+ last_config_ = extensions_dict->Clone();
|
||||
+
|
||||
+ // Parse and reload with new config
|
||||
+ ParseConfiguration(*response_body);
|
||||
+ } else {
|
||||
+ LOG(INFO) << "browseros: Config unchanged";
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::TriggerImmediateInstallation() {
|
||||
+ if (!profile_ || browseros_extension_ids_.empty()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Triggering immediate installation on first start";
|
||||
+
|
||||
+ // First, add all extensions to pending if they're not already installed
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ PendingExtensionManager* pending_manager = PendingExtensionManager::Get(profile_);
|
||||
+
|
||||
+ if (registry && pending_manager && !last_config_.empty()) {
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ // Skip if already installed
|
||||
+ if (registry->GetInstalledExtension(extension_id)) {
|
||||
+ LOG(INFO) << "browseros: Extension " << extension_id << " already installed";
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Add to pending extensions
|
||||
+ const base::Value::Dict* extension_config = last_config_.FindDict(extension_id);
|
||||
+ if (extension_config) {
|
||||
+ const std::string* update_url =
|
||||
+ extension_config->FindString(ExternalProviderImpl::kExternalUpdateUrl);
|
||||
+ if (update_url) {
|
||||
+ GURL update_gurl(*update_url);
|
||||
+ if (update_gurl.is_valid()) {
|
||||
+ pending_manager->AddFromExternalUpdateUrl(
|
||||
+ extension_id,
|
||||
+ std::string(), // No install param
|
||||
+ update_gurl,
|
||||
+ mojom::ManifestLocation::kExternalComponent,
|
||||
+ Extension::WAS_INSTALLED_BY_DEFAULT,
|
||||
+ false); // Don't mark acknowledged
|
||||
+ LOG(INFO) << "browseros: Added " << extension_id
|
||||
+ << " to pending for immediate installation";
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Now trigger immediate high-priority installation
|
||||
+ ExtensionUpdater* updater = ExtensionUpdater::Get(profile_);
|
||||
+ if (!updater) {
|
||||
+ LOG(WARNING) << "browseros: No extension updater available for immediate installation";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Executing CheckNow with immediate install for "
|
||||
+ << browseros_extension_ids_.size() << " BrowserOS extensions";
|
||||
+
|
||||
+ // Create CheckParams for immediate foreground installation
|
||||
+ ExtensionUpdater::CheckParams params;
|
||||
+ params.ids = std::list<ExtensionId>(browseros_extension_ids_.begin(),
|
||||
+ browseros_extension_ids_.end());
|
||||
+ params.install_immediately = true;
|
||||
+ params.fetch_priority = DownloadFetchPriority::kForeground;
|
||||
+
|
||||
+ // Trigger the installation
|
||||
+ updater->CheckNow(std::move(params));
|
||||
+
|
||||
+ // Schedule enforcement of extension state after installation completes.
|
||||
+ // This ensures the correct extension is enabled/disabled based on the
|
||||
+ // kBrowserOsAlphaFeatures flag after fresh installation.
|
||||
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::EnforceExtensionStateBasedOnFlag,
|
||||
+ weak_ptr_factory_.GetWeakPtr()),
|
||||
+ base::Seconds(30));
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::ForceUpdateCheck() {
|
||||
+ if (!profile_ || browseros_extension_ids_.empty()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionUpdater* updater = ExtensionUpdater::Get(profile_);
|
||||
+ if (!updater) {
|
||||
+ LOG(WARNING) << "browseros: No extension updater available";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Forcing immediate update check for "
|
||||
+ << browseros_extension_ids_.size() << " BrowserOS extensions";
|
||||
+
|
||||
+ // Create CheckParams for immediate foreground update
|
||||
+ ExtensionUpdater::CheckParams params;
|
||||
+ params.ids = std::list<ExtensionId>(browseros_extension_ids_.begin(),
|
||||
+ browseros_extension_ids_.end());
|
||||
+ params.install_immediately = true;
|
||||
+ params.fetch_priority = DownloadFetchPriority::kForeground;
|
||||
+
|
||||
+ // Trigger the update check
|
||||
+ updater->CheckNow(std::move(params));
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::LoadFromFile() {
|
||||
+ // This runs on a background thread to avoid blocking the UI
|
||||
+ base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
+ FROM_HERE,
|
||||
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
|
||||
+ base::BindOnce([](const base::FilePath& path) -> std::string {
|
||||
+ std::string contents;
|
||||
+ if (!base::ReadFileToString(path, &contents)) {
|
||||
+ LOG(ERROR) << "Failed to read BrowserOS config file: " << path;
|
||||
+ return std::string();
|
||||
+ }
|
||||
+ return contents;
|
||||
+ }, config_file_for_testing_),
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::ParseConfiguration,
|
||||
+ weak_ptr_factory_.GetWeakPtr()));
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::CheckAndLogExtensionState(
|
||||
+ const std::string& context) {
|
||||
+ if (!profile_) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
|
||||
+
|
||||
+ if (!registry || !prefs) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ // If extension is enabled, it's healthy - skip logging
|
||||
+ if (registry->enabled_extensions().Contains(extension_id)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ // Extension is NOT enabled - gather diagnostic information
|
||||
+ base::Value::Dict properties;
|
||||
+ properties.Set("extension_id", extension_id);
|
||||
+ properties.Set("context", context);
|
||||
+
|
||||
+ std::string state;
|
||||
+
|
||||
+ if (registry->disabled_extensions().Contains(extension_id)) {
|
||||
+ state = "disabled";
|
||||
+
|
||||
+ // Get extension version if available
|
||||
+ const Extension* extension = registry->disabled_extensions().GetByID(extension_id);
|
||||
+ if (extension) {
|
||||
+ properties.Set("version", extension->version().GetString());
|
||||
+ }
|
||||
+
|
||||
+ // Get disable reasons using public API
|
||||
+ DisableReasonSet disable_reasons = prefs->GetDisableReasons(extension_id);
|
||||
+
|
||||
+ // Convert to bitmask by ORing all reason values
|
||||
+ int bitmask = 0;
|
||||
+ for (disable_reason::DisableReason reason : disable_reasons) {
|
||||
+ bitmask |= static_cast<int>(reason);
|
||||
+ }
|
||||
+ properties.Set("disable_reasons_bitmask", bitmask);
|
||||
+
|
||||
+ // Log individual disable reason flags for easy querying
|
||||
+ properties.Set("reason_user_action",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_USER_ACTION));
|
||||
+ properties.Set("reason_permissions_increase",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_PERMISSIONS_INCREASE));
|
||||
+ properties.Set("reason_reload",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_RELOAD));
|
||||
+ properties.Set("reason_corrupted",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_CORRUPTED));
|
||||
+ properties.Set("reason_greylist",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_GREYLIST));
|
||||
+ properties.Set("reason_remote_install",
|
||||
+ disable_reasons.contains(disable_reason::DISABLE_REMOTE_INSTALL));
|
||||
+
|
||||
+ } else if (registry->blocklisted_extensions().Contains(extension_id)) {
|
||||
+ state = "blocklisted";
|
||||
+
|
||||
+ } else if (registry->blocked_extensions().Contains(extension_id)) {
|
||||
+ state = "blocked";
|
||||
+
|
||||
+ } else if (registry->terminated_extensions().Contains(extension_id)) {
|
||||
+ state = "terminated";
|
||||
+
|
||||
+ } else {
|
||||
+ state = "not_installed";
|
||||
+ }
|
||||
+
|
||||
+ properties.Set("state", state);
|
||||
+
|
||||
+ // Log to metrics
|
||||
+ browseros_metrics::BrowserOSMetrics::Log("ota.extension.unexpected_state",
|
||||
+ std::move(properties));
|
||||
+
|
||||
+ // Also log to Chrome logs for local debugging
|
||||
+ LOG(WARNING) << "browseros: Extension " << extension_id
|
||||
+ << " in unexpected state: " << state
|
||||
+ << " (context: " << context << ")";
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+} // namespace extensions
|
||||
@@ -1,134 +0,0 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_external_loader.h b/chrome/browser/extensions/browseros_external_loader.h
|
||||
new file mode 100644
|
||||
index 0000000000000..0365a5e231215
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_external_loader.h
|
||||
@@ -0,0 +1,128 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#ifndef CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTERNAL_LOADER_H_
|
||||
+#define CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTERNAL_LOADER_H_
|
||||
+
|
||||
+#include <memory>
|
||||
+#include <set>
|
||||
+#include <string>
|
||||
+
|
||||
+#include "base/files/file_path.h"
|
||||
+#include "base/memory/scoped_refptr.h"
|
||||
+#include "base/memory/weak_ptr.h"
|
||||
+#include "base/timer/timer.h"
|
||||
+#include "chrome/browser/extensions/external_loader.h"
|
||||
+#include "services/network/public/cpp/simple_url_loader.h"
|
||||
+
|
||||
+class Profile;
|
||||
+
|
||||
+namespace network {
|
||||
+class SharedURLLoaderFactory;
|
||||
+} // namespace network
|
||||
+
|
||||
+namespace extensions {
|
||||
+
|
||||
+// A specialization of the ExternalLoader that loads extension information
|
||||
+// from a remote URL. This is designed for BrowserOS to specify a set of
|
||||
+// extensions that should be installed at startup.
|
||||
+class BrowserOSExternalLoader : public ExternalLoader {
|
||||
+ public:
|
||||
+ explicit BrowserOSExternalLoader(Profile* profile);
|
||||
+
|
||||
+ BrowserOSExternalLoader(const BrowserOSExternalLoader&) = delete;
|
||||
+ BrowserOSExternalLoader& operator=(const BrowserOSExternalLoader&) = delete;
|
||||
+
|
||||
+ // Sets the URL from which to fetch the extension configuration.
|
||||
+ // Must be called before StartLoading().
|
||||
+ void SetConfigUrl(const GURL& url) { config_url_ = url; }
|
||||
+
|
||||
+ // For testing: sets a local file path instead of fetching from URL.
|
||||
+ void SetConfigFileForTesting(const base::FilePath& path) {
|
||||
+ config_file_for_testing_ = path;
|
||||
+ }
|
||||
+
|
||||
+ // Starts periodic maintenance loop (no-op if already running).
|
||||
+ void StartPeriodicCheck();
|
||||
+
|
||||
+ // Periodic maintenance: re-enables disabled extensions, checks config, and forces updates
|
||||
+ void PeriodicMaintenance();
|
||||
+
|
||||
+ // Fetches the latest config and checks for changes
|
||||
+ void FetchAndCheckConfig();
|
||||
+
|
||||
+ // Forces immediate update check for BrowserOS extensions
|
||||
+ void ForceUpdateCheck();
|
||||
+
|
||||
+ protected:
|
||||
+ ~BrowserOSExternalLoader() override;
|
||||
+
|
||||
+ // ExternalLoader:
|
||||
+ void StartLoading() override;
|
||||
+
|
||||
+ private:
|
||||
+ friend class base::RefCountedThreadSafe<ExternalLoader>;
|
||||
+
|
||||
+ // Called when the URL fetch completes.
|
||||
+ void OnURLFetchComplete(std::unique_ptr<std::string> response_body);
|
||||
+
|
||||
+ // Called when config check fetch completes
|
||||
+ void OnConfigCheckComplete(std::unique_ptr<network::SimpleURLLoader> loader,
|
||||
+ std::unique_ptr<std::string> response_body);
|
||||
+
|
||||
+ // Parses the fetched JSON configuration and loads extensions.
|
||||
+ void ParseConfiguration(const std::string& json_content);
|
||||
+
|
||||
+ // Loads configuration from a local file (for testing).
|
||||
+ void LoadFromFile();
|
||||
+
|
||||
+ // Checks for uninstalled BrowserOS extensions and reinstalls them
|
||||
+ void ReinstallUninstalledExtensions();
|
||||
+
|
||||
+ // Re-enables BrowserOS extensions that were disabled by user action
|
||||
+ void ReenableDisabledExtensions();
|
||||
+
|
||||
+ // Enforces extension enabled/disabled state based on kBrowserOsAlphaFeatures flag.
|
||||
+ // - When flag is ON: enables kAgentV2ExtensionId, disables kAISidePanelExtensionId
|
||||
+ // - When flag is OFF: enables kAISidePanelExtensionId, disables kAgentV2ExtensionId
|
||||
+ void EnforceExtensionStateBasedOnFlag();
|
||||
+
|
||||
+ // Triggers immediate installation of all BrowserOS extensions on first start
|
||||
+ void TriggerImmediateInstallation();
|
||||
+
|
||||
+ // Checks extension state and logs to metrics if not enabled
|
||||
+ void CheckAndLogExtensionState(const std::string& context);
|
||||
+
|
||||
+ // The profile associated with this loader.
|
||||
+ raw_ptr<Profile> profile_;
|
||||
+
|
||||
+ // URL from which to fetch the extension configuration.
|
||||
+ GURL config_url_;
|
||||
+
|
||||
+ // For testing: local file path instead of URL.
|
||||
+ base::FilePath config_file_for_testing_;
|
||||
+
|
||||
+ // URL loader for fetching the configuration.
|
||||
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
|
||||
+
|
||||
+ // URLLoaderFactory for making network requests.
|
||||
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
|
||||
+
|
||||
+ // List of BrowserOS extension IDs to monitor
|
||||
+ std::set<std::string> browseros_extension_ids_;
|
||||
+
|
||||
+ // Last fetched config for comparison
|
||||
+ base::Value::Dict last_config_;
|
||||
+
|
||||
+ // Tracks whether we have successfully applied a configuration during this session.
|
||||
+ bool has_successful_config_ = false;
|
||||
+
|
||||
+ base::RepeatingTimer periodic_timer_;
|
||||
+
|
||||
+ base::WeakPtrFactory<BrowserOSExternalLoader> weak_ptr_factory_{this};
|
||||
+};
|
||||
+
|
||||
+} // namespace extensions
|
||||
+
|
||||
+#endif // CHROME_BROWSER_EXTENSIONS_BROWSEROS_EXTERNAL_LOADER_H_
|
||||
@@ -33,7 +33,6 @@ def create_build_context(chromium_src: Optional[Path] = None) -> Optional[Contex
|
||||
return None
|
||||
|
||||
ctx = Context(
|
||||
root_dir=Path.cwd(),
|
||||
chromium_src=chromium_src,
|
||||
architecture="", # Not needed for patch operations
|
||||
build_type="debug", # Not needed for patch operations
|
||||
|
||||
@@ -20,6 +20,7 @@ from .utils import (
|
||||
IS_MACOS,
|
||||
)
|
||||
from .env import EnvConfig
|
||||
from .paths import get_package_root
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -182,7 +183,7 @@ class Context:
|
||||
Context Object pattern - ONE place for all build state
|
||||
"""
|
||||
|
||||
root_dir: Path
|
||||
root_dir: Path = field(default_factory=get_package_root)
|
||||
chromium_src: Path = Path()
|
||||
out_dir: str = "out/Default"
|
||||
architecture: str = "" # Will be set in __post_init__
|
||||
@@ -301,9 +302,9 @@ class Context:
|
||||
"""
|
||||
Initialize context from config
|
||||
Replaces __post_init__ logic for better testability
|
||||
"""
|
||||
|
||||
root_dir = Path(config.get("root_dir", Path.cwd()))
|
||||
Note: root_dir is always computed from package location, never from config.
|
||||
"""
|
||||
chromium_src = (
|
||||
Path(config.get("chromium_src", ""))
|
||||
if config.get("chromium_src")
|
||||
@@ -313,9 +314,8 @@ class Context:
|
||||
# Get architecture or use platform default
|
||||
arch = config.get("architecture") or get_platform_arch()
|
||||
|
||||
# Create instance
|
||||
# Create instance - root_dir uses default_factory (get_package_root)
|
||||
ctx = cls(
|
||||
root_dir=root_dir,
|
||||
chromium_src=chromium_src,
|
||||
architecture=arch,
|
||||
build_type=config.get("build_type", "debug"),
|
||||
|
||||
@@ -16,12 +16,10 @@ from dotenv import load_dotenv
|
||||
|
||||
|
||||
def _load_dotenv_file():
|
||||
"""Load .env file from project root (packages/browseros parent directory)"""
|
||||
# Find project root by going up from this file's location
|
||||
# This file is at: packages/browseros/build/common/env.py
|
||||
# Project root is at: packages/browseros/../../ (the repo root)
|
||||
current_dir = Path(__file__).parent # common/
|
||||
browseros_root = current_dir.parent.parent # packages/browseros/
|
||||
"""Load .env file from project root"""
|
||||
from .paths import get_package_root
|
||||
|
||||
browseros_root = get_package_root()
|
||||
project_root = browseros_root.parent.parent # repo root
|
||||
|
||||
# Try loading .env from multiple locations (most specific first)
|
||||
|
||||
@@ -16,8 +16,10 @@ def _ensure_log_file():
|
||||
"""Ensure log file is created with timestamp"""
|
||||
global _log_file
|
||||
if _log_file is None:
|
||||
from .paths import get_package_root
|
||||
|
||||
# Create logs directory if it doesn't exist
|
||||
log_dir = Path(__file__).parent.parent / "logs"
|
||||
log_dir = get_package_root() / "logs"
|
||||
log_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Create log file with timestamp
|
||||
|
||||
45
packages/browseros/build/common/paths.py
Normal file
45
packages/browseros/build/common/paths.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Package root detection for BrowserOS build system
|
||||
|
||||
This module provides a single function to find the browseros package root
|
||||
directory, regardless of the current working directory.
|
||||
|
||||
IMPORTANT: This module must have NO local imports to avoid circular dependencies.
|
||||
It is imported by both context.py and env.py at module load time.
|
||||
"""
|
||||
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_package_root() -> Path:
|
||||
"""Find browseros package root by walking up looking for pyproject.toml.
|
||||
|
||||
Walks up from this file's location looking for a pyproject.toml that
|
||||
contains the browseros package definition.
|
||||
|
||||
Returns:
|
||||
Path to the browseros package root (e.g., packages/browseros/)
|
||||
|
||||
Raises:
|
||||
RuntimeError: If package root cannot be found
|
||||
"""
|
||||
current = Path(__file__).resolve().parent
|
||||
|
||||
while current != current.parent:
|
||||
pyproject = current / "pyproject.toml"
|
||||
if pyproject.exists():
|
||||
content = pyproject.read_text()
|
||||
# Match name = "browseros" with flexible whitespace
|
||||
if re.search(r'^name\s*=\s*["\']browseros["\']', content, re.MULTILINE):
|
||||
return current
|
||||
current = current.parent
|
||||
|
||||
raise RuntimeError(
|
||||
"Could not find browseros package root. "
|
||||
"Expected to find pyproject.toml with name = 'browseros' "
|
||||
f"in ancestors of {Path(__file__).resolve()}"
|
||||
)
|
||||
@@ -30,14 +30,12 @@ from .utils import get_platform_arch, log_info
|
||||
def resolve_config(
|
||||
cli_args: Dict[str, Any],
|
||||
yaml_config: Optional[Dict[str, Any]] = None,
|
||||
root_dir: Optional[Path] = None,
|
||||
) -> Context:
|
||||
"""Resolve build configuration - single entry point.
|
||||
|
||||
Args:
|
||||
cli_args: Dictionary of CLI arguments (all values should be None if not provided)
|
||||
yaml_config: Optional YAML configuration (triggers CONFIG mode)
|
||||
root_dir: Optional root directory (defaults to CWD)
|
||||
|
||||
Returns:
|
||||
Fully resolved Context object
|
||||
@@ -48,24 +46,25 @@ def resolve_config(
|
||||
Modes:
|
||||
- CONFIG mode (yaml_config provided): YAML is authoritative
|
||||
- DIRECT mode (no yaml_config): CLI > Env > Defaults
|
||||
"""
|
||||
root_dir = root_dir or Path.cwd()
|
||||
|
||||
Note:
|
||||
root_dir is always computed from package location via get_package_root(),
|
||||
never from config or cwd.
|
||||
"""
|
||||
if yaml_config:
|
||||
return _resolve_config_mode(yaml_config, cli_args, root_dir)
|
||||
return _resolve_config_mode(yaml_config, cli_args)
|
||||
else:
|
||||
return _resolve_direct_mode(cli_args, root_dir)
|
||||
return _resolve_direct_mode(cli_args)
|
||||
|
||||
|
||||
def _resolve_config_mode(
|
||||
yaml_config: Dict[str, Any], cli_args: Dict[str, Any], root_dir: Path
|
||||
yaml_config: Dict[str, Any], cli_args: Dict[str, Any]
|
||||
) -> Context:
|
||||
"""CONFIG MODE: YAML is base, CLI can override.
|
||||
|
||||
Args:
|
||||
yaml_config: YAML configuration dictionary
|
||||
cli_args: CLI arguments (can override YAML values)
|
||||
root_dir: Project root directory
|
||||
|
||||
Returns:
|
||||
Context with values from YAML, optionally overridden by CLI
|
||||
@@ -116,19 +115,17 @@ def _resolve_config_mode(
|
||||
log_info(f"✓ CONFIG MODE: build_type={build_type} ({build_type_source})")
|
||||
|
||||
return Context(
|
||||
root_dir=root_dir,
|
||||
chromium_src=chromium_src,
|
||||
architecture=architecture,
|
||||
build_type=build_type,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_direct_mode(cli_args: Dict[str, Any], root_dir: Path) -> Context:
|
||||
def _resolve_direct_mode(cli_args: Dict[str, Any]) -> Context:
|
||||
"""DIRECT MODE: CLI > Env > Defaults.
|
||||
|
||||
Args:
|
||||
cli_args: CLI arguments (None if not provided by user)
|
||||
root_dir: Project root directory
|
||||
|
||||
Returns:
|
||||
Context with resolved values
|
||||
@@ -171,7 +168,6 @@ def _resolve_direct_mode(cli_args: Dict[str, Any], root_dir: Path) -> Context:
|
||||
log_info(f"✓ DIRECT MODE: build_type={build_type} (cli/default)")
|
||||
|
||||
return Context(
|
||||
root_dir=root_dir,
|
||||
chromium_src=chromium_src,
|
||||
architecture=architecture,
|
||||
build_type=build_type,
|
||||
|
||||
@@ -264,16 +264,15 @@ def handle_merge_command(
|
||||
log_error(f"Architecture 2 app not found: {arch2_path}")
|
||||
return False
|
||||
|
||||
# Get root_dir from where this module is located
|
||||
root_dir = Path(__file__).parent.parent.parent.parent
|
||||
log_info(f"📂 Using root directory: {root_dir}")
|
||||
|
||||
# Auto-generate output path in chromium source
|
||||
# Get the app name from BuildContext
|
||||
from ...common.context import Context
|
||||
from ...common.paths import get_package_root
|
||||
|
||||
root_dir = get_package_root()
|
||||
log_info(f"📂 Using root directory: {root_dir}")
|
||||
|
||||
temp_ctx = Context(
|
||||
root_dir=root_dir,
|
||||
chromium_src=chromium_src,
|
||||
architecture="universal",
|
||||
build_type="release",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/chrome/browser/browseros_server/browseros_server_manager.cc b/chrome/browser/browseros_server/browseros_server_manager.cc
|
||||
new file mode 100644
|
||||
index 0000000000000..81ab3d65f5a38
|
||||
index 0000000000000..0a3a2d12d08fc
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/browseros_server/browseros_server_manager.cc
|
||||
@@ -0,0 +1,1072 @@
|
||||
@@ -0,0 +1,1076 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
@@ -315,6 +315,7 @@ index 0000000000000..81ab3d65f5a38
|
||||
+void BrowserOSServerManager::SavePortsToPrefs() {
|
||||
+ PrefService* prefs = g_browser_process->local_state();
|
||||
+ if (!prefs) {
|
||||
+ LOG(WARNING) << "browseros: SavePortsToPrefs - no prefs available, skipping save";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
@@ -324,7 +325,10 @@ index 0000000000000..81ab3d65f5a38
|
||||
+ prefs->SetInteger(browseros_server::kExtensionServerPort, extension_port_);
|
||||
+ prefs->SetBoolean(browseros_server::kMCPServerEnabled, mcp_enabled_);
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Saved finalized ports to prefs";
|
||||
+ LOG(INFO) << "browseros: Saving to prefs - CDP: " << cdp_port_
|
||||
+ << ", MCP: " << mcp_port_ << ", Agent: " << agent_port_
|
||||
+ << ", Extension: " << extension_port_
|
||||
+ << ", MCP enabled: " << (mcp_enabled_ ? "true" : "false");
|
||||
+}
|
||||
+
|
||||
+void BrowserOSServerManager::Start() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_extension_constants.h b/chrome/browser/extensions/browseros_extension_constants.h
|
||||
new file mode 100644
|
||||
index 0000000000000..e2ffd24bff8d5
|
||||
index 0000000000000..5a3b518b224a7
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_extension_constants.h
|
||||
@@ -0,0 +1,118 @@
|
||||
@@ -0,0 +1,120 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
@@ -27,6 +27,12 @@ index 0000000000000..e2ffd24bff8d5
|
||||
+inline constexpr char kAgentV2ExtensionId[] =
|
||||
+ "bflpfmnmnokmjhmgnolecpppdbdophmk";
|
||||
+
|
||||
+// BrowserOS extension config URLs
|
||||
+inline constexpr char kBrowserOSConfigUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/extensions.json";
|
||||
+inline constexpr char kBrowserOSAlphaConfigUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/extensions.alpha.json";
|
||||
+
|
||||
+// Bug Reporter Extension ID
|
||||
+inline constexpr char kBugReporterExtensionId[] =
|
||||
+ "adlpneommgkgeanpaekgoaolcpncohkf";
|
||||
@@ -41,10 +47,6 @@ index 0000000000000..e2ffd24bff8d5
|
||||
+inline constexpr char kBrowserOSUpdateUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/update-manifest.xml";
|
||||
+
|
||||
+// BrowserOS extension config URL
|
||||
+inline constexpr char kBrowserOSConfigUrl[] =
|
||||
+ "https://cdn.browseros.com/extensions/extensions.json";
|
||||
+
|
||||
+struct BrowserOSExtensionInfo {
|
||||
+ const char* id;
|
||||
+ const char* display_name;
|
||||
@@ -56,7 +58,7 @@ index 0000000000000..e2ffd24bff8d5
|
||||
+ {kAISidePanelExtensionId, "BrowserOS", true, true},
|
||||
+ {kBugReporterExtensionId, "BrowserOS/bug-reporter", true, false},
|
||||
+ {kControllerExtensionId, "BrowserOS/controller", false, false},
|
||||
+ {kAgentV2ExtensionId, "BrowserOS", false, false},
|
||||
+ {kAgentV2ExtensionId, "BrowserOS", true, true},
|
||||
+};
|
||||
+
|
||||
+// Allowlist of BrowserOS extension IDs that are permitted to be installed.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_external_loader.cc b/chrome/browser/extensions/browseros_external_loader.cc
|
||||
new file mode 100644
|
||||
index 0000000000000..70842c316df3c
|
||||
index 0000000000000..8f01037dfcb90
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_external_loader.cc
|
||||
@@ -0,0 +1,669 @@
|
||||
@@ -0,0 +1,757 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
@@ -13,6 +13,7 @@ index 0000000000000..70842c316df3c
|
||||
+#include <memory>
|
||||
+#include <utility>
|
||||
+
|
||||
+#include "base/feature_list.h"
|
||||
+#include "base/files/file_util.h"
|
||||
+#include "base/functional/bind.h"
|
||||
+#include "base/json/json_reader.h"
|
||||
@@ -22,6 +23,7 @@ index 0000000000000..70842c316df3c
|
||||
+#include "base/task/thread_pool.h"
|
||||
+#include "base/task/single_thread_task_runner.h"
|
||||
+#include "base/values.h"
|
||||
+#include "chrome/browser/browser_features.h"
|
||||
+#include "chrome/browser/browser_process.h"
|
||||
+#include "chrome/browser/extensions/browseros_extension_constants.h"
|
||||
+#include "chrome/browser/extensions/extension_service.h"
|
||||
@@ -49,10 +51,6 @@ index 0000000000000..70842c316df3c
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+// Default config URL - should be updated to actual BrowserOS server
|
||||
+// Can be overridden via --browseros-extensions-url command line flag
|
||||
+constexpr char kBrowserOSConfigUrl[] = "https://cdn.browseros.com/extensions/extensions.json";
|
||||
+
|
||||
+// Interval for periodic maintenance
|
||||
+constexpr base::TimeDelta kPeriodicMaintenanceInterval = base::Minutes(15);
|
||||
+
|
||||
@@ -94,12 +92,41 @@ index 0000000000000..70842c316df3c
|
||||
+// }
|
||||
+// }
|
||||
+
|
||||
+// Determines if a BrowserOS extension should be enabled based on the
|
||||
+// kBrowserOsAlphaFeatures flag state.
|
||||
+// - kAgentV2ExtensionId: enabled only when flag is ON
|
||||
+// - kAISidePanelExtensionId: enabled only when flag is OFF
|
||||
+// - All other extensions: always enabled
|
||||
+bool ShouldExtensionBeEnabled(const std::string& extension_id) {
|
||||
+ bool alpha_features_enabled =
|
||||
+ base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures);
|
||||
+
|
||||
+ if (extension_id == browseros::kAgentV2ExtensionId) {
|
||||
+ return alpha_features_enabled;
|
||||
+ }
|
||||
+
|
||||
+ if (extension_id == browseros::kAISidePanelExtensionId) {
|
||||
+ return !alpha_features_enabled;
|
||||
+ }
|
||||
+
|
||||
+ // All other BrowserOS extensions are always enabled
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+// Returns the appropriate config URL based on the alpha features flag.
|
||||
+const char* GetConfigUrl() {
|
||||
+ if (base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures)) {
|
||||
+ return browseros::kBrowserOSAlphaConfigUrl;
|
||||
+ }
|
||||
+ return browseros::kBrowserOSConfigUrl;
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+BrowserOSExternalLoader::BrowserOSExternalLoader(Profile* profile)
|
||||
+ : profile_(profile) {
|
||||
+ // Default config URL - can be overridden via SetConfigUrl
|
||||
+ config_url_ = GURL(kBrowserOSConfigUrl);
|
||||
+ // Select config URL based on alpha features flag
|
||||
+ config_url_ = GURL(GetConfigUrl());
|
||||
+
|
||||
+ // Add known BrowserOS extension IDs
|
||||
+ for (const char* extension_id : browseros::kAllowedExtensions) {
|
||||
@@ -278,17 +305,20 @@ index 0000000000000..70842c316df3c
|
||||
+
|
||||
+ // 1. Check for and reinstall any uninstalled BrowserOS extensions
|
||||
+ ReinstallUninstalledExtensions();
|
||||
+
|
||||
+ // 2. Re-enable any disabled BrowserOS extensions
|
||||
+
|
||||
+ // 2. Re-enable any disabled BrowserOS extensions (respects flag state)
|
||||
+ ReenableDisabledExtensions();
|
||||
+
|
||||
+ // 3. Fetch latest config and check for changes
|
||||
+
|
||||
+ // 3. Enforce correct enabled/disabled state based on alpha features flag
|
||||
+ EnforceExtensionStateBasedOnFlag();
|
||||
+
|
||||
+ // 4. Fetch latest config and check for changes
|
||||
+ FetchAndCheckConfig();
|
||||
+
|
||||
+ // 4. Force immediate update check for all BrowserOS extensions
|
||||
+
|
||||
+ // 5. Force immediate update check for all BrowserOS extensions
|
||||
+ ForceUpdateCheck();
|
||||
+
|
||||
+ // 5. Log extension state after all maintenance attempts
|
||||
+ // 6. Log extension state after all maintenance attempts
|
||||
+ CheckAndLogExtensionState("periodic_maintenance");
|
||||
+
|
||||
+ // Schedule the next maintenance
|
||||
@@ -383,6 +413,11 @@ index 0000000000000..70842c316df3c
|
||||
+ continue; // Extension is not disabled, skip to next
|
||||
+ }
|
||||
+
|
||||
+ // Only re-enable if this extension should be enabled based on flag state
|
||||
+ if (!ShouldExtensionBeEnabled(extension_id)) {
|
||||
+ continue; // Extension should stay disabled based on flag
|
||||
+ }
|
||||
+
|
||||
+ // Re-enable BrowserOS extensions regardless of disable reason
|
||||
+ auto* registrar = extensions::ExtensionRegistrar::Get(profile_);
|
||||
+ if (!registrar) {
|
||||
@@ -396,6 +431,50 @@ index 0000000000000..70842c316df3c
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::EnforceExtensionStateBasedOnFlag() {
|
||||
+ if (!profile_) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
||||
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
|
||||
+ auto* registrar = extensions::ExtensionRegistrar::Get(profile_);
|
||||
+
|
||||
+ if (!registry || !prefs || !registrar) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ bool alpha_features_enabled =
|
||||
+ base::FeatureList::IsEnabled(features::kBrowserOsAlphaFeatures);
|
||||
+
|
||||
+ LOG(INFO) << "browseros: Enforcing extension state (alpha_features="
|
||||
+ << (alpha_features_enabled ? "ON" : "OFF") << ")";
|
||||
+
|
||||
+ for (const std::string& extension_id : browseros_extension_ids_) {
|
||||
+ bool should_be_enabled = ShouldExtensionBeEnabled(extension_id);
|
||||
+ bool is_enabled = registry->enabled_extensions().Contains(extension_id);
|
||||
+ bool is_disabled = registry->disabled_extensions().Contains(extension_id);
|
||||
+
|
||||
+ // Skip if extension is not installed yet
|
||||
+ if (!is_enabled && !is_disabled) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (should_be_enabled && is_disabled) {
|
||||
+ // Extension should be enabled but is disabled - enable it
|
||||
+ LOG(INFO) << "browseros: Enabling extension " << extension_id
|
||||
+ << " (flag state requires it to be enabled)";
|
||||
+ registrar->EnableExtension(extension_id);
|
||||
+ } else if (!should_be_enabled && is_enabled) {
|
||||
+ // Extension should be disabled but is enabled - disable it
|
||||
+ LOG(INFO) << "browseros: Disabling extension " << extension_id
|
||||
+ << " (flag state requires it to be disabled)";
|
||||
+ registrar->DisableExtension(extension_id,
|
||||
+ {disable_reason::DISABLE_USER_ACTION});
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::FetchAndCheckConfig() {
|
||||
+ LOG(INFO) << "browseros: Fetching latest config to check for changes";
|
||||
+
|
||||
@@ -536,13 +615,22 @@ index 0000000000000..70842c316df3c
|
||||
+
|
||||
+ // Create CheckParams for immediate foreground installation
|
||||
+ ExtensionUpdater::CheckParams params;
|
||||
+ params.ids = std::list<ExtensionId>(browseros_extension_ids_.begin(),
|
||||
+ params.ids = std::list<ExtensionId>(browseros_extension_ids_.begin(),
|
||||
+ browseros_extension_ids_.end());
|
||||
+ params.install_immediately = true;
|
||||
+ params.fetch_priority = DownloadFetchPriority::kForeground;
|
||||
+
|
||||
+
|
||||
+ // Trigger the installation
|
||||
+ updater->CheckNow(std::move(params));
|
||||
+
|
||||
+ // Schedule enforcement of extension state after installation completes.
|
||||
+ // This ensures the correct extension is enabled/disabled based on the
|
||||
+ // kBrowserOsAlphaFeatures flag after fresh installation.
|
||||
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(&BrowserOSExternalLoader::EnforceExtensionStateBasedOnFlag,
|
||||
+ weak_ptr_factory_.GetWeakPtr()),
|
||||
+ base::Seconds(30));
|
||||
+}
|
||||
+
|
||||
+void BrowserOSExternalLoader::ForceUpdateCheck() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/chrome/browser/extensions/browseros_external_loader.h b/chrome/browser/extensions/browseros_external_loader.h
|
||||
new file mode 100644
|
||||
index 0000000000000..33642a2fec9ee
|
||||
index 0000000000000..0365a5e231215
|
||||
--- /dev/null
|
||||
+++ b/chrome/browser/extensions/browseros_external_loader.h
|
||||
@@ -0,0 +1,123 @@
|
||||
@@ -0,0 +1,128 @@
|
||||
+// Copyright 2024 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
@@ -88,6 +88,11 @@ index 0000000000000..33642a2fec9ee
|
||||
+
|
||||
+ // Re-enables BrowserOS extensions that were disabled by user action
|
||||
+ void ReenableDisabledExtensions();
|
||||
+
|
||||
+ // Enforces extension enabled/disabled state based on kBrowserOsAlphaFeatures flag.
|
||||
+ // - When flag is ON: enables kAgentV2ExtensionId, disables kAISidePanelExtensionId
|
||||
+ // - When flag is OFF: enables kAISidePanelExtensionId, disables kAgentV2ExtensionId
|
||||
+ void EnforceExtensionStateBasedOnFlag();
|
||||
+
|
||||
+ // Triggers immediate installation of all BrowserOS extensions on first start
|
||||
+ void TriggerImmediateInstallation();
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "browseros"
|
||||
version = "0.29.0"
|
||||
version = "0.0.1"
|
||||
description = "BrowserOS Build System"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
@@ -21,18 +21,18 @@ browseros = "build.browseros:app"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = [
|
||||
"build",
|
||||
"build.cli",
|
||||
"build.common",
|
||||
"build.modules",
|
||||
"build.modules.resources",
|
||||
"build.modules.patches",
|
||||
"build.modules.setup",
|
||||
"build.modules.package",
|
||||
"build.modules.sign",
|
||||
"build.modules.extract",
|
||||
"build.modules.apply",
|
||||
"build.modules.feature"
|
||||
"build",
|
||||
"build.cli",
|
||||
"build.common",
|
||||
"build.modules",
|
||||
"build.modules.resources",
|
||||
"build.modules.patches",
|
||||
"build.modules.setup",
|
||||
"build.modules.package",
|
||||
"build.modules.sign",
|
||||
"build.modules.extract",
|
||||
"build.modules.apply",
|
||||
"build.modules.feature",
|
||||
]
|
||||
|
||||
[tool.black]
|
||||
@@ -65,8 +65,4 @@ line_length = 88
|
||||
skip_glob = ["env/*", "chromium_src/*", "chromium_src_bak/*", "third_party/*"]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff>=0.14.7",
|
||||
"pyright>=1.1.390",
|
||||
]
|
||||
|
||||
dev = ["ruff>=0.14.7", "pyright>=1.1.390"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
BROWSEROS_MAJOR=0
|
||||
BROWSEROS_MINOR=31
|
||||
BROWSEROS_BUILD=0
|
||||
BROWSEROS_PATCH=4
|
||||
BROWSEROS_PATCH=5
|
||||
|
||||
Reference in New Issue
Block a user