Merge mozilla-central and mozilla-inbound

This commit is contained in:
Ed Morley 2011-11-08 08:21:25 +00:00
Родитель 883920eef6 f49167fdae
Коммит 5fdd01ccd9
55 изменённых файлов: 1787 добавлений и 673 удалений

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

@ -5442,10 +5442,15 @@ void
nsContentUtils::CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
nsWrapperCache* aCache)
{
JSObject* wrapper = aCache->GetWrapper();
if (!wrapper) {
return;
}
nsXPCOMCycleCollectionParticipant* participant;
CallQueryInterface(aScriptObjectHolder, &participant);
DebugWrapperTraversalCallback callback(aCache->GetWrapper());
DebugWrapperTraversalCallback callback(wrapper);
participant->Traverse(aScriptObjectHolder, callback);
NS_ASSERTION(callback.mFound,
@ -5907,9 +5912,11 @@ nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
void *aClosure)
{
if (aCache->PreservingWrapper()) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT,
aCache->GetWrapperPreserveColor(),
"Preserved wrapper", aClosure);
JSObject *wrapper = aCache->GetWrapperPreserveColor();
if (wrapper) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT, wrapper,
"Preserved wrapper", aClosure);
}
}
else {
JSObject *expando = aCache->GetExpandoObjectPreserveColor();

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

@ -453,6 +453,14 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
// This can happen if the XHR was only used by C++ (and so never created a JS
// wrapper) that also made an ArrayBuffer.
if (PreservingWrapper()) {
nsContentUtils::ReleaseWrapper(
static_cast<nsIDOMEventTarget*>(
static_cast<nsDOMEventTargetHelper*>(this)), this);
}
nsLayoutStatics::Release();
}

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

@ -113,7 +113,6 @@ txExprParser::createAVT(const nsSubstring& aAttrValue,
}
newExpr = new txLiteralExpr(literalString +
Substring(start, iter));
NS_ENSURE_TRUE(newExpr, NS_ERROR_OUT_OF_MEMORY);
}
else {
// Parse expressions, iter is already past the initial '{' when
@ -171,7 +170,6 @@ txExprParser::createAVT(const nsSubstring& aAttrValue,
if (!expr) {
expr = new txLiteralExpr(EmptyString());
NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
}
*aResult = expr.forget();
@ -313,9 +311,9 @@ txExprParser::createExpr(txExprLexer& lexer, txIParseContext* aContext,
while (!done) {
MBool unary = MB_FALSE;
PRUint16 negations = 0;
while (lexer.peek()->mType == Token::SUBTRACTION_OP) {
unary = !unary;
negations++;
lexer.nextToken();
}
@ -324,15 +322,19 @@ txExprParser::createExpr(txExprLexer& lexer, txIParseContext* aContext,
break;
}
if (unary) {
Expr* unaryExpr = new UnaryExpr(expr);
if (!unaryExpr) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
if (negations > 0) {
if (negations % 2 == 0) {
FunctionCall* fcExpr = new txCoreFunctionCall(txCoreFunctionCall::NUMBER);
rv = fcExpr->addParam(expr);
if (NS_FAILED(rv))
return rv;
expr.forget();
expr = fcExpr;
}
else {
expr = new UnaryExpr(expr.forget());
}
expr.forget();
expr = unaryExpr;
}
Token* tok = lexer.nextToken();
@ -401,7 +403,6 @@ txExprParser::createFilterOrStep(txExprLexer& lexer, txIParseContext* aContext,
nspace);
NS_ENSURE_SUCCESS(rv, rv);
expr = new VariableRefExpr(prefix, lName, nspace);
NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
}
break;
case Token::L_PAREN:
@ -415,12 +416,10 @@ txExprParser::createFilterOrStep(txExprLexer& lexer, txIParseContext* aContext,
break;
case Token::LITERAL :
expr = new txLiteralExpr(tok->Value());
NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
break;
case Token::NUMBER:
{
expr = new txLiteralExpr(Double::toDouble(tok->Value()));
NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
break;
}
default:
@ -430,7 +429,6 @@ txExprParser::createFilterOrStep(txExprLexer& lexer, txIParseContext* aContext,
if (lexer.peek()->mType == Token::L_BRACKET) {
nsAutoPtr<FilterExpr> filterExpr(new FilterExpr(expr));
NS_ENSURE_TRUE(filterExpr, NS_ERROR_OUT_OF_MEMORY);
expr.forget();
@ -468,7 +466,6 @@ txExprParser::createFunctionCall(txExprLexer& lexer, txIParseContext* aContext,
txCoreFunctionCall::getTypeFromAtom(lName, type)) {
// It is a known built-in function.
fnCall = new txCoreFunctionCall(type);
NS_ENSURE_TRUE(fnCall, NS_ERROR_OUT_OF_MEMORY);
}
// check extension functions and xslt
@ -484,7 +481,6 @@ txExprParser::createFunctionCall(txExprLexer& lexer, txIParseContext* aContext,
*aResult = new txLiteralExpr(tok->Value() +
NS_LITERAL_STRING(" not implemented."));
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
@ -572,14 +568,12 @@ txExprParser::createLocationStep(txExprLexer& lexer, txIParseContext* aContext,
lexer.nextToken();
axisIdentifier = LocationStep::PARENT_AXIS;
nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
NS_ENSURE_TRUE(nodeTest, NS_ERROR_OUT_OF_MEMORY);
break;
case Token::SELF_NODE :
//-- eat token
lexer.nextToken();
axisIdentifier = LocationStep::SELF_AXIS;
nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
NS_ENSURE_TRUE(nodeTest, NS_ERROR_OUT_OF_MEMORY);
break;
default:
break;
@ -604,7 +598,6 @@ txExprParser::createLocationStep(txExprLexer& lexer, txIParseContext* aContext,
axisIdentifier == LocationStep::ATTRIBUTE_AXIS ?
static_cast<PRUint16>(txXPathNodeType::ATTRIBUTE_NODE) :
static_cast<PRUint16>(txXPathNodeType::ELEMENT_NODE));
NS_ENSURE_TRUE(nodeTest, NS_ERROR_OUT_OF_MEMORY);
}
else {
lexer.pushBack();
@ -614,7 +607,6 @@ txExprParser::createLocationStep(txExprLexer& lexer, txIParseContext* aContext,
}
nsAutoPtr<LocationStep> lstep(new LocationStep(nodeTest, axisIdentifier));
NS_ENSURE_TRUE(lstep, NS_ERROR_OUT_OF_MEMORY);
nodeTest.forget();
@ -690,7 +682,6 @@ txExprParser::createPathExpr(txExprLexer& lexer, txIParseContext* aContext,
lexer.nextToken();
if (!isLocationStepToken(lexer.peek())) {
*aResult = new RootExpr();
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
lexer.pushBack();
@ -713,7 +704,6 @@ txExprParser::createPathExpr(txExprLexer& lexer, txIParseContext* aContext,
}
else {
expr = new RootExpr();
NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
#ifdef TX_TO_STRING
static_cast<RootExpr*>(expr.get())->setSerialize(false);
@ -722,7 +712,6 @@ txExprParser::createPathExpr(txExprLexer& lexer, txIParseContext* aContext,
// We have a PathExpr containing several steps
nsAutoPtr<PathExpr> pathExpr(new PathExpr());
NS_ENSURE_TRUE(pathExpr, NS_ERROR_OUT_OF_MEMORY);
rv = pathExpr->addExpr(expr, PathExpr::RELATIVE_OP);
NS_ENSURE_SUCCESS(rv, rv);
@ -778,7 +767,6 @@ txExprParser::createUnionExpr(txExprLexer& lexer, txIParseContext* aContext,
}
nsAutoPtr<UnionExpr> unionExpr(new UnionExpr());
NS_ENSURE_TRUE(unionExpr, NS_ERROR_OUT_OF_MEMORY);
rv = unionExpr->addExpr(expr);
NS_ENSURE_SUCCESS(rv, rv);

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

@ -55,6 +55,7 @@ _TEST_FILES = test_bug319374.xhtml \
test_bug566629.html \
test_bug566629.xhtml \
test_bug603159.html \
test_bug616774.html \
test_bug667315.html \
test_exslt_regex.html \
$(NULL)

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

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=616774-->
<head>
<title>Test for Bug 616774</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=616774">Mozilla Bug 616774</a>
<p id="display"></p>
<div id="content" style="display: none">
42
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 616774 **/
is(document.evaluate('- "8"', document, null, XPathResult.ANY_TYPE, null).numberValue, -8, "Negated string literal should evaluate to itself negated");
is(document.evaluate('- - "999"', document, null, XPathResult.ANY_TYPE, null).numberValue, 999, "String literal should evaluate to itself");
is(document.evaluate('- - id("content")', document, null, XPathResult.ANY_TYPE, null).numberValue, 42, "DOM element should evaluate to itself coerced to a number");
</script>
</pre>
</body>
</html>

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

@ -224,10 +224,9 @@ AsyncConnectionHelper::Run()
{
if (NS_IsMainThread()) {
if (mTransaction &&
mTransaction->IsAborted() &&
NS_SUCCEEDED(mResultCode)) {
// Don't fire success events if the transaction has since been aborted.
// Instead convert to an error event.
mTransaction->IsAborted()) {
// Always fire a "error" event with ABORT_ERR if the transaction was
// aborted, even if the request succeeded or failed with another error.
mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
}

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

@ -141,7 +141,8 @@ CheckPermissionsHelper::Run()
NS_ENSURE_SUCCESS(rv, rv);
}
}
else if (permission == nsIPermissionManager::UNKNOWN_ACTION) {
else if (permission == nsIPermissionManager::UNKNOWN_ACTION &&
mPromptAllowed) {
nsCOMPtr<nsIObserverService> obs = GetObserverService();
rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
TOPIC_PERMISSIONS_PROMPT, nsnull);
@ -196,6 +197,7 @@ CheckPermissionsHelper::Observe(nsISupports* aSubject,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!");
NS_ASSERTION(mPromptAllowed, "How did we get here?");
mHasPrompted = true;

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

@ -64,10 +64,14 @@ public:
CheckPermissionsHelper(OpenDatabaseHelper* aHelper,
nsIDOMWindow* aWindow,
const nsACString& aASCIIOrigin)
const nsACString& aASCIIOrigin,
bool aForDeletion)
: mHelper(aHelper),
mWindow(aWindow),
mASCIIOrigin(aASCIIOrigin),
// If we're trying to delete the database, we should never prompt the user.
// Anything that would prompt is translated to denied.
mPromptAllowed(!aForDeletion),
mHasPrompted(false),
mPromptResult(0)
{
@ -80,6 +84,7 @@ private:
nsRefPtr<OpenDatabaseHelper> mHelper;
nsCOMPtr<nsIDOMWindow> mWindow;
nsCString mASCIIOrigin;
bool mPromptAllowed;
bool mHasPrompted;
PRUint32 mPromptResult;
};

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

@ -39,29 +39,14 @@
#include "DatabaseInfo.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsDataHashtable.h"
#include "nsThreadUtils.h"
USING_INDEXEDDB_NAMESPACE
namespace {
typedef nsClassHashtable<nsStringHashKey, ObjectStoreInfo>
ObjectStoreInfoHash;
struct DatabaseInfoHash
{
DatabaseInfoHash(DatabaseInfo* aInfo) {
NS_ASSERTION(aInfo, "Null pointer!");
info = aInfo;
}
nsAutoPtr<DatabaseInfo> info;
nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
};
typedef nsClassHashtable<nsISupportsHashKey, DatabaseInfoHash>
typedef nsDataHashtable<nsISupportsHashKey, DatabaseInfo*>
DatabaseHash;
DatabaseHash* gDatabaseHash = nsnull;
@ -79,22 +64,36 @@ EnumerateObjectStoreNames(const nsAString& aKey,
return PL_DHASH_NEXT;
}
PLDHashOperator
CloneObjectStoreInfo(const nsAString& aKey,
ObjectStoreInfo* aData,
void* aUserArg)
{
ObjectStoreInfoHash* hash = static_cast<ObjectStoreInfoHash*>(aUserArg);
nsAutoPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo(*aData));
if (!hash->Put(aKey, newInfo)) {
NS_WARNING("Out of memory?");
return PL_DHASH_STOP;
}
newInfo.forget();
return PL_DHASH_NEXT;
}
#ifdef NS_BUILD_REFCNT_LOGGING
DatabaseInfo::DatabaseInfo()
: nextObjectStoreId(1),
nextIndexId(1),
runningVersionChange(false)
{
MOZ_COUNT_CTOR(DatabaseInfo);
}
DatabaseInfo::~DatabaseInfo()
{
MOZ_COUNT_DTOR(DatabaseInfo);
// Clones are never in the hash.
if (!cloned) {
DatabaseInfo::Remove(id);
}
}
#ifdef NS_BUILD_REFCNT_LOGGING
IndexInfo::IndexInfo()
: id(LL_MININT),
unique(false),
@ -103,6 +102,16 @@ IndexInfo::IndexInfo()
MOZ_COUNT_CTOR(IndexInfo);
}
IndexInfo::IndexInfo(const IndexInfo& aOther)
: id(aOther.id),
name(aOther.name),
keyPath(aOther.keyPath),
unique(aOther.unique),
autoIncrement(aOther.autoIncrement)
{
MOZ_COUNT_CTOR(IndexInfo);
}
IndexInfo::~IndexInfo()
{
MOZ_COUNT_DTOR(IndexInfo);
@ -116,6 +125,17 @@ ObjectStoreInfo::ObjectStoreInfo()
MOZ_COUNT_CTOR(ObjectStoreInfo);
}
ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther)
: name(aOther.name),
id(aOther.id),
keyPath(aOther.keyPath),
autoIncrement(aOther.autoIncrement),
databaseId(aOther.databaseId),
indexes(aOther.indexes)
{
MOZ_COUNT_CTOR(ObjectStoreInfo);
}
ObjectStoreInfo::~ObjectStoreInfo()
{
MOZ_COUNT_DTOR(ObjectStoreInfo);
@ -140,14 +160,10 @@ DatabaseInfo::Get(nsIAtom* aId,
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aId, "Bad id!");
if (gDatabaseHash) {
DatabaseInfoHash* hash;
if (gDatabaseHash->Get(aId, &hash)) {
if (aInfo) {
*aInfo = hash->info;
}
return true;
}
if (gDatabaseHash &&
gDatabaseHash->Get(aId, aInfo)) {
NS_IF_ADDREF(*aInfo);
return true;
}
return false;
}
@ -174,13 +190,11 @@ DatabaseInfo::Put(DatabaseInfo* aInfo)
return false;
}
nsAutoPtr<DatabaseInfoHash> hash(new DatabaseInfoHash(aInfo));
if (!gDatabaseHash->Put(aInfo->id, hash)) {
if (!gDatabaseHash->Put(aInfo->id, aInfo)) {
NS_ERROR("Put failed!");
return false;
}
hash.forget();
return true;
}
@ -189,7 +203,11 @@ void
DatabaseInfo::Remove(nsIAtom* aId)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(aId, nsnull), "Don't know anything about this one!");
DatabaseInfo* info = nsnull;
DebugOnly<bool> got = Get(aId, &info);
NS_ASSERTION(got && info, "Don't know anything about this one!");
if (gDatabaseHash) {
gDatabaseHash->Remove(aId);
@ -205,20 +223,10 @@ bool
DatabaseInfo::GetObjectStoreNames(nsTArray<nsString>& aNames)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(id, nsnull), "Don't know anything about this one!");
if (!gDatabaseHash) {
return false;
}
DatabaseInfoHash* info;
if (!gDatabaseHash->Get(id, &info)) {
return false;
}
aNames.Clear();
if (info->objectStoreHash) {
info->objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames);
if (objectStoreHash) {
objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames);
}
return true;
}
@ -227,85 +235,81 @@ bool
DatabaseInfo::ContainsStoreName(const nsAString& aName)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(id, nsnull), "Don't know anything about this one!");
DatabaseInfoHash* hash;
ObjectStoreInfo* info;
return gDatabaseHash &&
gDatabaseHash->Get(id, &hash) &&
hash->objectStoreHash &&
hash->objectStoreHash->Get(aName, &info);
return objectStoreHash && objectStoreHash->Get(aName, nsnull);
}
// static
bool
ObjectStoreInfo::Get(nsIAtom* aDatabaseId,
const nsAString& aName,
ObjectStoreInfo** aInfo)
DatabaseInfo::GetObjectStore(const nsAString& aName,
ObjectStoreInfo** aInfo)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aName.IsEmpty(), "Empty object store name!");
if (gDatabaseHash) {
DatabaseInfoHash* hash;
if (gDatabaseHash->Get(aDatabaseId, &hash)) {
if (hash->objectStoreHash) {
return !!hash->objectStoreHash->Get(aName, aInfo);
}
}
if (objectStoreHash) {
return objectStoreHash->Get(aName, aInfo);
}
return false;
}
// static
bool
ObjectStoreInfo::Put(ObjectStoreInfo* aInfo)
DatabaseInfo::PutObjectStore(ObjectStoreInfo* aInfo)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aInfo, "Null pointer!");
if (!gDatabaseHash) {
NS_ERROR("No databases known!");
return false;
}
DatabaseInfoHash* hash;
if (!gDatabaseHash->Get(aInfo->databaseId, &hash)) {
NS_ERROR("Don't know about this database!");
return false;
}
if (!hash->objectStoreHash) {
nsAutoPtr<ObjectStoreInfoHash> objectStoreHash(new ObjectStoreInfoHash());
if (!objectStoreHash->Init()) {
if (!objectStoreHash) {
nsAutoPtr<ObjectStoreInfoHash> hash(new ObjectStoreInfoHash());
if (!hash->Init()) {
NS_ERROR("Failed to initialize hashtable!");
return false;
}
hash->objectStoreHash = objectStoreHash.forget();
objectStoreHash = hash.forget();
}
if (hash->objectStoreHash->Get(aInfo->name, nsnull)) {
if (objectStoreHash->Get(aInfo->name, nsnull)) {
NS_ERROR("Already have an entry for this objectstore!");
return false;
}
return !!hash->objectStoreHash->Put(aInfo->name, aInfo);
return objectStoreHash->Put(aInfo->name, aInfo);
}
// static
void
ObjectStoreInfo::Remove(nsIAtom* aDatabaseId,
const nsAString& aName)
DatabaseInfo::RemoveObjectStore(const nsAString& aName)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(Get(aDatabaseId, aName, nsnull), "Don't know about this one!");
NS_ASSERTION(GetObjectStore(aName, nsnull), "Don't know about this one!");
if (gDatabaseHash) {
DatabaseInfoHash* hash;
if (gDatabaseHash->Get(aDatabaseId, &hash) && hash->objectStoreHash) {
hash->objectStoreHash->Remove(aName);
}
if (objectStoreHash) {
objectStoreHash->Remove(aName);
}
}
already_AddRefed<DatabaseInfo>
DatabaseInfo::Clone()
{
NS_ASSERTION(!cloned, "Should never clone a clone!");
nsRefPtr<DatabaseInfo> dbInfo(new DatabaseInfo());
dbInfo->cloned = true;
dbInfo->name = name;
dbInfo->version = version;
dbInfo->id = id;
dbInfo->filePath = filePath;
dbInfo->nextObjectStoreId = nextObjectStoreId;
dbInfo->nextIndexId = nextIndexId;
if (objectStoreHash) {
dbInfo->objectStoreHash = new ObjectStoreInfoHash();
if (!dbInfo->objectStoreHash->Init()) {
return nsnull;
}
objectStoreHash->EnumerateRead(CloneObjectStoreInfo,
dbInfo->objectStoreHash);
}
return dbInfo.forget();
}

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

@ -46,18 +46,31 @@
#include "Key.h"
#include "IDBObjectStore.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
BEGIN_INDEXEDDB_NAMESPACE
struct ObjectStoreInfo;
typedef nsClassHashtable<nsStringHashKey, ObjectStoreInfo>
ObjectStoreInfoHash;
class IDBDatabase;
class OpenDatabaseHelper;
struct DatabaseInfo
{
#ifdef NS_BUILD_REFCNT_LOGGING
DatabaseInfo();
~DatabaseInfo();
#else
friend class IDBDatabase;
friend class OpenDatabaseHelper;
private:
DatabaseInfo()
: nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false)
: nextObjectStoreId(1),
nextIndexId(1),
cloned(false)
{ }
#endif
~DatabaseInfo();
static bool Get(nsIAtom* aId,
DatabaseInfo** aInfo);
@ -66,24 +79,37 @@ struct DatabaseInfo
static void Remove(nsIAtom* aId);
public:
bool GetObjectStoreNames(nsTArray<nsString>& aNames);
bool ContainsStoreName(const nsAString& aName);
bool GetObjectStore(const nsAString& aName,
ObjectStoreInfo** aInfo);
bool PutObjectStore(ObjectStoreInfo* aInfo);
void RemoveObjectStore(const nsAString& aName);
already_AddRefed<DatabaseInfo> Clone();
nsString name;
PRUint64 version;
nsIAtom* id;
nsString filePath;
PRInt64 nextObjectStoreId;
PRInt64 nextIndexId;
bool runningVersionChange;
bool cloned;
nsAutoRefCnt referenceCount;
nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
NS_INLINE_DECL_REFCOUNTING(DatabaseInfo)
};
struct IndexInfo
{
#ifdef NS_BUILD_REFCNT_LOGGING
IndexInfo();
IndexInfo(const IndexInfo& aOther);
~IndexInfo();
#else
IndexInfo()
@ -101,21 +127,13 @@ struct ObjectStoreInfo
{
#ifdef NS_BUILD_REFCNT_LOGGING
ObjectStoreInfo();
ObjectStoreInfo(ObjectStoreInfo& aOther);
~ObjectStoreInfo();
#else
ObjectStoreInfo()
: id(0), autoIncrement(false), databaseId(0) { }
#endif
static bool Get(nsIAtom* aDatabaseId,
const nsAString& aName,
ObjectStoreInfo** aInfo);
static bool Put(ObjectStoreInfo* aInfo);
static void Remove(nsIAtom* aDatabaseId,
const nsAString& aName);
nsString name;
PRInt64 id;
nsString keyPath;

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

@ -77,10 +77,13 @@ BEGIN_INDEXEDDB_NAMESPACE
class ContinueHelper : public AsyncConnectionHelper
{
public:
ContinueHelper(IDBCursor* aCursor)
ContinueHelper(IDBCursor* aCursor,
PRInt32 aCount)
: AsyncConnectionHelper(aCursor->mTransaction, aCursor->mRequest),
mCursor(aCursor)
{ }
mCursor(aCursor), mCount(aCount)
{
NS_ASSERTION(aCount > 0, "Must have a count!");
}
~ContinueHelper()
{
@ -106,6 +109,7 @@ protected:
protected:
nsRefPtr<IDBCursor> mCursor;
PRInt32 mCount;
Key mKey;
Key mObjectKey;
JSAutoStructuredCloneBuffer mCloneBuffer;
@ -114,8 +118,9 @@ protected:
class ContinueObjectStoreHelper : public ContinueHelper
{
public:
ContinueObjectStoreHelper(IDBCursor* aCursor)
: ContinueHelper(aCursor)
ContinueObjectStoreHelper(IDBCursor* aCursor,
PRUint32 aCount)
: ContinueHelper(aCursor, aCount)
{ }
private:
@ -126,8 +131,9 @@ private:
class ContinueIndexHelper : public ContinueHelper
{
public:
ContinueIndexHelper(IDBCursor* aCursor)
: ContinueHelper(aCursor)
ContinueIndexHelper(IDBCursor* aCursor,
PRUint32 aCount)
: ContinueHelper(aCursor, aCount)
{ }
private:
@ -138,8 +144,9 @@ private:
class ContinueIndexObjectHelper : public ContinueIndexHelper
{
public:
ContinueIndexObjectHelper(IDBCursor* aCursor)
: ContinueIndexHelper(aCursor)
ContinueIndexObjectHelper(IDBCursor* aCursor,
PRUint32 aCount)
: ContinueIndexHelper(aCursor, aCount)
{ }
private:
@ -296,6 +303,60 @@ IDBCursor::~IDBCursor()
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
}
nsresult
IDBCursor::ContinueInternal(const Key& aKey,
PRInt32 aCount)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aCount > 0, "Must have a count!");
if (!mTransaction->IsOpen()) {
return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
}
if (!mHaveValue || mContinueCalled) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
mContinueToKey = aKey;
#ifdef DEBUG
{
PRUint16 readyState;
if (NS_FAILED(mRequest->GetReadyState(&readyState))) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(readyState == nsIIDBRequest::DONE, "Should be DONE!");
}
#endif
mRequest->Reset();
nsRefPtr<ContinueHelper> helper;
switch (mType) {
case OBJECTSTORE:
helper = new ContinueObjectStoreHelper(this, aCount);
break;
case INDEXKEY:
helper = new ContinueIndexHelper(this, aCount);
break;
case INDEXOBJECT:
helper = new ContinueIndexObjectHelper(this, aCount);
break;
default:
NS_NOTREACHED("Unknown cursor type!");
}
nsresult rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mContinueCalled = true;
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
@ -487,14 +548,6 @@ IDBCursor::Continue(const jsval &aKey,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mTransaction->IsOpen()) {
return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
}
if (!mHaveValue || mContinueCalled) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
Key key;
nsresult rv = key.SetFromJSVal(aCx, aKey);
NS_ENSURE_SUCCESS(rv, rv);
@ -520,43 +573,7 @@ IDBCursor::Continue(const jsval &aKey,
}
}
mContinueToKey = key;
#ifdef DEBUG
{
PRUint16 readyState;
if (NS_FAILED(mRequest->GetReadyState(&readyState))) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(readyState == nsIIDBRequest::DONE, "Should be DONE!");
}
#endif
mRequest->Reset();
nsRefPtr<ContinueHelper> helper;
switch (mType) {
case OBJECTSTORE:
helper = new ContinueObjectStoreHelper(this);
break;
case INDEXKEY:
helper = new ContinueIndexHelper(this);
break;
case INDEXOBJECT:
helper = new ContinueIndexObjectHelper(this);
break;
default:
NS_NOTREACHED("Unknown cursor type!");
}
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mContinueCalled = true;
return NS_OK;
return ContinueInternal(key, 1);
}
NS_IMETHODIMP
@ -653,6 +670,19 @@ IDBCursor::Delete(JSContext* aCx,
return mObjectStore->Delete(key, aCx, _retval);
}
NS_IMETHODIMP
IDBCursor::Advance(PRInt32 aCount)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aCount < 1) {
return NS_ERROR_DOM_TYPE_ERR;
}
Key key;
return ContinueInternal(key, aCount);
}
nsresult
ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
@ -664,11 +694,17 @@ ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
// (less than, if we're running a PREV cursor) or equal to the key that was
// specified.
const nsCString& query = mCursor->mContinueToKey.IsUnset() ?
mCursor->mContinueQuery :
mCursor->mContinueToQuery;
nsCAutoString query;
if (mCursor->mContinueToKey.IsUnset()) {
query.Assign(mCursor->mContinueQuery);
}
else {
query.Assign(mCursor->mContinueToQuery);
}
NS_ASSERTION(!query.IsEmpty(), "Bad query!");
query.AppendInt(mCount);
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -677,9 +713,17 @@ ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
nsresult rv = BindArgumentsToStatement(stmt);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(mCount > 0, "Not ok!");
bool hasResult;
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
for (PRInt32 index = 0; index < mCount; index++) {
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!hasResult) {
break;
}
}
if (hasResult) {
rv = GatherResultsFromStatement(stmt);

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

@ -143,6 +143,10 @@ protected:
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery);
nsresult
ContinueInternal(const Key& aKey,
PRInt32 aCount);
nsRefPtr<IDBRequest> mRequest;
nsRefPtr<IDBTransaction> mTransaction;
nsRefPtr<IDBObjectStore> mObjectStore;

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

@ -131,24 +131,24 @@ NS_STACK_CLASS
class AutoRemoveObjectStore
{
public:
AutoRemoveObjectStore(nsIAtom* aId, const nsAString& aName)
: mId(aId), mName(aName)
AutoRemoveObjectStore(IDBDatabase* aDatabase, const nsAString& aName)
: mDatabase(aDatabase), mName(aName)
{ }
~AutoRemoveObjectStore()
{
if (mId) {
ObjectStoreInfo::Remove(mId, mName);
if (mDatabase) {
mDatabase->Info()->RemoveObjectStore(mName);
}
}
void forget()
{
mId = 0;
mDatabase = nsnull;
}
private:
nsCOMPtr<nsIAtom> mId;
IDBDatabase* mDatabase;
nsString mName;
};
@ -158,21 +158,24 @@ private:
already_AddRefed<IDBDatabase>
IDBDatabase::Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
DatabaseInfo* aDatabaseInfo,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDatabaseInfo, "Null pointer!");
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
NS_ASSERTION(databaseInfo, "Null pointer!");
nsRefPtr<IDBDatabase> db(new IDBDatabase());
db->mScriptContext = aScriptContext;
db->mOwner = aOwner;
db->mDatabaseId = aDatabaseInfo->id;
db->mName = aDatabaseInfo->name;
db->mFilePath = aDatabaseInfo->filePath;
db->mDatabaseId = databaseInfo->id;
db->mName = databaseInfo->name;
db->mFilePath = databaseInfo->filePath;
databaseInfo.swap(db->mDatabaseInfo);
db->mASCIIOrigin = aASCIIOrigin;
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@ -190,7 +193,8 @@ IDBDatabase::IDBDatabase()
: mDatabaseId(0),
mInvalidated(0),
mRegistered(false),
mClosed(false)
mClosed(false),
mRunningVersionChange(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -205,7 +209,7 @@ IDBDatabase::~IDBDatabase()
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mRegistered) {
CloseInternal();
CloseInternal(true);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
if (mgr) {
@ -213,18 +217,6 @@ IDBDatabase::~IDBDatabase()
}
}
if (mDatabaseId && !mInvalidated) {
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(info->referenceCount, "Bad reference count!");
if (--info->referenceCount == 0) {
DatabaseInfo::Remove(mDatabaseId);
}
}
if (mListenerManager) {
mListenerManager->Disconnect();
}
@ -309,17 +301,7 @@ IDBDatabase::Invalidate()
}
}
if (!PR_ATOMIC_SET(&mInvalidated, 1)) {
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(info->referenceCount, "Bad reference count!");
if (--info->referenceCount == 0) {
DatabaseInfo::Remove(mDatabaseId);
}
}
mInvalidated = true;
}
bool
@ -329,11 +311,23 @@ IDBDatabase::IsInvalidated()
}
void
IDBDatabase::CloseInternal()
IDBDatabase::CloseInternal(bool aIsDead)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mClosed) {
// If we're getting called from Unlink, avoid cloning the DatabaseInfo.
{
nsRefPtr<DatabaseInfo> previousInfo;
mDatabaseInfo.swap(previousInfo);
if (!aIsDead) {
nsRefPtr<DatabaseInfo> clonedInfo = previousInfo->Clone();
clonedInfo.swap(mDatabaseInfo);
}
}
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
if (mgr) {
mgr->OnDatabaseClosed(this);
@ -352,25 +346,15 @@ IDBDatabase::IsClosed()
void
IDBDatabase::EnterSetVersionTransaction()
{
DatabaseInfo* dbInfo;
if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(!dbInfo->runningVersionChange, "How did that happen?");
dbInfo->runningVersionChange = true;
NS_ASSERTION(!mRunningVersionChange, "How did that happen?");
mRunningVersionChange = true;
}
void
IDBDatabase::ExitSetVersionTransaction()
{
DatabaseInfo* dbInfo;
if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?");
dbInfo->runningVersionChange = false;
NS_ASSERTION(mRunningVersionChange, "How did that happen?");
mRunningVersionChange = false;
}
void
@ -381,7 +365,7 @@ IDBDatabase::OnUnlink()
// We've been unlinked, at the very least we should be able to prevent further
// transactions from starting and unblock any other SetVersion callers.
Close();
CloseInternal(true);
// No reason for the IndexedDatabaseManager to track us any longer.
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@ -432,11 +416,10 @@ NS_IMETHODIMP
IDBDatabase::GetVersion(PRUint64* aVersion)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* info = Info();
*aVersion = info->version;
return NS_OK;
}
@ -445,10 +428,7 @@ IDBDatabase::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* info = Info();
nsAutoTArray<nsString, 10> objectStoreNames;
if (!info->GetObjectStoreNames(objectStoreNames)) {
@ -475,11 +455,6 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aName.IsEmpty()) {
// XXX Update spec for a real error code here.
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
if (!transaction ||
@ -487,10 +462,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
DatabaseInfo* databaseInfo;
if (!DatabaseInfo::Get(mDatabaseId, &databaseInfo)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* databaseInfo = Info();
if (databaseInfo->ContainsStoreName(aName)) {
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
@ -501,58 +473,45 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
if (JSVAL_IS_PRIMITIVE(aOptions)) {
// XXX Update spec for a real code here
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
// XXX This isn't the right error
return NS_ERROR_DOM_TYPE_ERR;
}
NS_ASSERTION(JSVAL_IS_OBJECT(aOptions), "Huh?!");
JSObject* options = JSVAL_TO_OBJECT(aOptions);
js::AutoIdArray ids(aCx, JS_Enumerate(aCx, options));
if (!ids) {
jsval val;
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sKeyPath_id, &val)) {
NS_WARNING("JS_GetPropertyById failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
for (size_t index = 0; index < ids.length(); index++) {
jsid id = ids[index];
if (id != nsDOMClassInfo::sKeyPath_id &&
id != nsDOMClassInfo::sAutoIncrement_id) {
// XXX Update spec for a real code here
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
jsval val;
if (!JS_GetPropertyById(aCx, options, id, &val)) {
NS_WARNING("JS_GetPropertyById failed!");
if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) {
JSString* str = JS_ValueToString(aCx, val);
if (!str) {
NS_WARNING("JS_ValueToString failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (id == nsDOMClassInfo::sKeyPath_id) {
JSString* str = JS_ValueToString(aCx, val);
if (!str) {
NS_WARNING("JS_ValueToString failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsDependentJSString dependentKeyPath;
if (!dependentKeyPath.init(aCx, str)) {
NS_WARNING("Initializing keyPath failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
keyPath = dependentKeyPath;
}
else if (id == nsDOMClassInfo::sAutoIncrement_id) {
JSBool boolVal;
if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
NS_WARNING("JS_ValueToBoolean failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
autoIncrement = !!boolVal;
}
else {
NS_NOTREACHED("Shouldn't be able to get here!");
nsDependentJSString dependentKeyPath;
if (!dependentKeyPath.init(aCx, str)) {
NS_WARNING("Initializing keyPath failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
keyPath = dependentKeyPath;
}
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sAutoIncrement_id,
&val)) {
NS_WARNING("JS_GetPropertyById failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
JSBool boolVal;
if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
NS_WARNING("JS_ValueToBoolean failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
autoIncrement = !!boolVal;
}
nsAutoPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo());
@ -563,14 +522,14 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
newInfo->autoIncrement = autoIncrement;
newInfo->databaseId = mDatabaseId;
if (!ObjectStoreInfo::Put(newInfo)) {
if (!Info()->PutObjectStore(newInfo)) {
NS_WARNING("Put failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
ObjectStoreInfo* objectStoreInfo = newInfo.forget();
// Don't leave this in the hash if we fail below!
AutoRemoveObjectStore autoRemove(mDatabaseId, aName);
AutoRemoveObjectStore autoRemove(this, aName);
nsRefPtr<IDBObjectStore> objectStore =
transaction->GetOrCreateObjectStore(aName, objectStoreInfo);
@ -600,8 +559,9 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName)
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
DatabaseInfo* info = Info();
ObjectStoreInfo* objectStoreInfo;
if (!ObjectStoreInfo::Get(mDatabaseId, aName, &objectStoreInfo)) {
if (!info->GetObjectStore(aName, &objectStoreInfo)) {
return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
}
@ -610,7 +570,7 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName)
nsresult rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
ObjectStoreInfo::Remove(mDatabaseId, aName);
info->RemoveObjectStore(aName);
return NS_OK;
}
@ -631,12 +591,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames,
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabaseId, &info)) {
NS_ERROR("This should never fail!");
}
if (info->runningVersionChange) {
if (mRunningVersionChange) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
@ -742,6 +697,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames,
}
// Now check to make sure the object store names we collected actually exist.
DatabaseInfo* info = Info();
for (PRUint32 index = 0; index < storesToOpen.Length(); index++) {
if (!info->ContainsStoreName(storesToOpen[index])) {
return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
@ -762,7 +718,7 @@ IDBDatabase::Close()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
CloseInternal();
CloseInternal(false);
NS_ASSERTION(mClosed, "Should have set the closed flag!");
return NS_OK;

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

@ -77,17 +77,22 @@ public:
static already_AddRefed<IDBDatabase>
Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
DatabaseInfo* aDatabaseInfo,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin);
// nsIDOMEventTarget
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
nsIAtom* Id()
nsIAtom* Id() const
{
return mDatabaseId;
}
DatabaseInfo* Info() const
{
return mDatabaseInfo;
}
const nsString& Name()
{
return mName;
@ -130,7 +135,7 @@ public:
// transactions for this database will be allowed to run.
bool IsInvalidated();
void CloseInternal();
void CloseInternal(bool aIsDead);
// Whether or not the database has had Close called on it.
bool IsClosed();
@ -144,6 +149,7 @@ private:
void OnUnlink();
nsRefPtr<DatabaseInfo> mDatabaseInfo;
nsCOMPtr<nsIAtom> mDatabaseId;
nsString mName;
nsString mFilePath;
@ -152,6 +158,7 @@ private:
PRInt32 mInvalidated;
bool mRegistered;
bool mClosed;
bool mRunningVersionChange;
// Only touched on the main thread.
nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;

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

@ -346,7 +346,7 @@ IDBFactory::UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
// Remove all the old ones.
for (PRUint32 index = 0; index < existingNames.Length(); index++) {
ObjectStoreInfo::Remove(aDatabaseInfo->id, existingNames[index]);
aDatabaseInfo->RemoveObjectStore(existingNames[index]);
}
aDatabaseInfo->version = aVersion;
@ -355,7 +355,7 @@ IDBFactory::UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
nsAutoPtr<ObjectStoreInfo>& info = objectStores[index];
NS_ASSERTION(info->databaseId == aDatabaseInfo->id, "Huh?!");
if (!ObjectStoreInfo::Put(info)) {
if (!aDatabaseInfo->PutObjectStore(info)) {
NS_WARNING("Out of memory!");
return NS_ERROR_OUT_OF_MEMORY;
}
@ -377,19 +377,14 @@ NS_INTERFACE_MAP_END
DOMCI_DATA(IDBFactory, IDBFactory)
NS_IMETHODIMP
IDBFactory::Open(const nsAString& aName,
PRInt64 aVersion,
JSContext* aCx,
PRUint8 aOptionalArgCount,
nsIIDBOpenDBRequest** _retval)
nsresult
IDBFactory::OpenCommon(const nsAString& aName,
PRInt64 aVersion,
bool aDeleting,
nsIIDBOpenDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (aVersion < 1 && aOptionalArgCount) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
if (XRE_GetProcessType() == GeckoProcessType_Content) {
// Force ContentChild to cache the path from the parent, so that
// we do not end up in a side thread that asks for the path (which
@ -398,10 +393,6 @@ IDBFactory::Open(const nsAString& aName,
ContentChild::GetSingleton()->GetIndexedDBPath();
}
if (aName.IsEmpty()) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -435,13 +426,13 @@ IDBFactory::Open(const nsAString& aName,
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<OpenDatabaseHelper> openHelper =
new OpenDatabaseHelper(request, aName, origin, aVersion);
new OpenDatabaseHelper(request, aName, origin, aVersion, aDeleting);
rv = openHelper->Init();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, window, origin);
new CheckPermissionsHelper(openHelper, window, origin, aDeleting);
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -452,3 +443,23 @@ IDBFactory::Open(const nsAString& aName,
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBFactory::Open(const nsAString& aName,
PRInt64 aVersion,
PRUint8 aArgc,
nsIIDBOpenDBRequest** _retval)
{
if (aVersion < 1 && aArgc) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
return OpenCommon(aName, aVersion, false, _retval);
}
NS_IMETHODIMP
IDBFactory::DeleteDatabase(const nsAString& aName,
nsIIDBOpenDBRequest** _retval)
{
return OpenCommon(aName, 0, true, _retval);
}

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

@ -98,6 +98,12 @@ private:
IDBFactory();
~IDBFactory() { }
nsresult
OpenCommon(const nsAString& aName,
PRInt64 aVersion,
bool aDeleting,
nsIIDBOpenDBRequest** _retval);
nsCOMPtr<nsIWeakReference> mWindow;
};

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

@ -1092,20 +1092,23 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause.AppendLiteral(" ASC");
directionClause += NS_LITERAL_CSTRING(" ASC, ") + keyColumn +
NS_LITERAL_CSTRING(" ASC");
break;
case nsIIDBCursor::PREV:
directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyColumn +
NS_LITERAL_CSTRING(" DESC");
break;
case nsIIDBCursor::PREV_NO_DUPLICATE:
directionClause.AppendLiteral(" DESC");
directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyColumn +
NS_LITERAL_CSTRING(" ASC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
directionClause += NS_LITERAL_CSTRING(", ") + keyColumn +
NS_LITERAL_CSTRING(" ASC");
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn +
NS_LITERAL_CSTRING(" FROM ") + table +
NS_LITERAL_CSTRING(" WHERE index_id = :") + id +
@ -1164,7 +1167,7 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
currentKey + NS_LITERAL_CSTRING(" LIMIT ");
break;
case nsIIDBCursor::NEXT_NO_DUPLICATE:
@ -1178,7 +1181,7 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
NS_LITERAL_CSTRING(" LIMIT ");
break;
case nsIIDBCursor::PREV:
@ -1194,7 +1197,7 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
currentKey + NS_LITERAL_CSTRING(" LIMIT ");
break;
case nsIIDBCursor::PREV_NO_DUPLICATE:
@ -1208,7 +1211,7 @@ OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
NS_LITERAL_CSTRING(" LIMIT ");
break;
default:
@ -1283,19 +1286,23 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause.AppendLiteral(" ASC");
directionClause += NS_LITERAL_CSTRING(" ASC, ") + keyValue +
NS_LITERAL_CSTRING(" ASC");
break;
case nsIIDBCursor::PREV:
directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyValue +
NS_LITERAL_CSTRING(" DESC");
break;
case nsIIDBCursor::PREV_NO_DUPLICATE:
directionClause.AppendLiteral(" DESC");
directionClause += NS_LITERAL_CSTRING(" DESC, ") + keyValue +
NS_LITERAL_CSTRING(" ASC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
directionClause += NS_LITERAL_CSTRING(", ") + keyValue +
NS_LITERAL_CSTRING(" ASC");
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value +
NS_LITERAL_CSTRING(", ") + keyValue +

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

@ -377,18 +377,18 @@ NS_STACK_CLASS
class AutoRemoveIndex
{
public:
AutoRemoveIndex(nsIAtom* aDatabaseId,
AutoRemoveIndex(IDBDatabase* aDatabase,
const nsAString& aObjectStoreName,
const nsAString& aIndexName)
: mDatabaseId(aDatabaseId), mObjectStoreName(aObjectStoreName),
: mDatabase(aDatabase), mObjectStoreName(aObjectStoreName),
mIndexName(aIndexName)
{ }
~AutoRemoveIndex()
{
if (mDatabaseId) {
if (mDatabase) {
ObjectStoreInfo* info;
if (ObjectStoreInfo::Get(mDatabaseId, mObjectStoreName, &info)) {
if (mDatabase->Info()->GetObjectStore(mObjectStoreName, &info)) {
for (PRUint32 index = 0; index < info->indexes.Length(); index++) {
if (info->indexes[index].name == mIndexName) {
info->indexes.RemoveElementAt(index);
@ -401,11 +401,11 @@ public:
void forget()
{
mDatabaseId = 0;
mDatabase = nsnull;
}
private:
nsCOMPtr<nsIAtom> mDatabaseId;
IDBDatabase* mDatabase;
nsString mObjectStoreName;
nsString mIndexName;
};
@ -862,7 +862,7 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
// Figure out indexes and the index values to update here.
ObjectStoreInfo* info;
if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) {
NS_ERROR("This should never fail!");
}
@ -1037,7 +1037,7 @@ IDBObjectStore::GetIndexNames(nsIDOMDOMStringList** aIndexNames)
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
ObjectStoreInfo* info;
if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) {
NS_ERROR("This should never fail!");
}
@ -1265,7 +1265,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
if (aName.IsEmpty() || aKeyPath.IsEmpty()) {
if (aKeyPath.IsEmpty()) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
@ -1278,7 +1278,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
}
ObjectStoreInfo* info;
if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) {
NS_ERROR("This should never fail!");
}
@ -1302,50 +1302,28 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
// Get optional arguments.
if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
if (JSVAL_IS_PRIMITIVE(aOptions)) {
// XXX Update spec for a real code here
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
// XXX Update spec for a real code here
return NS_ERROR_DOM_TYPE_ERR;
}
NS_ASSERTION(JSVAL_IS_OBJECT(aOptions), "Huh?!");
JSObject* options = JSVAL_TO_OBJECT(aOptions);
js::AutoIdArray ids(aCx, JS_Enumerate(aCx, options));
if (!ids) {
jsval val;
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sUnique_id, &val)) {
NS_WARNING("JS_GetPropertyById failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
for (size_t index = 0; index < ids.length(); index++) {
jsid id = ids[index];
if (id != nsDOMClassInfo::sUnique_id) {
// XXX Update spec for a real code here
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
jsval val;
if (!JS_GetPropertyById(aCx, options, id, &val)) {
NS_WARNING("JS_GetPropertyById failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (id == nsDOMClassInfo::sUnique_id) {
JSBool boolVal;
if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
NS_WARNING("JS_ValueToBoolean failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
unique = !!boolVal;
}
else {
NS_NOTREACHED("Shouldn't be able to get here!");
}
JSBool boolVal;
if (!JS_ValueToBoolean(aCx, val, &boolVal)) {
NS_WARNING("JS_ValueToBoolean failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
unique = !!boolVal;
}
DatabaseInfo* databaseInfo;
if (!DatabaseInfo::Get(mTransaction->Database()->Id(), &databaseInfo)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* databaseInfo = mTransaction->Database()->Info();
IndexInfo* indexInfo = info->indexes.AppendElement();
if (!indexInfo) {
@ -1360,7 +1338,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
indexInfo->autoIncrement = mAutoIncrement;
// Don't leave this in the list if we fail below!
AutoRemoveIndex autoRemove(databaseInfo->id, mName, aName);
AutoRemoveIndex autoRemove(mTransaction->Database(), mName, aName);
#ifdef DEBUG
for (PRUint32 index = 0; index < mCreatedIndexes.Length(); index++) {
@ -1399,12 +1377,8 @@ IDBObjectStore::Index(const nsAString& aName,
return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
}
if (aName.IsEmpty()) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
ObjectStoreInfo* info;
if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) {
NS_ERROR("This should never fail!");
}
@ -1449,10 +1423,6 @@ IDBObjectStore::DeleteIndex(const nsAString& aName)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
if (aName.IsEmpty()) {
return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
}
IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
if (!transaction ||
@ -1464,7 +1434,7 @@ IDBObjectStore::DeleteIndex(const nsAString& aName)
NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
ObjectStoreInfo* info;
if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
if (!mTransaction->Database()->Info()->GetObjectStore(mName, &info)) {
NS_ERROR("This should never fail!");
}
@ -1835,8 +1805,7 @@ nsresult
DeleteHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
// XXX Will fix this for real in a bit.
*aVal = JSVAL_TRUE;
*aVal = JSVAL_VOID;
return NS_OK;
}
@ -1997,7 +1966,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
continueToKeyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
NS_LITERAL_CSTRING(" LIMIT ");
return NS_OK;
}

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

@ -492,7 +492,6 @@ IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
ObjectStoreInfo* aObjectStoreInfo)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aName.IsEmpty(), "Empty name!");
NS_ASSERTION(aObjectStoreInfo, "Null pointer!");
nsRefPtr<IDBObjectStore> retval;
@ -596,10 +595,7 @@ IDBTransaction::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
nsTArray<nsString>* arrayOfNames;
if (mMode == IDBTransaction::VERSION_CHANGE) {
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabase->Id(), &info)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* info = mDatabase->Info();
if (!info->GetObjectStoreNames(stackArray)) {
NS_ERROR("Out of memory!");
@ -636,7 +632,7 @@ IDBTransaction::ObjectStore(const nsAString& aName,
if (mMode == nsIIDBTransaction::VERSION_CHANGE ||
mObjectStoreNames.Contains(aName)) {
ObjectStoreInfo::Get(mDatabase->Id(), aName, &info);
mDatabase->Info()->GetObjectStore(aName, &info);
}
if (!info) {
@ -835,10 +831,7 @@ CommitHelper::Run()
NS_ASSERTION(mTransaction->Mode() == nsIIDBTransaction::VERSION_CHANGE,
"Bad transaction type!");
DatabaseInfo* dbInfo;
if (!DatabaseInfo::Get(mTransaction->Database()->Id(), &dbInfo)) {
NS_ERROR("This should never fail!");
}
DatabaseInfo* dbInfo = mTransaction->Database()->Info();
if (NS_FAILED(IDBFactory::UpdateDatabaseMetadata(dbInfo, mOldVersion,
mOldObjectStores))) {

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

@ -476,9 +476,12 @@ CreateDatabaseConnection(const nsAString& aName,
return NS_OK;
}
class VersionChangeEventsRunnable;
class SetVersionHelper : public AsyncConnectionHelper,
public IDBTransactionListener
{
friend class VersionChangeEventsRunnable;
public:
SetVersionHelper(IDBTransaction* aTransaction,
IDBOpenDBRequest* aRequest,
@ -498,9 +501,6 @@ public:
nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal);
static
void QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure);
protected:
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
nsresult Init();
@ -514,6 +514,11 @@ protected:
nsresult NotifyTransactionComplete(IDBTransaction* aTransaction);
PRUint64 RequestedVersion() const
{
return mRequestedVersion;
}
private:
// In-params
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
@ -522,6 +527,52 @@ private:
PRUint64 mCurrentVersion;
};
class DeleteDatabaseHelper : public AsyncConnectionHelper
{
friend class VersionChangeEventsRunnable;
public:
DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
OpenDatabaseHelper* aHelper,
PRUint64 aCurrentVersion,
const nsAString& aName,
const nsACString& aASCIIOrigin)
: AsyncConnectionHelper(static_cast<IDBDatabase*>(nsnull), aRequest),
mOpenRequest(aRequest), mOpenHelper(aHelper),
mCurrentVersion(aCurrentVersion), mName(aName),
mASCIIOrigin(aASCIIOrigin)
{ }
nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal);
protected:
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
nsresult Init();
// DeleteDatabaseHelper never fires events at the request. It hands that
// responsibility back to the OpenDatabaseHelper
void OnError()
{
mOpenHelper->NotifyDeleteFinished();
}
nsresult OnSuccess()
{
return mOpenHelper->NotifyDeleteFinished();
}
PRUint64 RequestedVersion() const
{
return 0;
}
private:
// In-params
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
nsRefPtr<IDBOpenDBRequest> mOpenRequest;
PRUint64 mCurrentVersion;
nsString mName;
nsCString mASCIIOrigin;
};
// Responsible for firing "versionchange" events at all live and non-closed
// databases, and for firing a "blocked" event at the requesting database if any
// databases fail to close.
@ -599,6 +650,10 @@ public:
return NS_OK;
}
template <class T>
static
void QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure);
private:
nsRefPtr<IDBDatabase> mRequestingDatabase;
nsRefPtr<IDBOpenDBRequest> mRequest;
@ -745,6 +800,11 @@ OpenDatabaseHelper::DoDatabaseWork()
mLastObjectStoreId = NS_MAX(objectStoreInfo->id, mLastObjectStoreId);
}
if (mForDeletion) {
mState = eDeletePending;
return NS_OK;
}
// See if we need to do a VERSION_CHANGE transaction
// Optional version semantics.
@ -797,7 +857,7 @@ OpenDatabaseHelper::StartSetVersion()
NS_ASSERTION(mgr, "This should never be null!");
rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
&SetVersionHelper::QueueVersionChange,
&VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
helper);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -808,6 +868,35 @@ OpenDatabaseHelper::StartSetVersion()
return NS_OK;
}
nsresult
OpenDatabaseHelper::StartDelete()
{
NS_ASSERTION(mState == eDeletePending, "Why are we here?");
// In case we fail, fire error events
mState = eFiringEvents;
nsresult rv = EnsureSuccessResult();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<DeleteDatabaseHelper> helper =
new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
mASCIIOrigin);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
&VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
helper);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// The DeleteDatabaseHelper is responsible for dispatching us back to the
// main thread again and changing the state to eDeleteCompleted.
mState = eDeletePending;
return NS_OK;
}
NS_IMETHODIMP
OpenDatabaseHelper::Run()
{
@ -825,30 +914,58 @@ OpenDatabaseHelper::Run()
SetError(rv);
// fall through and run the default error processing
}
else if (mState == eDeletePending) {
nsresult rv = StartDelete();
if (NS_SUCCEEDED(rv)) {
return rv;
}
SetError(rv);
// fall through and run the default error processing
}
// We've done whatever work we need to do on the DB thread, and any
// SetVersion stuff is done by now.
// SetVersion/DeleteDatabase stuff is done by now.
NS_ASSERTION(mState == eFiringEvents ||
mState == eSetVersionCompleted, "Why are we here?");
mState == eSetVersionCompleted ||
mState == eDeleteCompleted, "Why are we here?");
if (mState == eSetVersionCompleted) {
// Allow transaction creation/other version change transactions to proceed
// before we fire events. Other version changes will be postd to the end
// of the event loop, and will be behind whatever the page does in
// its error/success event handlers.
mDatabase->ExitSetVersionTransaction();
switch (mState) {
case eSetVersionCompleted: {
// Allow transaction creation/other version change transactions to proceed
// before we fire events. Other version changes will be postd to the end
// of the event loop, and will be behind whatever the page does in
// its error/success event handlers.
mDatabase->ExitSetVersionTransaction();
mState = eFiringEvents;
} else {
// Notify the request that we're done, but only if we didn't just finish
// a SetVersionHelper. In the SetVersionHelper case, that helper tells
// the request that it is done, and we avoid calling NotifyHandlerCompleted
// twice.
nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this);
if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
mResultCode = rv;
mState = eFiringEvents;
break;
}
case eDeleteCompleted: {
// Destroy the database now (we should have the only ref).
mDatabase = nsnull;
mState = eFiringEvents;
break;
}
case eFiringEvents: {
// Notify the request that we're done, but only if we didn't just
// finish a [SetVersion/DeleteDatabase]Helper. In that case, the
// helper tells the request that it is done, and we avoid calling
// NotifyHelperCompleted twice.
nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this);
if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
mResultCode = rv;
}
break;
}
default:
NS_NOTREACHED("Shouldn't get here!");
}
NS_ASSERTION(mState == eFiringEvents, "Why are we here?");
@ -880,10 +997,8 @@ OpenDatabaseHelper::Run()
nsresult
OpenDatabaseHelper::EnsureSuccessResult()
{
DatabaseInfo* dbInfo;
if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
NS_ASSERTION(dbInfo->referenceCount, "Bad reference count!");
++dbInfo->referenceCount;
nsRefPtr<DatabaseInfo> dbInfo;
if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
#ifdef DEBUG
{
@ -899,7 +1014,7 @@ OpenDatabaseHelper::EnsureSuccessResult()
NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!");
ObjectStoreInfo* otherInfo;
NS_ASSERTION(ObjectStoreInfo::Get(mDatabaseId, info->name, &otherInfo),
NS_ASSERTION(dbInfo->GetObjectStore(info->name, &otherInfo),
"ObjectStore not known!");
NS_ASSERTION(info->name == otherInfo->name &&
@ -934,19 +1049,18 @@ OpenDatabaseHelper::EnsureSuccessResult()
}
else {
nsAutoPtr<DatabaseInfo> newInfo(new DatabaseInfo());
nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
newInfo->name = mName;
newInfo->id = mDatabaseId;
newInfo->filePath = mDatabaseFilePath;
newInfo->referenceCount = 1;
if (!DatabaseInfo::Put(newInfo)) {
NS_ERROR("Failed to add to hash!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
dbInfo = newInfo.forget();
newInfo.swap(dbInfo);
nsresult rv = IDBFactory::UpdateDatabaseMetadata(dbInfo, mCurrentVersion,
mObjectStores);
@ -960,7 +1074,9 @@ OpenDatabaseHelper::EnsureSuccessResult()
nsRefPtr<IDBDatabase> database =
IDBDatabase::Create(mOpenDBRequest->ScriptContext(),
mOpenDBRequest->Owner(), dbInfo, mASCIIOrigin);
mOpenDBRequest->Owner(),
dbInfo.forget(),
mASCIIOrigin);
if (!database) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
@ -997,6 +1113,18 @@ OpenDatabaseHelper::NotifySetVersionFinished()
return NS_DispatchToCurrentThread(this);
}
nsresult
OpenDatabaseHelper::NotifyDeleteFinished()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
NS_ASSERTION(mState == eDeletePending, "How did we get here?");
mState = eDeleteCompleted;
// Dispatch ourself back to the main thread
return NS_DispatchToCurrentThread(this);
}
void
OpenDatabaseHelper::BlockDatabase()
{
@ -1009,8 +1137,6 @@ OpenDatabaseHelper::BlockDatabase()
void
OpenDatabaseHelper::DispatchSuccessEvent()
{
NS_ASSERTION(mDatabase, "Doesn't seem very successful to me.");
nsRefPtr<nsDOMEvent> event =
CreateGenericEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR));
if (!event) {
@ -1093,11 +1219,7 @@ nsresult
SetVersionHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
DatabaseInfo* info;
if (!DatabaseInfo::Get(mDatabase->Id(), &info)) {
NS_ERROR("This should never fail!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
DatabaseInfo* info = mDatabase->Info();
info->version = mRequestedVersion;
NS_ASSERTION(mTransaction, "Better have a transaction!");
@ -1109,22 +1231,23 @@ SetVersionHelper::GetSuccessResult(JSContext* aCx,
}
// static
template <class T>
void
SetVersionHelper::QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure)
VersionChangeEventsRunnable::QueueVersionChange(
nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
void* aClosure)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
SetVersionHelper* helper = static_cast<SetVersionHelper*>(aClosure);
NS_ASSERTION(helper, "Why don't we have a helper?");
T* closure = static_cast<T*>(aClosure);
nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
new VersionChangeEventsRunnable(helper->mOpenHelper->Database(),
helper->mOpenRequest,
new VersionChangeEventsRunnable(closure->mOpenHelper->Database(),
closure->mOpenRequest,
aDatabases,
helper->mCurrentVersion,
helper->mRequestedVersion);
closure->mCurrentVersion,
closure->RequestedVersion());
NS_DispatchToCurrentThread(eventsRunnable);
}
@ -1163,3 +1286,41 @@ SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction)
return rv;
}
nsresult
DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(!aConnection, "How did we get a connection here?");
nsCOMPtr<nsIFile> dbFile;
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(dbFile, "What?");
bool exists = false;
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (exists) {
rv = dbFile->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
return NS_OK;
}
nsresult
DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
{
return NS_OK;
}
nsresult
DeleteDatabaseHelper::Init()
{
// Note that there's no need to block the database here, since the page
// never gets to touch it, and all other databases must be closed.
return NS_OK;
}

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

@ -56,13 +56,17 @@ public:
OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
const nsAString& aName,
const nsACString& aASCIIOrigin,
PRUint64 aRequestedVersion)
PRUint64 aRequestedVersion,
bool aForDeletion)
: HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
mCurrentVersion(0), mDataVersion(DB_SCHEMA_VERSION), mDatabaseId(0),
mLastObjectStoreId(0), mLastIndexId(0), mState(eCreated),
mResultCode(NS_OK)
{ }
mForDeletion(aForDeletion), mCurrentVersion(0),
mDataVersion(DB_SCHEMA_VERSION), mDatabaseId(0), mLastObjectStoreId(0),
mLastIndexId(0), mState(eCreated), mResultCode(NS_OK)
{
NS_ASSERTION(!aForDeletion || !aRequestedVersion,
"Can't be for deletion and request a version!");
}
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
@ -84,6 +88,7 @@ public:
}
nsresult NotifySetVersionFinished();
nsresult NotifyDeleteFinished();
void BlockDatabase();
nsIAtom* Id() const
@ -101,6 +106,7 @@ protected:
// Methods only called on the main thread
nsresult EnsureSuccessResult();
nsresult StartSetVersion();
nsresult StartDelete();
nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal);
void DispatchSuccessEvent();
@ -116,6 +122,7 @@ private:
nsString mName;
nsCString mASCIIOrigin;
PRUint64 mRequestedVersion;
bool mForDeletion;
nsCOMPtr<nsIAtom> mDatabaseId;
// Out-params.
@ -134,6 +141,8 @@ private:
eFiringEvents, // Waiting to fire/firing events on the main thread
eSetVersionPending, // Waiting on a SetVersionHelper
eSetVersionCompleted, // SetVersionHelper is done
eDeletePending, // Waiting on a DeleteDatabaseHelper
eDeleteCompleted, // DeleteDatabaseHelper is done
};
OpenDatabaseState mState;
nsresult mResultCode;

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

@ -46,7 +46,7 @@ interface nsIIDBRequest;
* http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBCursor for more
* information.
*/
[scriptable, builtinclass, uuid(462a3607-b2d6-4f4b-9dd7-8ca0b26d3414)]
[scriptable, builtinclass, uuid(9d5ddd43-132d-418e-81e8-17d64a6467a2)]
interface nsIIDBCursor : nsISupports
{
const unsigned short NEXT = 0;
@ -75,4 +75,7 @@ interface nsIIDBCursor : nsISupports
// Success fires IDBTransactionEvent, result == null
[implicit_jscontext]
nsIIDBRequest delete();
void
advance(in long count);
};

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

@ -50,7 +50,7 @@ interface nsIDOMEventListener;
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBDatabase
* for more information.
*/
[scriptable, builtinclass, uuid(ddc9dd43-704f-45da-bb91-20f4b3103117)]
[scriptable, builtinclass, uuid(7ad626df-3328-476f-ba10-8ccec4924340)]
interface nsIIDBDatabase : nsISupports
{
readonly attribute DOMString name;
@ -69,11 +69,11 @@ interface nsIIDBDatabase : nsISupports
*/
[implicit_jscontext]
nsIIDBObjectStore
createObjectStore(in AString name,
createObjectStore([Null(Stringify)] in DOMString name,
[optional /* none */] in jsval options);
void
deleteObjectStore(in AString name);
deleteObjectStore([Null(Stringify)] in DOMString name);
[optional_argc, implicit_jscontext]
nsIIDBTransaction

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

@ -48,11 +48,14 @@ interface nsIIDBOpenDBRequest;
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory
* for more information.
*/
[scriptable, builtinclass, uuid(d2889b8f-662a-42d3-8a8f-ac5179b9d5b0)]
[scriptable, builtinclass, uuid(885abbb7-cf81-4945-b5f1-07fed07ada82)]
interface nsIIDBFactory : nsISupports
{
[implicit_jscontext, optional_argc]
[optional_argc]
nsIIDBOpenDBRequest
open(in AString name,
open([Null(Stringify)] in DOMString name,
[optional] in long long version);
nsIIDBOpenDBRequest
deleteDatabase(in AString name);
};

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

@ -50,7 +50,7 @@ interface nsIDOMDOMStringList;
* http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-nsIIDBObjectStore
* for more information.
*/
[scriptable, builtinclass, uuid(d25dff2f-81ad-4531-bcbe-e85c8a19f11a)]
[scriptable, builtinclass, uuid(adc6a1e2-9fd7-4d28-a7f9-9c653313124b)]
interface nsIIDBObjectStore : nsISupports
{
readonly attribute DOMString name;
@ -108,16 +108,16 @@ interface nsIIDBObjectStore : nsISupports
*/
[implicit_jscontext]
nsIIDBIndex
createIndex(in AString name,
in AString keyPath,
createIndex([Null(Stringify)] in DOMString name,
[Null(Stringify)] in DOMString keyPath,
[optional /* none */] in jsval options);
// Returns object immediately
nsIIDBIndex
index(in AString name);
index([Null(Stringify)] in DOMString name);
void
deleteIndex(in AString name);
deleteIndex([Null(Stringify)] in DOMString name);
// Accepts null, a key value, or a nsIIDBKeyRange object.
[implicit_jscontext, optional_argc]

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

@ -50,7 +50,7 @@ interface nsIDOMDOMStringList;
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBTransaction
* for more information.
*/
[scriptable, builtinclass, uuid(13e551a1-1a58-42ec-b0bd-7102ec0f64d6)]
[scriptable, builtinclass, uuid(4f25832d-de40-4c0b-a176-358d94384b19)]
interface nsIIDBTransaction : nsISupports
{
readonly attribute nsIIDBDatabase db;
@ -69,7 +69,7 @@ interface nsIIDBTransaction : nsISupports
readonly attribute nsIDOMDOMStringList objectStoreNames;
nsIIDBObjectStore
objectStore(in AString name);
objectStore([Null(Stringify)] in DOMString name);
// Don't commit the transaction.
void abort();

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

@ -53,6 +53,7 @@ TEST_FILES = \
helpers.js \
leaving_page_iframe.html \
test_add_twice_failure.html \
test_advance.html \
test_autoIncrement_indexes.html \
test_bad_keypath.html \
test_bfcache.html \
@ -64,6 +65,8 @@ TEST_FILES = \
test_cursors.html \
test_cursor_mutation.html \
test_cursor_update_updates_indexes.html \
test_deleteDatabase.html \
test_deleteDatabase_interactions.html \
test_error_events_abort_transactions.html \
test_event_propagation.html \
test_event_source.html \

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

@ -24,7 +24,7 @@
ok(event.target === request, "Good event target");
let objectStore = db.createObjectStore("foo", { keyPath: "" });
let objectStore = db.createObjectStore("foo", { keyPath: null });
let key = 10;
request = objectStore.add({}, key);

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

@ -0,0 +1,204 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const dataCount = 30;
let request = mozIndexedDB.open(window.location.pathname, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
db.onerror = errorHandler;
event.target.onsuccess = continueToNextStep;
let objectStore = db.createObjectStore("", { keyPath: "key" });
objectStore.createIndex("", "index");
for (let i = 0; i < dataCount; i++) {
objectStore.add({ key: i, index: i });
}
yield;
function getObjectStore() {
return db.transaction("").objectStore("");
}
function getIndex() {
return db.transaction("").objectStore("").index("");
}
let count = 0;
getObjectStore().openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
count++;
cursor.continue();
}
else {
continueToNextStep();
}
};
yield;
is(count, dataCount, "Saw all data");
count = 0;
getObjectStore().openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
if (count) {
count++;
cursor.continue();
}
else {
count = 10;
cursor.advance(10);
}
}
else {
continueToNextStep();
}
};
yield;
is(count, dataCount, "Saw all data");
count = 0;
getIndex().openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
if (count) {
count++;
cursor.continue();
}
else {
count = 10;
cursor.advance(10);
}
}
else {
continueToNextStep();
}
};
yield;
is(count, dataCount, "Saw all data");
count = 0;
getIndex().openKeyCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
if (count) {
count++;
cursor.continue();
}
else {
count = 10;
cursor.advance(10);
}
}
else {
continueToNextStep();
}
};
yield;
is(count, dataCount, "Saw all data");
count = 0;
getObjectStore().openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
if (count == 0) {
cursor.advance(dataCount + 1);
}
else {
ok(false, "Should never get here!");
cursor.continue();
}
}
else {
continueToNextStep();
}
};
yield;
is(count, 0, "Saw all data");
count = dataCount - 1;
getObjectStore().openCursor(null, IDBCursor.PREV).onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
count--;
if (count == dataCount - 2) {
cursor.advance(10);
count -= 9;
}
else {
cursor.continue();
}
}
else {
continueToNextStep();
}
};
yield;
is(count, -1, "Saw all data");
count = dataCount - 1;
getObjectStore().openCursor(null, IDBCursor.PREV).onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.primaryKey, count, "Got correct object");
if (count == dataCount - 1) {
cursor.advance(dataCount + 1);
}
else {
ok(false, "Should never get here!");
cursor.continue();
}
}
else {
continueToNextStep();
}
};
yield;
is(count, dataCount - 1, "Saw all data");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -25,6 +25,9 @@
{ name: "1", keyPath: "unique_value", options: { unique: true } },
{ name: "2", keyPath: "value", options: { unique: false } },
{ name: "3", keyPath: "value", options: { unique: false } },
{ name: "", keyPath: "value", options: { unique: false } },
{ name: null, keyPath: "value", options: { unique: false } },
{ name: undefined, keyPath: "value", options: { unique: false } },
];
let request = mozIndexedDB.open(name, 1, description);
@ -39,23 +42,6 @@
db.createObjectStore(info.name, info.options) :
db.createObjectStore(info.name);
// Test basic failure conditions.
try {
request = objectStore.createIndex(null, null);
ok(false, "createIndex with null name should throw");
}
catch(e) {
ok(true, "createIndex with null name should throw");
}
try {
request = objectStore.createIndex("", "");
ok(false, "createIndex with empty name should throw");
}
catch(e) {
ok(true, "createIndex with empty name should throw");
}
try {
request = objectStore.createIndex("Hola");
ok(false, "createIndex with no keyPath should throw");
@ -72,13 +58,9 @@
ok(true, "createIndex with bad options threw");
}
try {
request = objectStore.createIndex("foo", "bar", { foo: "" });
ok(false, "createIndex with bad options should throw");
}
catch(e) {
ok(true, "createIndex with bad options threw");
}
ok(objectStore.createIndex("foo", "bar", { foo: "" }),
"createIndex with unknown options should not throw");
objectStore.deleteIndex("foo");
// Test index creation, and that it ends up in indexNames.
let objectStoreName = info.name;
@ -90,7 +72,15 @@
info.options) :
objectStore.createIndex(info.name, info.keyPath);
is(index.name, info.name, "correct name");
let name = info.name;
if (name === null) {
name = "null";
}
else if (name === undefined) {
name = "undefined";
}
is(index.name, name, "correct name");
is(index.keyPath, info.keyPath, "correct keyPath");
is(index.unique, info.options.unique, "correct uniqueness");
@ -98,7 +88,7 @@
"indexNames grew in size");
let found = false;
for (let k = 0; k < objectStore.indexNames.length; k++) {
if (objectStore.indexNames.item(k) == info.name) {
if (objectStore.indexNames.item(k) == name) {
found = true;
break;
}

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

@ -18,17 +18,20 @@
const name = window.location.pathname;
const description = "My Test Database";
const objectStoreInfo = [
{ name: "1", options: { keyPath: "" } },
{ name: "2", options: { keyPath: "", autoIncrement: true } },
{ name: "3", options: { keyPath: "", autoIncrement: false } },
{ name: "4", options: { keyPath: "" } },
{ name: "1", options: { keyPath: null } },
{ name: "2", options: { keyPath: null, autoIncrement: true } },
{ name: "3", options: { keyPath: null, autoIncrement: false } },
{ name: "4", options: { keyPath: null } },
{ name: "5", options: { keyPath: "foo" } },
{ name: "6" },
{ name: "7", options: null },
{ name: "8", options: { autoIncrement: true } },
{ name: "9", options: { autoIncrement: false } },
{ name: "10", options: { keyPath: "foo", autoIncrement: false } },
{ name: "11", options: { keyPath: "foo", autoIncrement: true } }
{ name: "11", options: { keyPath: "foo", autoIncrement: true } },
{ name: "" },
{ name: null },
{ name: undefined }
];
let request = mozIndexedDB.open(name, 1, description);
@ -41,22 +44,6 @@
let count = db.objectStoreNames.length;
is(count, 0, "correct objectStoreNames length");
try {
db.createObjectStore(null);
ok(false, "createObjectStore with null name should throw");
}
catch(e) {
ok(true, "createObjectStore with null name should throw");
}
try {
db.createObjectStore("");
ok(false, "createObjectStore with empty name should throw");
}
catch(e) {
ok(true, "createObjectStore with empty name should throw");
}
try {
db.createObjectStore("foo", "bar");
ok(false, "createObjectStore with bad options should throw");
@ -65,13 +52,9 @@
ok(true, "createObjectStore with bad options");
}
try {
db.createObjectStore("foo", { foo: "" });
ok(false, "createObjectStore with bad options should throw");
}
catch(e) {
ok(true, "createObjectStore with bad options");
}
ok(db.createObjectStore("foo", { foo: "" }),
"createObjectStore with unknown options should not throw");
db.deleteObjectStore("foo");
for (let index in objectStoreInfo) {
index = parseInt(index);
@ -84,16 +67,24 @@
is(db.objectStoreNames.length, index + 1,
"updated objectStoreNames list");
let name = info.name;
if (name === null) {
name = "null";
}
else if (name === undefined) {
name = "undefined";
}
let found = false;
for (let i = 0; i <= index; i++) {
if (db.objectStoreNames.item(i) == info.name) {
if (db.objectStoreNames.item(i) == name) {
found = true;
break;
}
}
is(found, true, "objectStoreNames contains name");
is(objectStore.name, info.name, "Bad name");
is(objectStore.name, name, "Bad name");
is(objectStore.keyPath, info.options && info.options.keyPath ?
info.options.keyPath : "",
"Bad keyPath");
@ -110,7 +101,7 @@
found = false;
for (let j = 0; j < event.target.transaction.objectStoreNames.length;
j++) {
if (event.target.transaction.objectStoreNames.item(j) == info.name) {
if (event.target.transaction.objectStoreNames.item(j) == name) {
found = true;
break;
}

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

@ -20,11 +20,11 @@
const START_DATA = "hi";
const END_DATA = "bye";
const objectStoreInfo = [
{ name: "1", options: { keyPath: "" }, key: 1,
{ name: "1", options: { keyPath: null }, key: 1,
entry: { data: START_DATA } },
{ name: "2", options: { keyPath: "foo" },
entry: { foo: 1, data: START_DATA } },
{ name: "3", options: { keyPath: "", autoIncrement: true },
{ name: "3", options: { keyPath: null, autoIncrement: true },
entry: { data: START_DATA } },
{ name: "4", options: { keyPath: "foo", autoIncrement: true },
entry: { data: START_DATA } },

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

@ -304,7 +304,7 @@
request = cursor.delete();
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.result, true, "Actually deleted something");
ok(event.target.result === undefined, "Should be undefined");
is(keyIndex, 5, "Got result of remove before next continue");
gotRemoveEvent = true;
};

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

@ -0,0 +1,114 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database DeleteDatabase Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
const VERSION_CHANGE = Components.interfaces.nsIIDBTransaction.VERSION_CHANGE;
const name = window.location.pathname;
ok(mozIndexedDB.deleteDatabase, "deleteDatabase function should exist!");
let request = mozIndexedDB.open(name, 10);
request.onerror = errorHandler;
request.onsuccess = unexpectedSuccessHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let event = yield;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
let db = event.target.result;
db.createObjectStore("stuff");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.type, "success", "Expect a success event");
is(event.target, request, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
let request = mozIndexedDB.open(name, 10);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.type, "success", "Expect a success event");
is(event.target, request, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
let db2 = event.target.result;
is(db2.objectStoreNames.length, 1, "Expect an objectStore here");
var onversionchangecalled = false;
function closeDBs(event) {
onversionchangecalled = true;
ok(event instanceof IDBVersionChangeEvent, "expect a versionchange event");
is(event.oldVersion, 10, "oldVersion should be 10");
todo(event.newVersion, null, "newVersion should be null");
db.close();
db2.close();
db.onversionchange = errorHandler;
db2.onversionchange = errorHandler;
};
// The IDB spec doesn't guarantee the order that onversionchange will fire
// on the dbs.
db.onversionchange = closeDBs;
db2.onversionchange = closeDBs;
let request = mozIndexedDB.deleteDatabase(name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
event = yield;
ok(onversionchangecalled, "Expected versionchange events");
is(event.type, "success", "expect a success event");
is(event.target, request, "event has right target");
is(event.target.result, null, "event should have no result");
let request = mozIndexedDB.open(name, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.version, 1, "DB has proper version");
is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores");
let request = mozIndexedDB.deleteDatabase("thisDatabaseHadBetterNotExist");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
ok(true, "deleteDatabase on a non-existent database succeeded");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -0,0 +1,79 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database DeleteDatabase Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
const VERSION_CHANGE = Components.interfaces.nsIIDBTransaction.VERSION_CHANGE;
const name = window.location.pathname;
let request = mozIndexedDB.open(name, 10);
request.onerror = errorHandler;
request.onsuccess = unexpectedSuccessHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let event = yield;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
let db = event.target.result;
db.createObjectStore("stuff");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.type, "success", "Expect a success event");
is(event.target, request, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
db.close();
let request = mozIndexedDB.deleteDatabase(name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let openRequest = mozIndexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
event = yield;
is(event.type, "success", "expect a success event");
is(event.target, request, "event has right target");
is(event.target.result, null, "event should have no result");
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.version, 1, "DB has proper version");
is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

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

@ -13,15 +13,15 @@
function testSteps()
{
const objectStoreData = [
{ name: "3", options: { keyPath: "id", autoIncrement: true } },
{ name: "1", options: { keyPath: "ss" } },
{ name: "2", options: { } },
{ name: "", options: { keyPath: "id", autoIncrement: true } },
{ name: null, options: { keyPath: "ss" } },
{ name: undefined, options: { } },
{ name: "4", options: { autoIncrement: true } },
];
const indexData = [
{ name: "name", keyPath: "name", options: { unique: true } },
{ name: "height", keyPath: "height", options: { } }
{ name: "", keyPath: "name", options: { unique: true } },
{ name: null, keyPath: "height", options: { } }
];
const data = [
@ -87,7 +87,8 @@
return;
}
is(cursor.key, data[keyIndex][indexName], "Good key");
is(cursor.key, data[keyIndex][indexData[indexIndex].keyPath],
"Good key");
is(cursor.value.ss, data[keyIndex].ss, "Correct ss");
is(cursor.value.name, data[keyIndex].name, "Correct name");
is(cursor.value.height, data[keyIndex].height, "Correct height");

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

@ -76,7 +76,7 @@
let db = event.target.result;
let objectStore = db.createObjectStore(objectStoreName, { keyPath: "" });
let objectStore = db.createObjectStore(objectStoreName, { keyPath: null });
// First, add all our data to the object store.
let addedData = 0;
@ -892,6 +892,31 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 21.5");
keyIndex = 5;
request = objectStore.index("height").openKeyCursor(null, PREV);
request.onerror = errorHandler;
request.onsuccess = function (event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height,
"Correct key");
is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key,
"Correct value");
cursor.continue();
keyIndex--;
}
else {
testGenerator.next();
}
}
yield;
is(keyIndex, -1, "Saw all the expected keys");
ok(true, "Test group 22");
keyIndex = 5;
@ -994,6 +1019,41 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 24.5");
keyIndex = 5;
request = objectStore.index("height").openCursor(null, PREV);
request.onerror = errorHandler;
request.onsuccess = function (event) {
let cursor = event.target.result;
if (cursor) {
is(cursor.key, objectStoreDataHeightSort[keyIndex].value.height,
"Correct key");
is(cursor.primaryKey, objectStoreDataHeightSort[keyIndex].key,
"Correct primary key");
is(cursor.value.name, objectStoreDataHeightSort[keyIndex].value.name,
"Correct name");
is(cursor.value.height,
objectStoreDataHeightSort[keyIndex].value.height,
"Correct height");
if ("weight" in cursor.value) {
is(cursor.value.weight,
objectStoreDataHeightSort[keyIndex].value.weight,
"Correct weight");
}
cursor.continue();
keyIndex--;
}
else {
testGenerator.next();
}
}
yield;
is(keyIndex, -1, "Saw all the expected keys");
ok(true, "Test group 25");
keyIndex = 5;

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

@ -40,7 +40,7 @@ function testSteps()
{ name: "out of line key; no key generator",
autoIncrement: false,
storedObject: {name: "Lincoln"},
keyName: "",
keyName: null,
keyValue: 1,
}
];

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

@ -74,7 +74,7 @@
}, 0);
yield;
is(key, true, "Got the right key");
ok(key === undefined, "Got the right value");
finishTest();
yield;

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

@ -12,45 +12,38 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
const description = "My Test Database";
const names = [
"",
null,
undefined,
window.location.pathname
];
let request;
const version = 1;
try {
request = mozIndexedDB.open("", 1);
ok(false, "Open with empty name should have thrown!");
for each (let name in names) {
let request = mozIndexedDB.open(name, version);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
if (name === null) {
name = "null";
}
else if (name === undefined) {
name = "undefined";
}
let db = event.target.result;
is(db.name, name, "Bad name");
is(db.version, version, "Bad version");
is(db.objectStoreNames.length, 0, "Bad objectStores list");
is(db.name, request.result.name, "Bad name");
is(db.version, request.result.version, "Bad version");
is(db.objectStoreNames.length, request.result.objectStoreNames.length,
"Bad objectStores list");
}
catch(e) {
is(e instanceof IDBDatabaseException, true, "Got IDBDatabaseException");
is(e.code, IDBDatabaseException.NON_TRANSIENT_ERR, "Good error code");
is(request, undefined, "Shouldn't be set to anything");
}
try {
request = mozIndexedDB.open(null, 1);
ok(false, "Open with null name should have thrown!");
}
catch(e) {
is(e instanceof IDBDatabaseException, true, "Got IDBDatabaseException");
is(e.code, IDBDatabaseException.NON_TRANSIENT_ERR, "Good error code");
is(request, undefined, "Shouldn't be set to anything");
}
request = mozIndexedDB.open(name, 1, description);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
is(db.name, name, "Bad name");
is(db.version, 1, "Bad version");
is(db.objectStoreNames.length, 0, "Bad objectStores list");
is(db.name, request.result.name, "Bad name");
is(db.version, request.result.version, "Bad version");
is(db.objectStoreNames.length, request.result.objectStoreNames.length,
"Bad objectStores list");
finishTest();
yield;

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

@ -194,7 +194,7 @@
objectStore.delete(data[2].ssn).onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, true, "Correct result");
ok(event.target.result === undefined, "Correct result");
objectStore.count().onsuccess = grabEventAndContinueHandler;
event = yield;
@ -206,7 +206,7 @@
objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, true, "Correct result");
ok(event.target.result === undefined, "Correct result");
objectStore.count().onsuccess = grabEventAndContinueHandler;
event = yield;
@ -218,7 +218,7 @@
objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, true, "Correct result");
ok(event.target.result === undefined, "Correct result");
objectStore.count().onsuccess = grabEventAndContinueHandler;
event = yield;

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

@ -142,9 +142,9 @@
event = yield;
ok(event.target.result instanceof IDBDatabase, "Expect a database here");
is(event.target.result.version, 4, "Right version");
todo_is(db3.version, 3, "After closing the version should not change!");
todo_is(db2.version, 2, "After closing the version should not change!");
todo_is(db1.version, 1, "After closing the version should not change!");
is(db3.version, 3, "After closing the version should not change!");
is(db2.version, 2, "After closing the version should not change!");
is(db1.version, 1, "After closing the version should not change!");
is(versionChangeEventCount, 3, "Saw all expected events");

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

@ -36,9 +36,11 @@
let transaction;
let objectStore;
let index;
transaction = event.target.transaction;
objectStore = db.createObjectStore("foo", { autoIncrement: true });
index = objectStore.createIndex("fooindex", "indexKey", { unique: true });
is(transaction.db, db, "Correct database");
is(transaction.readyState, LOADING, "Correct readyState");
@ -52,7 +54,10 @@
is(objectStore.name, "foo", "Correct name");
is(objectStore.keyPath, "", "Correct keyPath");
is(objectStore.indexNames.length, 0, "Correct indexNames");
is(objectStore.indexNames.length, 1, "Correct indexNames length");
is(objectStore.indexNames[0], "fooindex", "Correct indexNames name");
is(objectStore.index("fooindex"), index, "Can get index");
// Wait until it's complete!
transaction.oncomplete = grabEventAndContinueHandler;
@ -76,7 +81,9 @@
is(objectStore.name, "foo", "Correct name");
is(objectStore.keyPath, "", "Correct keyPath");
is(objectStore.indexNames.length, 0, "Correct indexNames");
is(objectStore.indexNames.length, 1, "Correct indexNames length");
is(objectStore.indexNames[0], "fooindex", "Correct indexNames name");
try {
objectStore.add({});
@ -364,6 +371,35 @@
is(abortEventCount, expectedAbortEventCount,
"All abort errors fired");
// Abort both failing and succeeding requests
transaction = db.transaction("foo", READ_WRITE);
transaction.onabort = transaction.oncomplete = grabEventAndContinueHandler;
transaction.objectStore("foo").add({indexKey: "key"}).onsuccess = function(event) {
transaction.abort();
};
let request1 = transaction.objectStore("foo").add({indexKey: "key"});
request1.onsuccess = grabEventAndContinueHandler;
request1.onerror = grabEventAndContinueHandler;
let request2 = transaction.objectStore("foo").get(1);
request2.onsuccess = grabEventAndContinueHandler;
request2.onerror = grabEventAndContinueHandler;
event = yield;
is(event.type, "error", "abort() should make all requests fail");
is(event.target, request1, "abort() should make all requests fail");
is(event.target.errorCode, IDBDatabaseException.ABORT_ERR, "abort() should make all requests fail");
event.preventDefault();
event = yield;
is(event.type, "error", "abort() should make all requests fail");
is(event.target, request2, "abort() should make all requests fail");
is(event.target.errorCode, IDBDatabaseException.ABORT_ERR, "abort() should make all requests fail");
event.preventDefault();
event = yield;
is(event.type, "abort", "transaction should fail");
is(event.target, transaction, "transaction should fail");
finishTest();
yield;
}

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

@ -538,6 +538,13 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
AssertIsOnMainThread();
JSObject* clone =
MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
aClosure);
if (clone) {
return clone;
}
clone =
ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
aClosure);
if (clone) {
@ -554,14 +561,15 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
{
AssertIsOnMainThread();
JSBool ok =
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
if (ok) {
return ok;
if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
aClosure) ||
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
aClosure) ||
NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull)) {
return true;
}
JS_ClearPendingException(aCx);
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
return false;
}
static void

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

@ -42,6 +42,7 @@
#include "jscntxt.h"
#include "jsfriendapi.h"
#include "Exceptions.h"
#include "WorkerPrivate.h"
#include "XMLHttpRequestPrivate.h"
@ -275,10 +276,12 @@ class XMLHttpRequest
SLOT_status,
SLOT_statusText,
SLOT_readyState,
SLOT_response,
SLOT_multipart,
SLOT_mozBackgroundRequest,
SLOT_withCredentials,
SLOT_upload,
SLOT_responseType,
SLOT_COUNT
};
@ -342,6 +345,7 @@ public:
HANDLE_STATE_VALUE(mStatus, SLOT_status)
HANDLE_STATE_VALUE(mStatusText, SLOT_statusText)
HANDLE_STATE_VALUE(mReadyState, SLOT_readyState)
HANDLE_STATE_VALUE(mResponse, SLOT_response)
#undef HANDLE_STATE_VALUE
@ -395,6 +399,11 @@ private:
return false;
}
JSString* textStr = JS_NewStringCopyN(aCx, "text", 4);
if (!textStr) {
return false;
}
jsval emptyString = JS_GetEmptyStringValue(aCx);
jsval zero = INT_TO_JSVAL(0);
@ -407,7 +416,9 @@ private:
!JS_SetReservedSlot(aCx, obj, SLOT_multipart, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_mozBackgroundRequest, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_withCredentials, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_upload, JSVAL_NULL)) {
!JS_SetReservedSlot(aCx, obj, SLOT_upload, JSVAL_NULL) ||
!JS_SetReservedSlot(aCx, obj, SLOT_responseType,
STRING_TO_JSVAL(textStr))) {
return false;
}
@ -462,7 +473,7 @@ private:
if (JSVAL_IS_VOID(rval)) {
// Throw an exception.
JS_ReportError(aCx, "Unable to retrieve %s property", name);
exceptions::ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
return false;
}
@ -535,8 +546,13 @@ private:
return false; \
} \
\
jsval oldVal; \
if (!JS_GetReservedSlot(aCx, aObj, slot, &oldVal)) { \
return false; \
} \
\
jsval rval = *aVp; \
if (!priv->Set##_name (aCx, &rval) || \
if (!priv->Set##_name (aCx, oldVal, &rval) || \
!JS_SetReservedSlot(aCx, aObj, slot, rval)) { \
return false; \
} \
@ -548,6 +564,7 @@ private:
IMPL_SETTER(Multipart)
IMPL_SETTER(MozBackgroundRequest)
IMPL_SETTER(WithCredentials)
IMPL_SETTER(ResponseType)
#undef IMPL_SETTER
@ -783,13 +800,17 @@ JSPropertySpec XMLHttpRequest::sProperties[] = {
GENERIC_READONLY_PROPERTY(status)
GENERIC_READONLY_PROPERTY(statusText)
GENERIC_READONLY_PROPERTY(readyState)
GENERIC_READONLY_PROPERTY(response)
{ "multipart", 7, PROPERTY_FLAGS, GetProperty, SetMultipart },
{ "mozBackgroundRequest", 8, PROPERTY_FLAGS, GetProperty,
SetMozBackgroundRequest },
{ "withCredentials", 9, PROPERTY_FLAGS, GetProperty, SetWithCredentials },
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
{ "multipart", SLOT_multipart, PROPERTY_FLAGS, GetProperty, SetMultipart },
{ "mozBackgroundRequest", SLOT_mozBackgroundRequest, PROPERTY_FLAGS,
GetProperty, SetMozBackgroundRequest },
{ "withCredentials", SLOT_withCredentials, PROPERTY_FLAGS, GetProperty,
SetWithCredentials },
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
js_GetterOnlyPropertyStub },
{ "responseType", SLOT_responseType, PROPERTY_FLAGS, GetProperty,
SetResponseType },
{ sEventStrings[STRING_onreadystatechange], STRING_onreadystatechange,
PROPERTY_FLAGS, GetEventListener, SetEventListener },
{ sEventStrings[STRING_onabort], STRING_onabort, PROPERTY_FLAGS,

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

@ -56,10 +56,12 @@ struct StateData
jsval mStatus;
jsval mStatusText;
jsval mReadyState;
jsval mResponse;
bool mResponseTextException;
bool mStatusException;
bool mStatusTextException;
bool mReadyStateException;
bool mResponseException;
};
bool

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

@ -41,7 +41,6 @@
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMProgressEvent.h"
#include "nsIJSContextStack.h"
#include "nsIRunnable.h"
#include "nsIXMLHttpRequest.h"
#include "nsIXPConnect.h"
@ -88,7 +87,6 @@ public:
bool mSeenUploadLoadStart;
// Only touched on the main thread.
nsString mPreviousResponseText;
nsCString mPreviousStatusText;
PRUint32 mSyncQueueKey;
PRUint32 mSyncEventResponseSyncQueueKey;
@ -158,7 +156,6 @@ public:
{
AssertIsOnMainThread();
mPreviousResponseText.Truncate();
mPreviousStatusText.Truncate();
if (mUploadEventListenersAttached) {
@ -439,7 +436,10 @@ NS_IMPL_ISUPPORTS2(LoadStartDetectionRunnable, nsIRunnable, nsIDOMEventListener)
class EventRunnable : public MainThreadProxyRunnable
{
nsString mType;
nsString mResponseText;
nsString mResponseType;
JSAutoStructuredCloneBuffer mResponseBuffer;
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
jsval mResponse;
nsCString mStatusText;
PRUint64 mLoaded;
PRUint64 mTotal;
@ -453,25 +453,28 @@ class EventRunnable : public MainThreadProxyRunnable
bool mStatusException;
bool mStatusTextException;
bool mReadyStateException;
bool mResponseException;
public:
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
bool aLengthComputable, PRUint64 aLoaded, PRUint64 aTotal)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
mLoaded(aLoaded), mTotal(aTotal), mChannelId(aProxy->mInnerChannelId),
mStatus(0), mReadyState(0), mUploadEvent(aUploadEvent),
mProgressEvent(true), mLengthComputable(aLengthComputable),
mResponseTextException(false), mStatusException(false),
mStatusTextException(false), mReadyStateException(false)
mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
mUploadEvent(aUploadEvent), mProgressEvent(true),
mLengthComputable(aLengthComputable), mResponseTextException(false),
mStatusException(false), mStatusTextException(false),
mReadyStateException(false), mResponseException(false)
{ }
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
mLoaded(0), mTotal(0), mChannelId(aProxy->mInnerChannelId), mStatus(0),
mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(false),
mLengthComputable(0), mResponseTextException(false),
mStatusException(false), mStatusTextException(false),
mReadyStateException(false)
mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
mResponseTextException(false), mStatusException(false),
mStatusTextException(false), mReadyStateException(false),
mResponseException(false)
{ }
bool
@ -480,19 +483,41 @@ public:
nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
NS_ASSERTION(xhr, "Must have an XHR here!");
if (NS_SUCCEEDED(xhr->GetResponseText(mResponseText))) {
if (mResponseText == mProxy->mPreviousResponseText) {
mResponseText.SetIsVoid(true);
if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
NS_ERROR("This should never fail!");
}
jsval response;
if (NS_SUCCEEDED(xhr->GetResponse(aCx, &response))) {
if (JSVAL_IS_UNIVERSAL(response)) {
mResponse = response;
}
else {
mProxy->mPreviousResponseText = mResponseText;
// Anything subject to GC must be cloned.
JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(true) :
WorkerStructuredCloneCallbacks(true);
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
if (mResponseBuffer.write(aCx, response, callbacks, &clonedObjects)) {
mClonedObjects.SwapElements(clonedObjects);
}
else {
NS_ASSERTION(JS_IsExceptionPending(aCx),
"This should really never fail unless OOM!");
mResponseException = true;
}
}
mResponseTextException = false;
}
else {
mResponseTextException = true;
mResponseException = true;
}
nsString responseText;
mResponseTextException = NS_FAILED(xhr->GetResponseText(responseText));
mStatusException = NS_FAILED(xhr->GetStatus(&mStatus));
if (NS_SUCCEEDED(xhr->GetStatusText(mStatusText))) {
@ -566,21 +591,44 @@ public:
xhr::StateData state;
state.mResponseTextException = mResponseTextException;
if (mResponseTextException || mResponseText.IsVoid()) {
state.mResponseText = JSVAL_VOID;
state.mResponseException = mResponseException;
if (!mResponseException) {
if (mResponseBuffer.data()) {
NS_ASSERTION(JSVAL_IS_VOID(mResponse), "Huh?!");
JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(false) :
WorkerStructuredCloneCallbacks(false);
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
clonedObjects.SwapElements(mClonedObjects);
jsval response;
if (!mResponseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
return false;
}
mResponseBuffer.clear();
state.mResponse = response;
}
else {
state.mResponse = mResponse;
}
}
else if (mResponseText.IsEmpty()) {
state.mResponseText = JS_GetEmptyStringValue(aCx);
// This logic is all based on the assumption that mResponseTextException
// should be set if the responseType isn't "text". Otherwise we're going to
// hand out the wrong result if someone gets the responseText property.
state.mResponseTextException = mResponseTextException;
if (!mResponseTextException) {
NS_ASSERTION(JSVAL_IS_STRING(state.mResponse) ||
JSVAL_IS_NULL(state.mResponse),
"Bad response!");
state.mResponseText = state.mResponse;
}
else {
JSString* responseText = JS_NewUCStringCopyN(aCx, mResponseText.get(),
mResponseText.Length());
if (!responseText) {
return false;
}
mResponseText.Truncate();
state.mResponseText = STRING_TO_JSVAL(responseText);
state.mResponseText = JSVAL_VOID;
}
state.mStatusException = mStatusException;
@ -631,6 +679,19 @@ public:
JS_ReportPendingException(aCx);
}
// After firing the event set mResponse to JSVAL_NULL for chunked response
// types.
if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
xhr::StateData newState = {
JSVAL_NULL, JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, JSVAL_NULL,
false, false, false, false, false
};
if (!xhr::UpdateXHRState(aCx, target, mUploadEvent, newState)) {
return false;
}
}
return true;
}
};
@ -783,6 +844,34 @@ public:
}
};
class SetResponseTypeRunnable : public WorkerThreadProxySyncRunnable
{
nsString mResponseType;
public:
SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
const nsAString& aResponseType)
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
mResponseType(aResponseType)
{ }
intN
MainThreadRun()
{
nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
mResponseType.Truncate();
if (NS_SUCCEEDED(rv)) {
rv = mProxy->mXHR->GetResponseType(mResponseType);
}
return GetDOMExceptionCodeFromResult(rv);
}
void
GetResponseType(nsAString& aResponseType) {
aResponseType.Assign(mResponseType);
}
};
class AbortRunnable : public WorkerThreadProxySyncRunnable
{
public:
@ -911,10 +1000,12 @@ public:
}
mProxy->mInnerChannelId++;
mProxy->mPreviousResponseText.Truncate();
mProxy->mPreviousStatusText.Truncate();
rv = mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, 1);
if (NS_SUCCEEDED(rv)) {
rv = mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
}
return GetDOMExceptionCodeFromResult(rv);
}
};
@ -1175,7 +1266,10 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
runnable = new EventRunnable(this, !!uploadTarget, type);
}
runnable->Dispatch(nsnull);
{
RuntimeService::AutoSafeJSContext cx;
runnable->Dispatch(cx);
}
if (!uploadTarget) {
if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
@ -1276,7 +1370,7 @@ XMLHttpRequestPrivate::Notify(JSContext* aCx, Status aStatus)
}
bool
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1306,7 +1400,8 @@ XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
}
bool
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1336,7 +1431,8 @@ XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
}
bool
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1365,6 +1461,68 @@ XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
return true;
}
bool
XMLHttpRequestPrivate::SetResponseType(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
if (mCanceled) {
return false;
}
if (!mProxy || SendInProgress()) {
ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
return false;
}
JSString* jsstr = JS_ValueToString(aCx, *aVp);
if (!jsstr) {
return false;
}
nsDependentJSString responseType;
if (!responseType.init(aCx, jsstr)) {
return false;
}
// "document" is fine for the main thread but not for a worker. Short-circuit
// that here.
if (responseType.EqualsLiteral("document")) {
*aVp = aOldVal;
return true;
}
nsRefPtr<SetResponseTypeRunnable> runnable =
new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
if (!runnable->Dispatch(aCx)) {
return false;
}
nsString acceptedResponseType;
runnable->GetResponseType(acceptedResponseType);
if (acceptedResponseType == responseType) {
// Leave *aVp unchanged.
}
else if (acceptedResponseType.IsEmpty()) {
// Empty string.
*aVp = JS_GetEmptyStringValue(aCx);
}
else {
// Some other string.
jsstr = JS_NewUCStringCopyN(aCx, acceptedResponseType.get(),
acceptedResponseType.Length());
if (!jsstr) {
return false;
}
*aVp = STRING_TO_JSVAL(jsstr);
}
return true;
}
bool
XMLHttpRequestPrivate::Abort(JSContext* aCx)
{
@ -1680,8 +1838,8 @@ XMLHttpRequestPrivate::MaybeDispatchPrematureAbortEvents(JSContext* aCx,
NS_ASSERTION(mProxy, "Must have a proxy here!");
xhr::StateData state = {
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4),
false, false, false, false
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4), JSVAL_VOID,
false, false, false, false, false
};
if (mProxy->mSeenUploadLoadStart) {

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

@ -111,13 +111,16 @@ public:
Notify(JSContext* aCx, Status aStatus);
bool
SetMultipart(JSContext* aCx, jsval *aVp);
SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetMozBackgroundRequest(JSContext* aCx, jsval *aVp);
SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetWithCredentials(JSContext* aCx, jsval *aVp);
SetWithCredentials(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetResponseType(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
Abort(JSContext* aCx);

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

@ -104,6 +104,8 @@ _TEST_FILES = \
throwingOnerror_worker.js \
test_xhr.html \
xhr_worker.js \
test_xhr2.html \
xhr2_worker.js \
test_xhrAbort.html \
xhrAbort_worker.js \
testXHR.txt \

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

@ -0,0 +1,38 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads XHR(Bug 450452 )
-->
<head>
<title>Test for DOM Worker Threads XHR (Bug 450452 )</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads XHR (Bug 450452)</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("xhr2_worker.js");
worker.onmessage = function(event) {
is(event.data, "done", "Got correct result");
SimpleTest.finish();
}
worker.postMessage("testXHR.txt");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,155 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
onmessage = function(event) {
const url = event.data;
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.send();
const refText = xhr.responseText;
function getResponse(type) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
if (type !== undefined) {
xhr.responseType = type;
}
xhr.send();
return xhr.response;
}
if (getResponse() != refText) {
throw new Error("unset responseType failed");
}
if (getResponse("") != refText) {
throw new Error("'' responseType failed");
}
if (getResponse("text") != refText) {
throw new Error("'text' responseType failed");
}
var array = new Uint8Array(getResponse("arraybuffer"));
if (String.fromCharCode.apply(String, array) != refText) {
throw new Error("'arraybuffer' responseType failed");
}
var blob = getResponse("blob");
if (new FileReaderSync().readAsText(blob) != refText) {
throw new Error("'blob' responseType failed");
}
// Make sure that we get invalid state exceptions when getting the wrong
// property.
function testResponseTextException(type) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.responseType = type;
xhr.send();
var exception;
try {
xhr.responseText;
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when getting responseText on '" + type +
"' type");
}
}
testResponseTextException("arraybuffer");
testResponseTextException("blob");
// Make sure "document" works, but returns text.
xhr = new XMLHttpRequest();
if (xhr.responseType != "text") {
throw new Error("Default value for responseType is wrong!");
}
xhr.open("GET", url, false);
xhr.responseType = "document";
xhr.send();
if (xhr.responseText != refText) {
throw new Error("'document' type not working correctly");
}
// Make sure setting responseType before open or after send fails.
var exception;
xhr = new XMLHttpRequest();
try {
xhr.responseType = "arraybuffer";
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when setting responseType before " +
"calling open()");
}
xhr.open("GET", url);
xhr.responseType = "text";
xhr.onload = function(event) {
if (event.target.response != refText) {
throw new Error("Bad response!");
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "moz-chunked-text";
var lastIndex = 0;
xhr.onprogress = function(event) {
if (refText.substr(lastIndex, xhr.response.length) != xhr.response) {
throw new Error("Bad chunk!");
}
lastIndex += xhr.response.length;
};
xhr.onload = function(event) {
if (lastIndex != refText.length) {
throw new Error("Didn't see all the data!");
}
setTimeout(function() {
if (xhr.response !== null) {
throw new Error("Should have gotten null response outside of event!");
}
postMessage("done");
}, 0);
}
xhr.send(null);
};
xhr.send();
exception = null;
try {
xhr.responseType = "arraybuffer";
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when setting responseType after " +
"calling send()");
}
}

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

@ -1647,6 +1647,16 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY;
# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT)
#endif
/*
* Returns true iff the given jsval is immune to GC and can be used across
* multiple JSRuntimes without requiring any conversion API.
*/
static JS_ALWAYS_INLINE JSBool
JSVAL_IS_UNIVERSAL(jsval v)
{
return !JSVAL_IS_GCTHING(v);
}
/************************************************************************/
/* Lock and unlock the GC thing held by a jsval. */

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

@ -457,6 +457,8 @@ struct JSRuntime
size_t gcMaxBytes;
size_t gcMaxMallocBytes;
uint32 gcEmptyArenaPoolLifespan;
/* We access this without the GC lock, however a race will not affect correctness */
volatile uint32 gcNumFreeArenas;
uint32 gcNumber;
js::GCMarker *gcMarkingTracer;
bool gcChunkAllocationSinceLastGC;

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

@ -547,7 +547,7 @@ Chunk::allocate(JSRuntime *rt)
Chunk *chunk = static_cast<Chunk *>(AllocChunk());
if (!chunk)
return NULL;
chunk->init();
chunk->init(rt);
rt->gcStats.count(gcstats::STAT_NEW_CHUNK);
return chunk;
}
@ -561,7 +561,7 @@ Chunk::release(JSRuntime *rt, Chunk *chunk)
}
void
Chunk::init()
Chunk::init(JSRuntime *rt)
{
JS_POISON(this, JS_FREE_PATTERN, ChunkSize);
@ -580,6 +580,7 @@ Chunk::init()
info.numArenasFree = ArenasPerChunk;
info.numArenasFreeCommitted = ArenasPerChunk;
info.age = 0;
rt->gcNumFreeArenas += ArenasPerChunk;
/* Initialize the arena header state. */
for (jsuint i = 0; i < ArenasPerChunk; i++) {
@ -668,7 +669,7 @@ Chunk::fetchNextDecommittedArena()
}
inline ArenaHeader *
Chunk::fetchNextFreeArena()
Chunk::fetchNextFreeArena(JSRuntime *rt)
{
JS_ASSERT(info.numArenasFreeCommitted > 0);
@ -676,6 +677,7 @@ Chunk::fetchNextFreeArena()
info.freeArenasHead = aheader->next;
--info.numArenasFreeCommitted;
--info.numArenasFree;
--rt->gcNumFreeArenas;
return aheader;
}
@ -685,14 +687,15 @@ Chunk::allocateArena(JSCompartment *comp, AllocKind thingKind)
{
JS_ASSERT(!noAvailableArenas());
JSRuntime *rt = comp->rt;
ArenaHeader *aheader = JS_LIKELY(info.numArenasFreeCommitted > 0)
? fetchNextFreeArena()
? fetchNextFreeArena(rt)
: fetchNextDecommittedArena();
aheader->init(comp, thingKind);
if (JS_UNLIKELY(noAvailableArenas()))
removeFromAvailableList();
JSRuntime *rt = comp->rt;
Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize);
JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
JS_ATOMIC_ADD(&comp->gcBytes, ArenaSize);
@ -732,6 +735,7 @@ Chunk::releaseArena(ArenaHeader *aheader)
info.freeArenasHead = aheader;
++info.numArenasFreeCommitted;
++info.numArenasFree;
++rt->gcNumFreeArenas;
if (info.numArenasFree == 1) {
JS_ASSERT(!info.prevp);
@ -2137,7 +2141,7 @@ MaybeGC(JSContext *cx)
*/
int64 now = PRMJ_Now();
if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
if (rt->gcChunkAllocationSinceLastGC)
if (rt->gcChunkAllocationSinceLastGC || rt->gcNumFreeArenas > MaxFreeCommittedArenas)
js_GC(cx, NULL, GC_SHRINK, gcstats::MAYBEGC);
else
rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
@ -2404,6 +2408,7 @@ DecommitFreePages(JSContext *cx)
size_t arenaOffset = Chunk::arenaIndex(reinterpret_cast<uintptr_t>(aheader));
chunk->decommittedArenas.set(arenaOffset);
--chunk->info.numArenasFreeCommitted;
--rt->gcNumFreeArenas;
aheader = next;
}

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

@ -94,6 +94,12 @@ const size_t ArenaShift = 12;
const size_t ArenaSize = size_t(1) << ArenaShift;
const size_t ArenaMask = ArenaSize - 1;
/*
* This is the maximum number of arenas we allow in the FreeCommitted state
* before we trigger a GC_SHRINK to release free arenas to the OS.
*/
const static uint32 MaxFreeCommittedArenas = (32 << 20) / ArenaSize;
/*
* The mark bitmap has one bit per each GC cell. For multi-cell GC things this
* wastes space but allows to avoid expensive devisions by thing's size when
@ -742,14 +748,14 @@ struct Chunk {
static inline void release(JSRuntime *rt, Chunk *chunk);
private:
inline void init();
inline void init(JSRuntime *rt);
/* Search for a decommitted arena to allocate. */
jsuint findDecommittedArenaOffset();
ArenaHeader* fetchNextDecommittedArena();
/* Unlink and return the freeArenasHead. */
inline ArenaHeader* fetchNextFreeArena();
inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt);
};
JS_STATIC_ASSERT(sizeof(Chunk) == ChunkSize);