mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 663479: Move all IDB structured clone operations to the main thread. r=bent
This commit is contained in:
parent
9927e93ee8
commit
c87ac2f24d
@ -3309,7 +3309,7 @@ esac
|
||||
|
||||
_SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -D_GNU_SOURCE"
|
||||
AC_CHECK_FUNCS(dladdr)
|
||||
AC_CHECK_FUNCS(dladdr memmem)
|
||||
CFLAGS="$_SAVE_CFLAGS"
|
||||
|
||||
if test ! "$GNU_CXX"; then
|
||||
|
@ -63,6 +63,9 @@ USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
// This is just to give us some random marker in the byte stream
|
||||
static const PRUint64 kTotallyRandomNumber = LL_INIT(0x286F258B, 0x177D47A9);
|
||||
|
||||
class AddHelper : public AsyncConnectionHelper
|
||||
{
|
||||
public:
|
||||
@ -96,7 +99,6 @@ public:
|
||||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||
}
|
||||
|
||||
nsresult ModifyValueForNewKey();
|
||||
nsresult UpdateIndexes(mozIStorageConnection* aConnection,
|
||||
PRInt64 aObjectDataId);
|
||||
|
||||
@ -840,9 +842,8 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
jsval* aValue)
|
||||
{
|
||||
/*
|
||||
* This function can be called on multiple threads! Be careful!
|
||||
*/
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"Should only be deserializing on the main thread!");
|
||||
NS_ASSERTION(aCx, "A JSContext is required!");
|
||||
|
||||
if (!aBuffer.data()) {
|
||||
@ -861,9 +862,8 @@ IDBObjectStore::SerializeValue(JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
jsval aValue)
|
||||
{
|
||||
/*
|
||||
* This function can be called on multiple threads! Be careful!
|
||||
*/
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"Should only be serializing on the main thread!");
|
||||
NS_ASSERTION(aCx, "A JSContext is required!");
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
@ -871,9 +871,62 @@ IDBObjectStore::SerializeValue(JSContext* aCx,
|
||||
return aBuffer.write(aCx, aValue, nsnull);
|
||||
}
|
||||
|
||||
static inline jsdouble
|
||||
SwapBytes(PRUint64 u)
|
||||
{
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
return ((u & 0x00000000000000ffLLU) << 56) |
|
||||
((u & 0x000000000000ff00LLU) << 40) |
|
||||
((u & 0x0000000000ff0000LLU) << 24) |
|
||||
((u & 0x00000000ff000000LLU) << 8) |
|
||||
((u & 0x000000ff00000000LLU) >> 8) |
|
||||
((u & 0x0000ff0000000000LLU) >> 24) |
|
||||
((u & 0x00ff000000000000LLU) >> 40) |
|
||||
((u & 0xff00000000000000LLU) >> 56);
|
||||
#else
|
||||
return u;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
|
||||
Key& aKey)
|
||||
{
|
||||
NS_ASSERTION(IsAutoIncrement() && KeyPath().IsEmpty() && aKey.IsInt(),
|
||||
"Don't call me!");
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread");
|
||||
NS_ASSERTION(mKeyPathSerializationOffset, "How did this happen?");
|
||||
|
||||
// The minus 8 dangling off the end here is to account for the null entry
|
||||
// that terminates the buffer
|
||||
const PRUint32 keyPropLen = mKeyPathSerialization.nbytes() -
|
||||
mKeyPathSerializationOffset - sizeof(PRUint64);
|
||||
|
||||
const char* location = nsCRT::memmem((char*)aBuffer.data(),
|
||||
aBuffer.nbytes(),
|
||||
(char*)mKeyPathSerialization.data() +
|
||||
mKeyPathSerializationOffset,
|
||||
keyPropLen);
|
||||
NS_ASSERTION(location, "How did this happen?");
|
||||
|
||||
// This is a duplicate of the js engine's byte munging here
|
||||
union {
|
||||
jsdouble d;
|
||||
PRUint64 u;
|
||||
} pun;
|
||||
|
||||
pun.d = SwapBytes(aKey.IntValue());
|
||||
|
||||
memcpy(const_cast<char*>(location) + keyPropLen -
|
||||
sizeof(pun.u), // We're overwriting the last 8 bytes
|
||||
&pun.u, sizeof(PRUint64));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IDBObjectStore::IDBObjectStore()
|
||||
: mId(LL_MININT),
|
||||
mAutoIncrement(PR_FALSE)
|
||||
mAutoIncrement(PR_FALSE),
|
||||
mKeyPathSerializationOffset(0)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
@ -932,11 +985,47 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
|
||||
rv = GetIndexUpdateInfo(info, aCx, aValue, aUpdateInfoArray);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
if (!IDBObjectStore::SerializeValue(aCx, aCloneBuffer, aValue)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
const jschar* keyPathChars =
|
||||
reinterpret_cast<const jschar*>(mKeyPath.get());
|
||||
const size_t keyPathLen = mKeyPath.Length();
|
||||
JSBool ok = JS_FALSE;
|
||||
|
||||
if (!mKeyPath.IsEmpty() && aKey.IsUnset()) {
|
||||
NS_ASSERTION(mAutoIncrement, "Should have bailed earlier!");
|
||||
|
||||
jsval key;
|
||||
ok = JS_NewNumberValue(aCx, kTotallyRandomNumber, &key);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
ok = JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(aValue), keyPathChars,
|
||||
keyPathLen, key, nsnull, nsnull,
|
||||
JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
// From this point on we have to try to remove the property.
|
||||
rv = EnsureKeyPathSerializationData(aCx);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
// We guard on rv being a success because we need to run the property
|
||||
// deletion code below even if we should not be serializing the value
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
!IDBObjectStore::SerializeValue(aCx, aCloneBuffer, aValue)) {
|
||||
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
// If this fails, we lose, and the web page sees a magical property
|
||||
// appear on the object :-(
|
||||
jsval succeeded;
|
||||
ok = JS_DeleteUCProperty2(aCx, JSVAL_TO_OBJECT(aValue), keyPathChars,
|
||||
keyPathLen, &succeeded);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
NS_ASSERTION(JSVAL_IS_BOOLEAN(succeeded), "Wtf?");
|
||||
NS_ENSURE_TRUE(JSVAL_TO_BOOLEAN(succeeded),
|
||||
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -987,6 +1076,46 @@ IDBObjectStore::AddOrPut(const jsval& aValue,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
IDBObjectStore::EnsureKeyPathSerializationData(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
|
||||
|
||||
if (!mKeyPathSerializationOffset) {
|
||||
JSBool ok;
|
||||
|
||||
JSAutoStructuredCloneBuffer emptyObjectBuffer;
|
||||
JSAutoStructuredCloneBuffer fakeObjectBuffer;
|
||||
|
||||
const jschar* keyPathChars =
|
||||
reinterpret_cast<const jschar*>(mKeyPath.get());
|
||||
const size_t keyPathLen = mKeyPath.Length();
|
||||
|
||||
JSObject* object = JS_NewObject(aCx, nsnull, nsnull, nsnull);
|
||||
NS_ENSURE_TRUE(object, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
ok = emptyObjectBuffer.write(aCx, OBJECT_TO_JSVAL(object));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
jsval key;
|
||||
// This is just to give us some random marker in the byte stream
|
||||
ok = JS_NewNumberValue(aCx, kTotallyRandomNumber, &key);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
ok = JS_DefineUCProperty(aCx, object, keyPathChars, keyPathLen,
|
||||
key, nsnull, nsnull, JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
ok = fakeObjectBuffer.write(aCx, OBJECT_TO_JSVAL(object));
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
mKeyPathSerialization.swap(fakeObjectBuffer);
|
||||
mKeyPathSerializationOffset = emptyObjectBuffer.nbytes();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
|
||||
@ -1721,7 +1850,7 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||
// Special case where someone put an object into an autoIncrement'ing
|
||||
// objectStore with no key in its keyPath set. We needed to figure out
|
||||
// which row id we would get above before we could set that properly.
|
||||
rv = ModifyValueForNewKey();
|
||||
rv = mObjectStore->ModifyValueForNewKey(mCloneBuffer, mKey);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||
|
||||
scoper.Abandon();
|
||||
@ -1777,58 +1906,6 @@ AddHelper::GetSuccessResult(JSContext* aCx,
|
||||
return IDBObjectStore::GetJSValFromKey(mKey, aCx, aVal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AddHelper::ModifyValueForNewKey()
|
||||
{
|
||||
NS_ASSERTION(mObjectStore->IsAutoIncrement() &&
|
||||
!mObjectStore->KeyPath().IsEmpty() &&
|
||||
mKey.IsInt(),
|
||||
"Don't call me!");
|
||||
|
||||
const nsString& keyPath = mObjectStore->KeyPath();
|
||||
|
||||
JSContext* cx = nsnull;
|
||||
nsresult rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&cx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
jsval clone;
|
||||
if (!IDBObjectStore::DeserializeValue(cx, mCloneBuffer, &clone)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!JSVAL_IS_PRIMITIVE(clone), "We should have an object!");
|
||||
|
||||
JSObject* obj = JSVAL_TO_OBJECT(clone);
|
||||
JSBool ok;
|
||||
|
||||
const jschar* keyPathChars = reinterpret_cast<const jschar*>(keyPath.get());
|
||||
const size_t keyPathLen = keyPath.Length();
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
jsval prop;
|
||||
ok = JS_GetUCProperty(cx, obj, keyPathChars, keyPathLen, &prop);
|
||||
NS_ASSERTION(ok && JSVAL_IS_VOID(prop), "Already has a key prop!");
|
||||
}
|
||||
#endif
|
||||
|
||||
jsval key;
|
||||
ok = JS_NewNumberValue(cx, mKey.IntValue(), &key);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
|
||||
ok = JS_DefineUCProperty(cx, obj, keyPathChars, keyPathLen, key, nsnull,
|
||||
nsnull, JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
|
||||
if (!IDBObjectStore::SerializeValue(cx, mCloneBuffer, OBJECT_TO_JSVAL(obj))) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||
{
|
||||
|
@ -157,6 +157,9 @@ public:
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
nsresult ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
|
||||
Key& aKey);
|
||||
|
||||
protected:
|
||||
IDBObjectStore();
|
||||
~IDBObjectStore();
|
||||
@ -175,6 +178,8 @@ protected:
|
||||
nsIIDBRequest** _retval,
|
||||
bool aOverwrite);
|
||||
|
||||
nsresult EnsureKeyPathSerializationData(JSContext* aCx);
|
||||
|
||||
private:
|
||||
nsRefPtr<IDBTransaction> mTransaction;
|
||||
|
||||
@ -188,6 +193,11 @@ private:
|
||||
PRUint32 mDatabaseId;
|
||||
PRUint32 mStructuredCloneVersion;
|
||||
|
||||
// Used to store a serialized representation of the fake property
|
||||
// entry used to handle autoincrement with keypaths.
|
||||
JSAutoStructuredCloneBuffer mKeyPathSerialization;
|
||||
PRUint32 mKeyPathSerializationOffset;
|
||||
|
||||
nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;
|
||||
|
||||
};
|
||||
|
@ -163,6 +163,28 @@ PRInt32 nsCRT::strncmp(const PRUnichar* s1, const PRUnichar* s2, PRUint32 n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* nsCRT::memmem(const char* haystack, PRUint32 haystackLen,
|
||||
const char* needle, PRUint32 needleLen)
|
||||
{
|
||||
// Sanity checking
|
||||
if (!(haystack && needle && haystackLen && needleLen &&
|
||||
needleLen <= haystackLen))
|
||||
return NULL;
|
||||
|
||||
#ifdef HAVE_MEMMEM
|
||||
return (const char*)::memmem(haystack, haystackLen, needle, needleLen);
|
||||
#else
|
||||
// No memmem means we need to roll our own. This isn't really optimized
|
||||
// for performance ... if that becomes an issue we can take some inspiration
|
||||
// from the js string compare code in jsstr.cpp
|
||||
for (PRInt32 i = 0; i < haystackLen - needleLen; i++) {
|
||||
if (!memcmp(haystack + i, needle, needleLen))
|
||||
return haystack + i;
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PRUnichar* nsCRT::strdup(const PRUnichar* str)
|
||||
{
|
||||
PRUint32 len = nsCRT::strlen(str);
|
||||
|
@ -210,6 +210,12 @@ public:
|
||||
static PRInt32 strncmp(const PRUnichar* s1, const PRUnichar* s2,
|
||||
PRUint32 aMaxLen);
|
||||
|
||||
// The GNU libc has memmem, which is strstr except for binary data
|
||||
// This is our own implementation that uses memmem on platforms
|
||||
// where it's available.
|
||||
static const char* memmem(const char* haystack, PRUint32 haystackLen,
|
||||
const char* needle, PRUint32 needleLen);
|
||||
|
||||
// You must use nsCRT::free(PRUnichar*) to free memory allocated
|
||||
// by nsCRT::strdup(PRUnichar*).
|
||||
static PRUnichar* strdup(const PRUnichar* str);
|
||||
|
Loading…
Reference in New Issue
Block a user