Bug 1435100 - Ensure preloaded css and script caches are cleared when a WebExtension is shutting down. r=mixedpuppy

MozReview-Commit-ID: IHK7hBYVLMj

--HG--
extra : rebase_source : 9226ca3e5fcd13a7694ece3b44ede3f788c51070
This commit is contained in:
Luca Greco 2018-02-21 12:53:56 +01:00
Родитель 66f67aac20
Коммит 181642ad79
2 изменённых файлов: 36 добавлений и 31 удалений

Просмотреть файл

@ -669,6 +669,7 @@ class BrowserExtensionContent extends EventEmitter {
if (isContentProcess) {
MessageChannel.abortResponses({extensionId: this.id});
}
this.emit("shutdown");
}
getContext(window) {

Просмотреть файл

@ -97,12 +97,19 @@ const scriptCaches = new WeakSet();
const sheetCacheDocuments = new DefaultWeakMap(() => new WeakSet());
class CacheMap extends DefaultMap {
constructor(timeout, getter) {
constructor(timeout, getter, extension) {
super(getter);
this.expiryTimeout = timeout;
scriptCaches.add(this);
// This ensures that all the cached scripts and stylesheets are deleted
// from the cache and the xpi is no longer actively used.
// See Bug 1435100 for rationale.
extension.once("shutdown", () => {
this.clear(-1);
});
}
get(url) {
@ -130,7 +137,10 @@ class CacheMap extends DefaultMap {
clear(timeout = SCRIPT_CLEAR_TIMEOUT_MS) {
let now = Date.now();
for (let [url, promise] of this.entries()) {
if (now - promise.lastUsed >= timeout) {
// Delete the entry if expired or if clear has been called with timeout -1
// (which is used to force the cache to clear all the entries, e.g. when the
// extension is shutting down).
if (timeout === -1 || (now - promise.lastUsed >= timeout)) {
this.delete(url);
}
}
@ -138,8 +148,8 @@ class CacheMap extends DefaultMap {
}
class ScriptCache extends CacheMap {
constructor(options) {
super(SCRIPT_EXPIRY_TIMEOUT_MS);
constructor(options, extension) {
super(SCRIPT_EXPIRY_TIMEOUT_MS, null, extension);
this.options = options;
}
@ -158,6 +168,10 @@ class ScriptCache extends CacheMap {
* (for the stylesheet defined by plain CSS content as a string).
*/
class BaseCSSCache extends CacheMap {
constructor(expiryTimeout, defaultConstructor, extension) {
super(expiryTimeout, defaultConstructor, extension);
}
addDocument(key, document) {
sheetCacheDocuments.get(this.get(key)).add(document);
}
@ -188,13 +202,13 @@ class BaseCSSCache extends CacheMap {
* Cache of the preloaded stylesheet defined by url.
*/
class CSSCache extends BaseCSSCache {
constructor(sheetType) {
constructor(sheetType, extension) {
super(CSS_EXPIRY_TIMEOUT_MS, url => {
let uri = Services.io.newURI(url);
return styleSheetService.preloadSheetAsync(uri, sheetType).then(sheet => {
return {url, sheet};
});
});
}, extension);
}
}
@ -212,7 +226,7 @@ class CSSCodeCache extends BaseCSSCache {
}
return super.get(hash);
});
}, extension);
// Store the preferred sheetType (used to preload the expected stylesheet type in
// the addCSSCode method).
@ -233,20 +247,20 @@ class CSSCodeCache extends BaseCSSCache {
}
}
defineLazyGetter(BrowserExtensionContent.prototype, "staticScripts", () => {
return new ScriptCache({hasReturnValue: false});
defineLazyGetter(BrowserExtensionContent.prototype, "staticScripts", function() {
return new ScriptCache({hasReturnValue: false}, this);
});
defineLazyGetter(BrowserExtensionContent.prototype, "dynamicScripts", () => {
return new ScriptCache({hasReturnValue: true});
defineLazyGetter(BrowserExtensionContent.prototype, "dynamicScripts", function() {
return new ScriptCache({hasReturnValue: true}, this);
});
defineLazyGetter(BrowserExtensionContent.prototype, "userCSS", () => {
return new CSSCache(Ci.nsIStyleSheetService.USER_SHEET);
defineLazyGetter(BrowserExtensionContent.prototype, "userCSS", function() {
return new CSSCache(Ci.nsIStyleSheetService.USER_SHEET, this);
});
defineLazyGetter(BrowserExtensionContent.prototype, "authorCSS", () => {
return new CSSCache(Ci.nsIStyleSheetService.AUTHOR_SHEET);
defineLazyGetter(BrowserExtensionContent.prototype, "authorCSS", function() {
return new CSSCache(Ci.nsIStyleSheetService.AUTHOR_SHEET, this);
});
// These two caches are similar to the above but specialized to cache the cssCode
@ -319,7 +333,7 @@ class Script {
this.compileScripts();
}
cleanup(window, forceCacheClear = false) {
cleanup(window) {
if (this.requiresCleanup) {
if (window) {
let winUtils = getWinUtils(window);
@ -343,8 +357,8 @@ class Script {
// Clear any sheets that were kept alive past their timeout as
// a result of living in this document.
this.cssCodeCache.clear(forceCacheClear ? 0 : CSSCODE_EXPIRY_TIMEOUT_MS);
this.cssCache.clear(forceCacheClear ? 0 : CSS_EXPIRY_TIMEOUT_MS);
this.cssCodeCache.clear(CSSCODE_EXPIRY_TIMEOUT_MS);
this.cssCache.clear(CSS_EXPIRY_TIMEOUT_MS);
}
}
@ -608,19 +622,13 @@ class ContentScriptContextChild extends BaseContext {
}
}
cleanupScripts(forceCacheClear = false) {
// Cleanup the scripts (even if the contentWindow have been destroyed) and their
// related CSS and Script caches.
for (let script of this.scripts) {
script.cleanup(this.contentWindow, forceCacheClear);
}
}
close() {
super.unload();
// Cleanup the scripts even if the contentWindow have been destroyed.
this.cleanupScripts();
for (let script of this.scripts) {
script.cleanup(this.contentWindow);
}
if (this.contentWindow) {
// Overwrite the content script APIs with an empty object if the APIs objects are still
@ -716,10 +724,6 @@ DocumentManager = {
for (let extensions of this.contexts.values()) {
let context = extensions.get(extension);
if (context) {
// Passing true to context.cleanupScripts causes the caches for this context
// to be cleared.
context.cleanupScripts(true);
context.close();
extensions.delete(extension);
}