11diff --git a/chrome/browser/extensions/browseros_external_loader.cc b/chrome/browser/extensions/browseros_external_loader.cc
22new file mode 100644
3- index 0000000000000 ..83c9677581de2
3+ index 0000000000000 ..642ccd563582a
44--- /dev/null
55+++ b/chrome/browser/extensions/browseros_external_loader.cc
6- @@ -0 ,0 +1 ,709 @@
6+ @@ -0 ,0 +1 ,720 @@
77+// Copyright 2024 The Chromium Authors
88+// Use of this source code is governed by a BSD-style license that can be
99+// found in the LICENSE file.
@@ -102,10 +102,6 @@ index 0000000000000..83c9677581de2
102102+ } else {
103103+ config_url_ = GURL (browseros::kBrowserOSConfigUrl );
104104+ }
105- +
106- + for (const std::string& extension_id : browseros::GetBrowserOSExtensionIds ()) {
107- + browseros_extension_ids_.insert (extension_id);
108- + }
109105+}
110106+
111107+BrowserOSExternalLoader::~BrowserOSExternalLoader () = default ;
@@ -180,64 +176,74 @@ index 0000000000000..83c9677581de2
180176+
181177+ // Create the prefs dictionary in the format expected by ExternalProviderImpl
182178+ base::Value::Dict prefs;
183- +
179+ +
180+ + // Clear and repopulate server_extension_ids_ from this config
181+ + server_extension_ids_.clear ();
182+ +
184183+ for (const auto [extension_id, extension_config] : *extensions_dict) {
184+ + // Only accept extensions from the master list
185+ + if (!browseros::IsBrowserOSExtension (extension_id)) {
186+ + LOG (WARNING) << " browseros: Ignoring unknown extension from server config: "
187+ + << extension_id;
188+ + continue ;
189+ + }
190+ +
185191+ if (!extension_config.is_dict ()) {
186192+ LOG (WARNING) << " Invalid config for extension " << extension_id;
187193+ continue ;
188194+ }
189- +
195+ +
196+ + // Track this extension ID from server config
197+ + server_extension_ids_.insert (extension_id);
198+ +
190199+ const base::Value::Dict& config_dict = extension_config.GetDict ();
191200+ base::Value::Dict extension_prefs;
192- +
201+ +
193202+ // Copy supported fields
194- + if (const std::string* update_url =
203+ + if (const std::string* update_url =
195204+ config_dict.FindString (ExternalProviderImpl::kExternalUpdateUrl )) {
196205+ extension_prefs.Set (ExternalProviderImpl::kExternalUpdateUrl , *update_url);
197206+ }
198- +
199- + if (const std::string* crx_path =
207+ +
208+ + if (const std::string* crx_path =
200209+ config_dict.FindString (ExternalProviderImpl::kExternalCrx )) {
201210+ extension_prefs.Set (ExternalProviderImpl::kExternalCrx , *crx_path);
202211+ }
203- +
204- + if (const std::string* version =
212+ +
213+ + if (const std::string* version =
205214+ config_dict.FindString (ExternalProviderImpl::kExternalVersion )) {
206215+ extension_prefs.Set (ExternalProviderImpl::kExternalVersion , *version);
207216+ }
208- +
217+ +
209218+ // Add other supported fields as needed
210- + std::optional<bool > keep_if_present =
219+ + std::optional<bool > keep_if_present =
211220+ config_dict.FindBool (ExternalProviderImpl::kKeepIfPresent );
212221+ if (keep_if_present.has_value ()) {
213- + extension_prefs.Set (ExternalProviderImpl::kKeepIfPresent ,
222+ + extension_prefs.Set (ExternalProviderImpl::kKeepIfPresent ,
214223+ keep_if_present.value ());
215224+ }
216- +
225+ +
217226+ if (!extension_prefs.empty ()) {
218227+ prefs.Set (extension_id, std::move (extension_prefs));
219228+ }
220229+ }
221- +
230+ +
222231+ LOG (INFO) << " Loaded " << prefs.size () << " extensions from BrowserOS config" ;
223232+
224- + // Track the extension IDs we're managing
225- + for (const auto [extension_id, _] : prefs) {
226- + browseros_extension_ids_.insert (extension_id);
227- + }
228- +
229- + // Store the initial config for comparison
230- + if (!extensions_dict->empty ()) {
233+ + // Store the config for comparison and mark as successful
234+ + if (!server_extension_ids_.empty ()) {
231235+ last_config_ = extensions_dict->Clone ();
236+ + has_successful_config_ = true ;
232237+ }
233238+
234239+ // Pass the prefs to the external provider system
235240+ LoadFinished (std::move (prefs));
236- +
241+ +
237242+ // Use a delayed task to ensure the extension system is fully initialized
243+ + // Run both uninstall (cleanup deprecated) and install in the same callback
238244+ base::SingleThreadTaskRunner::GetCurrentDefault ()->PostDelayedTask (
239245+ FROM_HERE,
240- + base::BindOnce (&BrowserOSExternalLoader::TriggerImmediateInstallation ,
246+ + base::BindOnce (&BrowserOSExternalLoader::StartupExtensionMaintenance ,
241247+ weak_ptr_factory_.GetWeakPtr ()),
242248+ base::Seconds (2 ));
243249+
@@ -304,13 +310,13 @@ index 0000000000000..83c9677581de2
304310+ return ;
305311+ }
306312+
307- + for (const std::string& extension_id : browseros_extension_ids_ ) {
313+ + for (const std::string& extension_id : server_extension_ids_ ) {
308314+ // Check if extension exists (installed or disabled)
309315+ if (registry->GetInstalledExtension (extension_id)) {
310316+ continue ; // Extension is installed, skip to next
311317+ }
312- +
313- + LOG (INFO) << " browseros: Extension " << extension_id
318+ +
319+ + LOG (INFO) << " browseros: Extension " << extension_id
314320+ << " was uninstalled, attempting to reinstall" ;
315321+
316322+ // Find the extension's configuration
@@ -373,7 +379,7 @@ index 0000000000000..83c9677581de2
373379+ return ;
374380+ }
375381+
376- + for (const std::string& extension_id : browseros_extension_ids_ ) {
382+ + for (const std::string& extension_id : server_extension_ids_ ) {
377383+ if (!registry->disabled_extensions ().Contains (extension_id)) {
378384+ continue ;
379385+ }
@@ -477,28 +483,28 @@ index 0000000000000..83c9677581de2
477483+}
478484+
479485+void BrowserOSExternalLoader::TriggerImmediateInstallation () {
480- + if (!profile_ || browseros_extension_ids_ .empty ()) {
486+ + if (!profile_ || server_extension_ids_ .empty ()) {
481487+ return ;
482488+ }
483- +
489+ +
484490+ LOG (INFO) << " browseros: Triggering immediate installation on first start" ;
485- +
491+ +
486492+ // First, add all extensions to pending if they're not already installed
487493+ ExtensionRegistry* registry = ExtensionRegistry::Get (profile_);
488494+ PendingExtensionManager* pending_manager = PendingExtensionManager::Get (profile_);
489- +
495+ +
490496+ if (registry && pending_manager && !last_config_.empty ()) {
491- + for (const std::string& extension_id : browseros_extension_ids_ ) {
497+ + for (const std::string& extension_id : server_extension_ids_ ) {
492498+ // Skip if already installed
493499+ if (registry->GetInstalledExtension (extension_id)) {
494500+ LOG (INFO) << " browseros: Extension " << extension_id << " already installed" ;
495501+ continue ;
496502+ }
497- +
503+ +
498504+ // Add to pending extensions
499505+ const base::Value::Dict* extension_config = last_config_.FindDict (extension_id);
500506+ if (extension_config) {
501- + const std::string* update_url =
507+ + const std::string* update_url =
502508+ extension_config->FindString (ExternalProviderImpl::kExternalUpdateUrl );
503509+ if (update_url) {
504510+ GURL update_gurl (*update_url);
@@ -510,56 +516,66 @@ index 0000000000000..83c9677581de2
510516+ mojom::ManifestLocation::kExternalComponent ,
511517+ Extension::WAS_INSTALLED_BY_DEFAULT,
512518+ false ); // Don't mark acknowledged
513- + LOG (INFO) << " browseros: Added " << extension_id
519+ + LOG (INFO) << " browseros: Added " << extension_id
514520+ << " to pending for immediate installation" ;
515521+ }
516522+ }
517523+ }
518524+ }
519525+ }
520- +
526+ +
521527+ // Now trigger immediate high-priority installation
522528+ ExtensionUpdater* updater = ExtensionUpdater::Get (profile_);
523529+ if (!updater) {
524530+ LOG (WARNING) << " browseros: No extension updater available for immediate installation" ;
525531+ return ;
526532+ }
527- +
528- + LOG (INFO) << " browseros: Executing CheckNow with immediate install for "
529- + << browseros_extension_ids_ .size () << " BrowserOS extensions" ;
530- +
533+ +
534+ + LOG (INFO) << " browseros: Executing CheckNow with immediate install for "
535+ + << server_extension_ids_ .size () << " BrowserOS extensions" ;
536+ +
531537+ // Create CheckParams for immediate foreground installation
532538+ ExtensionUpdater::CheckParams params;
533- + params.ids = std::list<ExtensionId>(browseros_extension_ids_ .begin (),
534- + browseros_extension_ids_ .end ());
539+ + params.ids = std::list<ExtensionId>(server_extension_ids_ .begin (),
540+ + server_extension_ids_ .end ());
535541+ params.install_immediately = true ;
536542+ params.fetch_priority = DownloadFetchPriority::kForeground ;
537543+
538544+ // Trigger the installation
539545+ updater->CheckNow (std::move (params));
540546+}
541547+
548+ +void BrowserOSExternalLoader::StartupExtensionMaintenance () {
549+ + LOG (INFO) << " browseros: Running startup extension maintenance" ;
550+ +
551+ + // First, clean up any deprecated extensions (in master list but not in server config)
552+ + UninstallDeprecatedExtensions ();
553+ +
554+ + // Then trigger installation of current extensions
555+ + TriggerImmediateInstallation ();
556+ +}
557+ +
542558+void BrowserOSExternalLoader::ForceUpdateCheck () {
543- + if (!profile_ || browseros_extension_ids_ .empty ()) {
559+ + if (!profile_ || server_extension_ids_ .empty ()) {
544560+ return ;
545561+ }
546- +
562+ +
547563+ ExtensionUpdater* updater = ExtensionUpdater::Get (profile_);
548564+ if (!updater) {
549565+ LOG (WARNING) << " browseros: No extension updater available" ;
550566+ return ;
551567+ }
552- +
553- + LOG (INFO) << " browseros: Forcing immediate update check for "
554- + << browseros_extension_ids_ .size () << " BrowserOS extensions" ;
555- +
568+ +
569+ + LOG (INFO) << " browseros: Forcing immediate update check for "
570+ + << server_extension_ids_ .size () << " BrowserOS extensions" ;
571+ +
556572+ // Create CheckParams for immediate foreground update
557573+ ExtensionUpdater::CheckParams params;
558- + params.ids = std::list<ExtensionId>(browseros_extension_ids_ .begin (),
559- + browseros_extension_ids_ .end ());
574+ + params.ids = std::list<ExtensionId>(server_extension_ids_ .begin (),
575+ + server_extension_ids_ .end ());
560576+ params.install_immediately = true ;
561577+ params.fetch_priority = DownloadFetchPriority::kForeground ;
562- +
578+ +
563579+ // Trigger the update check
564580+ updater->CheckNow (std::move (params));
565581+}
@@ -594,7 +610,7 @@ index 0000000000000..83c9677581de2
594610+ return ;
595611+ }
596612+
597- + for (const std::string& extension_id : browseros_extension_ids_ ) {
613+ + for (const std::string& extension_id : server_extension_ids_ ) {
598614+ // If extension is enabled, it's healthy - skip logging
599615+ if (registry->enabled_extensions ().Contains (extension_id)) {
600616+ continue ;
@@ -667,7 +683,8 @@ index 0000000000000..83c9677581de2
667683+}
668684+
669685+void BrowserOSExternalLoader::UninstallDeprecatedExtensions () {
670- + if (!profile_ || last_config_.empty ()) {
686+ + // Safety: only uninstall if we have successfully fetched server config
687+ + if (!profile_ || !has_successful_config_ || server_extension_ids_.empty ()) {
671688+ return ;
672689+ }
673690+
@@ -681,16 +698,10 @@ index 0000000000000..83c9677581de2
681698+ return ;
682699+ }
683700+
684- + // Build set of extension IDs currently in server config
685- + std::set<std::string> server_extension_ids;
686- + for (const auto [extension_id, _] : last_config_) {
687- + server_extension_ids.insert (extension_id);
688- + }
689- +
690- + // Check all BrowserOS-managed extensions
701+ + // Check all BrowserOS-managed extensions from master list
691702+ for (const std::string& extension_id : browseros::GetBrowserOSExtensionIds ()) {
692703+ // Skip if extension is in server config (still wanted)
693- + if (server_extension_ids .contains (extension_id)) {
704+ + if (server_extension_ids_ .contains (extension_id)) {
694705+ continue ;
695706+ }
696707+
0 commit comments