Warning: lots of context.
TL;DR: Don't store external uninstalls as regular installs with a special state; instead, have a separate prefs entry set.
Extensions in Chrome have entries in the Preferences file (under exetnsions.settings), keyed by their id. For instance,
{
...
extensions: {
settings: {
<id1>: {
<stateful information>
<manifest>
},
<id2>: {
<stateful information>
<manifest>
},
...
}
}
}
We store stateful information in the prefs so that we can persist extension information across chrome restarts, etc.
Normally, when an extension is uninstalled, the preference is entirely removed - we remove all relevant state, so that when the extension is re-installed, it's a clean slate. However, if the extension was installed by an external provider, we do *not* remove the extension pref entry, instead simply updating the "state" pref to indicate that the extension was uninstalled.
Unfortunately, keeping these prefs around leads to a lot of complications, since the ExtensionPrefs and consumers need to know to ignore prefs for extensions that were uninstalled. ExtensionPefs, for instance, ignores these prefs when returning GetInstalledExtensionsInfo(). However, we don't always correctly do this. One concrete case where we don't is as follows:
Assume an edge case where an extension has a partially published version (2.0) and a stable version (1.0) (this is possible through staggered pushing in the webstore).
1. External provider adds Extension Alpha version 2.0
2. User uninstalls Alpha 2.0.
3. User tries to install Alpha from the webstore, version 1.0.
Here, there will be an error about attempting to downgrade the extension, even though there is in fact no extension installed. This is because the CrxInstaller checks for the current version through ExtensionPrefs, which doesn't correctly omit uninstalled extension preferences. While this is certainly an edge case, we have seen this happen internally.
It's hard to say if there are other cases where we incorrectly check these, as they would manifest in exceedingly strange ways.
Instead of maintaining the extension preferences for these uninstalled extensions as the same type of entry as installed extensions, we should store them separately in a different pref. e.g.,
{
...
extensions: {
external_uninstalls: {
<id1>: { ... },
<id2>: { ... },
...
}
}
}
This also has the advantage that we won't maintain the entirety of the extension preference for these extensions.
Comment 1 by rdevlin....@chromium.org
, Dec 14 2017