author | Kyle Huey <khuey@kylehuey.com> |
Sat, 03 Dec 2011 16:27:21 -0500 | |
changeset 81355 | c2102c45c8da7870239f2b313359a6da18703a4a |
parent 81351 | 13afcd4c097cf52b3fb653d9c59ee07bd78d863e (current diff) |
parent 81354 | 3838d56a1eafbf34e21d54fedf433cf0b45a1ea1 (diff) |
child 81356 | b45ac1c3ab89fee9309dc41408ec6c27984e6c49 |
child 81384 | 2ccbefd87c84b0aff01bfd7ff3576b1d7597cd0c |
push id | 21566 |
push user | khuey@mozilla.com |
push date | Sat, 03 Dec 2011 21:27:32 +0000 |
treeherder | mozilla-central@c2102c45c8da [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 11.0a1 |
first release with | nightly linux32
c2102c45c8da
/
11.0a1
/
20111204031102
/
files
nightly linux64
c2102c45c8da
/
11.0a1
/
20111204031102
/
files
nightly mac
c2102c45c8da
/
11.0a1
/
20111204031102
/
files
nightly win32
c2102c45c8da
/
11.0a1
/
20111204031102
/
files
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
11.0a1
/
20111204031102
/
pushlog to previous
nightly linux64
11.0a1
/
20111204031102
/
pushlog to previous
nightly mac
11.0a1
/
20111204031102
/
pushlog to previous
nightly win32
11.0a1
/
20111204031102
/
pushlog to previous
|
--- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -183,8 +183,12 @@ https://www.bank1.com:443 priv https://www.bank2.com:443 privileged,cert=escapeattack2 # # CONNECT for redirproxy results in a 302 redirect to # test1.example.com # 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
--- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -274,30 +274,30 @@ AsyncConnectionHelper::Run() if (NS_SUCCEEDED(rv)) { setProgressHandler = true; } } if (NS_SUCCEEDED(rv)) { bool hasSavepoint = false; if (mDatabase) { - IndexedDatabaseManager::SetCurrentDatabase(mDatabase); + IndexedDatabaseManager::SetCurrentWindow(mDatabase->Owner()); // Make the first savepoint. if (mTransaction) { if (!(hasSavepoint = mTransaction->StartSavepoint())) { NS_WARNING("Failed to make savepoint!"); } } } mResultCode = DoDatabaseWork(connection); if (mDatabase) { - IndexedDatabaseManager::SetCurrentDatabase(nsnull); + IndexedDatabaseManager::SetCurrentWindow(nsnull); // Release or roll back the savepoint depending on the error code. if (hasSavepoint) { NS_ASSERTION(mTransaction, "Huh?!"); if (NS_SUCCEEDED(mResultCode)) { mTransaction->ReleaseSavepoint(); } else {
--- a/dom/indexedDB/CheckQuotaHelper.cpp +++ b/dom/indexedDB/CheckQuotaHelper.cpp @@ -93,21 +93,19 @@ GetQuotaPermissions(const nsACString& aA &permission); NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION); return permission; } } // 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), mWaiting(true), mHasPrompted(false) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); mMutex.AssertCurrentThreadOwns(); @@ -170,61 +168,69 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuota nsIInterfaceRequestor, nsIObserver) NS_IMETHODIMP 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); - - 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, false); - NS_ENSURE_SUCCESS(rv, 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), 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(); - rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this), - TOPIC_QUOTA_PROMPT, quotaString.get()); - NS_ENSURE_SUCCESS(rv, rv); + nsString quotaString; + quotaString.AppendInt(quota); + + nsCOMPtr<nsIObserverService> obs = GetObserverService(); + NS_ENSURE_STATE(obs); - return NS_OK; + // 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); + + rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this), + TOPIC_QUOTA_PROMPT, quotaString.get()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } } MutexAutoLock lock(mMutex); NS_ASSERTION(mWaiting, "Huh?!"); // This should never be used again. mWindow = nsnull;
--- a/dom/indexedDB/CheckQuotaHelper.h +++ b/dom/indexedDB/CheckQuotaHelper.h @@ -60,33 +60,27 @@ class CheckQuotaHelper : public nsIRunna public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE 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; bool mWaiting; bool mHasPrompted; }; END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -59,22 +59,16 @@ #include "IndexedDatabaseManager.h" #include "LazyIdleThread.h" #include "TransactionThreadPool.h" USING_INDEXEDDB_NAMESPACE namespace { -PRUint32 gDatabaseInstanceCount = 0; -mozilla::Mutex* gPromptHelpersMutex = nsnull; - -// Protected by gPromptHelpersMutex. -nsTArray<nsRefPtr<CheckQuotaHelper> >* gPromptHelpers = nsnull; - class CreateObjectStoreHelper : public AsyncConnectionHelper { public: CreateObjectStoreHelper(IDBTransaction* aTransaction, IDBObjectStore* aObjectStore) : AsyncConnectionHelper(aTransaction, nsnull), mObjectStore(aObjectStore) { } @@ -190,21 +184,16 @@ IDBDatabase::Create(nsIScriptContext* aS IDBDatabase::IDBDatabase() : mDatabaseId(0), mInvalidated(0), mRegistered(false), mClosed(false), mRunningVersionChange(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!gDatabaseInstanceCount++) { - NS_ASSERTION(!gPromptHelpersMutex, "Should be null!"); - gPromptHelpersMutex = new mozilla::Mutex("IDBDatabase gPromptHelpersMutex"); - } } IDBDatabase::~IDBDatabase() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (mRegistered) { CloseInternal(true); @@ -213,96 +202,30 @@ IDBDatabase::~IDBDatabase() if (mgr) { mgr->UnregisterDatabase(this); } } 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; } bool IDBDatabase::IsInvalidated() { return !!mInvalidated;
--- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -117,18 +117,16 @@ public: already_AddRefed<nsIDocument> GetOwnerDocument() { NS_ASSERTION(mOwner, "This should never be null!"); nsCOMPtr<nsIDocument> doc = do_QueryInterface(mOwner->GetExtantDocument()); return doc.forget(); } - bool IsQuotaDisabled(); - nsCString& Origin() { return mASCIIOrigin; } void Invalidate(); // Whether or not the database has been invalidated. If it has then no further
--- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -398,34 +398,20 @@ IDBFactory::OpenCommon(const nsAString& NS_ENSURE_TRUE(window, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window); NS_ENSURE_TRUE(sgo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 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); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<OpenDatabaseHelper> openHelper = new OpenDatabaseHelper(request, aName, origin, aVersion, aDeleting);
--- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -882,17 +882,17 @@ CommitHelper::Run() } IDBDatabase* database = mTransaction->Database(); if (database->IsInvalidated()) { mAborted = true; } if (mConnection) { - IndexedDatabaseManager::SetCurrentDatabase(database); + IndexedDatabaseManager::SetCurrentWindow(database->Owner()); if (!mAborted) { NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION"); if (NS_FAILED(mConnection->ExecuteSimpleSQL(release))) { mAborted = true; } } @@ -918,13 +918,13 @@ CommitHelper::Run() } mDoomedObjects.Clear(); if (mConnection) { mConnection->Close(); mConnection = nsnull; - IndexedDatabaseManager::SetCurrentDatabase(nsnull); + IndexedDatabaseManager::SetCurrentWindow(nsnull); } return NS_OK; }
--- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -36,29 +36,32 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "IndexedDatabaseManager.h" #include "nsIFile.h" #include "nsIObserverService.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIScriptSecurityManager.h" #include "nsISHEntry.h" #include "nsISimpleEnumerator.h" #include "nsITimer.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/storage.h" #include "nsContentUtils.h" #include "nsThreadUtils.h" #include "nsXPCOM.h" #include "nsXPCOMPrivate.h" #include "AsyncConnectionHelper.h" +#include "CheckQuotaHelper.h" #include "IDBDatabase.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "LazyIdleThread.h" #include "TransactionThreadPool.h" // The amount of time, in milliseconds, that our IO thread will stay alive // after the last event it processes. @@ -70,52 +73,44 @@ // Amount of space that IndexedDB databases may use by default in megabytes. #define DEFAULT_QUOTA_MB 50 // Preference that users can set to override DEFAULT_QUOTA_MB #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; using mozilla::Preferences; namespace { 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 { public: NS_DECL_ISUPPORTS NS_IMETHOD QuotaExceeded(const nsACString& aFilename, PRInt64 aCurrentSizeLimit, PRInt64 aCurrentTotalSize, 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; } return NS_ERROR_FAILURE; } }; @@ -141,16 +136,18 @@ EnumerateToTArray(const nsACString& aKey } return PL_DHASH_NEXT; } } // anonymous namespace IndexedDatabaseManager::IndexedDatabaseManager() +: mCurrentWindowIndex(BAD_TLS_INDEX), + mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex") { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!gInstance, "More than one instance!"); } IndexedDatabaseManager::~IndexedDatabaseManager() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -167,40 +164,41 @@ IndexedDatabaseManager::GetOrCreate() if (IsShuttingDown()) { NS_ERROR("Calling GetOrCreateInstance() after shutdown!"); return nsnull; } 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); NS_ENSURE_TRUE(instance->mShutdownTimer, nsnull); nsCOMPtr<nsIObserverService> obs = GetObserverService(); NS_ENSURE_TRUE(obs, nsnull); @@ -543,40 +541,33 @@ IndexedDatabaseManager::OnDatabaseClosed } } break; } } } } -// 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 PRUint32 IndexedDatabaseManager::GetIndexedDBQuotaMB() { return PRUint32(NS_MAX(gIndexedDBQuotaMB, 0)); } @@ -657,16 +648,112 @@ IndexedDatabaseManager::EnsureQuotaManag } NS_ASSERTION(!mTrackedQuotaPaths.Contains(path), "What?!"); mTrackedQuotaPaths.AppendElement(path); 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) { nsresult rv = NS_OK; // If the helper has a transaction, dispatch it to the transaction // threadpool.
--- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -39,34 +39,39 @@ #ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__ #define mozilla_dom_indexeddb_indexeddatabasemanager_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" #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" #include "nsIThread.h" #include "nsIURI.h" #include "nsClassHashtable.h" +#include "nsRefPtrHashtable.h" #include "nsHashKeys.h" #define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1" class mozIStorageQuotaCallback; class nsITimer; BEGIN_INDEXEDDB_NAMESPACE class AsyncConnectionHelper; +class CheckQuotaHelper; + class IndexedDatabaseManager : public nsIIndexedDatabaseManager, public nsIObserver { friend class IDBDatabase; public: static already_AddRefed<IndexedDatabaseManager> GetOrCreate(); @@ -124,34 +129,69 @@ public: // Called when a window is being purged from the bfcache or the user leaves // a page which isn't going into the bfcache. Forces any live database // objects to close themselves and aborts any running transactions. void AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow); // 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(); nsresult AcquireExclusiveAccess(const nsACString& aOrigin, IDBDatabase* aDatabase, AsyncConnectionHelper* aHelper, WaitingOnDatabasesCallback aCallback, void* aClosure); + void SetCurrentWindowInternal(nsPIDOMWindow* aWindow); + bool QuotaIsLiftedInternal(); + void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow); + // Called when a database is created. bool RegisterDatabase(IDBDatabase* aDatabase); // Called when a database is being unlinked or destroyed. void UnregisterDatabase(IDBDatabase* aDatabase); // Called when a database has been closed. void OnDatabaseClosed(IDBDatabase* aDatabase); @@ -262,16 +302,25 @@ private: SynchronizedOp* mOp; }; static nsresult DispatchHelper(AsyncConnectionHelper* aHelper); // 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; // Maintains a list of synchronized operatons that are in progress or queued. nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps; // Thread on which IO is performed. @@ -285,11 +334,26 @@ private: nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton; // A list of all paths that are under SQLite's quota tracking system. This // list isn't protected by any mutex but it is only ever touched on the IO // thread. 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__ */
--- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -1085,16 +1085,24 @@ OpenDatabaseHelper::DoDatabaseWork() #endif mState = eFiringEvents; // In case we fail somewhere along the line. if (IndexedDatabaseManager::IsShuttingDown()) { 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); rv = dbFile->GetPath(mDatabaseFilePath); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsCOMPtr<nsIFile> dbDirectory;
--- a/dom/indexedDB/test/Makefile.in +++ b/dom/indexedDB/test/Makefile.in @@ -124,16 +124,18 @@ BROWSER_TEST_FILES = \ browserHelpers.js \ browser_permissionsPrompt.html \ browser_permissionsPromptAllow.js \ browser_permissionsPromptDeny.js \ browser_privateBrowsing.js \ browser_quotaPrompt.html \ browser_quotaPromptAllow.js \ browser_quotaPromptDeny.js \ + browser_quotaPromptDatabases.html \ + browser_quotaPromptDatabases.js \ head.js \ $(NULL) libs:: $(BROWSER_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) endif libs:: $(TEST_FILES)
--- a/dom/indexedDB/test/browser_quotaPromptAllow.js +++ b/dom/indexedDB/test/browser_quotaPromptAllow.js @@ -1,15 +1,15 @@ /** * 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://test1.example.org/browser/" + +const testPageURL = "http://bug704464-1.example.com/browser/" + "dom/indexedDB/test/browser_quotaPrompt.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); setPermission(testPageURL, "indexedDB");
new file mode 100644 --- /dev/null +++ b/dom/indexedDB/test/browser_quotaPromptDatabases.html @@ -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>
new file mode 100644 --- /dev/null +++ b/dom/indexedDB/test/browser_quotaPromptDatabases.js @@ -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; +}
--- a/dom/indexedDB/test/browser_quotaPromptDeny.js +++ b/dom/indexedDB/test/browser_quotaPromptDeny.js @@ -1,15 +1,15 @@ /** * 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://test2.example.org/browser/" + +const testPageURL = "http://bug704464-2.example.com/browser/" + "dom/indexedDB/test/browser_quotaPrompt.html"; const notificationID = "indexedDB-quota-prompt"; function test() { waitForExplicitFinish(); requestLongerTimeout(10); setPermission(testPageURL, "indexedDB");
--- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -53,16 +53,18 @@ #include "nsWrapperCache.h" #include "WrapperFactory.h" #include "AccessCheck.h" #include "nsJSUtils.h" #include "dombindings.h" #include "nsWrapperCacheInlines.h" +#include "jstypedarray.h" + using namespace mozilla; //#define STRICT_CHECK_OF_UNICODE #ifdef STRICT_CHECK_OF_UNICODE #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80)) #else // STRICT_CHECK_OF_UNICODE #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00)) #endif // STRICT_CHECK_OF_UNICODE @@ -1684,23 +1686,179 @@ XPCConvert::NativeArray2JS(XPCLazyCallCo return true; failure: return false; #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(); // No Action, FRee memory, RElease object enum CleanupMode {na, fr, re}; CleanupMode cleanupMode; @@ -1726,23 +1884,29 @@ XPCConvert::JSArray2Native(XPCCallContex if (!JSVAL_IS_OBJECT(s)) { if (pErr) *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY; return false; } 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; return false; } if (pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; @@ -1776,17 +1940,17 @@ XPCConvert::JSArray2Native(XPCCallContex case nsXPTType::T_I32 : POPULATE(na, int32); break; case nsXPTType::T_I64 : POPULATE(na, int64); break; case nsXPTType::T_U8 : POPULATE(na, uint8); break; case nsXPTType::T_U16 : POPULATE(na, uint16); break; case nsXPTType::T_U32 : POPULATE(na, uint32); break; 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; case nsXPTType::T_IID : POPULATE(fr, nsID*); break; case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure; case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break; case nsXPTType::T_WCHAR_STR : POPULATE(fr, jschar*); break; case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break; @@ -1869,17 +2033,17 @@ XPCConvert::NativeStringWithSize2JS(JSCo } return true; } // static 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"); JSContext* cx = ccx.GetJSContext(); JSUint32 len;
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -2429,18 +2429,17 @@ CallMethodHelper::GatherAndConvertResult return false; } nsID param_iid; if (datum_type.IsInterfacePointer() && !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid)) return false; - uintN err; - + nsresult err; if (isArray) { XPCLazyCallContext lccx(mCallContext); if (!XPCConvert::NativeArray2JS(lccx, &v, (const void**)&dp->val, datum_type, ¶m_iid, array_count, &err)) { // XXX need exception scheme for arrays to indicate bad element ThrowBadParam(err, i, mCallContext); return false;
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3322,17 +3322,24 @@ public: */ static JSBool NativeArray2JS(XPCLazyCallContext& ccx, jsval* d, const void** s, const nsXPTType& type, const nsID* iid, JSUint32 count, nsresult* pErr); 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, const nsXPTType& type, JSUint32 count, nsresult* pErr); static JSBool JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s,
--- a/js/xpconnect/tests/components/js/xpctest_params.js +++ b/js/xpconnect/tests/components/js/xpctest_params.js @@ -92,17 +92,17 @@ TestParams.prototype = { testWchar: f, testWstring: f, testDOMString: f, testAString: f, testAUTF8String: f, testACString: f, testJsval: f, testShortArray: f_is, - testLongLongArray: f_is, + testDoubleArray: f_is, testStringArray: f_is, testWstringArray: f_is, testInterfaceArray: f_is, testSizedString: f_is, testSizedWstring: f_is, testInterfaceIs: f_is, testInterfaceIsArray: f_size_and_iid, };
--- a/js/xpconnect/tests/components/native/xpctest_params.cpp +++ b/js/xpconnect/tests/components/native/xpctest_params.cpp @@ -241,24 +241,24 @@ NS_IMETHODIMP nsXPCTestParams::TestJsval * out unsigned long rvLength, [array, size_is (rvLength), retval] out short rv); */ NS_IMETHODIMP nsXPCTestParams::TestShortArray(PRUint32 aLength, PRInt16 *a, PRUint32 *bLength NS_INOUTPARAM, PRInt16 **b NS_INOUTPARAM, PRUint32 *rvLength NS_OUTPARAM, PRInt16 **rv NS_OUTPARAM) { 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, * inout unsigned long bLength, [array, size_is (bLength)] inout string b, * out unsigned long rvLength, [array, size_is (rvLength), retval] out string rv); */ NS_IMETHODIMP nsXPCTestParams::TestStringArray(PRUint32 aLength, const char * *a, PRUint32 *bLength NS_INOUTPARAM, char * **b NS_INOUTPARAM, PRUint32 *rvLength NS_OUTPARAM, char * **rv NS_OUTPARAM)
--- a/js/xpconnect/tests/idl/xpctest_params.idl +++ b/js/xpconnect/tests/idl/xpctest_params.idl @@ -42,17 +42,17 @@ * covered by the intersection of return values and inout). */ #include "nsISupports.idl" 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 boolean testBoolean(in boolean a, inout boolean b); octet testOctet(in octet a, inout octet b); short testShort(in short a, inout short b); long testLong(in long a, inout long b); long long testLongLong(in long long a, inout long long b); @@ -74,19 +74,19 @@ interface nsIXPCTestParams : nsISupports // // Dependent parameters use the same types as above, but are handled much differently. // // Test arrays. 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); void testWstringArray(in unsigned long aLength, [array, size_is(aLength)] in wstring a, inout unsigned long bLength, [array, size_is(bLength)] inout wstring b, out unsigned long rvLength, [retval, array, size_is(rvLength)] out wstring rv); void testInterfaceArray(in unsigned long aLength, [array, size_is(aLength)] in nsIXPCTestInterfaceA a, inout unsigned long bLength, [array, size_is(bLength)] inout nsIXPCTestInterfaceA b,
--- a/js/xpconnect/tests/unit/test_params.js +++ b/js/xpconnect/tests/unit/test_params.js @@ -114,16 +114,32 @@ function test_component(contractid) { do_check_true(arrayComparator(interfaceComparator)(rv, val2)); do_check_true(standardComparator(rvSize.value, val2Size)); do_check_true(dotEqualsComparator(rvIID.value, val2IID)); do_check_true(arrayComparator(interfaceComparator)(val1, b.value)); do_check_true(standardComparator(val1Size, bSize.value)); 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) { var a = val1; var b = {value: ""}; o[name].call(o, a, b); do_check_eq(val1, b.value); } @@ -160,30 +176,46 @@ function test_component(contractid) { function makeB() { var b = Cc["@mozilla.org/js/xpc/test/js/InterfaceB;1"].createInstance(Ci['nsIXPCTestInterfaceB']); b.name = 'testB' + numBsMade++; return b; }; // 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, ["we", "are", "being", "sooo", "international", "right", "now"], 7, arrayComparator(standardComparator)); 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); doIsTest("testSizedWstring", ssTests[2], ssTests[2].length, ssTests[3], ssTests[3].length, standardComparator); // Test iid_is. doIsTest("testInterfaceIs", makeA(), Ci['nsIXPCTestInterfaceA'], makeB(), Ci['nsIXPCTestInterfaceB'], interfaceComparator, dotEqualsComparator); // 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); }