diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2019-09-13 16:52:19 +0300 |
---|---|---|
committer | Lluis Sanchez <llsan@microsoft.com> | 2019-09-13 16:59:21 +0300 |
commit | d749da6e35a86455de33539103cfeb1a6191ea07 (patch) | |
tree | 112680ddc0b777b5eb5f7e3e470165740d8fd026 | |
parent | 5a5607d9969c25a472bcca422a8cc669d332058c (diff) |
Fix add-in status consistency issue
When initializing an add-in dabatase ensure that all add-ins have all
their dependencies enabled. The database takes care to keep the
enabled status consistent at run-time (so if an add-in is disabled,
all dependent add-ins will also be disabled). However, that status
may already be inconsistent when loading the database, and that
may cause add-in loading issues. This may happen for example if
add-in A is disabled, and then an add-in B that depends on A is
installed externally, outside of the control of the db. With the
changes of this commit, when the db is initialized it will detect
that B depends on a disabled add-in and it will be disabled too.
Fixes VSTS #952473
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinDatabase.cs | 52 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs | 27 | ||||
-rw-r--r-- | Mono.Addins/Mono.Addins/AddinRegistry.cs | 2 |
3 files changed, 66 insertions, 15 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs index 4dabf61..7d5a1d2 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs @@ -497,7 +497,7 @@ namespace Mono.Addins.Database addinEngine.ActivateAddin (id); } - public void DisableAddin (string domain, string id, bool exactVersionMatch = false) + public void DisableAddin (string domain, string id, bool exactVersionMatch = false, bool onlyForCurrentSession = false) { Addin ai = GetInstalledAddin (domain, id, true); if (ai == null) @@ -505,8 +505,8 @@ namespace Mono.Addins.Database if (!IsAddinEnabled (domain, id, exactVersionMatch)) return; - - Configuration.SetEnabled (id, false, ai.AddinInfo.EnabledByDefault, exactVersionMatch); + + Configuration.SetEnabled (id, false, ai.AddinInfo.EnabledByDefault, exactVersionMatch, onlyForCurrentSession); SaveConfiguration (); // Disable all add-ins which depend on it @@ -531,7 +531,7 @@ namespace Mono.Addins.Database Addin adepinfo = GetInstalledAddin (domain, adepid, false, true); if (adepinfo == null) { - DisableAddin (domain, ainfo.Id); + DisableAddin (domain, ainfo.Id, onlyForCurrentSession: onlyForCurrentSession); break; } } @@ -539,7 +539,7 @@ namespace Mono.Addins.Database } catch { // If something goes wrong, enable the add-in again - Configuration.SetEnabled (id, true, ai.AddinInfo.EnabledByDefault, false); + Configuration.SetEnabled (id, true, ai.AddinInfo.EnabledByDefault, false, onlyForCurrentSession); SaveConfiguration (); throw; } @@ -547,7 +547,46 @@ namespace Mono.Addins.Database if (addinEngine != null && addinEngine.IsInitialized) addinEngine.UnloadAddin (id); } - + + public void UpdateEnabledStatus (string domain) + { + // Ensure that all enabled addins that have dependencies also have their dependencies enabled. + HashSet<Addin> updatedAddins = new HashSet<Addin> (); + var allAddins = GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins | AddinSearchFlagsInternal.LatestVersionsOnly).ToList (); + foreach (Addin addin in allAddins) + UpdateEnabledStatus (domain, addin, allAddins, updatedAddins); + } + + void UpdateEnabledStatus (string domain, Addin addin, List<Addin> allAddins, HashSet<Addin> updatedAddins) + { + if (!updatedAddins.Add (addin)) + return; + + if (!addin.Enabled) + return; + + // Make sure all dependencies of this add-in have an up to date enabled status + + foreach (Dependency dep in addin.AddinInfo.Dependencies) { + var adep = dep as AddinDependency; + if (adep == null) + continue; + + string adepid = Addin.GetFullId (addin.AddinInfo.Namespace, adep.AddinId, null); + var dependency = allAddins.FirstOrDefault (a => Addin.GetFullId (a.Namespace, a.LocalId, null) == adepid); + if (dependency != null) { + UpdateEnabledStatus (domain, dependency, allAddins, updatedAddins); + if (!dependency.Enabled) { + // One of the dependencies is disabled, so this add-in also needs to be disabled. + // However, we disabled only for the current configuration, we don't want to change + // what the user configured. + DisableAddin (domain, addin.Id, onlyForCurrentSession: true); + return; + } + } + } + } + public void RegisterForUninstall (string domain, string id, IEnumerable<string> files) { DisableAddin (domain, id, true); @@ -1955,6 +1994,7 @@ namespace Mono.Addins.Database } // Keep in sync with AddinSearchFlags + [Flags] enum AddinSearchFlagsInternal { IncludeAddins = 1, diff --git a/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs b/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs index 37779ec..14d8840 100644 --- a/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs +++ b/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs @@ -39,7 +39,7 @@ namespace Mono.Addins.Database internal class DatabaseConfiguration { Dictionary<string,AddinStatus> addinStatus = new Dictionary<string,AddinStatus> (); - + internal class AddinStatus { public AddinStatus (string addinId) @@ -48,11 +48,15 @@ namespace Mono.Addins.Database } public string AddinId; - public bool Enabled; + public bool ConfigEnabled; + public bool? SessionEnabled; public bool Uninstalled; public List<string> Files; + public bool Enabled { + get { return SessionEnabled ?? ConfigEnabled; } + } } - + public bool IsEnabled (string addinId, bool defaultValue) { var addinName = Addin.GetIdName (addinId); @@ -71,7 +75,7 @@ namespace Mono.Addins.Database return defaultValue; } - public void SetEnabled (string addinId, bool enabled, bool defaultValue, bool exactVersionMatch) + public void SetEnabled (string addinId, bool enabled, bool defaultValue, bool exactVersionMatch, bool onlyForTheSession = false) { if (IsRegisteredForUninstall (addinId)) return; @@ -83,11 +87,16 @@ namespace Mono.Addins.Database if (s == null) s = addinStatus [addinName] = new AddinStatus (addinName); - s.Enabled = enabled; + if (onlyForTheSession) + s.SessionEnabled = enabled; + else { + s.ConfigEnabled = enabled; + s.SessionEnabled = null; + } // If enabling a specific version of an add-in, make sure the add-in is enabled as a whole if (enabled && exactVersionMatch) - SetEnabled (addinId, true, defaultValue, false); + SetEnabled (addinId, true, defaultValue, false, onlyForTheSession); } public void RegisterForUninstall (string addinId, IEnumerable<string> files) @@ -96,7 +105,7 @@ namespace Mono.Addins.Database if (!addinStatus.TryGetValue (addinId, out s)) s = addinStatus [addinId] = new AddinStatus (addinId); - s.Enabled = false; + s.ConfigEnabled = false; s.Uninstalled = true; s.Files = new List<string> (files); } @@ -171,7 +180,7 @@ namespace Mono.Addins.Database foreach (XmlElement elem in statusElem.SelectNodes ("Addin")) { AddinStatus status = new AddinStatus (elem.GetAttribute ("id")); string senabled = elem.GetAttribute ("enabled"); - status.Enabled = senabled.Length == 0 || senabled == "True"; + status.ConfigEnabled = senabled.Length == 0 || senabled == "True"; status.Uninstalled = elem.GetAttribute ("uninstalled") == "True"; config.addinStatus [status.AddinId] = status; foreach (XmlElement fileElem in elem.SelectNodes ("File")) { @@ -196,7 +205,7 @@ namespace Mono.Addins.Database foreach (AddinStatus e in addinStatus.Values) { tw.WriteStartElement ("Addin"); tw.WriteAttributeString ("id", e.AddinId); - tw.WriteAttributeString ("enabled", e.Enabled.ToString ()); + tw.WriteAttributeString ("enabled", e.ConfigEnabled.ToString ()); if (e.Uninstalled) tw.WriteAttributeString ("uninstalled", "True"); if (e.Files != null && e.Files.Count > 0) { diff --git a/Mono.Addins/Mono.Addins/AddinRegistry.cs b/Mono.Addins/Mono.Addins/AddinRegistry.cs index f51b4db..1efeb1c 100644 --- a/Mono.Addins/Mono.Addins/AddinRegistry.cs +++ b/Mono.Addins/Mono.Addins/AddinRegistry.cs @@ -220,6 +220,8 @@ namespace Mono.Addins currentDomain = database.GetFolderDomain (null, this.startupDirectory); } else currentDomain = AddinDatabase.GlobalDomain; + + database.UpdateEnabledStatus (currentDomain); } /// <summary> |