зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central and mozilla-inbound
This commit is contained in:
Коммит
5fdd01ccd9
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче