Bug 596776 - 'IndexedDB: Prevent quota prompt from hanging on shutdown'. r=sicking.

This commit is contained in:
Ben Turner 2010-10-19 10:58:39 -07:00
Родитель 84d9639d46
Коммит e8315ce8b9
5 изменённых файлов: 138 добавлений и 10 удалений

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

@ -5774,6 +5774,7 @@ var IndexedDBPromptHelper = {
_quotaPrompt: "indexedDB-quota-prompt",
_quotaResponse: "indexedDB-quota-response",
_quotaCancel: "indexedDB-quota-cancel",
_notificationIcon: "indexedDB-notification-icon",
@ -5781,18 +5782,21 @@ var IndexedDBPromptHelper = {
function IndexedDBPromptHelper_init() {
Services.obs.addObserver(this, this._permissionsPrompt, false);
Services.obs.addObserver(this, this._quotaPrompt, false);
Services.obs.addObserver(this, this._quotaCancel, false);
},
uninit:
function IndexedDBPromptHelper_uninit() {
Services.obs.removeObserver(this, this._permissionsPrompt, false);
Services.obs.removeObserver(this, this._quotaPrompt, false);
Services.obs.removeObserver(this, this._quotaCancel, false);
},
observe:
function IndexedDBPromptHelper_observe(subject, topic, data) {
if (topic != this._permissionsPrompt &&
topic != this._quotaPrompt) {
topic != this._quotaPrompt &&
topic != this._quotaCancel) {
throw new Error("Unexpected topic!");
}
@ -5824,6 +5828,9 @@ var IndexedDBPromptHelper = {
[ host, data ]);
responseTopic = this._quotaResponse;
}
else if (topic == this._quotaCancel) {
responseTopic = this._quotaResponse;
}
const hiddenTimeoutDuration = 30000; // 30 seconds
const firstTimeoutDuration = 120000; // 2 minutes
@ -5861,12 +5868,16 @@ var IndexedDBPromptHelper = {
}
];
// This will be set to the result of PopupNotifications.show() below.
// This will be set to the result of PopupNotifications.show() below, or to
// the result of PopupNotifications.getNotification() if this is a
// quotaCancel notification.
var notification;
function timeoutNotification() {
// Remove the notification.
notification.remove();
if (notification) {
notification.remove();
}
// Clear all of our timeout stuff.
cleanup();
@ -5900,6 +5911,13 @@ var IndexedDBPromptHelper = {
}
};
if (topic == this._quotaCancel) {
notification = PopupNotifications.getNotification(this._quotaPrompt,
browser);
timeoutNotification();
return;
}
notification = PopupNotifications.show(browser, topic, message,
this._notificationIcon, mainAction,
secondaryActions, options);

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

@ -53,6 +53,8 @@
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#include "IndexedDatabaseManager.h"
#define PERMISSION_INDEXEDDB "indexedDB"
#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
#define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
@ -194,5 +196,11 @@ CheckPermissionsHelper::Observe(nsISupports* aSubject,
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_DispatchToCurrentThread(this);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
rv = mgr->WaitForClearAndDispatch(mASCIIOrigin, this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

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

@ -58,6 +58,7 @@
#define TOPIC_QUOTA_PROMPT "indexedDB-quota-prompt"
#define TOPIC_QUOTA_RESPONSE "indexedDB-quota-response"
#define TOPIC_QUOTA_CANCEL "indexedDB-quota-cancel"
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
@ -132,6 +133,39 @@ CheckQuotaHelper::PromptAndReturnQuotaIsDisabled()
return mPromptResult == nsIPermissionManager::ALLOW_ACTION;
}
void
CheckQuotaHelper::Cancel()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mMutex.AssertCurrentThreadOwns();
if (mWaiting && !mHasPrompted) {
MutexAutoUnlock unlock(mMutex);
// First close any prompts that are open for this window.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_WARN_IF_FALSE(obs, "Failed to get observer service!");
if (obs && NS_FAILED(obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_QUOTA_CANCEL, nsnull))) {
NS_WARNING("Failed to notify observers!");
}
// If that didn't trigger an Observe callback (maybe the window had already
// died?) then go ahead and do it manually.
if (!mHasPrompted) {
nsAutoString response;
response.AppendInt(nsIPermissionManager::UNKNOWN_ACTION);
if (NS_SUCCEEDED(Observe(nsnull, TOPIC_QUOTA_RESPONSE, response.get()))) {
NS_ASSERTION(mHasPrompted, "Should have set this in Observe!");
}
else {
NS_WARNING("Failed to notify!");
}
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuotaHelper, nsIRunnable,
nsIInterfaceRequestor,
nsIObserver)
@ -170,6 +204,13 @@ CheckQuotaHelper::Run()
quotaString.AppendInt(quota);
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_STATE(obs);
// We have to watch to make sure that the window doesn't go away without
// responding to us. Otherwise our database threads will hang.
rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_QUOTA_PROMPT, quotaString.get());
NS_ENSURE_SUCCESS(rv, rv);
@ -214,13 +255,54 @@ CheckQuotaHelper::Observe(nsISupports* aSubject,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, TOPIC_QUOTA_RESPONSE), "Bad topic!");
mHasPrompted = true;
nsresult rv;
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_DispatchToCurrentThread(this);
if (!strcmp(aTopic, TOPIC_QUOTA_RESPONSE)) {
if (!mHasPrompted) {
mHasPrompted = true;
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_DispatchToCurrentThread(this);
NS_ENSURE_SUCCESS(rv, rv);
// We no longer care about the window here.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_STATE(obs);
rv = obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
NS_ASSERTION(!mHasPrompted, "Should have removed observer before now!");
NS_ASSERTION(mWindow, "This should never be null!");
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aSubject));
NS_ENSURE_STATE(window);
if (mWindow->GetSerial() == window->GetSerial()) {
// This is our window, dying, without responding to our prompt! Fake one.
mHasPrompted = true;
mPromptResult = nsIPermissionManager::UNKNOWN_ACTION;
rv = NS_DispatchToCurrentThread(this);
NS_ENSURE_SUCCESS(rv, rv);
// We no longer care about the window here.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_STATE(obs);
rv = obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_NOTREACHED("Unexpected topic!");
return NS_ERROR_UNEXPECTED;
}

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

@ -70,6 +70,8 @@ public:
bool PromptAndReturnQuotaIsDisabled();
void Cancel();
PRUint32 WindowSerial()
{
return mWindowSerial;

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

@ -396,6 +396,24 @@ void
IDBDatabase::Invalidate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(gPromptHelpersMutex, "This should never be null!");
// Cancel any quota prompts that are currently being displayed.
{
MutexAutoLock lock(*gPromptHelpersMutex);
if (gPromptHelpers) {
PRUint32 count = gPromptHelpers->Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<CheckQuotaHelper>& helper = gPromptHelpers->ElementAt(index);
if (helper->WindowSerial() == Owner()->GetSerial()) {
helper->Cancel();
break;
}
}
}
}
PR_AtomicSet(&mInvalidated, 1);
CloseConnection();