bug 1158561 - [e10s] Browser hang in PluginModuleParent::NPP_ClearSiteData() r=jimm,mak

This commit is contained in:
Brad Lassey 2015-06-30 18:08:19 -04:00
parent 37b4bca536
commit 218ffaae77
16 changed files with 452 additions and 176 deletions

View File

@ -94,6 +94,34 @@ Sanitizer.prototype = {
return deferred.promise;
}
let cookiesIndex = itemsToClear.indexOf("cookies");
if (cookiesIndex != -1) {
itemsToClear.splice(cookiesIndex, 1);
let item = this.items.cookies;
item.range = this.range;
let ok = item.clear(() => {
try {
if (!itemsToClear.length) {
// we're done
deferred.resolve();
return;
}
let clearedPromise = this.sanitize(itemsToClear);
clearedPromise.then(deferred.resolve, deferred.reject);
} catch(e) {
let error = "Sanitizer threw after clearing cookies: " + e;
Cu.reportError(error);
deferred.reject(error);
}
});
// When cancelled, reject immediately
if (!ok) {
deferred.reject("Sanitizer canceled clearing cookies");
}
return deferred.promise;
}
TelemetryStopwatch.start("FX_SANITIZE_TOTAL");
// Cache the range of times to clear
@ -177,7 +205,7 @@ Sanitizer.prototype = {
},
cookies: {
clear: function ()
clear: function (aCallback)
{
TelemetryStopwatch.start("FX_SANITIZE_COOKIES");
TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2");
@ -209,6 +237,16 @@ Sanitizer.prototype = {
// Clear plugin data.
TelemetryStopwatch.start("FX_SANITIZE_PLUGINS");
this.clearPluginCookies().then(
function() {
TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS");
TelemetryStopwatch.finish("FX_SANITIZE_COOKIES");
aCallback();
});
return true;
},
clearPluginCookies: function() {
const phInterface = Ci.nsIPluginHost;
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
@ -217,29 +255,35 @@ Sanitizer.prototype = {
// that this.range[1] is actually now, so we compute age range based
// on the lower bound. If this.range results in a negative age, do
// nothing.
let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000)
: -1;
let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000) : -1;
if (!this.range || age >= 0) {
let tags = ph.getPluginTags();
for (let i = 0; i < tags.length; i++) {
try {
ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, age);
} catch (e) {
// If the plugin doesn't support clearing by age, clear everything.
if (e.result == Components.results.
NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
try {
ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, -1);
} catch (e) {
// Ignore errors from the plugin
}
function iterate(tag) {
let promise = new Promise(resolve => {
try {
let onClear = function(rv) {
// If the plugin doesn't support clearing by age, clear everything.
if (rv == Components.results. NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, function() {
resolve();
});
} else {
resolve();
}
};
ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, onClear);
} catch (ex) {
resolve();
}
}
});
return promise;
}
let promises = [];
for (let tag of tags) {
promises.push(iterate(tag));
}
return Promise.all(promises);
}
TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS");
TelemetryStopwatch.finish("FX_SANITIZE_COOKIES");
},
get canClear()

View File

@ -84,7 +84,7 @@ add_task(function* () {
// Clear 20 seconds ago
let now_uSec = Date.now() * 1000;
sanitizer.range = [now_uSec - 20*1000000, now_uSec];
sanitizer.sanitize();
yield sanitizer.sanitize();
ok(stored(["bar.com","qux.com"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
@ -92,7 +92,7 @@ add_task(function* () {
// Clear everything
sanitizer.range = null;
sanitizer.sanitize();
yield sanitizer.sanitize();
ok(!stored(null), "All data cleared");
@ -117,7 +117,7 @@ add_task(function* () {
// clearing all data regardless of age.
let now_uSec = Date.now() * 1000;
sanitizer.range = [now_uSec - 20*1000000, now_uSec];
sanitizer.sanitize();
yield sanitizer.sanitize();
ok(!stored(null), "All data cleared");

View File

@ -204,7 +204,7 @@ PluginPRLibrary::NPP_New(NPMIMEType pluginType, NPP instance,
nsresult
PluginPRLibrary::NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge)
uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback)
{
if (!mNPP_ClearSiteData) {
return NS_ERROR_NOT_AVAILABLE;
@ -213,39 +213,44 @@ PluginPRLibrary::NPP_ClearSiteData(const char* site, uint64_t flags,
MAIN_THREAD_JNI_REF_GUARD;
NPError result = mNPP_ClearSiteData(site, flags, maxAge);
nsresult rv;
switch (result) {
case NPERR_NO_ERROR:
return NS_OK;
rv = NS_OK;
break;
case NPERR_TIME_RANGE_NOT_SUPPORTED:
return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
break;
case NPERR_MALFORMED_SITE:
return NS_ERROR_INVALID_ARG;
rv = NS_ERROR_INVALID_ARG;
break;
default:
return NS_ERROR_FAILURE;
rv = NS_ERROR_FAILURE;
}
callback->Callback(rv);
return NS_OK;
}
nsresult
PluginPRLibrary::NPP_GetSitesWithData(InfallibleTArray<nsCString>& result)
PluginPRLibrary::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
{
if (!mNPP_GetSitesWithData) {
return NS_ERROR_NOT_AVAILABLE;
}
result.Clear();
MAIN_THREAD_JNI_REF_GUARD;
char** sites = mNPP_GetSitesWithData();
if (!sites) {
return NS_OK;
}
InfallibleTArray<nsCString> result;
char** iterator = sites;
while (*iterator) {
result.AppendElement(*iterator);
free(*iterator);
++iterator;
}
callback->SitesWithData(result);
free(sites);
return NS_OK;

View File

@ -105,8 +105,8 @@ public:
NPError* aError) override;
virtual nsresult NPP_ClearSiteData(const char* aSite, uint64_t aFlags,
uint64_t aMaxAge) override;
virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) override;
uint64_t aMaxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) override;
virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
virtual nsresult AsyncSetWindow(NPP aInstance, NPWindow* aWindow) override;
virtual nsresult GetImageContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;

View File

@ -27,7 +27,16 @@ interface nsIPluginPlayPreviewInfo : nsISupports
boolean checkWhitelist(in AUTF8String pageURI, in AUTF8String objectURI);
};
[scriptable, uuid(d7d5b2e0-105b-4c9d-8558-b6b31f28b7df)]
[scriptable, function, uuid(9c311778-7c2c-4ad8-b439-b8a2786a20dd)]
interface nsIClearSiteDataCallback : nsISupports
{
/**
* callback with the result from a call to clearSiteData
*/
void callback(in nsresult rv);
};
[scriptable, uuid(a884e736-5396-4400-bc82-9a23d871d12c)]
interface nsIPluginHost : nsISupports
{
/**
@ -77,7 +86,8 @@ interface nsIPluginHost : nsISupports
* general or for that particular site and/or flag combination.
*/
void clearSiteData(in nsIPluginTag plugin, in AUTF8String domain,
in uint64_t flags, in int64_t maxAge);
in uint64_t flags, in int64_t maxAge,
in nsIClearSiteDataCallback callback);
/*
* Determine if a plugin has stored data for a given site.

View File

@ -1534,10 +1534,77 @@ nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType,
return NS_ERROR_NOT_AVAILABLE;
}
#define ClearDataFromSitesClosure_CID {0x9fb21761, 0x2403, 0x41ad, {0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e}}
// Class to hold all the data we need need for IterateMatchesAndClear and ClearDataFromSites
class ClearDataFromSitesClosure : public nsIClearSiteDataCallback, public nsIGetSitesWithDataCallback {
public:
ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags,
int64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback,
nsPluginHost* host) :
domain(domain), callback(callback), tag(plugin), flags(flags), maxAge(maxAge), host(host) {}
NS_DECL_ISUPPORTS
// Callback from NPP_ClearSiteData, continue to iterate the matches and clear
NS_IMETHOD Callback(nsresult rv) {
if (NS_FAILED(rv)) {
callback->Callback(rv);
return NS_OK;
}
if (!matches.Length()) {
callback->Callback(NS_OK);
return NS_OK;
}
const nsCString match(matches[0]);
matches.RemoveElement(match);
PluginLibrary* library = static_cast<nsPluginTag*>(tag)->mPlugin->GetLibrary();
rv = library->NPP_ClearSiteData(match.get(), flags, maxAge, this);
if (NS_FAILED(rv)) {
callback->Callback(rv);
return NS_OK;
}
return NS_OK;
}
// Callback from NPP_GetSitesWithData, kick the iteration off to clear the data
NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites)
{
// Enumerate the sites and build a list of matches.
nsresult rv = host->EnumerateSiteData(domain, sites, matches, false);
Callback(rv);
return NS_OK;
}
nsCString domain;
nsCOMPtr<nsIClearSiteDataCallback> callback;
InfallibleTArray<nsCString> matches;
nsIPluginTag* tag;
uint64_t flags;
int64_t maxAge;
nsPluginHost* host;
NS_DECLARE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure_CID)
private:
virtual ~ClearDataFromSitesClosure() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure, ClearDataFromSitesClosure_CID)
NS_IMPL_ADDREF(ClearDataFromSitesClosure)
NS_IMPL_RELEASE(ClearDataFromSitesClosure)
NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
uint64_t flags, int64_t maxAge)
uint64_t flags, int64_t maxAge, nsIClearSiteDataCallback* callbackFunc)
{
nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
// maxAge must be either a nonnegative integer or -1.
NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
@ -1569,29 +1636,71 @@ nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
// If 'domain' is the null string, clear everything.
if (domain.IsVoid()) {
return library->NPP_ClearSiteData(nullptr, flags, maxAge);
return library->NPP_ClearSiteData(nullptr, flags, maxAge, callback);
}
// Get the list of sites from the plugin.
InfallibleTArray<nsCString> sites;
rv = library->NPP_GetSitesWithData(sites);
nsCOMPtr<nsIGetSitesWithDataCallback> closure(new ClearDataFromSitesClosure(plugin, domain, flags,
maxAge, callback, this));
rv = library->NPP_GetSitesWithData(closure);
NS_ENSURE_SUCCESS(rv, rv);
// Enumerate the sites and build a list of matches.
InfallibleTArray<nsCString> matches;
rv = EnumerateSiteData(domain, sites, matches, false);
NS_ENSURE_SUCCESS(rv, rv);
// Clear the matches.
for (uint32_t i = 0; i < matches.Length(); ++i) {
const nsCString& match = matches[i];
rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
#define GetSitesClosure_CID {0x4c9268ac, 0x2fd1, 0x4f2a, {0x9a, 0x10, 0x7a, 0x09, 0xf1, 0xb7, 0x60, 0x3a}}
// Closure to contain the data needed to handle the callback from NPP_GetSitesWithData
class GetSitesClosure : public nsIGetSitesWithDataCallback {
public:
NS_DECL_ISUPPORTS
GetSitesClosure(const nsACString& domain, nsPluginHost* host)
: domain(domain), host(host), keepWaiting(true)
{
}
NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) {
retVal = HandleGetSites(sites);
keepWaiting = false;
return NS_OK;
}
nsresult HandleGetSites(InfallibleTArray<nsCString>& sites) {
// If there's no data, we're done.
if (sites.IsEmpty()) {
result = false;
return NS_OK;
}
// If 'domain' is the null string, and there's data for at least one site,
// we're done.
if (domain.IsVoid()) {
result = true;
return NS_OK;
}
// Enumerate the sites and determine if there's a match.
InfallibleTArray<nsCString> matches;
nsresult rv = host->EnumerateSiteData(domain, sites, matches, true);
NS_ENSURE_SUCCESS(rv, rv);
result = !matches.IsEmpty();
return NS_OK;
}
nsCString domain;
nsRefPtr<nsPluginHost> host;
bool result;
bool keepWaiting;
nsresult retVal;
NS_DECLARE_STATIC_IID_ACCESSOR(GetSitesClosure_CID)
private:
virtual ~GetSitesClosure() {
}
};
NS_DEFINE_STATIC_IID_ACCESSOR(GetSitesClosure, GetSitesClosure_CID)
NS_IMPL_ISUPPORTS(GetSitesClosure, GetSitesClosure, nsIGetSitesWithDataCallback)
// This will spin the event loop while waiting on an async
// call to GetSitesWithData
NS_IMETHODIMP
nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
bool* result)
@ -1619,31 +1728,16 @@ nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
PluginLibrary* library = tag->mPlugin->GetLibrary();
// Get the list of sites from the plugin.
InfallibleTArray<nsCString> sites;
rv = library->NPP_GetSitesWithData(sites);
// Get the list of sites from the plugin
nsCOMPtr<GetSitesClosure> closure(new GetSitesClosure(domain, this));
rv = library->NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback>(do_QueryInterface(closure)));
NS_ENSURE_SUCCESS(rv, rv);
// If there's no data, we're done.
if (sites.IsEmpty()) {
*result = false;
return NS_OK;
// Spin the event loop while we wait for the async call to GetSitesWithData
while (closure->keepWaiting) {
NS_ProcessNextEvent(nullptr, true);
}
// If 'domain' is the null string, and there's data for at least one site,
// we're done.
if (domain.IsVoid()) {
*result = true;
return NS_OK;
}
// Enumerate the sites and determine if there's a match.
InfallibleTArray<nsCString> matches;
rv = EnumerateSiteData(domain, sites, matches, true);
NS_ENSURE_SUCCESS(rv, rv);
*result = !matches.IsEmpty();
return NS_OK;
*result = closure->result;
return closure->retVal;
}
nsPluginHost::SpecialType

View File

@ -243,6 +243,11 @@ public:
void CreateWidget(nsPluginInstanceOwner* aOwner);
nsresult EnumerateSiteData(const nsACString& domain,
const InfallibleTArray<nsCString>& sites,
InfallibleTArray<nsCString>& result,
bool firstMatchOnly);
private:
friend class nsPluginUnloadRunnable;
@ -366,10 +371,6 @@ private:
// Helpers for ClearSiteData and SiteHasData.
nsresult NormalizeHostname(nsCString& host);
nsresult EnumerateSiteData(const nsACString& domain,
const InfallibleTArray<nsCString>& sites,
InfallibleTArray<nsCString>& result,
bool firstMatchOnly);
nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only

View File

@ -76,11 +76,9 @@ child:
returns (bool aURLRedirectNotify, bool aClearSiteData,
bool aGetSitesWithData);
intr NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge)
returns (NPError rv);
async NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge, uint64_t aCallbackId);
intr NPP_GetSitesWithData()
returns (nsCString[] sites);
async NPP_GetSitesWithData(uint64_t aCallbackId);
// Windows specific message to set up an audio session in the plugin process
async SetAudioSessionData(nsID aID,
@ -148,6 +146,12 @@ parent:
async NotifyContentModuleDestroyed();
async Profile(nsCString aProfile);
// Answers to request about site data
async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
};
} // namespace plugins

View File

@ -28,6 +28,15 @@ class ImageContainer;
}
}
class nsIClearSiteDataCallback;
#define nsIGetSitesWithDataCallback_CID {0xd0028b83, 0xfdf9, 0x4c53, {0xb7, 0xbb, 0x47, 0x46, 0x0f, 0x6b, 0x83, 0x6c}}
class nsIGetSitesWithDataCallback : public nsISupports {
public:
NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& result) = 0;
NS_DECLARE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback_CID)
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback, nsIGetSitesWithDataCallback_CID)
namespace mozilla {
@ -62,8 +71,8 @@ public:
NPError* error) = 0;
virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge) = 0;
virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) = 0;
uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) = 0;
virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) = 0;
virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0;
virtual nsresult GetImageContainer(NPP instance, mozilla::layers::ImageContainer** aContainer) = 0;

View File

@ -734,31 +734,34 @@ PluginModuleChild::AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
}
bool
PluginModuleChild::AnswerNPP_ClearSiteData(const nsCString& aSite,
PluginModuleChild::RecvNPP_ClearSiteData(const nsCString& aSite,
const uint64_t& aFlags,
const uint64_t& aMaxAge,
NPError* aResult)
const uint64_t& aCallbackId)
{
*aResult =
NPError result =
mFunctions.clearsitedata(NullableStringGet(aSite), aFlags, aMaxAge);
SendReturnClearSiteData(result, aCallbackId);
return true;
}
bool
PluginModuleChild::AnswerNPP_GetSitesWithData(InfallibleTArray<nsCString>* aResult)
PluginModuleChild::RecvNPP_GetSitesWithData(const uint64_t& aCallbackId)
{
char** result = mFunctions.getsiteswithdata();
if (!result)
InfallibleTArray<nsCString> array;
if (!result) {
SendReturnSitesWithData(array, aCallbackId);
return true;
}
char** iterator = result;
while (*iterator) {
aResult->AppendElement(*iterator);
array.AppendElement(*iterator);
free(*iterator);
++iterator;
}
SendReturnSitesWithData(array, aCallbackId);
free(result);
return true;
}

View File

@ -112,13 +112,13 @@ protected:
bool *aGetSitesWithData) override;
virtual bool
AnswerNPP_ClearSiteData(const nsCString& aSite,
RecvNPP_ClearSiteData(const nsCString& aSite,
const uint64_t& aFlags,
const uint64_t& aMaxAge,
NPError* aResult) override;
const uint64_t& aCallbackId) override;
virtual bool
AnswerNPP_GetSitesWithData(InfallibleTArray<nsCString>* aResult) override;
RecvNPP_GetSitesWithData(const uint64_t& aCallbackId) override;
virtual bool
RecvSetAudioSessionData(const nsID& aId,

View File

@ -2654,35 +2654,34 @@ PluginModuleChromeParent::UpdatePluginTimeout()
}
nsresult
PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge)
PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
nsCOMPtr<nsIClearSiteDataCallback> callback)
{
if (!mClearSiteDataSupported)
return NS_ERROR_NOT_AVAILABLE;
NPError result;
if (!CallNPP_ClearSiteData(NullableString(site), flags, maxAge, &result))
return NS_ERROR_FAILURE;
static uint64_t callbackId = 0;
callbackId++;
mClearSiteDataCallbacks[callbackId] = callback;
switch (result) {
case NPERR_NO_ERROR:
return NS_OK;
case NPERR_TIME_RANGE_NOT_SUPPORTED:
return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
case NPERR_MALFORMED_SITE:
return NS_ERROR_INVALID_ARG;
default:
if (!SendNPP_ClearSiteData(NullableString(site), flags, maxAge, callbackId)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
PluginModuleParent::NPP_GetSitesWithData(InfallibleTArray<nsCString>& result)
PluginModuleParent::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
{
if (!mGetSitesWithDataSupported)
return NS_ERROR_NOT_AVAILABLE;
if (!CallNPP_GetSitesWithData(&result))
static uint64_t callbackId = 0;
callbackId++;
mSitesWithDataCallbacks[callbackId] = callback;
if (!SendNPP_GetSitesWithData(callbackId))
return NS_ERROR_FAILURE;
return NS_OK;
@ -2936,6 +2935,49 @@ PluginModuleChromeParent::RecvNotifyContentModuleDestroyed()
return true;
}
bool
PluginModuleParent::RecvReturnClearSiteData(const NPError& aRv,
const uint64_t& aCallbackId)
{
if (mClearSiteDataCallbacks.find(aCallbackId) == mClearSiteDataCallbacks.end()) {
return true;
}
if (!!mClearSiteDataCallbacks[aCallbackId]) {
nsresult rv;
switch (aRv) {
case NPERR_NO_ERROR:
rv = NS_OK;
break;
case NPERR_TIME_RANGE_NOT_SUPPORTED:
rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
break;
case NPERR_MALFORMED_SITE:
rv = NS_ERROR_INVALID_ARG;
break;
default:
rv = NS_ERROR_FAILURE;
}
mClearSiteDataCallbacks[aCallbackId]->Callback(rv);
}
mClearSiteDataCallbacks.erase(aCallbackId);
return true;
}
bool
PluginModuleParent::RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
const uint64_t& aCallbackId)
{
if (mSitesWithDataCallbacks.find(aCallbackId) == mSitesWithDataCallbacks.end()) {
return true;
}
if (!!mSitesWithDataCallbacks[aCallbackId]) {
mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
}
mSitesWithDataCallbacks.erase(aCallbackId);
return true;
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
// We only add the crash reporter to subprocess which have the filename

View File

@ -200,6 +200,12 @@ protected:
virtual bool RecvProfile(const nsCString& aProfile) override { return true; }
virtual bool RecvReturnClearSiteData(const NPError& aRv,
const uint64_t& aCallbackId);
virtual bool RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
const uint64_t& aCallbackId);
void SetPluginFuncs(NPPluginFuncs* aFuncs);
nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode,
@ -264,9 +270,15 @@ protected:
uint16_t mode, int16_t argc, char* argn[],
char* argv[], NPSavedData* saved,
NPError* error) override;
virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge) override;
virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result) override;
virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
nsCOMPtr<nsIClearSiteDataCallback> callback) override;
virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
private:
std::map<uint64_t, nsCOMPtr<nsIClearSiteDataCallback>> mClearSiteDataCallbacks;
std::map<uint64_t, nsCOMPtr<nsIGetSitesWithDataCallback>> mSitesWithDataCallbacks;
public:
#if defined(XP_MACOSX)
virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) override;

View File

@ -28,9 +28,7 @@
SimpleTest.executeSoon(function() {
// Make sure clearing by timerange is supported.
p.setSitesWithDataCapabilities(true);
ok(PluginUtils.withTestPlugin(runTest), "Test plugin found");
SimpleTest.finish();
});
function stored(needles) {
@ -59,7 +57,6 @@
function runTest(pluginTag) {
this.pluginTag = pluginTag;
p.setSitesWithData(
"foo.com:0:5," +
"foo.com:0:7," +
@ -69,75 +66,99 @@
"qux.com:1:5," +
"quz.com:1:8"
);
ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
"Data stored for sites");
// Clear nothing.
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 4);
ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
"Data stored for sites");
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 4);
// Clear nothing.
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 4, {callback: function() { test1(); }});
}
function test1() {
ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
"Data stored for sites");
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 4, {callback: function() { test2(); }});
}
function test2() {
ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
"Data stored for sites");
// Clear cache data 5 seconds or older.
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 5);
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 5, {callback: function() { test3(); }});
}
function test3() {
ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
"Data stored for sites");
ok(!stored(["qux.com"]), "Data cleared for qux.com");
// Clear cache data for foo.com, but leave non-cache data.
pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_CACHE, 20);
pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_CACHE, 20, {callback: function() { test4(); }});
}
function test4() {
ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
"Data stored for sites");
// Clear all data 7 seconds or older.
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 7);
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 7, {callback: function() { test5(); }});
}
function test5() {
ok(stored(["bar.com","baz.com","quz.com"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["qux.com"]), "Data cleared for qux.com");
// Clear all cache data.
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20);
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function() { test6(); }});
}
function test6() {
ok(stored(["bar.com","baz.com"]), "Data stored for sites");
ok(!stored(["quz.com"]), "Data cleared for quz.com");
// Clear all data for bar.com.
pluginHost.clearSiteData(pluginTag, "bar.com", FLAG_CLEAR_ALL, 20);
pluginHost.clearSiteData(pluginTag, "bar.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) { test7(rv); }});
}
function test7(rv) {
ok(stored(["baz.com"]), "Data stored for baz.com");
ok(!stored(["bar.com"]), "Data cleared for bar.com");
// Disable clearing by age.
p.setSitesWithDataCapabilities(false);
// Attempt to clear data by age.
checkThrows(function() {
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 20);
}, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
checkThrows(function() {
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20);
}, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
checkThrows(function() {
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, 20);
}, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
checkThrows(function() {
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, 20);
}, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 20, {callback: function(rv) {
is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
test8(rv);
}});
}
function test8(rv) {
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
test9(rv);
}});
}
function test9(rv) {
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) {
is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
test10(rv);
}});
}
function test10(rv) {
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
test11();
}});
}
function test11() {
// Clear cache for baz.com and globally for all ages.
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, -1);
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, -1);
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test12()}});
}
function test12() {
pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test13()}});
}
function test13() {
// Check that all of the above were no-ops.
ok(stored(["baz.com"]), "Data stored for baz.com");
// Clear everything for baz.com.
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test14()}});
}
function test14() {
ok(!stored(["baz.com"]), "Data cleared for baz.com");
ok(!stored(null), "All data cleared");
@ -150,26 +171,33 @@
"[192.168.1.1]:0:0," +
"localhost:0:0"
);
ok(stored(["foo.com","nonexistent.foo.com","bar.com","192.168.1.1","localhost"]),
"Data stored for sites");
"Data stored for sites");
// Clear data for "foo.com" and its subdomains.
pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test15()}});
}
function test15() {
ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
// Clear data for "bar.com" using a subdomain.
pluginHost.clearSiteData(pluginTag, "foo.bar.com", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "foo.bar.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test16()}});
}
function test16() {
ok(!stored(["bar.com"]), "Data cleared for bar.com");
// Clear data for "192.168.1.1".
pluginHost.clearSiteData(pluginTag, "192.168.1.1", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "192.168.1.1", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test17()}});
}
function test17() {
ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
// Clear data for "localhost".
pluginHost.clearSiteData(pluginTag, "localhost", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "localhost", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test18()}});
}
function test18() {
ok(!stored(null), "All data cleared");
// Set data to test international domains.
@ -178,18 +206,21 @@
"b\u00FCcher.uk:0:0," +
"xn--bcher-kva.NZ:0:0"
);
// Check that both the ACE and UTF-8 representations register.
// Check that both the ACE and UTF-8 representations register.
ok(stored(["b\u00FCcher.es","xn--bcher-kva.es","b\u00FCcher.uk","xn--bcher-kva.uk"]),
"Data stored for sites");
"Data stored for sites");
// Clear data for the UTF-8 version.
pluginHost.clearSiteData(pluginTag, "b\u00FCcher.es", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "b\u00FCcher.es", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test19()}});
}
function test19() {
ok(!stored(["b\u00FCcher.es"]), "Data cleared for UTF-8 representation");
ok(!stored(["xn--bcher-kva.es"]), "Data cleared for ACE representation");
// Clear data for the ACE version.
pluginHost.clearSiteData(pluginTag, "xn--bcher-kva.uk", FLAG_CLEAR_ALL, -1);
pluginHost.clearSiteData(pluginTag, "xn--bcher-kva.uk", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test20()}});
}
function test20() {
ok(!stored(["b\u00FCcher.uk"]), "Data cleared for UTF-8 representation");
ok(!stored(["xn--bcher-kva.uk"]), "Data cleared for ACE representation");
@ -197,11 +228,14 @@
// UTF-8. We do happen to normalize the result anyway, so while that's not
// strictly required, we test it here.
ok(stored(["b\u00FCcher.nz","xn--bcher-kva.nz"]),
"Data stored for sites");
pluginHost.clearSiteData(pluginTag, "b\u00FCcher.nz", FLAG_CLEAR_ALL, -1);
"Data stored for sites");
pluginHost.clearSiteData(pluginTag, "b\u00FCcher.nz", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test21()}});
}
function test21() {
ok(!stored(["b\u00FCcher.nz"]), "Data cleared for UTF-8 representation");
ok(!stored(["xn--bcher-kva.nz"]), "Data cleared for ACE representation");
ok(!stored(null), "All data cleared");
SimpleTest.finish();
}
</script>
</body>

View File

@ -90,12 +90,20 @@ this.ForgetAboutSite = {
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
let tags = ph.getPluginTags();
let promises = [];
for (let i = 0; i < tags.length; i++) {
try {
ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1);
} catch (e) {
// Ignore errors from the plugin
}
let promise = new Promise(resolve => {
let tag = tags[i];
try {
ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1, function(rv) {
resolve();
});
} catch (e) {
// Ignore errors from the plugin, but resolve the promise
resolve();
}
});
promises.push(promise);
}
// Downloads
@ -168,5 +176,6 @@ this.ForgetAboutSite = {
let np = Cc["@mozilla.org/network/predictor;1"].
getService(Ci.nsINetworkPredictor);
np.reset();
return Promise.all(promises);
}
};

View File

@ -82,27 +82,36 @@ function do_test()
"Data stored for sites");
// Clear data for "foo.com" and its subdomains.
ForgetAboutSite.removeDataFromDomain("foo.com");
ForgetAboutSite.removeDataFromDomain("foo.com").then(test1);
});
function test1() {
dump("test1\n");
ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
ok(!stored(["foo.com"]), "Data cleared for foo.com");
ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
// Clear data for "bar.com" using a subdomain.
ForgetAboutSite.removeDataFromDomain("foo.bar.com");
ForgetAboutSite.removeDataFromDomain("foo.bar.com").then(test2);
}
function test2() {
ok(!stored(["bar.com"]), "Data cleared for bar.com");
// Clear data for "192.168.1.1".
ForgetAboutSite.removeDataFromDomain("192.168.1.1");
ForgetAboutSite.removeDataFromDomain("192.168.1.1").then(test3);
}
function test3() {
ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
// Clear data for "localhost".
ForgetAboutSite.removeDataFromDomain("localhost");
ForgetAboutSite.removeDataFromDomain("localhost").then(test4);
}
function test4() {
ok(!stored(null), "All data cleared");
gBrowser.removeCurrentTab();
executeSoon(finish);
});
}
}, true);
content.location = testURL;
}