|
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/. */ |
|
6 |
|
7 /* |
|
8 |
|
9 Implementation for the local store |
|
10 |
|
11 */ |
|
12 |
|
13 #include "nsNetUtil.h" |
|
14 #include "nsIURI.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" |
|
23 #include "nsRDFCID.h" |
|
24 #include "nsXPIDLString.h" |
|
25 #include "plstr.h" |
|
26 #include "rdf.h" |
|
27 #include "nsCOMPtr.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" |
|
34 #include "nsCRT.h" |
|
35 #include "nsEnumeratorUtils.h" |
|
36 #include "nsCycleCollectionParticipant.h" |
|
37 |
|
38 //////////////////////////////////////////////////////////////////////// |
|
39 |
|
40 class LocalStoreImpl : public nsILocalStore, |
|
41 public nsIRDFDataSource, |
|
42 public nsIRDFRemoteDataSource, |
|
43 public nsIObserver, |
|
44 public nsSupportsWeakReference |
|
45 { |
|
46 protected: |
|
47 nsCOMPtr<nsIRDFDataSource> mInner; |
|
48 |
|
49 LocalStoreImpl(); |
|
50 virtual ~LocalStoreImpl(); |
|
51 nsresult Init(); |
|
52 nsresult CreateLocalStore(nsIFile* aFile); |
|
53 nsresult LoadData(); |
|
54 |
|
55 friend nsresult |
|
56 NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult); |
|
57 |
|
58 nsCOMPtr<nsIRDFService> mRDFService; |
|
59 |
|
60 public: |
|
61 // nsISupports interface |
|
62 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
63 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(LocalStoreImpl, nsILocalStore) |
|
64 |
|
65 // nsILocalStore interface |
|
66 |
|
67 // nsIRDFDataSource interface. Most of these are just delegated to |
|
68 // the inner, in-memory datasource. |
|
69 NS_IMETHOD GetURI(char* *aURI); |
|
70 |
|
71 NS_IMETHOD GetSource(nsIRDFResource* aProperty, |
|
72 nsIRDFNode* aTarget, |
|
73 bool aTruthValue, |
|
74 nsIRDFResource** aSource) { |
|
75 return mInner->GetSource(aProperty, aTarget, aTruthValue, aSource); |
|
76 } |
|
77 |
|
78 NS_IMETHOD GetSources(nsIRDFResource* aProperty, |
|
79 nsIRDFNode* aTarget, |
|
80 bool aTruthValue, |
|
81 nsISimpleEnumerator** aSources) { |
|
82 return mInner->GetSources(aProperty, aTarget, aTruthValue, aSources); |
|
83 } |
|
84 |
|
85 NS_IMETHOD GetTarget(nsIRDFResource* aSource, |
|
86 nsIRDFResource* aProperty, |
|
87 bool aTruthValue, |
|
88 nsIRDFNode** aTarget) { |
|
89 return mInner->GetTarget(aSource, aProperty, aTruthValue, aTarget); |
|
90 } |
|
91 |
|
92 NS_IMETHOD GetTargets(nsIRDFResource* aSource, |
|
93 nsIRDFResource* aProperty, |
|
94 bool aTruthValue, |
|
95 nsISimpleEnumerator** aTargets) { |
|
96 return mInner->GetTargets(aSource, aProperty, aTruthValue, aTargets); |
|
97 } |
|
98 |
|
99 NS_IMETHOD Assert(nsIRDFResource* aSource, |
|
100 nsIRDFResource* aProperty, |
|
101 nsIRDFNode* aTarget, |
|
102 bool aTruthValue) { |
|
103 return mInner->Assert(aSource, aProperty, aTarget, aTruthValue); |
|
104 } |
|
105 |
|
106 NS_IMETHOD Unassert(nsIRDFResource* aSource, |
|
107 nsIRDFResource* aProperty, |
|
108 nsIRDFNode* aTarget) { |
|
109 return mInner->Unassert(aSource, aProperty, aTarget); |
|
110 } |
|
111 |
|
112 NS_IMETHOD Change(nsIRDFResource* aSource, |
|
113 nsIRDFResource* aProperty, |
|
114 nsIRDFNode* aOldTarget, |
|
115 nsIRDFNode* aNewTarget) { |
|
116 return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget); |
|
117 } |
|
118 |
|
119 NS_IMETHOD Move(nsIRDFResource* aOldSource, |
|
120 nsIRDFResource* aNewSource, |
|
121 nsIRDFResource* aProperty, |
|
122 nsIRDFNode* aTarget) { |
|
123 return mInner->Move(aOldSource, aNewSource, aProperty, aTarget); |
|
124 } |
|
125 |
|
126 NS_IMETHOD HasAssertion(nsIRDFResource* aSource, |
|
127 nsIRDFResource* aProperty, |
|
128 nsIRDFNode* aTarget, |
|
129 bool aTruthValue, |
|
130 bool* hasAssertion) { |
|
131 return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, hasAssertion); |
|
132 } |
|
133 |
|
134 NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) { |
|
135 return NS_ERROR_NOT_IMPLEMENTED; |
|
136 } |
|
137 |
|
138 NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) { |
|
139 return NS_ERROR_NOT_IMPLEMENTED; |
|
140 } |
|
141 |
|
142 NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval) { |
|
143 return mInner->HasArcIn(aNode, aArc, _retval); |
|
144 } |
|
145 |
|
146 NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval) { |
|
147 return mInner->HasArcOut(aSource, aArc, _retval); |
|
148 } |
|
149 |
|
150 NS_IMETHOD ArcLabelsIn(nsIRDFNode* aNode, |
|
151 nsISimpleEnumerator** aLabels) { |
|
152 return mInner->ArcLabelsIn(aNode, aLabels); |
|
153 } |
|
154 |
|
155 NS_IMETHOD ArcLabelsOut(nsIRDFResource* aSource, |
|
156 nsISimpleEnumerator** aLabels) { |
|
157 return mInner->ArcLabelsOut(aSource, aLabels); |
|
158 } |
|
159 |
|
160 NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) { |
|
161 return mInner->GetAllResources(aResult); |
|
162 } |
|
163 |
|
164 NS_IMETHOD GetAllCmds(nsIRDFResource* aSource, |
|
165 nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands); |
|
166 |
|
167 NS_IMETHOD IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources, |
|
168 nsIRDFResource* aCommand, |
|
169 nsISupportsArray/*<nsIRDFResource>*/* aArguments, |
|
170 bool* aResult); |
|
171 |
|
172 NS_IMETHOD DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources, |
|
173 nsIRDFResource* aCommand, |
|
174 nsISupportsArray/*<nsIRDFResource>*/* aArguments); |
|
175 |
|
176 NS_IMETHOD BeginUpdateBatch() { |
|
177 return mInner->BeginUpdateBatch(); |
|
178 } |
|
179 |
|
180 NS_IMETHOD EndUpdateBatch() { |
|
181 return mInner->EndUpdateBatch(); |
|
182 } |
|
183 |
|
184 NS_IMETHOD GetLoaded(bool* _result); |
|
185 NS_IMETHOD Init(const char *uri); |
|
186 NS_IMETHOD Flush(); |
|
187 NS_IMETHOD FlushTo(const char *aURI); |
|
188 NS_IMETHOD Refresh(bool sync); |
|
189 |
|
190 // nsIObserver |
|
191 NS_DECL_NSIOBSERVER |
|
192 }; |
|
193 |
|
194 //////////////////////////////////////////////////////////////////////// |
|
195 |
|
196 |
|
197 LocalStoreImpl::LocalStoreImpl(void) |
|
198 { |
|
199 } |
|
200 |
|
201 LocalStoreImpl::~LocalStoreImpl(void) |
|
202 { |
|
203 if (mRDFService) |
|
204 mRDFService->UnregisterDataSource(this); |
|
205 } |
|
206 |
|
207 |
|
208 nsresult |
|
209 NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult) |
|
210 { |
|
211 NS_PRECONDITION(aOuter == nullptr, "no aggregation"); |
|
212 if (aOuter) |
|
213 return NS_ERROR_NO_AGGREGATION; |
|
214 |
|
215 NS_PRECONDITION(aResult != nullptr, "null ptr"); |
|
216 if (! aResult) |
|
217 return NS_ERROR_NULL_POINTER; |
|
218 |
|
219 LocalStoreImpl* impl = new LocalStoreImpl(); |
|
220 if (! impl) |
|
221 return NS_ERROR_OUT_OF_MEMORY; |
|
222 |
|
223 NS_ADDREF(impl); |
|
224 |
|
225 nsresult rv; |
|
226 rv = impl->Init(); |
|
227 if (NS_SUCCEEDED(rv)) { |
|
228 // Set up the result pointer |
|
229 rv = impl->QueryInterface(aIID, aResult); |
|
230 } |
|
231 |
|
232 NS_RELEASE(impl); |
|
233 return rv; |
|
234 } |
|
235 |
|
236 NS_IMPL_CYCLE_COLLECTION(LocalStoreImpl, mInner) |
|
237 NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalStoreImpl) |
|
238 NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalStoreImpl) |
|
239 |
|
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) |
|
247 NS_INTERFACE_MAP_END |
|
248 |
|
249 // nsILocalStore interface |
|
250 |
|
251 // nsIRDFDataSource interface |
|
252 |
|
253 NS_IMETHODIMP |
|
254 LocalStoreImpl::GetLoaded(bool* _result) |
|
255 { |
|
256 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner); |
|
257 NS_ASSERTION(remote != nullptr, "not an nsIRDFRemoteDataSource"); |
|
258 if (! remote) |
|
259 return NS_ERROR_UNEXPECTED; |
|
260 |
|
261 return remote->GetLoaded(_result); |
|
262 } |
|
263 |
|
264 |
|
265 NS_IMETHODIMP |
|
266 LocalStoreImpl::Init(const char *uri) |
|
267 { |
|
268 return(NS_OK); |
|
269 } |
|
270 |
|
271 NS_IMETHODIMP |
|
272 LocalStoreImpl::Flush() |
|
273 { |
|
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"); |
|
280 if (! remote) |
|
281 return NS_ERROR_UNEXPECTED; |
|
282 |
|
283 return remote->Flush(); |
|
284 } |
|
285 |
|
286 NS_IMETHODIMP |
|
287 LocalStoreImpl::FlushTo(const char *aURI) |
|
288 { |
|
289 // Do not ever implement this (security) |
|
290 return NS_ERROR_NOT_IMPLEMENTED; |
|
291 } |
|
292 |
|
293 NS_IMETHODIMP |
|
294 LocalStoreImpl::Refresh(bool sync) |
|
295 { |
|
296 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner); |
|
297 NS_ASSERTION(remote != nullptr, "not an nsIRDFRemoteDataSource"); |
|
298 if (! remote) |
|
299 return NS_ERROR_UNEXPECTED; |
|
300 |
|
301 return remote->Refresh(sync); |
|
302 } |
|
303 |
|
304 nsresult |
|
305 LocalStoreImpl::Init() |
|
306 { |
|
307 nsresult rv; |
|
308 |
|
309 rv = LoadData(); |
|
310 if (NS_FAILED(rv)) return rv; |
|
311 |
|
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; |
|
315 |
|
316 mRDFService->RegisterDataSource(this, false); |
|
317 |
|
318 // Register as an observer of profile changes |
|
319 nsCOMPtr<nsIObserverService> obs = |
|
320 do_GetService("@mozilla.org/observer-service;1"); |
|
321 |
|
322 if (obs) { |
|
323 obs->AddObserver(this, "profile-before-change", true); |
|
324 obs->AddObserver(this, "profile-do-change", true); |
|
325 } |
|
326 |
|
327 return NS_OK; |
|
328 } |
|
329 |
|
330 nsresult |
|
331 LocalStoreImpl::CreateLocalStore(nsIFile* aFile) |
|
332 { |
|
333 nsresult rv; |
|
334 |
|
335 rv = aFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666); |
|
336 if (NS_FAILED(rv)) return rv; |
|
337 |
|
338 nsCOMPtr<nsIOutputStream> outStream; |
|
339 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), aFile); |
|
340 if (NS_FAILED(rv)) return rv; |
|
341 |
|
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" \ |
|
347 "</RDF:RDF>\n"; |
|
348 |
|
349 uint32_t count; |
|
350 rv = outStream->Write(defaultRDF, sizeof(defaultRDF)-1, &count); |
|
351 if (NS_FAILED(rv)) return rv; |
|
352 |
|
353 if (count != sizeof(defaultRDF)-1) |
|
354 return NS_ERROR_UNEXPECTED; |
|
355 |
|
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); |
|
361 if (!fileExistsFlag) |
|
362 return NS_ERROR_UNEXPECTED; |
|
363 |
|
364 return NS_OK; |
|
365 } |
|
366 |
|
367 nsresult |
|
368 LocalStoreImpl::LoadData() |
|
369 { |
|
370 nsresult rv; |
|
371 |
|
372 // Look for localstore.rdf in the current profile |
|
373 // directory. Bomb if we can't find it. |
|
374 |
|
375 nsCOMPtr<nsIFile> aFile; |
|
376 rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile)); |
|
377 if (NS_FAILED(rv)) return rv; |
|
378 |
|
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; |
|
385 } |
|
386 |
|
387 mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "xml-datasource", &rv); |
|
388 if (NS_FAILED(rv)) return rv; |
|
389 |
|
390 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner, &rv); |
|
391 if (NS_FAILED(rv)) return rv; |
|
392 |
|
393 nsCOMPtr<nsIURI> aURI; |
|
394 rv = NS_NewFileURI(getter_AddRefs(aURI), aFile); |
|
395 if (NS_FAILED(rv)) return rv; |
|
396 |
|
397 nsAutoCString spec; |
|
398 rv = aURI->GetSpec(spec); |
|
399 if (NS_FAILED(rv)) return rv; |
|
400 |
|
401 rv = remote->Init(spec.get()); |
|
402 if (NS_FAILED(rv)) return rv; |
|
403 |
|
404 // Read the datasource synchronously. |
|
405 rv = remote->Refresh(true); |
|
406 |
|
407 if (NS_FAILED(rv)) { |
|
408 // Load failed, delete and recreate a fresh localstore |
|
409 aFile->Remove(true); |
|
410 rv = CreateLocalStore(aFile); |
|
411 if (NS_FAILED(rv)) return rv; |
|
412 |
|
413 rv = remote->Refresh(true); |
|
414 } |
|
415 |
|
416 return rv; |
|
417 } |
|
418 |
|
419 |
|
420 NS_IMETHODIMP |
|
421 LocalStoreImpl::GetURI(char* *aURI) |
|
422 { |
|
423 NS_PRECONDITION(aURI != nullptr, "null ptr"); |
|
424 if (! aURI) |
|
425 return NS_ERROR_NULL_POINTER; |
|
426 |
|
427 *aURI = NS_strdup("rdf:local-store"); |
|
428 if (! *aURI) |
|
429 return NS_ERROR_OUT_OF_MEMORY; |
|
430 |
|
431 return NS_OK; |
|
432 } |
|
433 |
|
434 |
|
435 NS_IMETHODIMP |
|
436 LocalStoreImpl::GetAllCmds(nsIRDFResource* aSource, |
|
437 nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands) |
|
438 { |
|
439 return(NS_NewEmptyEnumerator(aCommands)); |
|
440 } |
|
441 |
|
442 NS_IMETHODIMP |
|
443 LocalStoreImpl::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources, |
|
444 nsIRDFResource* aCommand, |
|
445 nsISupportsArray/*<nsIRDFResource>*/* aArguments, |
|
446 bool* aResult) |
|
447 { |
|
448 *aResult = true; |
|
449 return NS_OK; |
|
450 } |
|
451 |
|
452 NS_IMETHODIMP |
|
453 LocalStoreImpl::DoCommand(nsISupportsArray* aSources, |
|
454 nsIRDFResource* aCommand, |
|
455 nsISupportsArray* aArguments) |
|
456 { |
|
457 // no-op |
|
458 return NS_OK; |
|
459 } |
|
460 |
|
461 NS_IMETHODIMP |
|
462 LocalStoreImpl::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) |
|
463 { |
|
464 nsresult rv = NS_OK; |
|
465 |
|
466 if (!nsCRT::strcmp(aTopic, "profile-before-change")) { |
|
467 // Write out the old datasource's contents. |
|
468 if (mInner) { |
|
469 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner); |
|
470 if (remote) |
|
471 remote->Flush(); |
|
472 } |
|
473 |
|
474 // Create an in-memory datasource for use while we're |
|
475 // profile-less. |
|
476 mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "in-memory-datasource"); |
|
477 |
|
478 if (!nsCRT::strcmp(NS_ConvertUTF16toUTF8(someData).get(), "shutdown-cleanse")) { |
|
479 nsCOMPtr<nsIFile> aFile; |
|
480 rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile)); |
|
481 if (NS_SUCCEEDED(rv)) |
|
482 rv = aFile->Remove(false); |
|
483 } |
|
484 } |
|
485 else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { |
|
486 rv = LoadData(); |
|
487 } |
|
488 return rv; |
|
489 } |