This commit is contained in:
Kyle Huey 2011-12-03 16:27:21 -05:00
Родитель f9e447082c 5c29212946
Коммит ff9a5d2684
23 изменённых файлов: 627 добавлений и 222 удалений

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

@ -188,3 +188,7 @@ https://www.bank2.com:443 privileged,cert=escapeattack2
#
https://redirproxy.example.com:443 privileged,redir=test1.example.com
# Host used for IndexedDB Quota testing
http://bug704464-1.example.com:80 privileged
http://bug704464-2.example.com:80 privileged
http://bug704464-3.example.com:80 privileged

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

@ -279,7 +279,7 @@ AsyncConnectionHelper::Run()
if (NS_SUCCEEDED(rv)) {
bool hasSavepoint = false;
if (mDatabase) {
IndexedDatabaseManager::SetCurrentDatabase(mDatabase);
IndexedDatabaseManager::SetCurrentWindow(mDatabase->Owner());
// Make the first savepoint.
if (mTransaction) {
@ -292,7 +292,7 @@ AsyncConnectionHelper::Run()
mResultCode = DoDatabaseWork(connection);
if (mDatabase) {
IndexedDatabaseManager::SetCurrentDatabase(nsnull);
IndexedDatabaseManager::SetCurrentWindow(nsnull);
// Release or roll back the savepoint depending on the error code.
if (hasSavepoint) {

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

@ -98,11 +98,9 @@ GetQuotaPermissions(const nsACString& aASCIIOrigin,
} // anonymous namespace
CheckQuotaHelper::CheckQuotaHelper(IDBDatabase* aDatabase,
CheckQuotaHelper::CheckQuotaHelper(nsPIDOMWindow* aWindow,
mozilla::Mutex& aMutex)
: mWindow(aDatabase->Owner()),
mWindowSerial(mWindow->GetSerial()),
mOrigin(aDatabase->Origin()),
: mWindow(aWindow),
mMutex(aMutex),
mCondVar(mMutex, "CheckQuotaHelper::mCondVar"),
mPromptResult(0),
@ -175,51 +173,59 @@ CheckQuotaHelper::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mHasPrompted) {
mPromptResult = GetQuotaPermissions(mOrigin, mWindow);
nsresult rv = NS_OK;
if (mASCIIOrigin.IsEmpty()) {
rv = IndexedDatabaseManager::GetASCIIOriginFromWindow(mWindow,
mASCIIOrigin);
}
nsresult rv;
if (mHasPrompted) {
// Add permissions to the database, but only if we are in the parent
// process (if we are in the child process, we have already
// set the permission when the prompt was shown in the parent, as
// we cannot set the permission from the child).
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
XRE_GetProcessType() == GeckoProcessType_Default) {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), mOrigin);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_STATE(permissionManager);
rv = permissionManager->Add(uri, PERMISSION_INDEXEDDB_UNLIMITED,
mPromptResult,
nsIPermissionManager::EXPIRE_NEVER, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_SUCCEEDED(rv)) {
if (!mHasPrompted) {
mPromptResult = GetQuotaPermissions(mASCIIOrigin, mWindow);
}
}
else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
PRUint32 quota = IndexedDatabaseManager::GetIndexedDBQuotaMB();
nsString quotaString;
quotaString.AppendInt(quota);
if (mHasPrompted) {
// Add permissions to the database, but only if we are in the parent
// process (if we are in the child process, we have already
// set the permission when the prompt was shown in the parent, as
// we cannot set the permission from the child).
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
XRE_GetProcessType() == GeckoProcessType_Default) {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), mASCIIOrigin);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_STATE(permissionManager);
rv = permissionManager->Add(uri, PERMISSION_INDEXEDDB_UNLIMITED,
mPromptResult,
nsIPermissionManager::EXPIRE_NEVER, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
PRUint32 quota = IndexedDatabaseManager::GetIndexedDBQuotaMB();
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_STATE(obs);
nsString quotaString;
quotaString.AppendInt(quota);
// 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, false);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_STATE(obs);
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_QUOTA_PROMPT, quotaString.get());
NS_ENSURE_SUCCESS(rv, rv);
// 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, false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_QUOTA_PROMPT, quotaString.get());
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
}
MutexAutoLock lock(mMutex);

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

@ -65,23 +65,17 @@ public:
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIOBSERVER
CheckQuotaHelper(IDBDatabase* aDatabase,
CheckQuotaHelper(nsPIDOMWindow* aWindow,
mozilla::Mutex& aMutex);
bool PromptAndReturnQuotaIsDisabled();
void Cancel();
PRUint32 WindowSerial()
{
return mWindowSerial;
}
private:
nsPIDOMWindow* mWindow;
PRUint32 mWindowSerial;
nsCString mOrigin;
nsCString mASCIIOrigin;
mozilla::Mutex& mMutex;
mozilla::CondVar mCondVar;
PRUint32 mPromptResult;

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

@ -64,12 +64,6 @@ USING_INDEXEDDB_NAMESPACE
namespace {
PRUint32 gDatabaseInstanceCount = 0;
mozilla::Mutex* gPromptHelpersMutex = nsnull;
// Protected by gPromptHelpersMutex.
nsTArray<nsRefPtr<CheckQuotaHelper> >* gPromptHelpers = nsnull;
class CreateObjectStoreHelper : public AsyncConnectionHelper
{
public:
@ -195,11 +189,6 @@ IDBDatabase::IDBDatabase()
mRunningVersionChange(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!gDatabaseInstanceCount++) {
NS_ASSERTION(!gPromptHelpersMutex, "Should be null!");
gPromptHelpersMutex = new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
}
}
IDBDatabase::~IDBDatabase()
@ -218,86 +207,20 @@ IDBDatabase::~IDBDatabase()
if (mListenerManager) {
mListenerManager->Disconnect();
}
if (!--gDatabaseInstanceCount) {
NS_ASSERTION(gPromptHelpersMutex, "Should not be null!");
delete gPromptHelpers;
gPromptHelpers = nsnull;
delete gPromptHelpersMutex;
gPromptHelpersMutex = nsnull;
}
}
bool
IDBDatabase::IsQuotaDisabled()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(gPromptHelpersMutex, "This should never be null!");
MutexAutoLock lock(*gPromptHelpersMutex);
if (!gPromptHelpers) {
gPromptHelpers = new nsAutoTArray<nsRefPtr<CheckQuotaHelper>, 10>();
}
CheckQuotaHelper* foundHelper = nsnull;
PRUint32 count = gPromptHelpers->Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<CheckQuotaHelper>& helper = gPromptHelpers->ElementAt(index);
if (helper->WindowSerial() == Owner()->GetSerial()) {
foundHelper = helper;
break;
}
}
if (!foundHelper) {
nsRefPtr<CheckQuotaHelper>* newHelper = gPromptHelpers->AppendElement();
if (!newHelper) {
NS_WARNING("Out of memory!");
return false;
}
*newHelper = new CheckQuotaHelper(this, *gPromptHelpersMutex);
foundHelper = *newHelper;
{
// Unlock before calling out to XPCOM.
MutexAutoUnlock unlock(*gPromptHelpersMutex);
nsresult rv = NS_DispatchToMainThread(foundHelper, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, false);
}
}
return foundHelper->PromptAndReturnQuotaIsDisabled();
}
void
IDBDatabase::Invalidate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(gPromptHelpersMutex, "This should never be null!");
// Make sure we're closed too.
Close();
// 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;
}
}
}
}
// When the IndexedDatabaseManager needs to invalidate databases, all it has
// is an origin, so we call back into the manager to cancel any prompts for
// our owner.
IndexedDatabaseManager::CancelPromptsForWindow(Owner());
mInvalidated = true;
}

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

@ -122,8 +122,6 @@ public:
return doc.forget();
}
bool IsQuotaDisabled();
nsCString& Origin()
{
return mASCIIOrigin;

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

@ -403,24 +403,10 @@ IDBFactory::OpenCommon(const nsAString& aName,
nsIScriptContext* context = sgo->GetContext();
NS_ENSURE_TRUE(context, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCString origin;
if (nsContentUtils::IsSystemPrincipal(principal)) {
origin.AssignLiteral("chrome");
}
else {
rv = nsContentUtils::GetASCIIOrigin(principal, origin);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (origin.EqualsLiteral("null")) {
NS_WARNING("IndexedDB databases not allowed for this principal!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
nsresult rv =
IndexedDatabaseManager::GetASCIIOriginFromWindow(window, origin);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<IDBOpenDBRequest> request =
IDBOpenDBRequest::Create(context, window);

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

@ -887,7 +887,7 @@ CommitHelper::Run()
}
if (mConnection) {
IndexedDatabaseManager::SetCurrentDatabase(database);
IndexedDatabaseManager::SetCurrentWindow(database->Owner());
if (!mAborted) {
NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
@ -923,7 +923,7 @@ CommitHelper::Run()
mConnection->Close();
mConnection = nsnull;
IndexedDatabaseManager::SetCurrentDatabase(nsnull);
IndexedDatabaseManager::SetCurrentWindow(nsnull);
}
return NS_OK;

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

@ -41,6 +41,8 @@
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsISHEntry.h"
#include "nsISimpleEnumerator.h"
#include "nsITimer.h"
@ -54,6 +56,7 @@
#include "nsXPCOMPrivate.h"
#include "AsyncConnectionHelper.h"
#include "CheckQuotaHelper.h"
#include "IDBDatabase.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
@ -75,7 +78,7 @@
#define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
// A bad TLS index number.
#define BAD_TLS_INDEX (PRUintn)-1
#define BAD_TLS_INDEX (PRUintn)-1
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
@ -88,8 +91,6 @@ PRInt32 gShutdown = 0;
// Does not hold a reference.
IndexedDatabaseManager* gInstance = nsnull;
PRUintn gCurrentDatabaseIndex = BAD_TLS_INDEX;
PRInt32 gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
class QuotaCallback : public mozIStorageQuotaCallback
@ -104,13 +105,7 @@ public:
nsISupports* aUserData,
PRInt64* _retval)
{
NS_ASSERTION(gCurrentDatabaseIndex != BAD_TLS_INDEX,
"This should be impossible!");
IDBDatabase* database =
static_cast<IDBDatabase*>(PR_GetThreadPrivate(gCurrentDatabaseIndex));
if (database && database->IsQuotaDisabled()) {
if (IndexedDatabaseManager::QuotaIsLifted()) {
*_retval = 0;
return NS_OK;
}
@ -146,6 +141,8 @@ EnumerateToTArray(const nsACString& aKey,
} // anonymous namespace
IndexedDatabaseManager::IndexedDatabaseManager()
: mCurrentWindowIndex(BAD_TLS_INDEX),
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex")
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
@ -172,30 +169,31 @@ IndexedDatabaseManager::GetOrCreate()
nsRefPtr<IndexedDatabaseManager> instance(gInstance);
if (!instance) {
// We need a thread-local to hold our current database.
if (gCurrentDatabaseIndex == BAD_TLS_INDEX) {
if (PR_NewThreadPrivateIndex(&gCurrentDatabaseIndex, nsnull) !=
PR_SUCCESS) {
NS_ERROR("PR_NewThreadPrivateIndex failed!");
gCurrentDatabaseIndex = BAD_TLS_INDEX;
return nsnull;
}
if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
PREF_INDEXEDDB_QUOTA,
DEFAULT_QUOTA_MB))) {
NS_WARNING("Unable to respond to quota pref changes!");
gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
}
if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
PREF_INDEXEDDB_QUOTA,
DEFAULT_QUOTA_MB))) {
NS_WARNING("Unable to respond to quota pref changes!");
gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
}
instance = new IndexedDatabaseManager();
if (!instance->mLiveDatabases.Init()) {
if (!instance->mLiveDatabases.Init() ||
!instance->mQuotaHelperHash.Init()) {
NS_WARNING("Out of memory!");
return nsnull;
}
// We need a thread-local to hold the current window.
NS_ASSERTION(instance->mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
if (PR_NewThreadPrivateIndex(&instance->mCurrentWindowIndex, nsnull) !=
PR_SUCCESS) {
NS_ERROR("PR_NewThreadPrivateIndex failed, IndexedDB disabled");
instance->mCurrentWindowIndex = BAD_TLS_INDEX;
return nsnull;
}
// Make a timer here to avoid potential failures later. We don't actually
// initialize the timer until shutdown.
instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
@ -548,30 +546,23 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
}
}
// static
bool
IndexedDatabaseManager::SetCurrentDatabase(IDBDatabase* aDatabase)
void
IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
{
NS_ASSERTION(gCurrentDatabaseIndex != BAD_TLS_INDEX,
"This should have been set already!");
if (aWindow) {
#ifdef DEBUG
if (aDatabase) {
NS_ASSERTION(!PR_GetThreadPrivate(gCurrentDatabaseIndex),
"Someone forgot to unset gCurrentDatabaseIndex!");
NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
"Somebody forgot to clear the current window!");
#endif
PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
}
else {
NS_ASSERTION(PR_GetThreadPrivate(gCurrentDatabaseIndex),
"Someone forgot to set gCurrentDatabaseIndex!");
}
#ifdef DEBUG
NS_ASSERTION(PR_GetThreadPrivate(mCurrentWindowIndex),
"Somebody forgot to clear the current window!");
#endif
if (PR_SetThreadPrivate(gCurrentDatabaseIndex, aDatabase) != PR_SUCCESS) {
NS_WARNING("Failed to set gCurrentDatabaseIndex!");
return false;
PR_SetThreadPrivate(mCurrentWindowIndex, nsnull);
}
return true;
}
// static
@ -662,6 +653,102 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
return rv;
}
bool
IndexedDatabaseManager::QuotaIsLiftedInternal()
{
nsPIDOMWindow* window = nsnull;
nsRefPtr<CheckQuotaHelper> helper = nsnull;
bool createdHelper = false;
window =
static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
// Once IDB is supported outside of Windows this should become an early
// return true.
NS_ASSERTION(window, "Why don't we have a Window here?");
// Hold the lock from here on.
MutexAutoLock autoLock(mQuotaHelperMutex);
mQuotaHelperHash.Get(window, getter_AddRefs(helper));
if (!helper) {
helper = new CheckQuotaHelper(window, mQuotaHelperMutex);
createdHelper = true;
bool result = mQuotaHelperHash.Put(window, helper);
NS_ENSURE_TRUE(result, result);
// Unlock while calling out to XPCOM
{
MutexAutoUnlock autoUnlock(mQuotaHelperMutex);
nsresult rv = NS_DispatchToMainThread(helper);
NS_ENSURE_SUCCESS(rv, false);
}
// Relocked. If any other threads hit the quota limit on the same Window,
// they are using the helper we created here and are now blocking in
// PromptAndReturnQuotaDisabled.
}
bool result = helper->PromptAndReturnQuotaIsDisabled();
// If this thread created the helper and added it to the hash, this thread
// must remove it.
if (createdHelper) {
mQuotaHelperHash.Remove(window);
}
return result;
}
void
IndexedDatabaseManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<CheckQuotaHelper> helper;
MutexAutoLock autoLock(mQuotaHelperMutex);
mQuotaHelperHash.Get(aWindow, getter_AddRefs(helper));
if (helper) {
helper->Cancel();
}
}
// static
nsresult
IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
nsCString& aASCIIOrigin)
{
NS_ASSERTION(NS_IsMainThread(),
"We're about to touch a window off the main thread!");
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
NS_ENSURE_TRUE(sop, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (nsContentUtils::IsSystemPrincipal(principal)) {
aASCIIOrigin.AssignLiteral("chrome");
}
else {
nsresult rv = nsContentUtils::GetASCIIOrigin(principal, aASCIIOrigin);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (aASCIIOrigin.EqualsLiteral("null")) {
NS_WARNING("IndexedDB databases not allowed for this principal!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
return NS_OK;
}
// static
nsresult
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)

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

@ -44,6 +44,8 @@
#include "mozilla/dom/indexedDB/IDBDatabase.h"
#include "mozilla/dom/indexedDB/IDBRequest.h"
#include "mozilla/Mutex.h"
#include "nsIIndexedDatabaseManager.h"
#include "nsIObserver.h"
#include "nsIRunnable.h"
@ -51,6 +53,7 @@
#include "nsIURI.h"
#include "nsClassHashtable.h"
#include "nsRefPtrHashtable.h"
#include "nsHashKeys.h"
#define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
@ -62,6 +65,8 @@ BEGIN_INDEXEDDB_NAMESPACE
class AsyncConnectionHelper;
class CheckQuotaHelper;
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
public nsIObserver
{
@ -129,14 +134,45 @@ public:
// Used to check if there are running transactions in a given window.
bool HasOpenTransactions(nsPIDOMWindow* aWindow);
static bool
SetCurrentDatabase(IDBDatabase* aDatabase);
// Set the Window that the current thread is doing operations for.
// The caller is responsible for ensuring that aWindow is held alive.
static inline void
SetCurrentWindow(nsPIDOMWindow* aWindow)
{
IndexedDatabaseManager* mgr = Get();
NS_ASSERTION(mgr, "Must have a manager here!");
return mgr->SetCurrentWindowInternal(aWindow);
}
static PRUint32
GetIndexedDBQuotaMB();
nsresult EnsureQuotaManagementForDirectory(nsIFile* aDirectory);
// Determine if the quota is lifted for the Window the current thread is
// using.
static inline bool
QuotaIsLifted()
{
IndexedDatabaseManager* mgr = Get();
NS_ASSERTION(mgr, "Must have a manager here!");
return mgr->QuotaIsLiftedInternal();
}
static inline void
CancelPromptsForWindow(nsPIDOMWindow* aWindow)
{
IndexedDatabaseManager* mgr = Get();
NS_ASSERTION(mgr, "Must have a manager here!");
mgr->CancelPromptsForWindowInternal(aWindow);
}
static nsresult
GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
private:
IndexedDatabaseManager();
~IndexedDatabaseManager();
@ -147,6 +183,10 @@ private:
WaitingOnDatabasesCallback aCallback,
void* aClosure);
void SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
bool QuotaIsLiftedInternal();
void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
// Called when a database is created.
bool RegisterDatabase(IDBDatabase* aDatabase);
@ -267,6 +307,15 @@ private:
// Maintains a list of live databases per origin.
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
// TLS storage index for the current thread's window
PRUintn mCurrentWindowIndex;
// Lock protecting mQuotaHelperHash
mozilla::Mutex mQuotaHelperMutex;
// A map of Windows to the corresponding quota helper.
nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
// Maintains a list of origins that we're currently enumerating to gather
// usage statistics.
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
@ -290,6 +339,21 @@ private:
nsTArray<nsCString> mTrackedQuotaPaths;
};
class AutoEnterWindow
{
public:
AutoEnterWindow(nsPIDOMWindow* aWindow)
{
NS_ASSERTION(aWindow, "This should never be null!");
IndexedDatabaseManager::SetCurrentWindow(aWindow);
}
~AutoEnterWindow()
{
IndexedDatabaseManager::SetCurrentWindow(nsnull);
}
};
END_INDEXEDDB_NAMESPACE
#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */

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

@ -1090,6 +1090,14 @@ OpenDatabaseHelper::DoDatabaseWork()
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
NS_ASSERTION(mOpenDBRequest, "This should never be null!");
// Once we support IDB outside of Windows this assertion will no longer hold.
nsPIDOMWindow* window = mOpenDBRequest->Owner();
NS_ASSERTION(window, "This should never be null");
AutoEnterWindow autoWindow(window);
nsCOMPtr<nsIFile> dbFile;
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

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

@ -129,6 +129,8 @@ BROWSER_TEST_FILES = \
browser_quotaPrompt.html \
browser_quotaPromptAllow.js \
browser_quotaPromptDeny.js \
browser_quotaPromptDatabases.html \
browser_quotaPromptDatabases.js \
head.js \
$(NULL)

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

@ -4,7 +4,7 @@
*/
// Make sure this is a unique origin or the tests will randomly fail!
const testPageURL = "http://test1.example.org/browser/" +
const testPageURL = "http://bug704464-1.example.com/browser/" +
"dom/indexedDB/test/browser_quotaPrompt.html";
const notificationID = "indexedDB-quota-prompt";

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

@ -0,0 +1,55 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Test</title>
<script type="text/javascript;version=1.7">
const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
let db;
let i = 0;
function onAddMore() {
const name = window.location.pathname + i++;
let request = mozIndexedDB.open(name, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
request.onsuccess = function(event) {
setTimeout(testFinishedCallback, 0, "complete");
}
request.onerror = function(event) {
setTimeout(testFinishedCallback, 0, "abort");
}
}
function onDone() {
window.removeEventListener("indexedDB-addMore", onAddMore, true);
window.removeEventListener("indexedDB-done", onDone, true);
testResult = "finished";
testException = undefined;
finishTest();
}
function testSteps()
{
window.addEventListener("indexedDB-addMore", onAddMore, true);
window.addEventListener("indexedDB-done", onDone, true);
setTimeout(testFinishedCallback, 0, "ready");
yield;
}
</script>
<script type="text/javascript;version=1.7" src="browserHelpers.js"></script>
</head>
<body onload="runTest();" onunload="finishTestNow();"></body>
</html>

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

@ -0,0 +1,76 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Make sure this is a unique origin or the tests will randomly fail!
const testPageURL = "http://bug704464-3.example.com/browser/" +
"dom/indexedDB/test/browser_quotaPromptDatabases.html";
const notificationID = "indexedDB-quota-prompt";
function test()
{
waitForExplicitFinish();
requestLongerTimeout(10);
setPermission(testPageURL, "indexedDB");
removePermission(testPageURL, "indexedDB-unlimited");
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
executeSoon(test1);
}
let addMoreTest1Count = 0;
function test1()
{
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function () {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
let seenPopupCount;
setFinishedCallback(function(result) {
is(result, "ready", "Got 'ready' result");
setFinishedCallback(function(result) {
is(result, "complete", "Got 'complete' result");
if (addMoreTest1Count >= seenPopupCount + 5) {
setFinishedCallback(function(result) {
is(result, "finished", "Got 'finished' result");
is(getPermission(testPageURL, "indexedDB-unlimited"),
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
"Correct permission set");
gBrowser.removeCurrentTab();
unregisterAllPopupEventHandlers();
addMoreTest1Count = seenPopupCount;
executeSoon(finish);
});
executeSoon(function() { dispatchEvent("indexedDB-done"); });
}
else {
++addMoreTest1Count;
executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
}
});
++addMoreTest1Count;
executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
});
registerPopupEventHandler("popupshowing", function () {
ok(true, "prompt showing");
seenPopupCount = addMoreTest1Count - 1;
});
registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown");
triggerMainCommand(this);
});
registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden");
});
}, true);
info("loading test page: " + testPageURL);
content.location = testPageURL;
}

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

@ -4,7 +4,7 @@
*/
// Make sure this is a unique origin or the tests will randomly fail!
const testPageURL = "http://test2.example.org/browser/" +
const testPageURL = "http://bug704464-2.example.com/browser/" +
"dom/indexedDB/test/browser_quotaPrompt.html";
const notificationID = "indexedDB-quota-prompt";

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

@ -58,6 +58,8 @@
#include "dombindings.h"
#include "nsWrapperCacheInlines.h"
#include "jstypedarray.h"
using namespace mozilla;
//#define STRICT_CHECK_OF_UNICODE
@ -1689,13 +1691,169 @@ failure:
#undef POPULATE
}
// Check that the tag part of the type matches the type
// of the array. If the check succeeds, check that the size
// of the output does not exceed PR_UINT32_MAX bytes. Allocate
// the memory and copy the elements by memcpy.
static JSBool
CheckTargetAndPopulate(const nsXPTType& type,
PRUint8 requiredType,
size_t typeSize,
JSUint32 count,
JSObject* tArr,
void** output,
nsresult* pErr)
{
// Check that the element type expected by the interface matches
// the type of the elements in the typed array exactly, including
// signedness.
if (type.TagPart() != requiredType) {
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
return false;
}
// Calulate the maximum number of elements that can fit in
// PR_UINT32_MAX bytes.
size_t max = PR_UINT32_MAX / typeSize;
// This could overflow on 32-bit systems so check max first.
size_t byteSize = count * typeSize;
if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
if (pErr)
*pErr = NS_ERROR_OUT_OF_MEMORY;
return false;
}
memcpy(*output, JS_GetTypedArrayData(tArr), byteSize);
return true;
}
// Fast conversion of typed arrays to native using memcpy.
// No float or double canonicalization is done. Called by
// JSarray2Native whenever a TypedArray is met. ArrayBuffers
// are not accepted; create a properly typed array view on them
// first. The element type of array must match the XPCOM
// type in size, type and signedness exactly. As an exception,
// Uint8ClampedArray is allowed for arrays of uint8.
// static
JSBool
XPCConvert::JSTypedArray2Native(XPCCallContext& ccx,
void** d,
JSObject* jsArray,
JSUint32 count,
const nsXPTType& type,
nsresult* pErr)
{
NS_ABORT_IF_FALSE(jsArray, "bad param");
NS_ABORT_IF_FALSE(d, "bad param");
NS_ABORT_IF_FALSE(js_IsTypedArray(jsArray), "not a typed array");
// Check the actual length of the input array against the
// given size_is.
JSUint32 len = JS_GetTypedArrayLength(jsArray);
if (len < count) {
if (pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
return false;
}
void* output = nsnull;
switch (JS_GetTypedArrayType(jsArray)) {
case js::TypedArray::TYPE_INT8:
if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
sizeof(int8), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT8:
case js::TypedArray::TYPE_UINT8_CLAMPED:
if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
sizeof(uint8), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_INT16:
if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
sizeof(int16), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT16:
if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
sizeof(uint16), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_INT32:
if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
sizeof(int32), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT32:
if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
sizeof(uint32), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_FLOAT32:
if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
sizeof(float), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_FLOAT64:
if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
sizeof(double), count,
jsArray, &output, pErr)) {
return false;
}
break;
// Yet another array type was defined? It is not supported yet...
default:
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
return false;
}
*d = output;
if (pErr)
*pErr = NS_OK;
return true;
}
// static
JSBool
XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
JSUint32 count, const nsXPTType& type,
const nsID* iid, uintN* pErr)
const nsID* iid, nsresult* pErr)
{
NS_PRECONDITION(d, "bad param");
NS_ABORT_IF_FALSE(d, "bad param");
JSContext* cx = ccx.GetJSContext();
@ -1731,13 +1889,19 @@ XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
}
jsarray = JSVAL_TO_OBJECT(s);
// If this is a typed array, then do a fast conversion with memcpy.
if (js_IsTypedArray(jsarray)) {
return JSTypedArray2Native(ccx, d, jsarray, count, type, pErr);
}
if (!JS_IsArrayObject(cx, jsarray)) {
if (pErr)
*pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
return false;
}
jsuint len;
JSUint32 len;
if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
if (pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
@ -1781,7 +1945,7 @@ XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
case nsXPTType::T_U64 : POPULATE(na, uint64); break;
case nsXPTType::T_FLOAT : POPULATE(na, float); break;
case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
case nsXPTType::T_BOOL : POPULATE(na, bool); break;
case nsXPTType::T_BOOL : POPULATE(na, bool); break;
case nsXPTType::T_CHAR : POPULATE(na, char); break;
case nsXPTType::T_WCHAR : POPULATE(na, jschar); break;
case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
@ -1874,7 +2038,7 @@ XPCConvert::NativeStringWithSize2JS(JSContext* cx,
JSBool
XPCConvert::JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s,
JSUint32 count, const nsXPTType& type,
uintN* pErr)
nsresult* pErr)
{
NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param");
NS_PRECONDITION(d, "bad param");

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

@ -2434,8 +2434,7 @@ CallMethodHelper::GatherAndConvertResults()
!GetInterfaceTypeFromParam(i, datum_type, &param_iid))
return false;
uintN err;
nsresult err;
if (isArray) {
XPCLazyCallContext lccx(mCallContext);
if (!XPCConvert::NativeArray2JS(lccx, &v, (const void**)&dp->val,

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

@ -3327,7 +3327,14 @@ public:
static JSBool JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
JSUint32 count, const nsXPTType& type,
const nsID* iid, uintN* pErr);
const nsID* iid, nsresult* pErr);
static JSBool JSTypedArray2Native(XPCCallContext& ccx,
void** d,
JSObject* jsarray,
JSUint32 count,
const nsXPTType& type,
nsresult* pErr);
static JSBool NativeStringWithSize2JS(JSContext* cx,
jsval* d, const void* s,

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

@ -97,7 +97,7 @@ TestParams.prototype = {
testACString: f,
testJsval: f,
testShortArray: f_is,
testLongLongArray: f_is,
testDoubleArray: f_is,
testStringArray: f_is,
testWstringArray: f_is,
testInterfaceArray: f_is,

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

@ -246,14 +246,14 @@ NS_IMETHODIMP nsXPCTestParams::TestShortArray(PRUint32 aLength, PRInt16 *a,
BUFFER_METHOD_IMPL(PRInt16, 0, TAKE_OWNERSHIP_NOOP);
}
/* void testLongLongArray (in unsigned long aLength, [array, size_is (aLength)] in long long a,
* inout unsigned long bLength, [array, size_is (bLength)] inout long long b,
* out unsigned long rvLength, [array, size_is (rvLength), retval] out long long rv); */
NS_IMETHODIMP nsXPCTestParams::TestLongLongArray(PRUint32 aLength, PRInt64 *a,
PRUint32 *bLength NS_INOUTPARAM, PRInt64 **b NS_INOUTPARAM,
PRUint32 *rvLength NS_OUTPARAM, PRInt64 **rv NS_OUTPARAM)
/* void testDoubleArray (in unsigned long aLength, [array, size_is (aLength)] in double a,
* inout unsigned long bLength, [array, size_is (bLength)] inout double b,
* out unsigned long rvLength, [array, size_is (rvLength), retval] out double rv); */
NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(PRUint32 aLength, double *a,
PRUint32 *bLength NS_INOUTPARAM, double **b NS_INOUTPARAM,
PRUint32 *rvLength NS_OUTPARAM, double **rv NS_OUTPARAM)
{
BUFFER_METHOD_IMPL(PRInt64, 0, TAKE_OWNERSHIP_NOOP);
BUFFER_METHOD_IMPL(double, 0, TAKE_OWNERSHIP_NOOP);
}
/* void testStringArray (in unsigned long aLength, [array, size_is (aLength)] in string a,

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

@ -47,7 +47,7 @@
interface nsIXPCTestInterfaceA;
interface nsIXPCTestInterfaceB;
[scriptable, uuid(b94cd289-d0df-4d25-8995-facf687d921d)]
[scriptable, uuid(fe2b7433-ac3b-49ef-9344-b67228bfdd46)]
interface nsIXPCTestParams : nsISupports {
// These types correspond to the ones in typelib.py
@ -79,9 +79,9 @@ interface nsIXPCTestParams : nsISupports {
void testShortArray(in unsigned long aLength, [array, size_is(aLength)] in short a,
inout unsigned long bLength, [array, size_is(bLength)] inout short b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out short rv);
void testLongLongArray(in unsigned long aLength, [array, size_is(aLength)] in long long a,
inout unsigned long bLength, [array, size_is(bLength)] inout long long b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out long long rv);
void testDoubleArray(in unsigned long aLength, [array, size_is(aLength)] in double a,
inout unsigned long bLength, [array, size_is(bLength)] inout double b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out double rv);
void testStringArray(in unsigned long aLength, [array, size_is(aLength)] in string a,
inout unsigned long bLength, [array, size_is(bLength)] inout string b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out string rv);

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

@ -119,6 +119,22 @@ function test_component(contractid) {
do_check_true(dotEqualsComparator(val1IID, bIID.value));
}
// Check that the given call (type mismatch) results in an exception being thrown.
function doTypedArrayMismatchTest(name, val1, val1Size, val2, val2Size) {
var comparator = arrayComparator(standardComparator);
var error = false;
try {
doIsTest(name, val1, val1Size, val2, val2Size, comparator);
// An exception was not thrown as would have been expected.
do_check_true(false);
}
catch (e) {
// An exception was thrown as expected.
do_check_true(true);
}
}
// Workaround for bug 687612 (inout parameters broken for dipper types).
// We do a simple test of copying a into b, and ignore the rv.
function doTestWorkaround(name, val1) {
@ -165,7 +181,8 @@ function test_component(contractid) {
// Test arrays.
doIsTest("testShortArray", [2, 4, 6], 3, [1, 3, 5, 7], 4, arrayComparator(standardComparator));
doIsTest("testLongLongArray", [-10000000000], 1, [1, 3, 1234511234551], 3, arrayComparator(standardComparator));
doIsTest("testDoubleArray", [-10, -0.5], 2, [1, 3, 1e11, -8e-5 ], 4, arrayComparator(fuzzComparator));
doIsTest("testStringArray", ["mary", "hat", "hey", "lid", "tell", "lam"], 6,
["ids", "fleas", "woes", "wide", "has", "know", "!"], 7, arrayComparator(standardComparator));
doIsTest("testWstringArray", ["沒有語言", "的偉大嗎?]"], 2,
@ -173,6 +190,13 @@ function test_component(contractid) {
doIsTest("testInterfaceArray", [makeA(), makeA()], 2,
[makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator));
// Test typed arrays and ArrayBuffer aliasing.
var arrayBuffer = new ArrayBuffer(16);
var int16Array = new Int16Array(arrayBuffer, 2, 3);
int16Array.set([-32768, 0, 32767]);
doIsTest("testShortArray", int16Array, 3, new Int16Array([1773, -32768, 32767, 7]), 4, arrayComparator(standardComparator));
doIsTest("testDoubleArray", new Float64Array([-10, -0.5]), 2, new Float64Array([0, 3.2, 1.0e10, -8.33 ]), 4, arrayComparator(fuzzComparator));
// Test sized strings.
var ssTests = ["Tis not possible, I muttered", "give me back my free hardcore!", "quoth the server:", "4〠4"];
doIsTest("testSizedString", ssTests[0], ssTests[0].length, ssTests[1], ssTests[1].length, standardComparator);
@ -186,4 +210,12 @@ function test_component(contractid) {
// Test arrays of iids.
doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'],
[makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']);
// Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS.
doTypedArrayMismatchTest("testShortArray", Int16Array([-3, 7, 4]), 4,
Int16Array([1, -32, 6]), 3);
// Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS.
doTypedArrayMismatchTest("testShortArray", Uint16Array([0, 7, 4, 3]), 4,
Uint16Array([1, 5, 6]), 3);
}