Files
palemoon27/dom/indexedDB/IndexedDatabaseManager.cpp
T
roytam1 b6f6aa65fb import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1165518 - Part 2: Replace prlog.h with Logging.h. rs=froydnj (342dfd711)
- Bug 1085567 - Fixup synth events within pointer lock implementation for e10s. r=billm (45c50a0e6)
- Bug 1128238 - Ajust NPP_SetWindow and synth native event coords to account for the chrome offset of remote tabs. Prior to this the content process always assumed the tab was the window origin. r=aklotz (202ffc006)
- Bug 1128934 - Avoid calling GetClientBounds from BasicComposior. r=roc, karlt (5e98232bd)
- Bug 1149041 - Make nsIWidget::SizeConstraints use LayoutDeviceIntSize instead of unit-less nsIntSize. r=roc (d0b345926)
- shuffle around (fcdc51510)
- Bug 1142852 P1 Fix NS_NewNonOwningRunnableMethodWithArgs() so that it compiles. r=froydnj (394d2b200)
- Bug 1142852 P2 Make Cache Action use non-threadsafe ref-counting. r=ehsan (0b2b91d86)
- Bug 1146349 - Allow NS_NewRunnableMethodWithArgs to take up to 8 arguments. r=froydnj (7207e74d0)
- Bug 1146349 - Make the native event synthesization functions in DOMWindowUtils async. r=smaug (e43579a1d)
- Bug 949614 - Use === for SimpleTest.is; r=Waldo (9f54faca4)
- Bug 1146349 - Update some widget tests to deal with async native key event synthesization. r=smaug,masayuki (f1e82df79)
- Bug 920013 - Rewrite test_cocoa_focus.html to use SpecialPowers. r=smichaud (8708d6a71)
- Bug 1146349 - Update DOM mochitests to deal with async native event synthesization. r=smaug (a4eceb9f6)
- Bug 1146349 - Update browser-chrome mochitests to deal with async native event synthesization. r=Gijs (c864ad939)
- Bug 1146349 - Wire up native event synthesization in child processes.  r=smaug (e8574457e)
- Bug 1146349 - Mark SynthesizedEventObserver::Observe() as override (9063f04b5)
- Bug 1149613 - Remove unneded field. r=botond (9aa7155e2)
- Bug 1153613 - [e10s], ensure the buffer for the drag image is large enough for SourceSurfaceRawData::GuaranteePersistance() to succeed, r=mattwoodrow (aa885dae7)
- remove spurious file (ff199d3ae)
- Bug 1043013 - Generalize nsIntRegion into a BaseIntRegion template. r=jrmuizel (9d3e02820)
- Bug 1043013 - Introduce IntRegionTyped. r=jrmuizel (f73902921)
- Bug 1043013 - Use strongly-typed regions in HitTestingTreeNode. r=kats (7b8c8ae44)
- Bug 913443 - Remove mention of the word ThebesLayer in a comment. r=roc (e9706e025)
- Bug 913443 - Remove some #ifdefs. r=roc (da76f831b)
- Bug 913443 - Break up CreateOrRecyclePaintedLayer into more parts. r=roc (dd94560fe)
- Bug 913443 - Extract layer hint calculation. r=roc (5c7806a95)
- Bug 913443 - Break CreateOrRecyclePaintedLayer up even more. r=roc (9e5edab17)
- Bug 913443 - Remove unused aItemVisibleRect argument. r=roc (f3d1a5195)
- Bug 913443 - Move IsWidgetLayerManager() check out of UpdateCommonClipCount. r=roc (3be7b19a6)
- Bug 913443 - Change the order of these calls. r=roc (c7bc59fee)
- Bug 913443 - Add a display item buffer for PaintedLayerData so that we can assign items without needing to know the actual Layer. r=roc (674df6aa8)
- Bug 913443 - Delay PaintedLayer recycling until PopPaintedLayerData() r=roc (9d6de961a)
- Bug 913443 - When determining the layer to recycle, only consider layers that have display items in common with the layer we need. r=roc (3327c31c5)
- Bug 913443 - Recycle PaintedLayers as soon as possible. r=roc (37ef6d530)
- Bug 913443 - Remove duplicated argument variables. r=roc (19c5af270)
- Bug 1141408 - Don't include the display item's memory address in the profile label. r=BenWa (5bb1d73c1)
- Bug 1141281 - Don't put empty Info: lines into the layers dump. r=BenWa (85f4b28be)
- Bug 1035611 - Part 1: Updated Matrix4x4::ProjectRectBounds to properly handle infinite values when untransformed rects cross the w=0 plane. r=mattwoodrow (f47ed4383)
- Bug 1035611 - Part 2: Test to ensure that transformed rects crossing the w=0 plane are clipped correctly. r=mattwoodrow (5701b435f)
- Bug 1141089 - Add some release assertions to gather more data. r=roc (805c2994d)
- Bug 1141595 - Make scroll info display item hoisting work after bug 913443. r=tn (1efcaf164)
- Bug 1144307 - Destroy scroll info layer items when we're done with them. r=tn (3ffd3b1f2)
- Bug 1148855 - Mark some ContainerState methods as const. r=roc (791585baf)
- Bug 1144820 - Use rvalue reference in AddProfileTimelineMarker. r=smaug (12316b65f)
- Bug 1148855 - Set overflow:hidden on scrollbar tracks so that layerization knows that the scrollbar thumb won't leave the scrollbar. r=roc (082ec87bf)
- Bug 1148855 - Intermediate state that unifies APZ and non-APZ layerization behavior somewhat. (3da45ab96)
- Bug 1148855 - Rework how display items are assigned to PaintedLayers and make it work the same way with and without APZ. r=roc (7258f9c90)
- Bug 913443 - Add some tests. r=roc (9977682da)
- Bug 1039926 - Add a test for not invalidating stuff inside mask layers. (1f8aef65e)
- Bug 1148855 - Add some tests. r=roc (2ba1f7f18)
- Bug 1148855 - Tweak reftest fuzziness. (a600be7d5)
- Bug 1151306 - Add a missing check during background color pulling. r=roc (b3a6c886b)
- Bug 1150774 (Part 1) - Add helpers to simplify using typed units in ConfigureLayer. r=botond (baecb2c2e)
- Bug 1150774 (Part 2) - Use the correct units in nsDisplayImageContainer::ConfigureLayer and related code. r=botond (a79beb1fb)
- Bug 1141089 - Initialize DisplayItemData with a non-null mLayer, so that mLayer is non-null throughout the object's whole lifetime. r=roc (7c2f9eb09)
- Bug 1141089 - Remove unused DisplayItemData copy constructor. r=roc (ba5c46c45)
- Bug 1141089 - Add a few more MOZ_RELEASE_ASSERT(mLayer) checks, just for fun. r=roc (50a8f2db7)
- Bug 1141089 - Use const nsTArray* in all the places where we don't change the array's contents. r=roc (cd07a5bbd)
- Bug 1141089 - Turn these two assertions into release assertions. r=roc (71daf5ef3)
- Bug 1141089 - Check that we're unsetting sDestroyedFrame correctly. r=roc (6c7da67e7)
- Bug 1143522 - Convert Layer::mClipRect to ParentLayerIntRect. r=botond (ddab1c0ff)
- Bug 1144951. Warn if the result of ConvertAppUnits is unused. r=mats (e1d79c6ca)
- Bug 1144951. Rename ConvertAppUnits to ScaleToOtherAppUnits. r=mats (675c918c3)
- Bug 1149035 - Make nsXULPopupManager::PopupResized take a LayoutDeviceIntSize param instead of a unit-less nsIntSize. r=roc (eb9529af7)
- missing bit of Bug 1156110 - Make nsView::mWindow an nsCOMPtr; r=roc (972255b4f)
- Bug 1147706 - Warn if we don't use the result of const methods on BaseRect and its subclasses. r=tn (9e91deada)
- Bug 1152838 - Fix BaseRect::InflateToMultiple and move it to nsIntRect. r=mattwoodrow, r=jrmuizel (5a942df29)
- Bug 1125055 - Sync decode layerized images if necessary. r=roc,tn (1379db5c7)
- Bug 1145167 - make imgIContainer::type getter infallible; r=seth (7b7091b89)
- Bug 1148213 - Implement image locking for VectorImage. r=dholbert (240e14cb3)
- Bug 1143653 - Crash in CompositorD3D9 rather than TextureD3D9 if device reset fails, after a few attemps. r=jrmuizel (76607ddc3)
- Bug 1147894 - Only use non-null compositors with TextureHost::SetCompositor. r=sotaro (605bf34f6)
- Bug 1146912 - Finish replacing TextureHost::GetTextureSource by BindTextureSource. r=sotaro (cf3763879)
- Bug 1132854 - Remove useless ThebesIntSize conversion helper. r=Bas (78cdfee14)
- Bug 1068881 - Inflate the dest rect to at least one device pixel when computing the optimal image size for drawing. r=roc (3d5644913)
2020-05-30 12:49:08 +08:00

1124 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IndexedDatabaseManager.h"
#include "nsIConsoleService.h"
#include "nsIDiskSpaceWatcher.h"
#include "nsIDOMWindow.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "jsapi.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/CondVar.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/PBlobChild.h"
#include "mozilla/dom/quota/OriginOrPatternString.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/quota/Utilities.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "nsThreadUtils.h"
#include "mozilla/Logging.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBKeyRange.h"
#include "IDBRequest.h"
#include "ProfilerHelpers.h"
#include "WorkerScope.h"
#include "WorkerPrivate.h"
// Bindings for ResolveConstructors
#include "mozilla/dom/IDBCursorBinding.h"
#include "mozilla/dom/IDBDatabaseBinding.h"
#include "mozilla/dom/IDBFactoryBinding.h"
#include "mozilla/dom/IDBIndexBinding.h"
#include "mozilla/dom/IDBKeyRangeBinding.h"
#include "mozilla/dom/IDBMutableFileBinding.h"
#include "mozilla/dom/IDBObjectStoreBinding.h"
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
#include "mozilla/dom/IDBRequestBinding.h"
#include "mozilla/dom/IDBTransactionBinding.h"
#include "mozilla/dom/IDBVersionChangeEventBinding.h"
#define IDB_STR "indexedDB"
// The two possible values for the data argument when receiving the disk space
// observer notification.
#define LOW_DISK_SPACE_DATA_FULL "full"
#define LOW_DISK_SPACE_DATA_FREE "free"
namespace mozilla {
namespace dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::dom::workers;
class FileManagerInfo
{
public:
already_AddRefed<FileManager>
GetFileManager(PersistenceType aPersistenceType,
const nsAString& aName) const;
void
AddFileManager(FileManager* aFileManager);
bool
HasFileManagers() const
{
AssertIsOnIOThread();
return !mPersistentStorageFileManagers.IsEmpty() ||
!mTemporaryStorageFileManagers.IsEmpty() ||
!mDefaultStorageFileManagers.IsEmpty();
}
void
InvalidateAllFileManagers() const;
void
InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
void
InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
const nsAString& aName);
private:
nsTArray<nsRefPtr<FileManager> >&
GetArray(PersistenceType aPersistenceType);
const nsTArray<nsRefPtr<FileManager> >&
GetImmutableArray(PersistenceType aPersistenceType) const
{
return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
}
nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
nsTArray<nsRefPtr<FileManager> > mDefaultStorageFileManagers;
};
namespace {
NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
#undef IDB_PREF_LOGGING_BRANCH_ROOT
#undef IDB_PREF_BRANCH_ROOT
StaticRefPtr<IndexedDatabaseManager> gDBManager;
Atomic<bool> gInitialized(false);
Atomic<bool> gClosed(false);
Atomic<bool> gTestingMode(false);
Atomic<bool> gExperimentalFeaturesEnabled(false);
class AsyncDeleteFileRunnable final : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
private:
~AsyncDeleteFileRunnable() {}
nsRefPtr<FileManager> mFileManager;
int64_t mFileId;
};
class GetFileReferencesHelper final : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
GetFileReferencesHelper(PersistenceType aPersistenceType,
const nsACString& aOrigin,
const nsAString& aDatabaseName,
int64_t aFileId)
: mPersistenceType(aPersistenceType),
mOrigin(aOrigin),
mDatabaseName(aDatabaseName),
mFileId(aFileId),
mMutex(IndexedDatabaseManager::FileMutex()),
mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
mMemRefCnt(-1),
mDBRefCnt(-1),
mSliceRefCnt(-1),
mResult(false),
mWaiting(true)
{ }
nsresult
DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
int32_t* aDBRefCnt,
int32_t* aSliceRefCnt,
bool* aResult);
private:
~GetFileReferencesHelper() {}
PersistenceType mPersistenceType;
nsCString mOrigin;
nsString mDatabaseName;
int64_t mFileId;
mozilla::Mutex& mMutex;
mozilla::CondVar mCondVar;
int32_t mMemRefCnt;
int32_t mDBRefCnt;
int32_t mSliceRefCnt;
bool mResult;
bool mWaiting;
};
void
AtomicBoolPrefChangedCallback(const char* aPrefName, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aClosure);
*static_cast<Atomic<bool>*>(aClosure) = Preferences::GetBool(aPrefName);
}
} // namespace
IndexedDatabaseManager::IndexedDatabaseManager()
: mFileMutex("IndexedDatabaseManager.mFileMutex")
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
IndexedDatabaseManager::~IndexedDatabaseManager()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
bool IndexedDatabaseManager::sIsMainProcess = false;
bool IndexedDatabaseManager::sFullSynchronousMode = false;
PRLogModuleInfo* IndexedDatabaseManager::sLoggingModule;
Atomic<IndexedDatabaseManager::LoggingMode>
IndexedDatabaseManager::sLoggingMode(
IndexedDatabaseManager::Logging_Disabled);
mozilla::Atomic<bool> IndexedDatabaseManager::sLowDiskSpaceMode(false);
// static
IndexedDatabaseManager*
IndexedDatabaseManager::GetOrCreate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (IsClosed()) {
NS_ERROR("Calling GetOrCreate() after shutdown!");
return nullptr;
}
if (!gDBManager) {
sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
if (!sLoggingModule) {
sLoggingModule = PR_NewLogModule("IndexedDB");
}
if (sIsMainProcess && Preferences::GetBool("disk_space_watcher.enabled", false)) {
// See if we're starting up in low disk space conditions.
nsCOMPtr<nsIDiskSpaceWatcher> watcher =
do_GetService(DISKSPACEWATCHER_CONTRACTID);
if (watcher) {
bool isDiskFull;
if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) {
sLowDiskSpaceMode = isDiskFull;
}
else {
NS_WARNING("GetIsDiskFull failed!");
}
}
else {
NS_WARNING("No disk space watcher component available!");
}
}
nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
nsresult rv = instance->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
if (gInitialized.exchange(true)) {
NS_ERROR("Initialized more than once?!");
}
gDBManager = instance;
ClearOnShutdown(&gDBManager);
}
return gDBManager;
}
// static
IndexedDatabaseManager*
IndexedDatabaseManager::Get()
{
// Does not return an owning reference.
return gDBManager;
}
nsresult
IndexedDatabaseManager::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
// directly.
if (sIsMainProcess) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
NS_ENSURE_STATE(obs);
nsresult rv =
obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
NS_ENSURE_SUCCESS(rv, rv);
}
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kTestingPref,
&gTestingMode);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
// By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
// guarantees (unlike synchronous = OFF) atomicity and consistency, but not
// necessarily durability in situations such as power loss. This preference
// allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
// durability, but with an extra fsync() and the corresponding performance
// hit.
sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
return NS_OK;
}
void
IndexedDatabaseManager::Destroy()
{
// Setting the closed flag prevents the service from being recreated.
// Don't set it though if there's no real instance created.
if (gInitialized && gClosed.exchange(true)) {
NS_ERROR("Shutdown more than once?!");
}
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kTestingPref,
&gTestingMode);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
delete this;
}
// static
nsresult
IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor,
IDBFactory* aFactory)
{
MOZ_ASSERT(aVisitor.mDOMEvent);
MOZ_ASSERT(aFactory);
if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
Event* internalEvent = aVisitor.mDOMEvent->InternalDOMEvent();
MOZ_ASSERT(internalEvent);
if (!internalEvent->IsTrusted()) {
return NS_OK;
}
nsString type;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(internalEvent->GetType(type)));
MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
if (!type.EqualsLiteral("error")) {
return NS_OK;
}
nsCOMPtr<EventTarget> eventTarget = internalEvent->GetTarget();
MOZ_ASSERT(eventTarget);
// Only mess with events that were originally targeted to an IDBRequest.
nsRefPtr<IDBRequest> request;
if (NS_FAILED(eventTarget->QueryInterface(kIDBRequestIID,
getter_AddRefs(request))) ||
!request) {
return NS_OK;
}
nsRefPtr<DOMError> error = request->GetErrorAfterResult();
nsString errorName;
if (error) {
error->GetName(errorName);
}
ThreadsafeAutoJSContext cx;
RootedDictionary<ErrorEventInit> init(cx);
request->GetCallerLocation(init.mFilename, &init.mLineno);
init.mMessage = errorName;
init.mCancelable = true;
init.mBubbles = true;
nsEventStatus status = nsEventStatus_eIgnore;
if (NS_IsMainThread()) {
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(eventTarget->GetOwnerGlobal());
if (window) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
MOZ_ASSERT(sgo);
if (NS_WARN_IF(NS_FAILED(sgo->HandleScriptError(init, &status)))) {
status = nsEventStatus_eIgnore;
}
} else {
// We don't fire error events at any global for non-window JS on the main
// thread.
}
} else {
// Not on the main thread, must be in a worker.
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
nsRefPtr<WorkerGlobalScope> globalScope = workerPrivate->GlobalScope();
MOZ_ASSERT(globalScope);
nsRefPtr<ErrorEvent> errorEvent =
ErrorEvent::Constructor(globalScope,
nsDependentString(kErrorEventType),
init);
MOZ_ASSERT(errorEvent);
errorEvent->SetTrusted(true);
auto* target = static_cast<EventTarget*>(globalScope.get());
if (NS_WARN_IF(NS_FAILED(
EventDispatcher::DispatchDOMEvent(target,
/* aWidgetEvent */ nullptr,
errorEvent,
/* aPresContext */ nullptr,
&status)))) {
status = nsEventStatus_eIgnore;
}
}
if (status == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
nsAutoCString category;
if (aFactory->IsChrome()) {
category.AssignLiteral("chrome ");
} else {
category.AssignLiteral("content ");
}
category.AppendLiteral("javascript");
// Log the error to the error console.
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
MOZ_ASSERT(consoleService);
nsCOMPtr<nsIScriptError> scriptError =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
MOZ_ASSERT(consoleService);
if (uint64_t innerWindowID = aFactory->InnerWindowID()) {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
scriptError->InitWithWindowID(errorName,
init.mFilename,
/* aSourceLine */ EmptyString(),
init.mLineno,
/* aColumnNumber */ 0,
nsIScriptError::errorFlag,
category,
innerWindowID)));
} else {
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
scriptError->Init(errorName,
init.mFilename,
/* aSourceLine */ EmptyString(),
init.mLineno,
/* aColumnNumber */ 0,
nsIScriptError::errorFlag,
category.get())));
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
return NS_OK;
}
// static
bool
IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext,
const nsACString& aOrigin)
{
NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
// If aContext is for a browser element, it's allowed only to access other
// browser elements. But if aContext is not for a browser element, it may
// access both browser and non-browser elements.
nsAutoCString pattern;
QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(
aContext.OwnOrContainingAppId(),
aContext.IsBrowserElement(),
pattern);
return PatternMatchesOrigin(pattern, aOrigin);
}
// static
bool
IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
JS::Handle<JSObject*> aGlobal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
// We need to ensure that the manager has been created already here so that we
// load preferences that may control which properties are exposed.
if (NS_WARN_IF(!GetOrCreate())) {
return false;
}
if (!IDBCursorBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBCursorWithValueBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBDatabaseBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBFactoryBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBIndexBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBKeyRangeBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBMutableFileBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBObjectStoreBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBOpenDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBTransactionBinding::GetConstructorObject(aCx, aGlobal) ||
!IDBVersionChangeEventBinding::GetConstructorObject(aCx, aGlobal))
{
return false;
}
nsRefPtr<IDBFactory> factory;
if (NS_FAILED(IDBFactory::CreateForChromeJS(aCx,
aGlobal,
getter_AddRefs(factory)))) {
return false;
}
MOZ_ASSERT(factory, "This should never fail for chrome!");
JS::Rooted<JS::Value> indexedDB(aCx);
js::AssertSameCompartment(aCx, aGlobal);
if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) {
return false;
}
return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE);
}
// static
bool
IndexedDatabaseManager::IsClosed()
{
return gClosed;
}
#ifdef DEBUG
// static
bool
IndexedDatabaseManager::IsMainProcess()
{
NS_ASSERTION(gDBManager,
"IsMainProcess() called before indexedDB has been initialized!");
NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) ==
sIsMainProcess, "XRE_GetProcessType changed its tune!");
return sIsMainProcess;
}
//static
bool
IndexedDatabaseManager::InLowDiskSpaceMode()
{
NS_ASSERTION(gDBManager,
"InLowDiskSpaceMode() called before indexedDB has been "
"initialized!");
return sLowDiskSpaceMode;
}
// static
IndexedDatabaseManager::LoggingMode
IndexedDatabaseManager::GetLoggingMode()
{
MOZ_ASSERT(gDBManager,
"GetLoggingMode called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingMode;
}
// static
PRLogModuleInfo*
IndexedDatabaseManager::GetLoggingModule()
{
MOZ_ASSERT(gDBManager,
"GetLoggingModule called before IndexedDatabaseManager has been "
"initialized!");
return sLoggingModule;
}
#endif // DEBUG
// static
bool
IndexedDatabaseManager::InTestingMode()
{
MOZ_ASSERT(gDBManager,
"InTestingMode() called before indexedDB has been initialized!");
return gTestingMode;
}
// static
bool
IndexedDatabaseManager::FullSynchronous()
{
MOZ_ASSERT(gDBManager,
"FullSynchronous() called before indexedDB has been initialized!");
return sFullSynchronousMode;
}
// static
bool
IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
JSObject* aGlobal)
{
if (NS_IsMainThread()) {
if (NS_WARN_IF(!GetOrCreate())) {
return false;
}
} else {
MOZ_ASSERT(Get(),
"ExperimentalFeaturesEnabled() called off the main thread "
"before indexedDB has been initialized!");
}
return gExperimentalFeaturesEnabled;
}
already_AddRefed<FileManager>
IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
const nsACString& aOrigin,
const nsAString& aDatabaseName)
{
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return nullptr;
}
nsRefPtr<FileManager> fileManager =
info->GetFileManager(aPersistenceType, aDatabaseName);
return fileManager.forget();
}
void
IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
{
AssertIsOnIOThread();
NS_ASSERTION(aFileManager, "Null file manager!");
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
info = new FileManagerInfo();
mFileManagerInfos.Put(aFileManager->Origin(), info);
}
info->AddFileManager(aFileManager);
}
void
IndexedDatabaseManager::InvalidateAllFileManagers()
{
AssertIsOnIOThread();
class MOZ_STACK_CLASS Helper final
{
public:
static PLDHashOperator
Enumerate(const nsACString& aKey,
FileManagerInfo* aValue,
void* aUserArg)
{
AssertIsOnIOThread();
MOZ_ASSERT(!aKey.IsEmpty());
MOZ_ASSERT(aValue);
aValue->InvalidateAllFileManagers();
return PL_DHASH_NEXT;
}
};
mFileManagerInfos.EnumerateRead(Helper::Enumerate, nullptr);
mFileManagerInfos.Clear();
}
void
IndexedDatabaseManager::InvalidateFileManagers(
PersistenceType aPersistenceType,
const nsACString& aOrigin)
{
AssertIsOnIOThread();
MOZ_ASSERT(!aOrigin.IsEmpty());
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return;
}
info->InvalidateAndRemoveFileManagers(aPersistenceType);
if (!info->HasFileManagers()) {
mFileManagerInfos.Remove(aOrigin);
}
}
void
IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType,
const nsACString& aOrigin,
const nsAString& aDatabaseName)
{
AssertIsOnIOThread();
FileManagerInfo* info;
if (!mFileManagerInfos.Get(aOrigin, &info)) {
return;
}
info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
if (!info->HasFileManagers()) {
mFileManagerInfos.Remove(aOrigin);
}
}
nsresult
IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
int64_t aFileId)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aFileManager);
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
// See if we're currently clearing the storages for this origin. If so then
// we pretend that we've already deleted everything.
if (quotaManager->IsClearOriginPending(
aFileManager->Origin(),
Nullable<PersistenceType>(aFileManager->Type()))) {
return NS_OK;
}
nsRefPtr<AsyncDeleteFileRunnable> runnable =
new AsyncDeleteFileRunnable(aFileManager, aFileId);
nsresult rv =
quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
IndexedDatabaseManager::BlockAndGetFileReferences(
PersistenceType aPersistenceType,
const nsACString& aOrigin,
const nsAString& aDatabaseName,
int64_t aFileId,
int32_t* aRefCnt,
int32_t* aDBRefCnt,
int32_t* aSliceRefCnt,
bool* aResult)
{
if (NS_WARN_IF(!InTestingMode())) {
return NS_ERROR_UNEXPECTED;
}
if (IsMainProcess()) {
nsRefPtr<GetFileReferencesHelper> helper =
new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
aFileId);
nsresult rv =
helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
aSliceRefCnt, aResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
ContentChild* contentChild = ContentChild::GetSingleton();
if (NS_WARN_IF(!contentChild)) {
return NS_ERROR_FAILURE;
}
if (!contentChild->SendGetFileReferences(aPersistenceType,
nsCString(aOrigin),
nsString(aDatabaseName),
aFileId,
aRefCnt,
aDBRefCnt,
aSliceRefCnt,
aResult)) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
// static
void
IndexedDatabaseManager::LoggingModePrefChangedCallback(
const char* /* aPrefName */,
void* /* aClosure */)
{
MOZ_ASSERT(NS_IsMainThread());
if (!Preferences::GetBool(kPrefLoggingEnabled)) {
sLoggingMode = Logging_Disabled;
return;
}
bool useProfiler =
false;
const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
if (useProfiler) {
sLoggingMode = logDetails ?
Logging_DetailedProfilerMarks :
Logging_ConciseProfilerMarks;
} else {
sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
}
}
NS_IMPL_ADDREF(IndexedDatabaseManager)
NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver)
NS_IMETHODIMP
IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
NS_ASSERTION(IsMainProcess(), "Wrong process!");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!strcmp(aTopic, DISKSPACEWATCHER_OBSERVER_TOPIC)) {
NS_ASSERTION(aData, "No data?!");
const nsDependentString data(aData);
if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FULL)) {
sLowDiskSpaceMode = true;
}
else if (data.EqualsLiteral(LOW_DISK_SPACE_DATA_FREE)) {
sLowDiskSpaceMode = false;
}
else {
NS_NOTREACHED("Unknown data value!");
}
return NS_OK;
}
NS_NOTREACHED("Unknown topic!");
return NS_ERROR_UNEXPECTED;
}
already_AddRefed<FileManager>
FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
const nsAString& aName) const
{
AssertIsOnIOThread();
const nsTArray<nsRefPtr<FileManager> >& managers =
GetImmutableArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
const nsRefPtr<FileManager>& fileManager = managers[i];
if (fileManager->DatabaseName() == aName) {
nsRefPtr<FileManager> result = fileManager;
return result.forget();
}
}
return nullptr;
}
void
FileManagerInfo::AddFileManager(FileManager* aFileManager)
{
AssertIsOnIOThread();
nsTArray<nsRefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
managers.AppendElement(aFileManager);
}
void
FileManagerInfo::InvalidateAllFileManagers() const
{
AssertIsOnIOThread();
uint32_t i;
for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
mPersistentStorageFileManagers[i]->Invalidate();
}
for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
mTemporaryStorageFileManagers[i]->Invalidate();
}
for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
mDefaultStorageFileManagers[i]->Invalidate();
}
}
void
FileManagerInfo::InvalidateAndRemoveFileManagers(
PersistenceType aPersistenceType)
{
AssertIsOnIOThread();
nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
managers[i]->Invalidate();
}
managers.Clear();
}
void
FileManagerInfo::InvalidateAndRemoveFileManager(
PersistenceType aPersistenceType,
const nsAString& aName)
{
AssertIsOnIOThread();
nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
nsRefPtr<FileManager>& fileManager = managers[i];
if (fileManager->DatabaseName() == aName) {
fileManager->Invalidate();
managers.RemoveElementAt(i);
return;
}
}
}
nsTArray<nsRefPtr<FileManager> >&
FileManagerInfo::GetArray(PersistenceType aPersistenceType)
{
switch (aPersistenceType) {
case PERSISTENCE_TYPE_PERSISTENT:
return mPersistentStorageFileManagers;
case PERSISTENCE_TYPE_TEMPORARY:
return mTemporaryStorageFileManagers;
case PERSISTENCE_TYPE_DEFAULT:
return mDefaultStorageFileManagers;
case PERSISTENCE_TYPE_INVALID:
default:
MOZ_CRASH("Bad storage type value!");
}
}
AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
int64_t aFileId)
: mFileManager(aFileManager), mFileId(aFileId)
{
}
NS_IMPL_ISUPPORTS(AsyncDeleteFileRunnable,
nsIRunnable)
NS_IMETHODIMP
AsyncDeleteFileRunnable::Run()
{
AssertIsOnIOThread();
nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory();
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
nsresult rv;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(), fileSize);
}
directory = mFileManager->GetJournalDirectory();
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
file = mFileManager->GetFileForId(directory, mFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
int32_t* aDBRefCnt,
int32_t* aSliceRefCnt,
bool* aResult)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
nsresult rv =
quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
mozilla::MutexAutoLock autolock(mMutex);
while (mWaiting) {
mCondVar.Wait();
}
*aMemRefCnt = mMemRefCnt;
*aDBRefCnt = mDBRefCnt;
*aSliceRefCnt = mSliceRefCnt;
*aResult = mResult;
return NS_OK;
}
NS_IMPL_ISUPPORTS(GetFileReferencesHelper,
nsIRunnable)
NS_IMETHODIMP
GetFileReferencesHelper::Run()
{
AssertIsOnIOThread();
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never fail!");
nsRefPtr<FileManager> fileManager =
mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
if (fileManager) {
nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
if (fileInfo) {
fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
if (mMemRefCnt != -1) {
// We added an extra temp ref, so account for that accordingly.
mMemRefCnt--;
}
mResult = true;
}
}
mozilla::MutexAutoLock lock(mMutex);
NS_ASSERTION(mWaiting, "Huh?!");
mWaiting = false;
mCondVar.Notify();
return NS_OK;
}
} // namespace indexedDB
} // namespace dom
} // namespace mozilla