michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set cindent tabstop=4 expandtab shiftwidth=4: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: michael@0: Implementation for the local store michael@0: michael@0: */ michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsILocalStore.h" michael@0: #include "nsIRDFDataSource.h" michael@0: #include "nsIRDFRemoteDataSource.h" michael@0: #include "nsIRDFService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "plstr.h" michael@0: #include "rdf.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsWeakPtr.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "nsCRT.h" michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: class LocalStoreImpl : public nsILocalStore, michael@0: public nsIRDFDataSource, michael@0: public nsIRDFRemoteDataSource, michael@0: public nsIObserver, michael@0: public nsSupportsWeakReference michael@0: { michael@0: protected: michael@0: nsCOMPtr mInner; michael@0: michael@0: LocalStoreImpl(); michael@0: virtual ~LocalStoreImpl(); michael@0: nsresult Init(); michael@0: nsresult CreateLocalStore(nsIFile* aFile); michael@0: nsresult LoadData(); michael@0: michael@0: friend nsresult michael@0: NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult); michael@0: michael@0: nsCOMPtr mRDFService; michael@0: michael@0: public: michael@0: // nsISupports interface michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(LocalStoreImpl, nsILocalStore) michael@0: michael@0: // nsILocalStore interface michael@0: michael@0: // nsIRDFDataSource interface. Most of these are just delegated to michael@0: // the inner, in-memory datasource. michael@0: NS_IMETHOD GetURI(char* *aURI); michael@0: michael@0: NS_IMETHOD GetSource(nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: bool aTruthValue, michael@0: nsIRDFResource** aSource) { michael@0: return mInner->GetSource(aProperty, aTarget, aTruthValue, aSource); michael@0: } michael@0: michael@0: NS_IMETHOD GetSources(nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: bool aTruthValue, michael@0: nsISimpleEnumerator** aSources) { michael@0: return mInner->GetSources(aProperty, aTarget, aTruthValue, aSources); michael@0: } michael@0: michael@0: NS_IMETHOD GetTarget(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: bool aTruthValue, michael@0: nsIRDFNode** aTarget) { michael@0: return mInner->GetTarget(aSource, aProperty, aTruthValue, aTarget); michael@0: } michael@0: michael@0: NS_IMETHOD GetTargets(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: bool aTruthValue, michael@0: nsISimpleEnumerator** aTargets) { michael@0: return mInner->GetTargets(aSource, aProperty, aTruthValue, aTargets); michael@0: } michael@0: michael@0: NS_IMETHOD Assert(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: bool aTruthValue) { michael@0: return mInner->Assert(aSource, aProperty, aTarget, aTruthValue); michael@0: } michael@0: michael@0: NS_IMETHOD Unassert(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget) { michael@0: return mInner->Unassert(aSource, aProperty, aTarget); michael@0: } michael@0: michael@0: NS_IMETHOD Change(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aOldTarget, michael@0: nsIRDFNode* aNewTarget) { michael@0: return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget); michael@0: } michael@0: michael@0: NS_IMETHOD Move(nsIRDFResource* aOldSource, michael@0: nsIRDFResource* aNewSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget) { michael@0: return mInner->Move(aOldSource, aNewSource, aProperty, aTarget); michael@0: } michael@0: michael@0: NS_IMETHOD HasAssertion(nsIRDFResource* aSource, michael@0: nsIRDFResource* aProperty, michael@0: nsIRDFNode* aTarget, michael@0: bool aTruthValue, michael@0: bool* hasAssertion) { michael@0: return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, hasAssertion); michael@0: } michael@0: michael@0: NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval) { michael@0: return mInner->HasArcIn(aNode, aArc, _retval); michael@0: } michael@0: michael@0: NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval) { michael@0: return mInner->HasArcOut(aSource, aArc, _retval); michael@0: } michael@0: michael@0: NS_IMETHOD ArcLabelsIn(nsIRDFNode* aNode, michael@0: nsISimpleEnumerator** aLabels) { michael@0: return mInner->ArcLabelsIn(aNode, aLabels); michael@0: } michael@0: michael@0: NS_IMETHOD ArcLabelsOut(nsIRDFResource* aSource, michael@0: nsISimpleEnumerator** aLabels) { michael@0: return mInner->ArcLabelsOut(aSource, aLabels); michael@0: } michael@0: michael@0: NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) { michael@0: return mInner->GetAllResources(aResult); michael@0: } michael@0: michael@0: NS_IMETHOD GetAllCmds(nsIRDFResource* aSource, michael@0: nsISimpleEnumerator/**/** aCommands); michael@0: michael@0: NS_IMETHOD IsCommandEnabled(nsISupportsArray/**/* aSources, michael@0: nsIRDFResource* aCommand, michael@0: nsISupportsArray/**/* aArguments, michael@0: bool* aResult); michael@0: michael@0: NS_IMETHOD DoCommand(nsISupportsArray/**/* aSources, michael@0: nsIRDFResource* aCommand, michael@0: nsISupportsArray/**/* aArguments); michael@0: michael@0: NS_IMETHOD BeginUpdateBatch() { michael@0: return mInner->BeginUpdateBatch(); michael@0: } michael@0: michael@0: NS_IMETHOD EndUpdateBatch() { michael@0: return mInner->EndUpdateBatch(); michael@0: } michael@0: michael@0: NS_IMETHOD GetLoaded(bool* _result); michael@0: NS_IMETHOD Init(const char *uri); michael@0: NS_IMETHOD Flush(); michael@0: NS_IMETHOD FlushTo(const char *aURI); michael@0: NS_IMETHOD Refresh(bool sync); michael@0: michael@0: // nsIObserver michael@0: NS_DECL_NSIOBSERVER michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: michael@0: LocalStoreImpl::LocalStoreImpl(void) michael@0: { michael@0: } michael@0: michael@0: LocalStoreImpl::~LocalStoreImpl(void) michael@0: { michael@0: if (mRDFService) michael@0: mRDFService->UnregisterDataSource(this); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult) michael@0: { michael@0: NS_PRECONDITION(aOuter == nullptr, "no aggregation"); michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: NS_PRECONDITION(aResult != nullptr, "null ptr"); michael@0: if (! aResult) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: LocalStoreImpl* impl = new LocalStoreImpl(); michael@0: if (! impl) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(impl); michael@0: michael@0: nsresult rv; michael@0: rv = impl->Init(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Set up the result pointer michael@0: rv = impl->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_RELEASE(impl); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(LocalStoreImpl, mInner) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalStoreImpl) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalStoreImpl) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStoreImpl) michael@0: NS_INTERFACE_MAP_ENTRY(nsILocalStore) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRDFRemoteDataSource) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILocalStore) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: // nsILocalStore interface michael@0: michael@0: // nsIRDFDataSource interface michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::GetLoaded(bool* _result) michael@0: { michael@0: nsCOMPtr remote = do_QueryInterface(mInner); michael@0: NS_ASSERTION(remote != nullptr, "not an nsIRDFRemoteDataSource"); michael@0: if (! remote) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return remote->GetLoaded(_result); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::Init(const char *uri) michael@0: { michael@0: return(NS_OK); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::Flush() michael@0: { michael@0: nsCOMPtr remote = do_QueryInterface(mInner); michael@0: // FIXME Bug 340242: Temporarily make this a warning rather than an michael@0: // assertion until we sort out the ordering of how we write michael@0: // everything to the localstore, flush it, and disconnect it when michael@0: // we're getting profile-change notifications. michael@0: NS_WARN_IF_FALSE(remote != nullptr, "not an nsIRDFRemoteDataSource"); michael@0: if (! remote) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return remote->Flush(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::FlushTo(const char *aURI) michael@0: { michael@0: // Do not ever implement this (security) michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::Refresh(bool sync) michael@0: { michael@0: nsCOMPtr remote = do_QueryInterface(mInner); michael@0: NS_ASSERTION(remote != nullptr, "not an nsIRDFRemoteDataSource"); michael@0: if (! remote) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return remote->Refresh(sync); michael@0: } michael@0: michael@0: nsresult michael@0: LocalStoreImpl::Init() michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = LoadData(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // register this as a named data source with the RDF service michael@0: mRDFService = do_GetService(NS_RDF_CONTRACTID "/rdf-service;1", &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mRDFService->RegisterDataSource(this, false); michael@0: michael@0: // Register as an observer of profile changes michael@0: nsCOMPtr obs = michael@0: do_GetService("@mozilla.org/observer-service;1"); michael@0: michael@0: if (obs) { michael@0: obs->AddObserver(this, "profile-before-change", true); michael@0: obs->AddObserver(this, "profile-do-change", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: LocalStoreImpl::CreateLocalStore(nsIFile* aFile) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = aFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr outStream; michael@0: rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), aFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: const char defaultRDF[] = michael@0: "\n" \ michael@0: "\n" \ michael@0: " \n" \ michael@0: "\n"; michael@0: michael@0: uint32_t count; michael@0: rv = outStream->Write(defaultRDF, sizeof(defaultRDF)-1, &count); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (count != sizeof(defaultRDF)-1) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Okay, now see if the file exists _for real_. If it's still michael@0: // not there, it could be that the profile service gave us michael@0: // back a read-only directory. Whatever. michael@0: bool fileExistsFlag = false; michael@0: aFile->Exists(&fileExistsFlag); michael@0: if (!fileExistsFlag) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: LocalStoreImpl::LoadData() michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Look for localstore.rdf in the current profile michael@0: // directory. Bomb if we can't find it. michael@0: michael@0: nsCOMPtr aFile; michael@0: rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool fileExistsFlag = false; michael@0: (void)aFile->Exists(&fileExistsFlag); michael@0: if (!fileExistsFlag) { michael@0: // if file doesn't exist, create it michael@0: rv = CreateLocalStore(aFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "xml-datasource", &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr remote = do_QueryInterface(mInner, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr aURI; michael@0: rv = NS_NewFileURI(getter_AddRefs(aURI), aFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString spec; michael@0: rv = aURI->GetSpec(spec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = remote->Init(spec.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Read the datasource synchronously. michael@0: rv = remote->Refresh(true); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // Load failed, delete and recreate a fresh localstore michael@0: aFile->Remove(true); michael@0: rv = CreateLocalStore(aFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = remote->Refresh(true); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::GetURI(char* *aURI) michael@0: { michael@0: NS_PRECONDITION(aURI != nullptr, "null ptr"); michael@0: if (! aURI) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *aURI = NS_strdup("rdf:local-store"); michael@0: if (! *aURI) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::GetAllCmds(nsIRDFResource* aSource, michael@0: nsISimpleEnumerator/**/** aCommands) michael@0: { michael@0: return(NS_NewEmptyEnumerator(aCommands)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::IsCommandEnabled(nsISupportsArray/**/* aSources, michael@0: nsIRDFResource* aCommand, michael@0: nsISupportsArray/**/* aArguments, michael@0: bool* aResult) michael@0: { michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::DoCommand(nsISupportsArray* aSources, michael@0: nsIRDFResource* aCommand, michael@0: nsISupportsArray* aArguments) michael@0: { michael@0: // no-op michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: LocalStoreImpl::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!nsCRT::strcmp(aTopic, "profile-before-change")) { michael@0: // Write out the old datasource's contents. michael@0: if (mInner) { michael@0: nsCOMPtr remote = do_QueryInterface(mInner); michael@0: if (remote) michael@0: remote->Flush(); michael@0: } michael@0: michael@0: // Create an in-memory datasource for use while we're michael@0: // profile-less. michael@0: mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "in-memory-datasource"); michael@0: michael@0: if (!nsCRT::strcmp(NS_ConvertUTF16toUTF8(someData).get(), "shutdown-cleanse")) { michael@0: nsCOMPtr aFile; michael@0: rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = aFile->Remove(false); michael@0: } michael@0: } michael@0: else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { michael@0: rv = LoadData(); michael@0: } michael@0: return rv; michael@0: }