From 7a1184f9d69f2f3c889b271dab88150b4ac99bd3 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Fri, 1 Jun 2018 14:29:59 +0200 Subject: [PATCH] Bug 1422365 - Introduce nsIClearDataService - part 3 - plugin data, r=johannh --- browser/modules/Sanitizer.jsm | 94 ++----------------- .../components/cleardata/ClearDataService.js | 85 +++++++++++++++++ .../cleardata/nsIClearDataService.idl | 8 +- .../cleardata/tests/unit/test_basic.js | 3 +- toolkit/forgetaboutsite/ForgetAboutSite.jsm | 17 ---- 5 files changed, 99 insertions(+), 108 deletions(-) diff --git a/browser/modules/Sanitizer.jsm b/browser/modules/Sanitizer.jsm index 17fca3c31c51..0e4138d4c6e8 100644 --- a/browser/modules/Sanitizer.jsm +++ b/browser/modules/Sanitizer.jsm @@ -14,7 +14,6 @@ XPCOMUtils.defineLazyModuleGetters(this, { FormHistory: "resource://gre/modules/FormHistory.jsm", Downloads: "resource://gre/modules/Downloads.jsm", TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm", - setTimeout: "resource://gre/modules/Timer.jsm", ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm", OfflineAppCacheHelper: "resource://gre/modules/offlineAppCache.jsm", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm", @@ -325,33 +324,18 @@ var Sanitizer = { cookies: { async clear(range) { - let seenException; let refObj = {}; - // Clear cookies. + // Clear cookies and plugin data. TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2", refObj); - await clearData(range, Ci.nsIClearDataService.CLEAR_COOKIES); + await clearData(range, Ci.nsIClearDataService.CLEAR_COOKIES | + Ci.nsIClearDataService.CLEAR_PLUGIN_DATA); TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2", refObj); // Clear deviceIds. Done asynchronously (returns before complete). - try { - let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"] - .getService(Ci.nsIMediaManagerService); - mediaMgr.sanitizeDeviceIds(range && range[0]); - } catch (ex) { - seenException = ex; - } - - // Clear plugin data. - try { - await clearPluginData(range); - } catch (ex) { - seenException = ex; - } - - if (seenException) { - throw seenException; - } + let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"] + .getService(Ci.nsIMediaManagerService); + mediaMgr.sanitizeDeviceIds(range && range[0]); }, }, @@ -772,7 +756,7 @@ var Sanitizer = { pluginData: { async clear(range) { - await clearPluginData(range); + await clearData(range, Ci.nsIClearDataService.CLEAR_PLUGIN_DATA); }, }, }, @@ -862,70 +846,6 @@ async function sanitizeInternal(items, aItemsToClear, progress, options = {}) { } } -async function clearPluginData(range) { - // Clear plugin data. - // As evidenced in bug 1253204, clearing plugin data can sometimes be - // very, very long, for mysterious reasons. Unfortunately, this is not - // something actionable by Mozilla, so crashing here serves no purpose. - // - // For this reason, instead of waiting for sanitization to always - // complete, we introduce a soft timeout. Once this timeout has - // elapsed, we proceed with the shutdown of Firefox. - let seenException; - - let promiseClearPluginData = async function() { - const FLAG_CLEAR_ALL = Ci.nsIPluginHost.FLAG_CLEAR_ALL; - let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); - - // Determine age range in seconds. (-1 means clear all.) We don't know - // that range[1] is actually now, so we compute age range based - // on the lower bound. If range results in a negative age, do nothing. - let age = range ? (Date.now() / 1000 - range[0] / 1000000) : -1; - if (!range || age >= 0) { - let tags = ph.getPluginTags(); - for (let tag of tags) { - try { - let rv = await new Promise(resolve => - ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, resolve) - ); - // If the plugin doesn't support clearing by age, clear everything. - if (rv == Cr.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) { - await new Promise(resolve => - ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve) - ); - } - } catch (ex) { - // Ignore errors from plug-ins - } - } - } - }; - - try { - // We don't want to wait for this operation to complete... - promiseClearPluginData = promiseClearPluginData(range); - - // ... at least, not for more than 10 seconds. - await Promise.race([ - promiseClearPluginData, - new Promise(resolve => setTimeout(resolve, 10000 /* 10 seconds */)) - ]); - } catch (ex) { - seenException = ex; - } - - // Detach waiting for plugin data to be cleared. - promiseClearPluginData.catch(() => { - // If this exception is raised before the soft timeout, it - // will appear in `seenException`. Otherwise, it's too late - // to do anything about it. - }); - - if (seenException) { - throw seenException; - } -} - async function sanitizeOnShutdown(progress) { if (Sanitizer.shouldSanitizeOnShutdown) { // Need to sanitize upon shutdown diff --git a/toolkit/components/cleardata/ClearDataService.js b/toolkit/components/cleardata/ClearDataService.js index 7c3fb45d3eea..165e7f081f8c 100644 --- a/toolkit/components/cleardata/ClearDataService.js +++ b/toolkit/components/cleardata/ClearDataService.js @@ -89,6 +89,88 @@ const ImageCacheCleaner = { }, }; +const PluginDataCleaner = { + deleteByHost(aHost, aOriginAttributes) { + return this._deleteInternal((aPh, aTag) => { + return new Promise(aResolve => { + try { + aPh.clearSiteData(aTag, aHost, + Ci.nsIPluginHost.FLAG_CLEAR_ALL, + -1, aResolve); + } catch (e) { + // Ignore errors from the plugin, but resolve the promise + // We cannot check if something is a bailout or an error + aResolve(); + } + }); + }); + }, + + deleteByRange(aFrom, aTo) { + let age = Date.now() / 1000 - aFrom / 1000000; + + return this._deleteInternal((aPh, aTag) => { + return new Promise(aResolve => { + try { + aPh.clearSiteData(aTag, null, Ci.nsIPluginHost.FLAG_CLEAR_ALL, + age, aResolve); + } catch (e) { + aResolve(Cr.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED); + } + }).then(aRv => { + // If the plugin doesn't support clearing by age, clear everything. + if (aRv == Cr.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) { + return new Promise(aResolve => { + try { + aPh.clearSiteData(aTag, null, Ci.nsIPluginHost.FLAG_CLEAR_ALL, + -1, aResolve); + } catch (e) { + aResolve(); + } + }); + } + + return true; + }); + }); + }, + + deleteAll() { + return this._deleteInternal((aPh, aTag) => { + return new Promise(aResolve => { + try { + aPh.clearSiteData(aTag, null, Ci.nsIPluginHost.FLAG_CLEAR_ALL, -1, + aResolve); + } catch (e) { + aResolve(); + } + }); + }); + }, + + _deleteInternal(aCb) { + let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + + let promises = []; + let tags = ph.getPluginTags(); + for (let tag of tags) { + promises.push(aCb(ph, tag)); + } + + // As evidenced in bug 1253204, clearing plugin data can sometimes be + // very, very long, for mysterious reasons. Unfortunately, this is not + // something actionable by Mozilla, so crashing here serves no purpose. + // + // For this reason, instead of waiting for sanitization to always + // complete, we introduce a soft timeout. Once this timeout has + // elapsed, we proceed with the shutdown of Firefox. + return Promise.race([ + Promise.all(promises), + new Promise(aResolve => setTimeout(aResolve, 10000 /* 10 seconds */)) + ]); + }, +}; + // Here the map of Flags-Cleaner. const FLAGS_MAP = [ { flag: Ci.nsIClearDataService.CLEAR_COOKIES, @@ -99,6 +181,9 @@ const FLAGS_MAP = [ { flag: Ci.nsIClearDataService.CLEAR_IMAGE_CACHE, cleaner: ImageCacheCleaner, }, + + { flag: Ci.nsIClearDataService.CLEAR_PLUGIN_DATA, + cleaner: PluginDataCleaner, }, ]; this.ClearDataService = function() {}; diff --git a/toolkit/components/cleardata/nsIClearDataService.idl b/toolkit/components/cleardata/nsIClearDataService.idl index c655a71fb2d2..23583e121a36 100644 --- a/toolkit/components/cleardata/nsIClearDataService.idl +++ b/toolkit/components/cleardata/nsIClearDataService.idl @@ -98,9 +98,13 @@ interface nsIClearDataService : nsISupports */ const uint32_t CLEAR_IMAGE_CACHE = 1 << 2; + /** + * Data stored by external plugins. + */ + const uint32_t CLEAR_PLUGIN_DATA = 1 << 3; + /* TODO - const uint32_t CLEAR_EME = 1 << 3; - const uint32_t CLEAR_PLUGIN_DATA = 1 << 4; + const uint32_t CLEAR_EME = 1 << 4; const uint32_t CLEAR_DOWNLOADS = 1 << 5; const uint32_t CLEAR_PASSWORDS = 1 << 6; const uint32_t CLEAR_PERMISSIONS = 1 << 7; diff --git a/toolkit/components/cleardata/tests/unit/test_basic.js b/toolkit/components/cleardata/tests/unit/test_basic.js index 6f740f9190cb..1c0db3661fe0 100644 --- a/toolkit/components/cleardata/tests/unit/test_basic.js +++ b/toolkit/components/cleardata/tests/unit/test_basic.js @@ -13,8 +13,7 @@ add_task(async function test_basic() { Assert.ok(!!service); await new Promise(aResolve => { - service.deleteData(Ci.nsIClearDataService.CLEAR_IMAGE_CACHE | - Ci.nsIClearDataService.CLEAR_COOKIES, value => { + service.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => { Assert.equal(value, 0); aResolve(); }); diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index c09337f204a8..d34aff7a689a 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -64,23 +64,6 @@ var ForgetAboutSite = { })); - // Plugin data - const phInterface = Ci.nsIPluginHost; - const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL; - let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface); - let tags = ph.getPluginTags(); - for (let i = 0; i < tags.length; i++) { - promises.push(new Promise(resolve => { - try { - ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1, resolve); - } catch (e) { - // Ignore errors from the plugin, but resolve the promise - // We cannot check if something is a bailout or an error - resolve(); - } - })); - } - // Downloads promises.push((async function() { let list = await Downloads.getList(Downloads.ALL);