1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set cindent tabstop=4 expandtab shiftwidth=4: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 Implementation for the local store
13 #include "nsNetUtil.h"
15 #include "nsIIOService.h"
16 #include "nsIOutputStream.h"
17 #include "nsIComponentManager.h"
18 #include "nsILocalStore.h"
19 #include "nsIRDFDataSource.h"
20 #include "nsIRDFRemoteDataSource.h"
21 #include "nsIRDFService.h"
22 #include "nsIServiceManager.h"
24 #include "nsXPIDLString.h"
28 #include "nsWeakPtr.h"
29 #include "nsAppDirectoryServiceDefs.h"
30 #include "nsIObserver.h"
31 #include "nsIObserverService.h"
32 #include "nsWeakReference.h"
33 #include "nsCRTGlue.h"
35 #include "nsEnumeratorUtils.h"
36 #include "nsCycleCollectionParticipant.h"
38 ////////////////////////////////////////////////////////////////////////
40 class LocalStoreImpl
: public nsILocalStore
,
41 public nsIRDFDataSource
,
42 public nsIRDFRemoteDataSource
,
44 public nsSupportsWeakReference
47 nsCOMPtr
<nsIRDFDataSource
> mInner
;
50 virtual ~LocalStoreImpl();
52 nsresult
CreateLocalStore(nsIFile
* aFile
);
56 NS_NewLocalStore(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
);
58 nsCOMPtr
<nsIRDFService
> mRDFService
;
61 // nsISupports interface
62 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
63 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(LocalStoreImpl
, nsILocalStore
)
65 // nsILocalStore interface
67 // nsIRDFDataSource interface. Most of these are just delegated to
68 // the inner, in-memory datasource.
69 NS_IMETHOD
GetURI(char* *aURI
);
71 NS_IMETHOD
GetSource(nsIRDFResource
* aProperty
,
74 nsIRDFResource
** aSource
) {
75 return mInner
->GetSource(aProperty
, aTarget
, aTruthValue
, aSource
);
78 NS_IMETHOD
GetSources(nsIRDFResource
* aProperty
,
81 nsISimpleEnumerator
** aSources
) {
82 return mInner
->GetSources(aProperty
, aTarget
, aTruthValue
, aSources
);
85 NS_IMETHOD
GetTarget(nsIRDFResource
* aSource
,
86 nsIRDFResource
* aProperty
,
88 nsIRDFNode
** aTarget
) {
89 return mInner
->GetTarget(aSource
, aProperty
, aTruthValue
, aTarget
);
92 NS_IMETHOD
GetTargets(nsIRDFResource
* aSource
,
93 nsIRDFResource
* aProperty
,
95 nsISimpleEnumerator
** aTargets
) {
96 return mInner
->GetTargets(aSource
, aProperty
, aTruthValue
, aTargets
);
99 NS_IMETHOD
Assert(nsIRDFResource
* aSource
,
100 nsIRDFResource
* aProperty
,
103 return mInner
->Assert(aSource
, aProperty
, aTarget
, aTruthValue
);
106 NS_IMETHOD
Unassert(nsIRDFResource
* aSource
,
107 nsIRDFResource
* aProperty
,
108 nsIRDFNode
* aTarget
) {
109 return mInner
->Unassert(aSource
, aProperty
, aTarget
);
112 NS_IMETHOD
Change(nsIRDFResource
* aSource
,
113 nsIRDFResource
* aProperty
,
114 nsIRDFNode
* aOldTarget
,
115 nsIRDFNode
* aNewTarget
) {
116 return mInner
->Change(aSource
, aProperty
, aOldTarget
, aNewTarget
);
119 NS_IMETHOD
Move(nsIRDFResource
* aOldSource
,
120 nsIRDFResource
* aNewSource
,
121 nsIRDFResource
* aProperty
,
122 nsIRDFNode
* aTarget
) {
123 return mInner
->Move(aOldSource
, aNewSource
, aProperty
, aTarget
);
126 NS_IMETHOD
HasAssertion(nsIRDFResource
* aSource
,
127 nsIRDFResource
* aProperty
,
130 bool* hasAssertion
) {
131 return mInner
->HasAssertion(aSource
, aProperty
, aTarget
, aTruthValue
, hasAssertion
);
134 NS_IMETHOD
AddObserver(nsIRDFObserver
* aObserver
) {
135 return NS_ERROR_NOT_IMPLEMENTED
;
138 NS_IMETHOD
RemoveObserver(nsIRDFObserver
* aObserver
) {
139 return NS_ERROR_NOT_IMPLEMENTED
;
142 NS_IMETHOD
HasArcIn(nsIRDFNode
*aNode
, nsIRDFResource
*aArc
, bool *_retval
) {
143 return mInner
->HasArcIn(aNode
, aArc
, _retval
);
146 NS_IMETHOD
HasArcOut(nsIRDFResource
*aSource
, nsIRDFResource
*aArc
, bool *_retval
) {
147 return mInner
->HasArcOut(aSource
, aArc
, _retval
);
150 NS_IMETHOD
ArcLabelsIn(nsIRDFNode
* aNode
,
151 nsISimpleEnumerator
** aLabels
) {
152 return mInner
->ArcLabelsIn(aNode
, aLabels
);
155 NS_IMETHOD
ArcLabelsOut(nsIRDFResource
* aSource
,
156 nsISimpleEnumerator
** aLabels
) {
157 return mInner
->ArcLabelsOut(aSource
, aLabels
);
160 NS_IMETHOD
GetAllResources(nsISimpleEnumerator
** aResult
) {
161 return mInner
->GetAllResources(aResult
);
164 NS_IMETHOD
GetAllCmds(nsIRDFResource
* aSource
,
165 nsISimpleEnumerator
/*<nsIRDFResource>*/** aCommands
);
167 NS_IMETHOD
IsCommandEnabled(nsISupportsArray
/*<nsIRDFResource>*/* aSources
,
168 nsIRDFResource
* aCommand
,
169 nsISupportsArray
/*<nsIRDFResource>*/* aArguments
,
172 NS_IMETHOD
DoCommand(nsISupportsArray
/*<nsIRDFResource>*/* aSources
,
173 nsIRDFResource
* aCommand
,
174 nsISupportsArray
/*<nsIRDFResource>*/* aArguments
);
176 NS_IMETHOD
BeginUpdateBatch() {
177 return mInner
->BeginUpdateBatch();
180 NS_IMETHOD
EndUpdateBatch() {
181 return mInner
->EndUpdateBatch();
184 NS_IMETHOD
GetLoaded(bool* _result
);
185 NS_IMETHOD
Init(const char *uri
);
187 NS_IMETHOD
FlushTo(const char *aURI
);
188 NS_IMETHOD
Refresh(bool sync
);
194 ////////////////////////////////////////////////////////////////////////
197 LocalStoreImpl::LocalStoreImpl(void)
201 LocalStoreImpl::~LocalStoreImpl(void)
204 mRDFService
->UnregisterDataSource(this);
209 NS_NewLocalStore(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
211 NS_PRECONDITION(aOuter
== nullptr, "no aggregation");
213 return NS_ERROR_NO_AGGREGATION
;
215 NS_PRECONDITION(aResult
!= nullptr, "null ptr");
217 return NS_ERROR_NULL_POINTER
;
219 LocalStoreImpl
* impl
= new LocalStoreImpl();
221 return NS_ERROR_OUT_OF_MEMORY
;
227 if (NS_SUCCEEDED(rv
)) {
228 // Set up the result pointer
229 rv
= impl
->QueryInterface(aIID
, aResult
);
236 NS_IMPL_CYCLE_COLLECTION(LocalStoreImpl
, mInner
)
237 NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalStoreImpl
)
238 NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalStoreImpl
)
240 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStoreImpl
)
241 NS_INTERFACE_MAP_ENTRY(nsILocalStore
)
242 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource
)
243 NS_INTERFACE_MAP_ENTRY(nsIRDFRemoteDataSource
)
244 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
245 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
246 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsILocalStore
)
249 // nsILocalStore interface
251 // nsIRDFDataSource interface
254 LocalStoreImpl::GetLoaded(bool* _result
)
256 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
= do_QueryInterface(mInner
);
257 NS_ASSERTION(remote
!= nullptr, "not an nsIRDFRemoteDataSource");
259 return NS_ERROR_UNEXPECTED
;
261 return remote
->GetLoaded(_result
);
266 LocalStoreImpl::Init(const char *uri
)
272 LocalStoreImpl::Flush()
274 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
= do_QueryInterface(mInner
);
275 // FIXME Bug 340242: Temporarily make this a warning rather than an
276 // assertion until we sort out the ordering of how we write
277 // everything to the localstore, flush it, and disconnect it when
278 // we're getting profile-change notifications.
279 NS_WARN_IF_FALSE(remote
!= nullptr, "not an nsIRDFRemoteDataSource");
281 return NS_ERROR_UNEXPECTED
;
283 return remote
->Flush();
287 LocalStoreImpl::FlushTo(const char *aURI
)
289 // Do not ever implement this (security)
290 return NS_ERROR_NOT_IMPLEMENTED
;
294 LocalStoreImpl::Refresh(bool sync
)
296 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
= do_QueryInterface(mInner
);
297 NS_ASSERTION(remote
!= nullptr, "not an nsIRDFRemoteDataSource");
299 return NS_ERROR_UNEXPECTED
;
301 return remote
->Refresh(sync
);
305 LocalStoreImpl::Init()
310 if (NS_FAILED(rv
)) return rv
;
312 // register this as a named data source with the RDF service
313 mRDFService
= do_GetService(NS_RDF_CONTRACTID
"/rdf-service;1", &rv
);
314 if (NS_FAILED(rv
)) return rv
;
316 mRDFService
->RegisterDataSource(this, false);
318 // Register as an observer of profile changes
319 nsCOMPtr
<nsIObserverService
> obs
=
320 do_GetService("@mozilla.org/observer-service;1");
323 obs
->AddObserver(this, "profile-before-change", true);
324 obs
->AddObserver(this, "profile-do-change", true);
331 LocalStoreImpl::CreateLocalStore(nsIFile
* aFile
)
335 rv
= aFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0666);
336 if (NS_FAILED(rv
)) return rv
;
338 nsCOMPtr
<nsIOutputStream
> outStream
;
339 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStream
), aFile
);
340 if (NS_FAILED(rv
)) return rv
;
342 const char defaultRDF
[] =
343 "<?xml version=\"1.0\"?>\n" \
344 "<RDF:RDF xmlns:RDF=\"" RDF_NAMESPACE_URI
"\"\n" \
345 " xmlns:NC=\"" NC_NAMESPACE_URI
"\">\n" \
346 " <!-- Empty -->\n" \
350 rv
= outStream
->Write(defaultRDF
, sizeof(defaultRDF
)-1, &count
);
351 if (NS_FAILED(rv
)) return rv
;
353 if (count
!= sizeof(defaultRDF
)-1)
354 return NS_ERROR_UNEXPECTED
;
356 // Okay, now see if the file exists _for real_. If it's still
357 // not there, it could be that the profile service gave us
358 // back a read-only directory. Whatever.
359 bool fileExistsFlag
= false;
360 aFile
->Exists(&fileExistsFlag
);
362 return NS_ERROR_UNEXPECTED
;
368 LocalStoreImpl::LoadData()
372 // Look for localstore.rdf in the current profile
373 // directory. Bomb if we can't find it.
375 nsCOMPtr
<nsIFile
> aFile
;
376 rv
= NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE
, getter_AddRefs(aFile
));
377 if (NS_FAILED(rv
)) return rv
;
379 bool fileExistsFlag
= false;
380 (void)aFile
->Exists(&fileExistsFlag
);
381 if (!fileExistsFlag
) {
382 // if file doesn't exist, create it
383 rv
= CreateLocalStore(aFile
);
384 if (NS_FAILED(rv
)) return rv
;
387 mInner
= do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX
"xml-datasource", &rv
);
388 if (NS_FAILED(rv
)) return rv
;
390 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
= do_QueryInterface(mInner
, &rv
);
391 if (NS_FAILED(rv
)) return rv
;
393 nsCOMPtr
<nsIURI
> aURI
;
394 rv
= NS_NewFileURI(getter_AddRefs(aURI
), aFile
);
395 if (NS_FAILED(rv
)) return rv
;
398 rv
= aURI
->GetSpec(spec
);
399 if (NS_FAILED(rv
)) return rv
;
401 rv
= remote
->Init(spec
.get());
402 if (NS_FAILED(rv
)) return rv
;
404 // Read the datasource synchronously.
405 rv
= remote
->Refresh(true);
408 // Load failed, delete and recreate a fresh localstore
410 rv
= CreateLocalStore(aFile
);
411 if (NS_FAILED(rv
)) return rv
;
413 rv
= remote
->Refresh(true);
421 LocalStoreImpl::GetURI(char* *aURI
)
423 NS_PRECONDITION(aURI
!= nullptr, "null ptr");
425 return NS_ERROR_NULL_POINTER
;
427 *aURI
= NS_strdup("rdf:local-store");
429 return NS_ERROR_OUT_OF_MEMORY
;
436 LocalStoreImpl::GetAllCmds(nsIRDFResource
* aSource
,
437 nsISimpleEnumerator
/*<nsIRDFResource>*/** aCommands
)
439 return(NS_NewEmptyEnumerator(aCommands
));
443 LocalStoreImpl::IsCommandEnabled(nsISupportsArray
/*<nsIRDFResource>*/* aSources
,
444 nsIRDFResource
* aCommand
,
445 nsISupportsArray
/*<nsIRDFResource>*/* aArguments
,
453 LocalStoreImpl::DoCommand(nsISupportsArray
* aSources
,
454 nsIRDFResource
* aCommand
,
455 nsISupportsArray
* aArguments
)
462 LocalStoreImpl::Observe(nsISupports
*aSubject
, const char *aTopic
, const char16_t
*someData
)
466 if (!nsCRT::strcmp(aTopic
, "profile-before-change")) {
467 // Write out the old datasource's contents.
469 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
= do_QueryInterface(mInner
);
474 // Create an in-memory datasource for use while we're
476 mInner
= do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX
"in-memory-datasource");
478 else if (!nsCRT::strcmp(aTopic
, "profile-do-change")) {