Bug 1083285 - Fix noisy IndexedDB error, replace with better warning. r=khuey.

This commit is contained in:
Ben Turner 2014-11-13 18:20:38 -08:00
parent 342da05988
commit 7b98a7adee
11 changed files with 194 additions and 45 deletions

View File

@ -1240,8 +1240,14 @@ BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
auto actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
MOZ_ASSERT(request);
nsRefPtr<IDBTransaction> transaction =
IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId,
IDBTransaction::CreateVersionChange(mDatabase,
actor,
request,
aNextObjectStoreId,
aNextIndexId);
if (NS_WARN_IF(!transaction)) {
return false;
@ -1253,9 +1259,6 @@ BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
mDatabase->EnterSetVersionTransaction(aRequestedVersion);
nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
MOZ_ASSERT(request);
request->SetTransaction(transaction);
nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
@ -1316,7 +1319,7 @@ BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
if (shouldAbortAndClose) {
// Invalidate() doesn't close the database in the parent, so we have
// to call Close() and AbortTransactions() manually.
mDatabase->AbortTransactions();
mDatabase->AbortTransactions(/* aShouldWarn */ false);
mDatabase->Close();
return true;
}

View File

@ -39,9 +39,12 @@
#include "mozilla/ipc/InputStreamParams.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIConsoleService.h"
#include "nsIDocument.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIScriptError.h"
#include "nsISupportsPrimitives.h"
#include "nsThreadUtils.h"
#include "ProfilerHelpers.h"
@ -314,7 +317,7 @@ IDBDatabase::InvalidateInternal()
AssertIsOnOwningThread();
InvalidateMutableFiles();
AbortTransactions();
AbortTransactions(/* aShouldWarn */ true);
CloseInternal();
}
@ -751,7 +754,7 @@ IDBDatabase::UnregisterTransaction(IDBTransaction* aTransaction)
}
void
IDBDatabase::AbortTransactions()
IDBDatabase::AbortTransactions(bool aShouldWarn)
{
AssertIsOnOwningThread();
@ -759,27 +762,31 @@ IDBDatabase::AbortTransactions()
{
public:
static void
AbortTransactions(nsTHashtable<nsPtrHashKey<IDBTransaction>>& aTable)
AbortTransactions(nsTHashtable<nsPtrHashKey<IDBTransaction>>& aTable,
nsTArray<nsRefPtr<IDBTransaction>>& aAbortedTransactions)
{
const uint32_t count = aTable.Count();
if (!count) {
return;
}
nsTArray<nsRefPtr<IDBTransaction>> transactions;
nsAutoTArray<nsRefPtr<IDBTransaction>, 20> transactions;
transactions.SetCapacity(count);
aTable.EnumerateEntries(Collect, &transactions);
MOZ_ASSERT(transactions.Length() == count);
IDB_REPORT_INTERNAL_ERR();
for (uint32_t index = 0; index < count; index++) {
nsRefPtr<IDBTransaction> transaction = transactions[index].forget();
nsRefPtr<IDBTransaction> transaction = Move(transactions[index]);
MOZ_ASSERT(transaction);
transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// We only care about warning for write transactions.
if (transaction->GetMode() != IDBTransaction::READ_ONLY) {
aAbortedTransactions.AppendElement(Move(transaction));
}
}
}
@ -798,7 +805,25 @@ IDBDatabase::AbortTransactions()
}
};
Helper::AbortTransactions(mTransactions);
nsAutoTArray<nsRefPtr<IDBTransaction>, 5> abortedTransactions;
Helper::AbortTransactions(mTransactions, abortedTransactions);
if (aShouldWarn && !abortedTransactions.IsEmpty()) {
static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation";
for (uint32_t count = abortedTransactions.Length(), index = 0;
index < count;
index++) {
nsRefPtr<IDBTransaction>& transaction = abortedTransactions[index];
MOZ_ASSERT(transaction);
nsString filename;
uint32_t lineNo;
transaction->GetCallerLocation(filename, &lineNo);
LogWarning(kWarningMessage, filename, lineNo);
}
}
}
PBackgroundIDBDatabaseFileChild*
@ -1155,6 +1180,65 @@ IDBDatabase::Invalidate()
}
}
void
IDBDatabase::LogWarning(const char* aMessageName,
const nsAString& aFilename,
uint32_t aLineNumber)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aMessageName);
// For now this is main-thread only.
MOZ_ASSERT(NS_IsMainThread());
nsXPIDLString localizedMessage;
if (NS_WARN_IF(NS_FAILED(
nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
aMessageName,
localizedMessage)))) {
return;
}
nsAutoCString category;
if (mFactory->IsChrome()) {
category.AssignLiteral("chrome ");
} else {
category.AssignLiteral("content ");
}
category.AppendLiteral("javascript");
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
MOZ_ASSERT(consoleService);
nsCOMPtr<nsIScriptError> scriptError =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
MOZ_ASSERT(consoleService);
if (mFactory->GetParentObject()) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
scriptError->InitWithWindowID(localizedMessage,
aFilename,
/* aSourceLine */ EmptyString(),
aLineNumber,
/* aColumnNumber */ 0,
nsIScriptError::warningFlag,
category,
mFactory->InnerWindowID())));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
scriptError->Init(localizedMessage,
aFilename,
/* aSourceLine */ EmptyString(),
aLineNumber,
/* aColumnNumber */ 0,
nsIScriptError::warningFlag,
category.get())));
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
}
NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)

View File

@ -171,7 +171,7 @@ public:
UnregisterTransaction(IDBTransaction* aTransaction);
void
AbortTransactions();
AbortTransactions(bool aShouldWarn);
PBackgroundIDBDatabaseFileChild*
GetOrCreateFileActorForBlob(File* aBlob);
@ -299,6 +299,11 @@ private:
void
InvalidateMutableFiles();
void
LogWarning(const char* aMessageName,
const nsAString& aFilename,
uint32_t aLineNumber);
};
} // namespace indexedDB

View File

@ -29,7 +29,7 @@
#include "ActorsChild.h"
#ifdef DEBUG
#include "nsContentUtils.h" // For IsCallerChrome assertions.
#include "nsContentUtils.h" // For assertions.
#endif
namespace mozilla {
@ -110,6 +110,7 @@ struct IDBFactory::PendingRequestInfo
IDBFactory::IDBFactory()
: mOwningObject(nullptr)
, mBackgroundActor(nullptr)
, mInnerWindowID(0)
, mBackgroundActorFailed(false)
, mPrivateBrowsingMode(false)
{
@ -183,6 +184,7 @@ IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow,
factory->mPrincipalInfo = Move(principalInfo);
factory->mWindow = aWindow;
factory->mTabChild = TabChild::GetFrom(aWindow);
factory->mInnerWindowID = aWindow->WindowID();
factory->mPrivateBrowsingMode = privateBrowsingMode;
factory.forget(aFactory);
@ -285,6 +287,15 @@ IDBFactory::AssertIsOnOwningThread() const
#endif // DEBUG
bool
IDBFactory::IsChrome() const
{
AssertIsOnOwningThread();
MOZ_ASSERT(mPrincipalInfo);
return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo;
}
void
IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor)
{
@ -524,19 +535,20 @@ IDBFactory::OpenInternal(nsIPrincipal* aPrincipal,
nsRefPtr<IDBOpenDBRequest> request;
if (mWindow) {
if (NS_WARN_IF(!autoJS.Init(mWindow))) {
AutoJSContext cx;
if (NS_WARN_IF(!autoJS.Init(mWindow, cx))) {
IDB_REPORT_INTERNAL_ERR();
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
JS::Rooted<JSObject*> scriptOwner(autoJS.cx(),
JS::Rooted<JSObject*> scriptOwner(cx,
static_cast<nsGlobalWindow*>(mWindow.get())->FastGetGlobalJSObject());
MOZ_ASSERT(scriptOwner);
request = IDBOpenDBRequest::CreateForWindow(this, mWindow, scriptOwner);
} else {
autoJS.Init();
autoJS.Init(mOwningObject.get());
JS::Rooted<JSObject*> scriptOwner(autoJS.cx(), mOwningObject);
request = IDBOpenDBRequest::CreateForJS(this, scriptOwner);

View File

@ -73,6 +73,8 @@ class IDBFactory MOZ_FINAL
PRThread* mOwningThread;
#endif
uint64_t mInnerWindowID;
bool mBackgroundActorFailed;
bool mPrivateBrowsingMode;
@ -130,6 +132,17 @@ public:
return mPrincipalInfo;
}
uint64_t
InnerWindowID() const
{
AssertIsOnOwningThread();
return mInnerWindowID;
}
bool
IsChrome() const;
already_AddRefed<IDBOpenDBRequest>
Open(const nsAString& aName,
uint64_t aVersion,

View File

@ -99,12 +99,11 @@ IDBRequest::Create(IDBDatabase* aDatabase,
aDatabase->AssertIsOnOwningThread();
nsRefPtr<IDBRequest> request = new IDBRequest(aDatabase);
CaptureCaller(request->mFilename, &request->mLineNo);
request->mTransaction = aTransaction;
request->SetScriptOwner(aDatabase->GetScriptOwner());
request->CaptureCaller();
return request.forget();
}
@ -140,6 +139,26 @@ IDBRequest::Create(IDBIndex* aSourceAsIndex,
return request.forget();
}
// static
void
IDBRequest::CaptureCaller(nsAString& aFilename, uint32_t* aLineNo)
{
MOZ_ASSERT(aFilename.IsEmpty());
MOZ_ASSERT(aLineNo);
ThreadsafeAutoJSContext cx;
const char* filename = nullptr;
uint32_t lineNo = 0;
if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
*aLineNo = 0;
return;
}
aFilename.Assign(NS_ConvertUTF8toUTF16(filename));
*aLineNo = lineNo;
}
void
IDBRequest::GetSource(
Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const
@ -227,25 +246,13 @@ IDBRequest::GetErrorCode() const
#endif // DEBUG
void
IDBRequest::CaptureCaller()
IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const
{
AutoJSContext cx;
AssertIsOnOwningThread();
MOZ_ASSERT(aLineNo);
const char* filename = nullptr;
uint32_t lineNo = 0;
if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
return;
}
mFilename.Assign(NS_ConvertUTF8toUTF16(filename));
mLineNo = lineNo;
}
void
IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const
{
aEventInit.mLineno = mLineNo;
aEventInit.mFilename = mFilename;
aFilename = mFilename;
*aLineNo = mLineNo;
}
IDBRequestReadyState
@ -301,7 +308,6 @@ IDBRequest::SetResultCallback(ResultCallback* aCallback)
// See if our window is still valid.
if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
IDB_REPORT_INTERNAL_ERR();
SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return;
}
@ -424,7 +430,7 @@ IDBOpenDBRequest::CreateForWindow(IDBFactory* aFactory,
MOZ_ASSERT(aScriptOwner);
nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, aOwner);
request->CaptureCaller();
CaptureCaller(request->mFilename, &request->mLineNo);
request->SetScriptOwner(aScriptOwner);
@ -441,7 +447,7 @@ IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory,
MOZ_ASSERT(aScriptOwner);
nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, nullptr);
request->CaptureCaller();
CaptureCaller(request->mFilename, &request->mLineNo);
request->SetScriptOwner(aScriptOwner);

View File

@ -81,6 +81,9 @@ public:
IDBDatabase* aDatabase,
IDBTransaction* aTransaction);
static void
CaptureCaller(nsAString& aFilename, uint32_t* aLineNo);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
@ -114,7 +117,7 @@ public:
GetError(ErrorResult& aRv);
void
FillScriptErrorEvent(ErrorEventInit& aEventInit) const;
GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
bool
IsPending() const
@ -189,9 +192,6 @@ protected:
void
ConstructResult();
void
CaptureCaller();
};
class NS_NO_VTABLE IDBRequest::ResultCallback

View File

@ -48,6 +48,7 @@ IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
, mNextIndexId(0)
, mAbortCode(NS_OK)
, mPendingRequestCount(0)
, mLineNo(0)
, mReadyState(IDBTransaction::INITIAL)
, mMode(aMode)
, mCreating(false)
@ -127,12 +128,14 @@ already_AddRefed<IDBTransaction>
IDBTransaction::CreateVersionChange(
IDBDatabase* aDatabase,
BackgroundVersionChangeTransactionChild* aActor,
IDBOpenDBRequest* aOpenRequest,
int64_t aNextObjectStoreId,
int64_t aNextIndexId)
{
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aOpenRequest);
MOZ_ASSERT(aNextObjectStoreId > 0);
MOZ_ASSERT(aNextIndexId > 0);
@ -140,6 +143,8 @@ IDBTransaction::CreateVersionChange(
nsRefPtr<IDBTransaction> transaction =
new IDBTransaction(aDatabase, emptyObjectStoreNames, VERSION_CHANGE);
aOpenRequest->GetCallerLocation(transaction->mFilename,
&transaction->mLineNo);
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
@ -175,6 +180,7 @@ IDBTransaction::Create(IDBDatabase* aDatabase,
nsRefPtr<IDBTransaction> transaction =
new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
IDBRequest::CaptureCaller(transaction->mFilename, &transaction->mLineNo);
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
@ -391,6 +397,16 @@ IDBTransaction::IsOpen() const
return false;
}
void
IDBTransaction::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const
{
AssertIsOnOwningThread();
MOZ_ASSERT(aLineNo);
aFilename = mFilename;
*aLineNo = mLineNo;
}
already_AddRefed<IDBObjectStore>
IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec)
{

View File

@ -37,6 +37,7 @@ class BackgroundTransactionChild;
class BackgroundVersionChangeTransactionChild;
class IDBDatabase;
class IDBObjectStore;
class IDBOpenDBRequest;
class IDBRequest;
class IndexMetadata;
class ObjectStoreSpec;
@ -94,6 +95,9 @@ private:
nsresult mAbortCode;
uint32_t mPendingRequestCount;
nsString mFilename;
uint32_t mLineNo;
ReadyState mReadyState;
Mode mMode;
@ -109,6 +113,7 @@ public:
static already_AddRefed<IDBTransaction>
CreateVersionChange(IDBDatabase* aDatabase,
BackgroundVersionChangeTransactionChild* aActor,
IDBOpenDBRequest* aOpenRequest,
int64_t aNextObjectStoreId,
int64_t aNextIndexId);
@ -184,6 +189,9 @@ public:
return NS_FAILED(mAbortCode);
}
void
GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
// 'Get' prefix is to avoid name collisions with the enum
Mode
GetMode() const

View File

@ -366,7 +366,7 @@ IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
ThreadsafeAutoJSContext cx;
RootedDictionary<ErrorEventInit> init(cx);
request->FillScriptErrorEvent(init);
request->GetCallerLocation(init.mFilename, &init.mLineno);
init.mMessage = errorName;
init.mCancelable = true;

View File

@ -209,3 +209,5 @@ KeyNameZoomWarning=KeyboardEvent.key value "Zoom" is obsolete and will be rename
KeyNameDeadKeysWarning=KeyboardEvent.key values starting with "Dead" are obsolete and will be merged into just "Dead". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
# LOCALIZATION NOTE: Do not translate "IndexedDB".
IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.