ExtensionCommon.loadModule uses loadSubscript that may load an outdated experiment script from cache
Categories
(WebExtensions :: Experiments, defect, P3)
Tracking
(Not tracked)
People
(Reporter: robwu, Unassigned)
References
Details
In bug 1781428, we found that a module from an experiment was cached after updates, because the add-on used loadSubscript with a moz-extension:-URL. That was fixed by updating the extension and passing the ignoreCache flag to loadSubscript.
This issue is still relevant, albeit in a corner case. More details below, but the short version is that the extension framework uses loadSubscript without passing the ignoreCache flag in ExtensionCommon.loadModule, which triggers the same stale-cache issue: https://searchfox.org/mozilla-central/rev/c2a2bf5a49626d63c4c0a5be42b6a93838c1b595/toolkit/components/extensions/ExtensionCommon.jsm#1613
Longer version (originally posted at https://bugzilla.mozilla.org/show_bug.cgi?id=1781428#c2):
loadSubscript is not only called by the Firefox Translations extension itself, but also by our core code:
loadModuleat https://searchfox.org/mozilla-central/rev/c2a2bf5a49626d63c4c0a5be42b6a93838c1b595/toolkit/components/extensions/ExtensionCommon.jsm#1613- called by
SchemaAPIManager'sgetAPI:- NOTE:
getAPIcaches its result for re-use of later invocations. This cache is shared withasyncLoadAPI.asyncLoadAPIdoes not useloadSubscript, butChromeUtils.compileScript(viaasyncGetAPI).
- NOTE:
SchemaAPIManager'sgetAPIhas multiple callers:(dead code)emitManifestEntryEventManagerconstructor, when in use with persistent events.EventManager.primeListeners, when the background page is starting and the persisted listeners are being revived.- CanOfAPI's
loadAPI, when a schema-generated API invokes the implementation of an API (via ChildAPIManager'sgetImplementation). In practice, this only affects experiment scripts with"scopes": ["addon_child"](example of manifest that defines anaddon_childscript).- This case doesn't actually trigger the bug (unless e10s is off), because loadSubscript uses
StartupCache, which is only available to the parent process.
- This case doesn't actually trigger the bug (unless e10s is off), because loadSubscript uses
Because of the shared cache between getAPI and asyncGetAPI, if getAPI is called first, then the loadSubscript-version of the script will be loaded, which will suffer from the bug described above for the lifetime of the extension. Conversely, if asyncGetAPI is called first, then the bug is not triggered. Out of all options above, this bug can currently only be triggered when an experiment uses a persistent listener in an experiment (i.e. constructs EventManager with module and event).
An easy way to fix this may be to unconditionally disable the cache in loadModule: https://searchfox.org/mozilla-central/rev/c2a2bf5a49626d63c4c0a5be42b6a93838c1b595/toolkit/components/extensions/ExtensionCommon.jsm#1613. In practice that code path is rarely taken, and even if it is, the cached script rarely serves a purpose (most experimental scripts will be loaded through the asyncGetAPI path that uses ChromeUtils.compileScript).
Description
•