Bug 1324760 - Expose a function that clears plugin data in sanitize.js, r=mak

MozReview-Commit-ID: A8AxhyzyEwk

--HG--
extra : rebase_source : 6c3c2c09fa011f36f38549d442bd6c85c880b281
This commit is contained in:
Bob Silverberg 2016-12-20 09:24:06 -05:00
parent 9e010f1049
commit 526630bfba
2 changed files with 115 additions and 103 deletions

View File

@ -278,79 +278,16 @@ Sanitizer.prototype = {
}
// 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 promiseClearPluginCookies;
try {
// We don't want to wait for this operation to complete...
promiseClearPluginCookies = this.promiseClearPluginCookies(range);
// ... at least, not for more than 10 seconds.
yield Promise.race([
promiseClearPluginCookies,
new Promise(resolve => setTimeout(resolve, 10000 /* 10 seconds */))
]);
yield Sanitizer.clearPluginData(range);
} catch (ex) {
seenException = ex;
}
// Detach waiting for plugin cookies to be cleared.
promiseClearPluginCookies.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;
}
}),
promiseClearPluginCookies: Task.async(function* (range) {
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) {
let refObj = {};
let probe = "";
if (/\bFlash\b/.test(tag.name)) {
probe = tag.loaded ? "FX_SANITIZE_LOADED_FLASH"
: "FX_SANITIZE_UNLOADED_FLASH";
TelemetryStopwatch.start(probe, refObj);
}
try {
let rv = yield 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 == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
yield new Promise(resolve =>
ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve)
);
}
if (probe) {
TelemetryStopwatch.finish(probe, refObj);
}
} catch (ex) {
// Ignore errors from plug-ins
if (probe) {
TelemetryStopwatch.cancel(probe, refObj);
}
}
}
}
})
},
offlineApps: {
@ -705,6 +642,12 @@ Sanitizer.prototype = {
yield promiseReady;
})
},
pluginData: {
clear: Task.async(function* (range) {
yield Sanitizer.clearPluginData(range);
}),
},
}
};
@ -774,6 +717,83 @@ Sanitizer.getClearRange = function(ts) {
return [startDate, endDate];
};
Sanitizer.clearPluginData = Task.async(function* (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 = Task.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) {
let refObj = {};
let probe = "";
if (/\bFlash\b/.test(tag.name)) {
probe = tag.loaded ? "FX_SANITIZE_LOADED_FLASH"
: "FX_SANITIZE_UNLOADED_FLASH";
TelemetryStopwatch.start(probe, refObj);
}
try {
let rv = yield 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 == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
yield new Promise(resolve =>
ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve)
);
}
if (probe) {
TelemetryStopwatch.finish(probe, refObj);
}
} catch (ex) {
// Ignore errors from plug-ins
if (probe) {
TelemetryStopwatch.cancel(probe, refObj);
}
}
}
}
});
try {
// We don't want to wait for this operation to complete...
promiseClearPluginData = promiseClearPluginData(range);
// ... at least, not for more than 10 seconds.
yield 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;
}
});
Sanitizer._prefs = null;
Sanitizer.__defineGetter__("prefs", function()
{

View File

@ -46,14 +46,14 @@ add_task(function* () {
window.focus();
gTestBrowser = null;
});
});
add_task(function* () {
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
});
function* setPrefs(cookies, pluginData) {
sanitizer = new Sanitizer();
sanitizer.ignoreTimespan = false;
sanitizer.prefDomain = "privacy.cpd.";
@ -61,67 +61,59 @@ add_task(function* () {
itemPrefs.setBoolPref("history", false);
itemPrefs.setBoolPref("downloads", false);
itemPrefs.setBoolPref("cache", false);
itemPrefs.setBoolPref("cookies", true); // plugin data
itemPrefs.setBoolPref("cookies", cookies);
itemPrefs.setBoolPref("formdata", false);
itemPrefs.setBoolPref("offlineApps", false);
itemPrefs.setBoolPref("passwords", false);
itemPrefs.setBoolPref("sessions", false);
itemPrefs.setBoolPref("siteSettings", false);
});
itemPrefs.setBoolPref("pluginData", pluginData);
}
add_task(function* () {
function* testClearingData(url) {
// Load page to set data for the plugin.
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
yield promiseTabLoadEvent(gBrowser.selectedTab, testURL1);
yield promiseTabLoadEvent(gBrowser.selectedTab, url);
yield promiseUpdatePluginBindings(gTestBrowser);
ok(stored(["foo.com", "bar.com", "baz.com", "qux.com"]),
"Data stored for sites");
// Clear 20 seconds ago
let now_uSec = Date.now() * 1000;
sanitizer.range = [now_uSec - 20 * 1000000, now_uSec];
yield sanitizer.sanitize();
ok(stored(["bar.com", "qux.com"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["baz.com"]), "Data cleared for baz.com");
// Clear everything
sanitizer.range = null;
yield sanitizer.sanitize();
ok(!stored(null), "All data cleared");
gBrowser.removeCurrentTab();
gTestBrowser = null;
});
add_task(function* () {
// Load page to set data for the plugin.
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
yield promiseTabLoadEvent(gBrowser.selectedTab, testURL2);
yield promiseUpdatePluginBindings(gTestBrowser);
ok(stored(["foo.com", "bar.com", "baz.com", "qux.com"]),
"Data stored for sites");
// Attempt to clear 20 seconds ago. The plugin will throw
// Clear 20 seconds ago.
// In the case of testURL2 the plugin will throw
// NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, which should result in us
// clearing all data regardless of age.
let now_uSec = Date.now() * 1000;
sanitizer.range = [now_uSec - 20 * 1000000, now_uSec];
yield sanitizer.sanitize();
if (url == testURL1) {
ok(stored(["bar.com", "qux.com"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["baz.com"]), "Data cleared for baz.com");
// Clear everything.
sanitizer.range = null;
yield sanitizer.sanitize();
}
ok(!stored(null), "All data cleared");
gBrowser.removeCurrentTab();
gTestBrowser = null;
});
}
add_task(function* () {
// Test when santizing cookies.
yield setPrefs(true, false);
yield testClearingData(testURL1);
yield testClearingData(testURL2);
// Test when santizing pluginData.
yield setPrefs(false, true);
yield testClearingData(testURL1);
yield testClearingData(testURL2);
});