Bug 591516 - 'IndexedDB: Add some UI to prompt for IndexedDB permissions'. r=sicking+gavin.

This commit is contained in:
Ben Turner 2010-09-09 15:15:40 -07:00
parent 5158899107
commit b88dc31fa2
24 changed files with 1048 additions and 55 deletions

View File

@ -366,6 +366,7 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
}
#notification-popup-box[anchorid="geo-notification-icon"] > #geo-notification-icon,
#notification-popup-box[anchorid="indexedDB-notification-icon"] > #indexedDB-notification-icon,
#notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon {
display: -moz-box;
}

View File

@ -1321,6 +1321,7 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
BrowserOffline.init();
OfflineApps.init();
IndexedDBPromptHelper.init();
gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
@ -1580,6 +1581,7 @@ function BrowserShutdown()
OfflineApps.uninit();
DownloadMonitorPanel.uninit();
gPrivateBrowsingUI.uninit();
IndexedDBPromptHelper.uninit();
var enumerator = Services.wm.getEnumerator(null);
enumerator.getNext();
@ -5852,6 +5854,92 @@ var OfflineApps = {
}
};
var IndexedDBPromptHelper = {
_permissionsPrompt: "indexedDB-permissions-prompt",
_permissionsResponse: "indexedDB-permissions-response",
_quotaPrompt: "indexedDB-quota-prompt",
_quotaResponse: "indexedDB-quota-response",
_notificationIcon: "indexedDB-notification-icon",
init:
function IndexedDBPromptHelper_init() {
Services.obs.addObserver(this, this._permissionsPrompt, false);
Services.obs.addObserver(this, this._quotaPrompt, false);
},
uninit:
function IndexedDBPromptHelper_uninit() {
Services.obs.removeObserver(this, this._permissionsPrompt, false);
Services.obs.removeObserver(this, this._quotaPrompt, false);
},
observe:
function IndexedDBPromptHelper_observe(subject, topic, data) {
if (topic != this._permissionsPrompt &&
topic != this._quotaPrompt) {
throw new Error("Unexpected topic!");
}
var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
var contentDocument = contentWindow.document;
var browserWindow =
OfflineApps._getBrowserWindowForContentWindow(contentWindow);
var browser =
OfflineApps._getBrowserForContentWindow(browserWindow, contentWindow);
if (!browser) {
// Must belong to some other window.
return;
}
var host = contentDocument.documentURIObject.asciiHost;
var message;
var responseTopic;
if (topic == this._permissionsPrompt) {
message = gNavigatorBundle.getFormattedString("offlineApps.available",
[ host ]);
responseTopic = this._permissionsResponse;
}
else if (topic == this._quotaPrompt) {
message = gNavigatorBundle.getFormattedString("indexedDB.usage",
[ host, data ]);
responseTopic = this._quotaResponse;
}
var self = this;
var observer = requestor.getInterface(Ci.nsIObserver);
var mainAction = {
label: gNavigatorBundle.getString("offlineApps.allow"),
accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
callback: function() {
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.ALLOW_ACTION);
}
};
var secondaryActions = [
{
label: gNavigatorBundle.getString("offlineApps.never"),
accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
callback: function() {
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.DENY_ACTION);
}
}
];
PopupNotifications.show(browser, topic, message, this._notificationIcon,
mainAction, secondaryActions);
}
};
function WindowIsClosing()
{
var reallyClose = closeWindow(false, warnAboutClosingWindow);

View File

@ -875,6 +875,7 @@
<box id="notification-popup-box" hidden="true" align="center">
<image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup
code fires onmousedown, and hence eats our favicon drag events.

View File

@ -185,6 +185,10 @@ offlineApps.usage=This website (%S) is now storing more than %SMB of data on you
offlineApps.manageUsage=Show settings
offlineApps.manageUsageAccessKey=S
# LOCALIZATION NOTE (indexedDB.usage): %1$S is the website host name
# %2$S a number of megabytes.
indexedDB.usage=This website (%1$S) is attempting to store more than %2$S MB of data on your computer for offline use.
identity.identified.verifier=Verified by: %S
identity.identified.verified_by_you=You have added a security exception for this site.
identity.identified.state_and_country=%S, %S

View File

@ -1034,6 +1034,11 @@ toolbar[iconsize="small"] #fullscreen-button {
height: 32px;
}
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
/* Notification icon box */
#notification-popup-box {
margin: 0 3px;
@ -1061,6 +1066,10 @@ toolbar[iconsize="small"] #fullscreen-button {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png);
}
#indexedDB-notification-icon {
list-style-image: url(chrome://global/skin/icons/question-16.png);
}
/* Feed icon */
#feed-button,
#feed-button > .button-box,

View File

@ -1962,6 +1962,15 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
height: 32px;
}
#indexedDB-notification-icon {
list-style-image: url(chrome://global/skin/icons/question-16.png);
}
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
#identity-popup-container,
#identity-popup-notification-container {
margin: 4px 3px 2px -30px;

View File

@ -1885,6 +1885,11 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
height: 32px;
}
.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
/* Notification icon box */
#notification-popup-box {
margin: 0 3px;
@ -1908,6 +1913,10 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png);
}
#indexedDB-notification-icon {
list-style-image: url(chrome://global/skin/icons/question-16.png);
}
#identity-popup-container {
min-width: 280px;
padding: 9px;

View File

@ -243,10 +243,7 @@ static PRPackedBool gMouseDown = PR_FALSE;
static PRPackedBool gDragServiceDisabled = PR_FALSE;
static FILE *gDumpFile = nsnull;
static PRUint64 gNextWindowID = 0;
#ifdef DEBUG
static PRUint32 gSerialCounter = 0;
#endif
#ifdef DEBUG_jst
PRInt32 gTimeoutCnt = 0;
@ -688,7 +685,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mJSObject(nsnull),
mPendingStorageEventsObsolete(nsnull),
mTimeoutsSuspendDepth(0),
mFocusMethod(0)
mFocusMethod(0),
mSerial(0)
#ifdef DEBUG
, mSetOpenerWindowCalled(PR_FALSE)
#endif
@ -764,11 +762,12 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
}
mSerial = ++gSerialCounter;
#ifdef DEBUG
printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt,
static_cast<void*>(static_cast<nsIScriptGlobalObject*>(this)),
++gSerialCounter, static_cast<void*>(aOuterWindow));
mSerial = gSerialCounter;
gSerialCounter, static_cast<void*>(aOuterWindow));
#endif
#ifdef PR_LOGGING

View File

@ -486,6 +486,10 @@ public:
const nsAString &aPopupWindowName,
const nsAString &aPopupWindowFeatures);
virtual PRUint32 GetSerial() {
return mSerial;
}
protected:
// Object Management
virtual ~nsGlobalWindow();
@ -837,9 +841,10 @@ protected:
// the method that was used to focus mFocusedNode
PRUint32 mFocusMethod;
PRUint32 mSerial;
#ifdef DEBUG
PRBool mSetOpenerWindowCalled;
PRUint32 mSerial;
nsCOMPtr<nsIURI> mLastOpenedURI;
#endif

View File

@ -546,6 +546,13 @@ public:
*/
virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) = 0;
/**
* NOTE! This function *will* be called on multiple threads so the
* implementation must not do any AddRef/Release or other actions that will
* mutate internal state.
*/
virtual PRUint32 GetSerial() = 0;
protected:
// The nsPIDOMWindow constructor. The aOuterWindow argument should
// be null if and only if the created window itself is an outer

View File

@ -47,6 +47,7 @@
#include "nsThreadUtils.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBTransaction.h"
#include "TransactionThreadPool.h"
@ -192,7 +193,13 @@ AsyncConnectionHelper::Run()
}
if (NS_SUCCEEDED(rv)) {
if (mDatabase) {
IDBFactory::SetCurrentDatabase(mDatabase);
}
mErrorCode = DoDatabaseWork(connection);
if (mDatabase) {
IDBFactory::SetCurrentDatabase(nsnull);
}
}
else {
mErrorCode = nsIIDBDatabaseException::UNKNOWN_ERR;

View File

@ -96,6 +96,12 @@ public:
// Only for transactions!
nsresult DispatchToTransactionPool();
void SetError(PRUint16 aErrorCode)
{
mError = true;
mErrorCode = aErrorCode;
}
protected:
AsyncConnectionHelper(IDBDatabase* aDatabase,
IDBRequest* aRequest);

View File

@ -0,0 +1,198 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "CheckPermissionsHelper.h"
#include "nsIDOMWindow.h"
#include "nsIIDBDatabaseException.h"
#include "nsIObserverService.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "nsContentUtils.h"
#include "nsDOMStorage.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#define PERMISSION_INDEXEDDB "indexedDB"
#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
#define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
#define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response"
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
namespace {
inline
PRUint32
GetIndexedDBPermissions(const nsACString& aASCIIOrigin,
nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!nsContentUtils::GetBoolPref(PREF_INDEXEDDB_ENABLED)) {
return nsIPermissionManager::DENY_ACTION;
}
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
return nsIPermissionManager::ALLOW_ACTION;
}
if (nsDOMStorageManager::gStorageManager->InPrivateBrowsingMode()) {
// TODO Support private browsing indexedDB?
return nsIPermissionManager::DENY_ACTION;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aASCIIOrigin);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(permissionManager, nsIPermissionManager::DENY_ACTION);
PRUint32 permission;
rv = permissionManager->TestPermission(uri, PERMISSION_INDEXEDDB,
&permission);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
return permission;
}
} // anonymous namespace
NS_IMPL_THREADSAFE_ISUPPORTS3(CheckPermissionsHelper, nsIRunnable,
nsIInterfaceRequestor,
nsIObserver)
NS_IMETHODIMP
CheckPermissionsHelper::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
PRUint32 permission = mHasPrompted ?
mPromptResult :
GetIndexedDBPermissions(mASCIIOrigin, mWindow);
nsresult rv;
if (mHasPrompted) {
if (permission != nsIPermissionManager::UNKNOWN_ACTION) {
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, permission,
nsIPermissionManager::EXPIRE_NEVER, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
else if (permission == nsIPermissionManager::UNKNOWN_ACTION) {
nsCOMPtr<nsIObserverService> obs = GetObserverService();
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_PERMISSIONS_PROMPT, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsRefPtr<AsyncConnectionHelper> helper;
helper.swap(mHelper);
nsCOMPtr<nsIThread> thread;
thread.swap(mThread);
nsCOMPtr<nsIDOMWindow> window;
window.swap(mWindow);
if (permission == nsIPermissionManager::ALLOW_ACTION) {
return helper->Dispatch(thread);
}
NS_ASSERTION(permission == nsIPermissionManager::UNKNOWN_ACTION ||
permission == nsIPermissionManager::DENY_ACTION,
"Unknown permission!");
helper->SetError(nsIIDBDatabaseException::NOT_ALLOWED_ERR);
return helper->Run();
}
NS_IMETHODIMP
CheckPermissionsHelper::GetInterface(const nsIID& aIID,
void** aResult)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aIID.Equals(NS_GET_IID(nsIObserver))) {
return QueryInterface(aIID, aResult);
}
if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
return mWindow->QueryInterface(aIID, aResult);
}
*aResult = nsnull;
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
CheckPermissionsHelper::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!");
mHasPrompted = PR_TRUE;
nsresult rv;
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_DispatchToCurrentThread(this);
}

View File

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_indexeddb_checkpermissionshelper_h__
#define mozilla_dom_indexeddb_checkpermissionshelper_h__
// Only meant to be included in IndexedDB source files, not exported.
#include "AsyncConnectionHelper.h"
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsIRunnable.h"
class nsIDOMWindow;
class nsIThread;
BEGIN_INDEXEDDB_NAMESPACE
class CheckPermissionsHelper : public nsIRunnable,
public nsIInterfaceRequestor,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIOBSERVER
CheckPermissionsHelper(AsyncConnectionHelper* aHelper,
nsIThread* aThread,
nsIDOMWindow* aWindow,
const nsACString& aASCIIOrigin)
: mHelper(aHelper),
mThread(aThread),
mWindow(aWindow),
mASCIIOrigin(aASCIIOrigin),
mHasPrompted(PR_FALSE),
mPromptResult(0)
{
NS_ASSERTION(aHelper, "Null pointer!");
NS_ASSERTION(aThread, "Null pointer!");
NS_ASSERTION(aWindow, "Null pointer!");
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty host!");
}
private:
nsRefPtr<AsyncConnectionHelper> mHelper;
nsCOMPtr<nsIThread> mThread;
nsCOMPtr<nsIDOMWindow> mWindow;
nsCString mASCIIOrigin;
PRBool mHasPrompted;
PRUint32 mPromptResult;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_checkpermissionshelper_h__

View File

@ -0,0 +1,226 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "CheckQuotaHelper.h"
#include "nsIDOMWindow.h"
#include "nsIIDBDatabaseException.h"
#include "nsIObserverService.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "nsContentUtils.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#include "IDBFactory.h"
#define PERMISSION_INDEXEDDB_UNLIMITED "indexedDB-unlimited"
#define TOPIC_QUOTA_PROMPT "indexedDB-quota-prompt"
#define TOPIC_QUOTA_RESPONSE "indexedDB-quota-response"
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
using mozilla::MutexAutoLock;
namespace {
inline
PRUint32
GetQuotaPermissions(const nsACString& aASCIIOrigin,
nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
return nsIPermissionManager::ALLOW_ACTION;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aASCIIOrigin);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(permissionManager, nsIPermissionManager::DENY_ACTION);
PRUint32 permission;
rv = permissionManager->TestPermission(uri, PERMISSION_INDEXEDDB_UNLIMITED,
&permission);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
return permission;
}
} // anonymous namespace
CheckQuotaHelper::CheckQuotaHelper(IDBDatabase* aDatabase,
mozilla::Mutex& aMutex)
: mWindow(aDatabase->Owner()),
mWindowSerial(mWindow->GetSerial()),
mOrigin(aDatabase->Origin()),
mMutex(aMutex),
mCondVar(mMutex, "CheckQuotaHelper::mCondVar"),
mPromptResult(0),
mWaiting(true),
mHasPrompted(false)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
mMutex.AssertCurrentThreadOwns();
}
bool
CheckQuotaHelper::PromptAndReturnQuotaIsDisabled()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
mMutex.AssertCurrentThreadOwns();
while (mWaiting) {
mCondVar.Wait();
}
NS_ASSERTION(!mWindow, "This should always be null here!");
NS_ASSERTION(mPromptResult == nsIPermissionManager::ALLOW_ACTION ||
mPromptResult == nsIPermissionManager::DENY_ACTION ||
mPromptResult == nsIPermissionManager::UNKNOWN_ACTION,
"Unknown permission!");
return mPromptResult == nsIPermissionManager::ALLOW_ACTION;
}
NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuotaHelper, nsIRunnable,
nsIInterfaceRequestor,
nsIObserver)
NS_IMETHODIMP
CheckQuotaHelper::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mHasPrompted) {
mPromptResult = GetQuotaPermissions(mOrigin, mWindow);
}
nsresult rv;
if (mHasPrompted) {
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION) {
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);
}
}
else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
PRUint32 quota = IDBFactory::GetIndexedDBQuota();
NS_ASSERTION(quota, "Shouldn't get here if quota is disabled!");
nsString quotaString;
quotaString.AppendInt(quota);
nsCOMPtr<nsIObserverService> obs = GetObserverService();
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;
mWaiting = false;
mCondVar.NotifyAll();
return NS_OK;
}
NS_IMETHODIMP
CheckQuotaHelper::GetInterface(const nsIID& aIID,
void** aResult)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aIID.Equals(NS_GET_IID(nsIObserver))) {
return QueryInterface(aIID, aResult);
}
if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
return mWindow->QueryInterface(aIID, aResult);
}
*aResult = nsnull;
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
CheckQuotaHelper::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, TOPIC_QUOTA_RESPONSE), "Bad topic!");
mHasPrompted = true;
nsresult rv;
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_DispatchToCurrentThread(this);
}

View File

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Indexed Database.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_indexeddb_checkquotahelper_h__
#define mozilla_dom_indexeddb_checkquotahelper_h__
// Only meant to be included in IndexedDB source files, not exported.
#include "IndexedDatabase.h"
#include "IDBDatabase.h"
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsIRunnable.h"
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
class nsPIDOMWindow;
BEGIN_INDEXEDDB_NAMESPACE
class CheckQuotaHelper : public nsIRunnable,
public nsIInterfaceRequestor,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIOBSERVER
CheckQuotaHelper(IDBDatabase* aDatabase,
mozilla::Mutex& aMutex);
bool PromptAndReturnQuotaIsDisabled();
PRUint32 WindowSerial()
{
return mWindowSerial;
}
private:
nsPIDOMWindow* mWindow;
PRUint32 mWindowSerial;
nsCString mOrigin;
mozilla::Mutex& mMutex;
mozilla::CondVar mCondVar;
PRUint32 mPromptResult;
bool mWaiting;
bool mHasPrompted;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_checkquotahelper_h__

View File

@ -41,12 +41,14 @@
#include "nsIIDBDatabaseException.h"
#include "mozilla/Mutex.h"
#include "mozilla/storage.h"
#include "nsDOMClassInfo.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "AsyncConnectionHelper.h"
#include "CheckQuotaHelper.h"
#include "DatabaseInfo.h"
#include "IDBEvents.h"
#include "IDBObjectStore.h"
@ -60,6 +62,12 @@ namespace {
const PRUint32 kDefaultDatabaseTimeoutSeconds = 30;
PRUint32 gDatabaseInstanceCount = 0;
mozilla::Mutex* gPromptHelpersMutex = nsnull;
// Protected by gPromptHelpersMutex.
nsTArray<nsRefPtr<CheckQuotaHelper> >* gPromptHelpers = nsnull;
class SetVersionHelper : public AsyncConnectionHelper
{
public:
@ -246,7 +254,10 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext,
IDBDatabase::IDBDatabase()
: mDatabaseId(0)
{
if (!gDatabaseInstanceCount++) {
NS_ASSERTION(!gPromptHelpersMutex, "Should be null!");
gPromptHelpersMutex = new mozilla::Mutex("IDBDatabase gPromptHelpersMutex");
}
}
IDBDatabase::~IDBDatabase()
@ -272,6 +283,16 @@ IDBDatabase::~IDBDatabase()
if (mListenerManager) {
mListenerManager->Disconnect();
}
if (!--gDatabaseInstanceCount) {
NS_ASSERTION(gPromptHelpersMutex, "Should not be null!");
delete gPromptHelpers;
gPromptHelpers = nsnull;
delete gPromptHelpersMutex;
gPromptHelpersMutex = nsnull;
}
}
nsresult
@ -304,6 +325,50 @@ IDBDatabase::CloseConnection()
}
}
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();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase,

View File

@ -111,6 +111,14 @@ public:
return mOwner;
}
bool
IsQuotaDisabled();
nsCString& Origin()
{
return mASCIIOrigin;
}
private:
IDBDatabase();
~IDBDatabase();

View File

@ -49,6 +49,7 @@
#include "nsContentUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDOMClassInfo.h"
#include "nsEscape.h"
#include "nsHashKeys.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
@ -56,17 +57,58 @@
#include "nsXPCOMCID.h"
#include "AsyncConnectionHelper.h"
#include "CheckPermissionsHelper.h"
#include "DatabaseInfo.h"
#include "IDBDatabase.h"
#include "IDBKeyRange.h"
#include "LazyIdleThread.h"
#define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
// megabytes
#define DEFAULT_QUOTA 50
#define BAD_TLS_INDEX (PRUintn)-1
#define DB_SCHEMA_VERSION 3
USING_INDEXEDDB_NAMESPACE
namespace {
PRUintn gCurrentDatabaseIndex = BAD_TLS_INDEX;
PRUint32 gIndexedDBQuota = DEFAULT_QUOTA;
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()) {
*_retval = 0;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback)
const PRUint32 kDefaultThreadTimeoutMS = 30000;
struct ObjectStoreInfoMap
@ -78,6 +120,7 @@ struct ObjectStoreInfoMap
ObjectStoreInfo* info;
};
class OpenDatabaseHelper : public AsyncConnectionHelper
{
public:
@ -318,51 +361,94 @@ CreateDatabaseConnection(const nsACString& aASCIIOrigin,
aDatabaseFilePath.Truncate();
nsCOMPtr<nsIFile> dbFile;
nsCOMPtr<nsIFile> dbDirectory;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(dbFile));
getter_AddRefs(dbDirectory));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbFile->Append(NS_LITERAL_STRING("indexedDB"));
rv = dbDirectory->Append(NS_LITERAL_STRING("indexedDB"));
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = dbFile->Exists(&exists);
rv = dbDirectory->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
PRBool isDirectory;
rv = dbFile->IsDirectory(&isDirectory);
rv = dbDirectory->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
}
else {
rv = dbFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin);
originSanitized.ReplaceChar(":/", '+');
rv = dbDirectory->Append(originSanitized);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbDirectory->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
PRBool isDirectory;
rv = dbDirectory->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
}
else {
rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIFile> dbFile;
rv = dbDirectory->Clone(getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
rv = dbDirectory->Append(NS_LITERAL_STRING("*"));
NS_ENSURE_SUCCESS(rv, rv);
nsCString pattern;
rv = dbDirectory->GetNativePath(pattern);
NS_ENSURE_SUCCESS(rv, rv);
if (gIndexedDBQuota) {
PRUint64 quota = gIndexedDBQuota * 1024 * 1024;
nsRefPtr<QuotaCallback> callback(new QuotaCallback());
rv = ss->SetQuotaForFilenamePattern(pattern, quota, callback, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
nsAutoString filename;
filename.AppendInt(HashString(aASCIIOrigin));
rv = dbFile->Append(filename);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
PRBool isDirectory;
rv = dbFile->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
}
else {
rv = dbFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
NS_ENSURE_SUCCESS(rv, rv);
}
filename.Truncate();
filename.AppendInt(HashString(aName));
nsCString escapedName;
if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
NS_WARNING("Can't escape database name!");
return NS_ERROR_UNEXPECTED;
}
const char* forwardIter = escapedName.BeginReading();
const char* backwardIter = escapedName.EndReading() - 1;
nsCString substring;
while (forwardIter <= backwardIter && substring.Length() < 21) {
if (substring.Length() % 2) {
substring.Append(*backwardIter--);
}
else {
substring.Append(*forwardIter++);
}
}
filename.Append(NS_ConvertASCIItoUTF16(substring));
filename.AppendLiteral(".sqlite");
rv = dbFile->Append(filename);
@ -371,12 +457,10 @@ CreateDatabaseConnection(const nsACString& aASCIIOrigin,
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
NS_NAMED_LITERAL_CSTRING(quota, "quota");
nsCOMPtr<mozIStorageConnection> connection;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(connection));
rv = ss->OpenDatabaseWithVFS(dbFile, quota, getter_AddRefs(connection));
if (rv == NS_ERROR_FILE_CORRUPTED) {
// Nuke the database file. The web services can recreate their data.
rv = dbFile->Remove(PR_FALSE);
@ -384,7 +468,7 @@ CreateDatabaseConnection(const nsACString& aASCIIOrigin,
exists = PR_FALSE;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(connection));
rv = ss->OpenDatabaseWithVFS(dbFile, quota, getter_AddRefs(connection));
}
NS_ENSURE_SUCCESS(rv, rv);
@ -399,7 +483,7 @@ CreateDatabaseConnection(const nsACString& aASCIIOrigin,
rv = dbFile->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = ss->OpenDatabase(dbFile, getter_AddRefs(connection));
rv = ss->OpenDatabaseWithVFS(dbFile, quota, getter_AddRefs(connection));
NS_ENSURE_SUCCESS(rv, rv);
}
@ -478,12 +562,13 @@ IDBFactory::GetConnection(const nsAString& aDatabaseFilePath)
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ENSURE_TRUE(exists, nsnull);
nsCOMPtr<mozIStorageService> ss =
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, nsnull);
nsCOMPtr<mozIStorageConnection> connection;
rv = ss->OpenDatabase(dbFile, getter_AddRefs(connection));
rv = ss->OpenDatabaseWithVFS(dbFile, NS_LITERAL_CSTRING("quota"),
getter_AddRefs(connection));
NS_ENSURE_SUCCESS(rv, nsnull);
#ifdef DEBUG
@ -505,6 +590,39 @@ IDBFactory::GetConnection(const nsAString& aDatabaseFilePath)
return connection.forget();
}
// static
bool
IDBFactory::SetCurrentDatabase(IDBDatabase* aDatabase)
{
NS_ASSERTION(gCurrentDatabaseIndex != BAD_TLS_INDEX,
"This should have been set already!");
#ifdef DEBUG
if (aDatabase) {
NS_ASSERTION(!PR_GetThreadPrivate(gCurrentDatabaseIndex),
"Someone forgot to unset gCurrentDatabaseIndex!");
}
else {
NS_ASSERTION(PR_GetThreadPrivate(gCurrentDatabaseIndex),
"Someone forgot to set gCurrentDatabaseIndex!");
}
#endif
if (PR_SetThreadPrivate(gCurrentDatabaseIndex, aDatabase) != PR_SUCCESS) {
NS_WARNING("Failed to set gCurrentDatabaseIndex!");
return false;
}
return true;
}
// static
PRUint32
IDBFactory::GetIndexedDBQuota()
{
return gIndexedDBQuota;
}
NS_IMPL_ADDREF(IDBFactory)
NS_IMPL_RELEASE(IDBFactory)
@ -524,12 +642,27 @@ IDBFactory::Open(const nsAString& aName,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (gCurrentDatabaseIndex == BAD_TLS_INDEX) {
// First time we're creating a database.
if (PR_NewThreadPrivateIndex(&gCurrentDatabaseIndex, NULL) != PR_SUCCESS) {
NS_ERROR("PR_NewThreadPrivateIndex failed!");
gCurrentDatabaseIndex = BAD_TLS_INDEX;
return NS_ERROR_FAILURE;
}
PRInt32 quota = nsContentUtils::GetIntPref(PREF_INDEXEDDB_QUOTA,
DEFAULT_QUOTA);
gIndexedDBQuota = PRUint32(PR_MAX(quota, 0));
}
if (aName.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = nsContentUtils::GetSecurityManager()->
rv = nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, nsnull);
@ -540,6 +673,11 @@ IDBFactory::Open(const nsAString& aName,
else {
rv = nsContentUtils::GetASCIIOrigin(principal, origin);
NS_ENSURE_SUCCESS(rv, nsnull);
if (origin.EqualsLiteral("null")) {
NS_WARNING("IndexedDB databases not allowed for this principal!");
return nsnull;
}
}
nsIScriptContext* context = GetScriptContextFromJSContext(aCx);
@ -560,10 +698,13 @@ IDBFactory::Open(const nsAString& aName,
nsRefPtr<LazyIdleThread> thread(new LazyIdleThread(kDefaultThreadTimeoutMS,
nsnull));
nsRefPtr<OpenDatabaseHelper> runnable =
nsRefPtr<OpenDatabaseHelper> openHelper =
new OpenDatabaseHelper(request, aName, aDescription, origin, thread);
rv = runnable->Dispatch(thread);
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, thread, innerWindow, origin);
rv = NS_DispatchToCurrentThread(permissionHelper);
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);

View File

@ -47,20 +47,25 @@
BEGIN_INDEXEDDB_NAMESPACE
class IDBDatabase;
class IDBFactory : public nsIIDBFactory
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIIDBFACTORY
static
already_AddRefed<nsIIDBFactory>
Create();
static already_AddRefed<nsIIDBFactory> Create();
static
already_AddRefed<mozIStorageConnection>
static already_AddRefed<mozIStorageConnection>
GetConnection(const nsAString& aDatabaseFilePath);
static bool
SetCurrentDatabase(IDBDatabase* aDatabase);
static PRUint32
GetIndexedDBQuota();
private:
IDBFactory() { }
~IDBFactory() { }

View File

@ -47,11 +47,11 @@
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "IDBEvents.h"
#include "IDBCursor.h"
#include "IDBObjectStore.h"
#include "IDBFactory.h"
#include "DatabaseInfo.h"
#include "IDBCursor.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBObjectStore.h"
#include "TransactionThreadPool.h"
#define SAVEPOINT_INITIAL "initial"
@ -765,6 +765,8 @@ CommitHelper::Run()
return NS_OK;
}
IDBFactory::SetCurrentDatabase(mTransaction->Database());
if (mAborted) {
NS_ASSERTION(mConnection, "This had better not be null!");
@ -778,7 +780,7 @@ CommitHelper::Run()
NS_NAMED_LITERAL_CSTRING(release, "RELEASE " SAVEPOINT_INITIAL);
if (NS_FAILED(mConnection->ExecuteSimpleSQL(release))) {
NS_WARNING("Failed to release transaction!");
mAborted = PR_TRUE;
}
}
@ -787,5 +789,7 @@ CommitHelper::Run()
mConnection->Close();
mConnection = nsnull;
IDBFactory::SetCurrentDatabase(nsnull);
return NS_OK;
}

View File

@ -53,6 +53,8 @@ EXPORTS_NAMESPACES = mozilla/dom/indexedDB
CPPSRCS = \
AsyncConnectionHelper.cpp \
CheckPermissionsHelper.cpp \
CheckQuotaHelper.cpp \
DatabaseInfo.cpp \
IDBCursor.cpp \
IDBDatabase.cpp \
@ -83,6 +85,7 @@ EXPORTS_mozilla/dom/indexedDB = \
LOCAL_INCLUDES = \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/src/storage \
-I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
$(NULL)

View File

@ -6,6 +6,14 @@
var testGenerator = testSteps();
function runTest()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let uri = window.document.documentURIObject;
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.add(uri, "indexedDB",
Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
SimpleTest.waitForExplicitFinish();
testGenerator.next();
}

View File

@ -96,6 +96,11 @@ pref("offline-apps.quota.max", 7000);
pref("offline-apps.quota.warn", 4000);
#endif
// Whether or not indexedDB is enabled.
pref("dom.indexedDB.enabled", true);
// Space to allow indexedDB databases before prompting (in MB).
pref("dom.indexedDB.warningQuota", 50);
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);