зеркало из https://github.com/mozilla/gecko-dev.git
Merge b-s to m-c.
This commit is contained in:
Коммит
ff9a5d2684
|
@ -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, ¶m_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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче