Merge b-s to m-c.
authorKyle Huey <khuey@kylehuey.com>
Sat, 03 Dec 2011 16:27:21 -0500
changeset 82994 c2102c45c8da7870239f2b313359a6da18703a4a
parent 82990 13afcd4c097cf52b3fb653d9c59ee07bd78d863e (current diff)
parent 82993 3838d56a1eafbf34e21d54fedf433cf0b45a1ea1 (diff)
child 82995 b45ac1c3ab89fee9309dc41408ec6c27984e6c49
child 83023 2ccbefd87c84b0aff01bfd7ff3576b1d7597cd0c
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone11.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
nightly linux64
nightly mac
nightly win32
Merge b-s to m-c.
js/xpconnect/src/xpcprivate.h
--- 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, &param_iid))
             return false;
 
-        uintN err;
-
+        nsresult err;
         if (isArray) {
             XPCLazyCallContext lccx(mCallContext);
             if (!XPCConvert::NativeArray2JS(lccx, &v, (const void**)&dp->val,
                                             datum_type, &param_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);
 }