Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
8 A data source that can read itself from and write itself to an
9 RDF/XML stream.
11 For more information on the RDF/XML syntax,
12 see http://www.w3.org/TR/REC-rdf-syntax/.
14 This code is based on the final W3C Recommendation,
15 http://www.w3.org/TR/1999/REC-rdf-syntax-19990222.
18 TO DO
19 -----
21 1) Right now, the only kind of stream data sources that are _really_
22 writable are "file:" URIs. (In fact, _all_ "file:" URIs are
23 writable, modulo file system permissions; this may lead to some
24 surprising behavior.) Eventually, it'd be great if we could open
25 an arbitrary nsIOutputStream on *any* URL, and Netlib could just
26 do the magic.
28 2) Implement a more terse output for "typed" nodes; that is, instead
29 of "RDF:Description type='ns:foo'", just output "ns:foo".
31 3) When re-serializing, we "cheat" for Descriptions that talk about
32 inline resources (i.e.., using the `ID' attribute specified in
33 [6.21]). Instead of writing an `ID="foo"' for the first instance,
34 and then `about="#foo"' for each subsequent instance, we just
35 _always_ write `about="#foo"'.
37 We do this so that we can handle the case where an RDF container
38 has been assigned arbitrary properties: the spec says we can't
39 dangle the attributes directly off the container, so we need to
40 refer to it. Of course, with a little cleverness, we could fix
41 this. But who cares?
43 4) When re-serializing containers. We have to cheat on some
44 containers, and use an illegal "about=" construct. We do this to
45 handle containers that have been assigned URIs outside of the
46 local document.
49 Logging
50 -------
52 To turn on logging for this module, set
54 NSPR_LOG_MODULES=nsRDFXMLDataSource:5
56 */
58 #include "nsIFileStreams.h"
59 #include "nsIOutputStream.h"
60 #include "nsIFile.h"
61 #include "nsIFileChannel.h"
62 #include "nsIDTD.h"
63 #include "nsIRDFPurgeableDataSource.h"
64 #include "nsIInputStream.h"
65 #include "nsIOutputStream.h"
66 #include "nsIRDFContainerUtils.h"
67 #include "nsIRDFNode.h"
68 #include "nsIRDFRemoteDataSource.h"
69 #include "nsIRDFService.h"
70 #include "nsIRDFXMLParser.h"
71 #include "nsIRDFXMLSerializer.h"
72 #include "nsIRDFXMLSink.h"
73 #include "nsIRDFXMLSource.h"
74 #include "nsIServiceManager.h"
75 #include "nsIStreamListener.h"
76 #include "nsIURL.h"
77 #include "nsIFileURL.h"
78 #include "nsNetUtil.h"
79 #include "nsIChannel.h"
80 #include "nsRDFCID.h"
81 #include "nsRDFBaseDataSources.h"
82 #include "nsCOMArray.h"
83 #include "nsXPIDLString.h"
84 #include "plstr.h"
85 #include "prio.h"
86 #include "prthread.h"
87 #include "rdf.h"
88 #include "rdfutil.h"
89 #include "prlog.h"
90 #include "nsNameSpaceMap.h"
91 #include "nsCRT.h"
92 #include "nsCycleCollectionParticipant.h"
93 #include "nsIScriptSecurityManager.h"
94 #include "nsIChannelEventSink.h"
95 #include "nsIAsyncVerifyRedirectCallback.h"
96 #include "nsNetUtil.h"
98 #include "rdfIDataSource.h"
100 //----------------------------------------------------------------------
101 //
102 // RDFXMLDataSourceImpl
103 //
105 class RDFXMLDataSourceImpl : public nsIRDFDataSource,
106 public nsIRDFRemoteDataSource,
107 public nsIRDFXMLSink,
108 public nsIRDFXMLSource,
109 public nsIStreamListener,
110 public rdfIDataSource,
111 public nsIInterfaceRequestor,
112 public nsIChannelEventSink
113 {
114 protected:
115 enum LoadState {
116 eLoadState_Unloaded,
117 eLoadState_Pending,
118 eLoadState_Loading,
119 eLoadState_Loaded
120 };
122 nsCOMPtr<nsIRDFDataSource> mInner;
123 bool mIsWritable; // true if the document can be written back
124 bool mIsDirty; // true if the document should be written back
125 LoadState mLoadState; // what we're doing now
126 nsCOMArray<nsIRDFXMLSinkObserver> mObservers;
127 nsCOMPtr<nsIURI> mURL;
128 nsCOMPtr<nsIStreamListener> mListener;
129 nsNameSpaceMap mNameSpaces;
131 // pseudo-constants
132 static int32_t gRefCnt;
133 static nsIRDFService* gRDFService;
135 #ifdef PR_LOGGING
136 static PRLogModuleInfo* gLog;
137 #endif
139 nsresult Init();
140 RDFXMLDataSourceImpl(void);
141 virtual ~RDFXMLDataSourceImpl(void);
142 nsresult rdfXMLFlush(nsIURI *aURI);
144 friend nsresult
145 NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult);
147 inline bool IsLoading() {
148 return (mLoadState == eLoadState_Pending) ||
149 (mLoadState == eLoadState_Loading);
150 }
152 public:
153 // nsISupports
154 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
155 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(RDFXMLDataSourceImpl,
156 nsIRDFDataSource)
158 // nsIRDFDataSource
159 NS_IMETHOD GetURI(char* *uri);
161 NS_IMETHOD GetSource(nsIRDFResource* property,
162 nsIRDFNode* target,
163 bool tv,
164 nsIRDFResource** source) {
165 return mInner->GetSource(property, target, tv, source);
166 }
168 NS_IMETHOD GetSources(nsIRDFResource* property,
169 nsIRDFNode* target,
170 bool tv,
171 nsISimpleEnumerator** sources) {
172 return mInner->GetSources(property, target, tv, sources);
173 }
175 NS_IMETHOD GetTarget(nsIRDFResource* source,
176 nsIRDFResource* property,
177 bool tv,
178 nsIRDFNode** target) {
179 return mInner->GetTarget(source, property, tv, target);
180 }
182 NS_IMETHOD GetTargets(nsIRDFResource* source,
183 nsIRDFResource* property,
184 bool tv,
185 nsISimpleEnumerator** targets) {
186 return mInner->GetTargets(source, property, tv, targets);
187 }
189 NS_IMETHOD Assert(nsIRDFResource* aSource,
190 nsIRDFResource* aProperty,
191 nsIRDFNode* aTarget,
192 bool tv);
194 NS_IMETHOD Unassert(nsIRDFResource* source,
195 nsIRDFResource* property,
196 nsIRDFNode* target);
198 NS_IMETHOD Change(nsIRDFResource* aSource,
199 nsIRDFResource* aProperty,
200 nsIRDFNode* aOldTarget,
201 nsIRDFNode* aNewTarget);
203 NS_IMETHOD Move(nsIRDFResource* aOldSource,
204 nsIRDFResource* aNewSource,
205 nsIRDFResource* aProperty,
206 nsIRDFNode* aTarget);
208 NS_IMETHOD HasAssertion(nsIRDFResource* source,
209 nsIRDFResource* property,
210 nsIRDFNode* target,
211 bool tv,
212 bool* hasAssertion) {
213 return mInner->HasAssertion(source, property, target, tv, hasAssertion);
214 }
216 NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) {
217 return mInner->AddObserver(aObserver);
218 }
220 NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) {
221 return mInner->RemoveObserver(aObserver);
222 }
224 NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval) {
225 return mInner->HasArcIn(aNode, aArc, _retval);
226 }
228 NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval) {
229 return mInner->HasArcOut(aSource, aArc, _retval);
230 }
232 NS_IMETHOD ArcLabelsIn(nsIRDFNode* node,
233 nsISimpleEnumerator** labels) {
234 return mInner->ArcLabelsIn(node, labels);
235 }
237 NS_IMETHOD ArcLabelsOut(nsIRDFResource* source,
238 nsISimpleEnumerator** labels) {
239 return mInner->ArcLabelsOut(source, labels);
240 }
242 NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) {
243 return mInner->GetAllResources(aResult);
244 }
246 NS_IMETHOD GetAllCmds(nsIRDFResource* source,
247 nsISimpleEnumerator/*<nsIRDFResource>*/** commands) {
248 return mInner->GetAllCmds(source, commands);
249 }
251 NS_IMETHOD IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
252 nsIRDFResource* aCommand,
253 nsISupportsArray/*<nsIRDFResource>*/* aArguments,
254 bool* aResult) {
255 return mInner->IsCommandEnabled(aSources, aCommand, aArguments, aResult);
256 }
258 NS_IMETHOD DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
259 nsIRDFResource* aCommand,
260 nsISupportsArray/*<nsIRDFResource>*/* aArguments) {
261 // XXX Uh oh, this could cause problems wrt. the "dirty" flag
262 // if it changes the in-memory store's internal state.
263 return mInner->DoCommand(aSources, aCommand, aArguments);
264 }
266 NS_IMETHOD BeginUpdateBatch() {
267 return mInner->BeginUpdateBatch();
268 }
270 NS_IMETHOD EndUpdateBatch() {
271 return mInner->EndUpdateBatch();
272 }
274 // nsIRDFRemoteDataSource interface
275 NS_DECL_NSIRDFREMOTEDATASOURCE
277 // nsIRDFXMLSink interface
278 NS_DECL_NSIRDFXMLSINK
280 // nsIRDFXMLSource interface
281 NS_DECL_NSIRDFXMLSOURCE
283 // nsIRequestObserver
284 NS_DECL_NSIREQUESTOBSERVER
286 // nsIStreamListener
287 NS_DECL_NSISTREAMLISTENER
289 // nsIInterfaceRequestor
290 NS_DECL_NSIINTERFACEREQUESTOR
292 // nsIChannelEventSink
293 NS_DECL_NSICHANNELEVENTSINK
295 // rdfIDataSource
296 NS_IMETHOD VisitAllSubjects(rdfITripleVisitor *aVisitor) {
297 nsresult rv;
298 nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
299 if (NS_FAILED(rv)) return rv;
300 return rdfds->VisitAllSubjects(aVisitor);
301 }
303 NS_IMETHOD VisitAllTriples(rdfITripleVisitor *aVisitor) {
304 nsresult rv;
305 nsCOMPtr<rdfIDataSource> rdfds = do_QueryInterface(mInner, &rv);
306 if (NS_FAILED(rv)) return rv;
307 return rdfds->VisitAllTriples(aVisitor);
308 }
310 // Implementation methods
311 bool
312 MakeQName(nsIRDFResource* aResource,
313 nsString& property,
314 nsString& nameSpacePrefix,
315 nsString& nameSpaceURI);
317 nsresult
318 SerializeAssertion(nsIOutputStream* aStream,
319 nsIRDFResource* aResource,
320 nsIRDFResource* aProperty,
321 nsIRDFNode* aValue);
323 nsresult
324 SerializeProperty(nsIOutputStream* aStream,
325 nsIRDFResource* aResource,
326 nsIRDFResource* aProperty);
328 bool
329 IsContainerProperty(nsIRDFResource* aProperty);
331 nsresult
332 SerializeDescription(nsIOutputStream* aStream,
333 nsIRDFResource* aResource);
335 nsresult
336 SerializeMember(nsIOutputStream* aStream,
337 nsIRDFResource* aContainer,
338 nsIRDFNode* aMember);
340 nsresult
341 SerializeContainer(nsIOutputStream* aStream,
342 nsIRDFResource* aContainer);
344 nsresult
345 SerializePrologue(nsIOutputStream* aStream);
347 nsresult
348 SerializeEpilogue(nsIOutputStream* aStream);
350 bool
351 IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType);
353 protected:
354 nsresult
355 BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer);
356 };
358 int32_t RDFXMLDataSourceImpl::gRefCnt = 0;
359 nsIRDFService* RDFXMLDataSourceImpl::gRDFService;
361 #ifdef PR_LOGGING
362 PRLogModuleInfo* RDFXMLDataSourceImpl::gLog;
363 #endif
365 static const char kFileURIPrefix[] = "file:";
366 static const char kResourceURIPrefix[] = "resource:";
369 //----------------------------------------------------------------------
371 nsresult
372 NS_NewRDFXMLDataSource(nsIRDFDataSource** aResult)
373 {
374 NS_PRECONDITION(aResult != nullptr, "null ptr");
375 if (! aResult)
376 return NS_ERROR_NULL_POINTER;
378 RDFXMLDataSourceImpl* datasource = new RDFXMLDataSourceImpl();
379 if (! datasource)
380 return NS_ERROR_OUT_OF_MEMORY;
382 nsresult rv;
383 rv = datasource->Init();
385 if (NS_FAILED(rv)) {
386 delete datasource;
387 return rv;
388 }
390 NS_ADDREF(datasource);
391 *aResult = datasource;
392 return NS_OK;
393 }
396 RDFXMLDataSourceImpl::RDFXMLDataSourceImpl(void)
397 : mIsWritable(true),
398 mIsDirty(false),
399 mLoadState(eLoadState_Unloaded)
400 {
401 #ifdef PR_LOGGING
402 if (! gLog)
403 gLog = PR_NewLogModule("nsRDFXMLDataSource");
404 #endif
405 }
408 nsresult
409 RDFXMLDataSourceImpl::Init()
410 {
411 nsresult rv;
412 NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
413 mInner = do_CreateInstance(kRDFInMemoryDataSourceCID, &rv);
414 if (NS_FAILED(rv)) return rv;
416 if (gRefCnt++ == 0) {
417 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
418 rv = CallGetService(kRDFServiceCID, &gRDFService);
420 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
421 if (NS_FAILED(rv)) return rv;
422 }
424 return NS_OK;
425 }
428 RDFXMLDataSourceImpl::~RDFXMLDataSourceImpl(void)
429 {
430 // Unregister first so that nobody else tries to get us.
431 (void) gRDFService->UnregisterDataSource(this);
433 // Now flush contents
434 (void) Flush();
436 // Release RDF/XML sink observers
437 mObservers.Clear();
439 if (--gRefCnt == 0)
440 NS_IF_RELEASE(gRDFService);
441 }
443 NS_IMPL_CYCLE_COLLECTION_CLASS(RDFXMLDataSourceImpl)
445 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(RDFXMLDataSourceImpl)
446 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(RDFXMLDataSourceImpl)
447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
450 NS_IMPL_CYCLE_COLLECTING_ADDREF(RDFXMLDataSourceImpl)
451 NS_IMPL_CYCLE_COLLECTING_RELEASE(RDFXMLDataSourceImpl)
453 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RDFXMLDataSourceImpl)
454 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
455 NS_INTERFACE_MAP_ENTRY(nsIRDFRemoteDataSource)
456 NS_INTERFACE_MAP_ENTRY(nsIRDFXMLSink)
457 NS_INTERFACE_MAP_ENTRY(nsIRDFXMLSource)
458 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
459 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
460 NS_INTERFACE_MAP_ENTRY(rdfIDataSource)
461 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
462 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
463 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFDataSource)
464 NS_INTERFACE_MAP_END
466 // nsIInterfaceRequestor
467 NS_IMETHODIMP
468 RDFXMLDataSourceImpl::GetInterface(const nsIID& aIID, void** aSink)
469 {
470 return QueryInterface(aIID, aSink);
471 }
473 nsresult
474 RDFXMLDataSourceImpl::BlockingParse(nsIURI* aURL, nsIStreamListener* aConsumer)
475 {
476 nsresult rv;
478 // XXX I really hate the way that we're spoon-feeding this stuff
479 // to the parser: it seems like this is something that netlib
480 // should be able to do by itself.
482 nsCOMPtr<nsIChannel> channel;
483 nsCOMPtr<nsIRequest> request;
485 // Null LoadGroup ?
486 rv = NS_NewChannel(getter_AddRefs(channel), aURL, nullptr);
487 if (NS_FAILED(rv)) return rv;
488 nsCOMPtr<nsIInputStream> in;
489 rv = channel->Open(getter_AddRefs(in));
491 // Report success if the file doesn't exist, but propagate other errors.
492 if (rv == NS_ERROR_FILE_NOT_FOUND) return NS_OK;
493 if (NS_FAILED(rv)) return rv;
495 if (! in) {
496 NS_ERROR("no input stream");
497 return NS_ERROR_FAILURE;
498 }
500 // Wrap the channel's input stream in a buffered stream to ensure that
501 // ReadSegments is implemented (which OnDataAvailable expects).
502 nsCOMPtr<nsIInputStream> bufStream;
503 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), in,
504 4096 /* buffer size */);
505 if (NS_FAILED(rv)) return rv;
507 // Notify load observers
508 int32_t i;
509 for (i = mObservers.Count() - 1; i >= 0; --i) {
510 // Make sure to hold a strong reference to the observer so
511 // that it doesn't go away in this call if it removes itself
512 // as an observer
513 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
515 if (obs) {
516 obs->OnBeginLoad(this);
517 }
518 }
520 rv = aConsumer->OnStartRequest(channel, nullptr);
522 uint64_t offset = 0;
523 while (NS_SUCCEEDED(rv)) {
524 // Skip ODA if the channel is canceled
525 channel->GetStatus(&rv);
526 if (NS_FAILED(rv))
527 break;
529 uint64_t avail;
530 if (NS_FAILED(rv = bufStream->Available(&avail)))
531 break; // error
533 if (avail == 0)
534 break; // eof
536 if (avail > UINT32_MAX)
537 avail = UINT32_MAX;
539 rv = aConsumer->OnDataAvailable(channel, nullptr, bufStream, offset, (uint32_t)avail);
540 if (NS_SUCCEEDED(rv))
541 offset += avail;
542 }
544 if (NS_FAILED(rv))
545 channel->Cancel(rv);
547 channel->GetStatus(&rv);
548 aConsumer->OnStopRequest(channel, nullptr, rv);
550 // Notify load observers
551 for (i = mObservers.Count() - 1; i >= 0; --i) {
552 // Make sure to hold a strong reference to the observer so
553 // that it doesn't go away in this call if it removes itself
554 // as an observer
555 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
557 if (obs) {
558 if (NS_FAILED(rv))
559 obs->OnError(this, rv, nullptr);
561 obs->OnEndLoad(this);
562 }
563 }
565 return rv;
566 }
568 NS_IMETHODIMP
569 RDFXMLDataSourceImpl::GetLoaded(bool* _result)
570 {
571 *_result = (mLoadState == eLoadState_Loaded);
572 return NS_OK;
573 }
575 NS_IMETHODIMP
576 RDFXMLDataSourceImpl::Init(const char* uri)
577 {
578 NS_PRECONDITION(mInner != nullptr, "not initialized");
579 if (! mInner)
580 return NS_ERROR_OUT_OF_MEMORY;
582 nsresult rv;
584 rv = NS_NewURI(getter_AddRefs(mURL), nsDependentCString(uri));
585 if (NS_FAILED(rv)) return rv;
587 // XXX this is a hack: any "file:" URI is considered writable. All
588 // others are considered read-only.
589 if ((PL_strncmp(uri, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
590 (PL_strncmp(uri, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0)) {
591 mIsWritable = false;
592 }
594 rv = gRDFService->RegisterDataSource(this, false);
595 if (NS_FAILED(rv)) return rv;
597 return NS_OK;
598 }
601 NS_IMETHODIMP
602 RDFXMLDataSourceImpl::GetURI(char* *aURI)
603 {
604 *aURI = nullptr;
605 if (!mURL) {
606 return NS_OK;
607 }
609 nsAutoCString spec;
610 mURL->GetSpec(spec);
611 *aURI = ToNewCString(spec);
612 if (!*aURI) {
613 return NS_ERROR_OUT_OF_MEMORY;
614 }
616 return NS_OK;
617 }
619 NS_IMETHODIMP
620 RDFXMLDataSourceImpl::Assert(nsIRDFResource* aSource,
621 nsIRDFResource* aProperty,
622 nsIRDFNode* aTarget,
623 bool aTruthValue)
624 {
625 // We don't accept assertions unless we're writable (except in the
626 // case that we're actually _reading_ the datasource in).
627 nsresult rv;
629 if (IsLoading()) {
630 bool hasAssertion = false;
632 nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
633 if (gcable) {
634 rv = gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &hasAssertion);
635 if (NS_FAILED(rv)) return rv;
636 }
638 rv = NS_RDF_ASSERTION_ACCEPTED;
640 if (! hasAssertion) {
641 rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
643 if (NS_SUCCEEDED(rv) && gcable) {
644 // Now mark the new assertion, so it doesn't get
645 // removed when we sweep. Ignore rv, because we want
646 // to return what mInner->Assert() gave us.
647 bool didMark;
648 (void) gcable->Mark(aSource, aProperty, aTarget, aTruthValue, &didMark);
649 }
651 if (NS_FAILED(rv)) return rv;
652 }
654 return rv;
655 }
656 else if (mIsWritable) {
657 rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
659 if (rv == NS_RDF_ASSERTION_ACCEPTED)
660 mIsDirty = true;
662 return rv;
663 }
664 else {
665 return NS_RDF_ASSERTION_REJECTED;
666 }
667 }
670 NS_IMETHODIMP
671 RDFXMLDataSourceImpl::Unassert(nsIRDFResource* source,
672 nsIRDFResource* property,
673 nsIRDFNode* target)
674 {
675 // We don't accept assertions unless we're writable (except in the
676 // case that we're actually _reading_ the datasource in).
677 nsresult rv;
679 if (IsLoading() || mIsWritable) {
680 rv = mInner->Unassert(source, property, target);
681 if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
682 mIsDirty = true;
683 }
684 else {
685 rv = NS_RDF_ASSERTION_REJECTED;
686 }
688 return rv;
689 }
691 NS_IMETHODIMP
692 RDFXMLDataSourceImpl::Change(nsIRDFResource* aSource,
693 nsIRDFResource* aProperty,
694 nsIRDFNode* aOldTarget,
695 nsIRDFNode* aNewTarget)
696 {
697 nsresult rv;
699 if (IsLoading() || mIsWritable) {
700 rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
702 if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
703 mIsDirty = true;
704 }
705 else {
706 rv = NS_RDF_ASSERTION_REJECTED;
707 }
709 return rv;
710 }
712 NS_IMETHODIMP
713 RDFXMLDataSourceImpl::Move(nsIRDFResource* aOldSource,
714 nsIRDFResource* aNewSource,
715 nsIRDFResource* aProperty,
716 nsIRDFNode* aTarget)
717 {
718 nsresult rv;
720 if (IsLoading() || mIsWritable) {
721 rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
722 if (!IsLoading() && rv == NS_RDF_ASSERTION_ACCEPTED)
723 mIsDirty = true;
724 }
725 else {
726 rv = NS_RDF_ASSERTION_REJECTED;
727 }
729 return rv;
730 }
733 nsresult
734 RDFXMLDataSourceImpl::rdfXMLFlush(nsIURI *aURI)
735 {
737 nsresult rv;
739 {
740 // Quick and dirty check to see if we're in XPCOM shutdown. If
741 // we are, we're screwed: it's too late to serialize because
742 // many of the services that we'll need to acquire to properly
743 // write the file will be unaquirable.
744 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
745 nsCOMPtr<nsIRDFService> dummy = do_GetService(kRDFServiceCID, &rv);
746 if (NS_FAILED(rv)) {
747 NS_WARNING("unable to Flush() dirty datasource during XPCOM shutdown");
748 return rv;
749 }
750 }
752 // Is it a file? If so, we can write to it. Some day, it'd be nice
753 // if we didn't care what kind of stream this was...
754 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI);
756 if (fileURL) {
757 nsCOMPtr<nsIFile> file;
758 fileURL->GetFile(getter_AddRefs(file));
759 if (file) {
760 // get a safe output stream, so we don't clobber the datasource file unless
761 // all the writes succeeded.
762 nsCOMPtr<nsIOutputStream> out;
763 rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
764 file,
765 PR_WRONLY | PR_CREATE_FILE,
766 /*octal*/ 0666,
767 0);
768 if (NS_FAILED(rv)) return rv;
770 nsCOMPtr<nsIOutputStream> bufferedOut;
771 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 4096);
772 if (NS_FAILED(rv)) return rv;
774 rv = Serialize(bufferedOut);
775 if (NS_FAILED(rv)) return rv;
777 // All went ok. Maybe except for problems in Write(), but the stream detects
778 // that for us
779 nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOut, &rv);
780 if (NS_FAILED(rv)) return rv;
782 rv = safeStream->Finish();
783 if (NS_FAILED(rv)) {
784 NS_WARNING("failed to save datasource file! possible dataloss");
785 return rv;
786 }
787 }
788 }
790 return NS_OK;
791 }
794 NS_IMETHODIMP
795 RDFXMLDataSourceImpl::FlushTo(const char *aURI)
796 {
797 NS_PRECONDITION(aURI != nullptr, "not initialized");
798 if (!aURI)
799 return NS_ERROR_NULL_POINTER;
801 // XXX this is a hack: any "file:" URI is considered writable. All
802 // others are considered read-only.
803 if ((PL_strncmp(aURI, kFileURIPrefix, sizeof(kFileURIPrefix) - 1) != 0) &&
804 (PL_strncmp(aURI, kResourceURIPrefix, sizeof(kResourceURIPrefix) - 1) != 0))
805 {
806 return NS_ERROR_ILLEGAL_VALUE;
807 }
809 nsCOMPtr<nsIURI> url;
810 nsresult rv = NS_NewURI(getter_AddRefs(url), aURI);
811 if (NS_FAILED(rv))
812 return rv;
813 rv = rdfXMLFlush(url);
814 return rv;
815 }
818 NS_IMETHODIMP
819 RDFXMLDataSourceImpl::Flush(void)
820 {
821 if (!mIsWritable || !mIsDirty)
822 return NS_OK;
824 // while it is not fatal if mURL is not set,
825 // indicate failure since we can't flush back to an unknown origin
826 if (! mURL)
827 return NS_ERROR_NOT_INITIALIZED;
829 #ifdef PR_LOGGING
830 nsAutoCString spec;
831 mURL->GetSpec(spec);
832 PR_LOG(gLog, PR_LOG_NOTICE,
833 ("rdfxml[%p] flush(%s)", this, spec.get()));
834 #endif
836 nsresult rv;
837 if (NS_SUCCEEDED(rv = rdfXMLFlush(mURL)))
838 {
839 mIsDirty = false;
840 }
841 return rv;
842 }
845 //----------------------------------------------------------------------
846 //
847 // nsIRDFXMLDataSource methods
848 //
850 NS_IMETHODIMP
851 RDFXMLDataSourceImpl::GetReadOnly(bool* aIsReadOnly)
852 {
853 *aIsReadOnly = !mIsWritable;
854 return NS_OK;
855 }
858 NS_IMETHODIMP
859 RDFXMLDataSourceImpl::SetReadOnly(bool aIsReadOnly)
860 {
861 if (mIsWritable && aIsReadOnly)
862 mIsWritable = false;
864 return NS_OK;
865 }
867 // nsIChannelEventSink
869 // This code is copied from nsSameOriginChecker::OnChannelRedirect. See
870 // bug 475940 on providing this code in a shared location.
871 NS_IMETHODIMP
872 RDFXMLDataSourceImpl::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
873 nsIChannel *aNewChannel,
874 uint32_t aFlags,
875 nsIAsyncVerifyRedirectCallback *cb)
876 {
877 NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
879 nsresult rv;
880 nsCOMPtr<nsIScriptSecurityManager> secMan =
881 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
882 NS_ENSURE_SUCCESS(rv, rv);
884 nsCOMPtr<nsIPrincipal> oldPrincipal;
885 secMan->GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
887 nsCOMPtr<nsIURI> newURI;
888 aNewChannel->GetURI(getter_AddRefs(newURI));
889 nsCOMPtr<nsIURI> newOriginalURI;
890 aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
892 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
894 rv = oldPrincipal->CheckMayLoad(newURI, false, false);
895 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
896 rv = oldPrincipal->CheckMayLoad(newOriginalURI, false, false);
897 }
899 if (NS_FAILED(rv))
900 return rv;
902 cb->OnRedirectVerifyCallback(NS_OK);
903 return NS_OK;
904 }
906 NS_IMETHODIMP
907 RDFXMLDataSourceImpl::Refresh(bool aBlocking)
908 {
909 #ifdef PR_LOGGING
910 nsAutoCString spec;
911 if (mURL) {
912 mURL->GetSpec(spec);
913 }
914 PR_LOG(gLog, PR_LOG_NOTICE,
915 ("rdfxml[%p] refresh(%s) %sblocking", this, spec.get(), (aBlocking ? "" : "non")));
916 #endif
918 // If an asynchronous load is already pending, then just let it do
919 // the honors.
920 if (IsLoading()) {
921 PR_LOG(gLog, PR_LOG_NOTICE,
922 ("rdfxml[%p] refresh(%s) a load was pending", this, spec.get()));
924 if (aBlocking) {
925 NS_WARNING("blocking load requested when async load pending");
926 return NS_ERROR_FAILURE;
927 }
928 else {
929 return NS_OK;
930 }
931 }
933 if (! mURL)
934 return NS_ERROR_FAILURE;
935 nsCOMPtr<nsIRDFXMLParser> parser = do_CreateInstance("@mozilla.org/rdf/xml-parser;1");
936 if (! parser)
937 return NS_ERROR_FAILURE;
939 nsresult rv = parser->ParseAsync(this, mURL, getter_AddRefs(mListener));
940 if (NS_FAILED(rv)) return rv;
942 if (aBlocking) {
943 rv = BlockingParse(mURL, this);
945 mListener = nullptr; // release the parser
947 if (NS_FAILED(rv)) return rv;
948 }
949 else {
950 // Null LoadGroup ?
951 rv = NS_OpenURI(this, nullptr, mURL, nullptr, nullptr, this);
952 if (NS_FAILED(rv)) return rv;
954 // So we don't try to issue two asynchronous loads at once.
955 mLoadState = eLoadState_Pending;
956 }
958 return NS_OK;
959 }
961 NS_IMETHODIMP
962 RDFXMLDataSourceImpl::BeginLoad(void)
963 {
964 #ifdef PR_LOGGING
965 nsAutoCString spec;
966 if (mURL) {
967 mURL->GetSpec(spec);
968 }
969 PR_LOG(gLog, PR_LOG_NOTICE,
970 ("rdfxml[%p] begin-load(%s)", this, spec.get()));
971 #endif
973 mLoadState = eLoadState_Loading;
974 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
975 // Make sure to hold a strong reference to the observer so
976 // that it doesn't go away in this call if it removes itself
977 // as an observer
978 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
980 if (obs) {
981 obs->OnBeginLoad(this);
982 }
983 }
984 return NS_OK;
985 }
987 NS_IMETHODIMP
988 RDFXMLDataSourceImpl::Interrupt(void)
989 {
990 #ifdef PR_LOGGING
991 nsAutoCString spec;
992 if (mURL) {
993 mURL->GetSpec(spec);
994 }
995 PR_LOG(gLog, PR_LOG_NOTICE,
996 ("rdfxml[%p] interrupt(%s)", this, spec.get()));
997 #endif
999 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1000 // Make sure to hold a strong reference to the observer so
1001 // that it doesn't go away in this call if it removes itself
1002 // as an observer
1003 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
1005 if (obs) {
1006 obs->OnInterrupt(this);
1007 }
1008 }
1009 return NS_OK;
1010 }
1012 NS_IMETHODIMP
1013 RDFXMLDataSourceImpl::Resume(void)
1014 {
1015 #ifdef PR_LOGGING
1016 nsAutoCString spec;
1017 if (mURL) {
1018 mURL->GetSpec(spec);
1019 }
1020 PR_LOG(gLog, PR_LOG_NOTICE,
1021 ("rdfxml[%p] resume(%s)", this, spec.get()));
1022 #endif
1024 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1025 // Make sure to hold a strong reference to the observer so
1026 // that it doesn't go away in this call if it removes itself
1027 // as an observer
1028 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
1030 if (obs) {
1031 obs->OnResume(this);
1032 }
1033 }
1034 return NS_OK;
1035 }
1037 NS_IMETHODIMP
1038 RDFXMLDataSourceImpl::EndLoad(void)
1039 {
1040 #ifdef PR_LOGGING
1041 nsAutoCString spec;
1042 if (mURL) {
1043 mURL->GetSpec(spec);
1044 }
1045 PR_LOG(gLog, PR_LOG_NOTICE,
1046 ("rdfxml[%p] end-load(%s)", this, spec.get()));
1047 #endif
1049 mLoadState = eLoadState_Loaded;
1051 // Clear out any unmarked assertions from the datasource.
1052 nsCOMPtr<nsIRDFPurgeableDataSource> gcable = do_QueryInterface(mInner);
1053 if (gcable) {
1054 gcable->Sweep();
1055 }
1057 // Notify load observers
1058 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1059 // Make sure to hold a strong reference to the observer so
1060 // that it doesn't go away in this call if it removes itself
1061 // as an observer
1062 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
1064 if (obs) {
1065 obs->OnEndLoad(this);
1066 }
1067 }
1068 return NS_OK;
1069 }
1071 NS_IMETHODIMP
1072 RDFXMLDataSourceImpl::AddNameSpace(nsIAtom* aPrefix, const nsString& aURI)
1073 {
1074 mNameSpaces.Put(aURI, aPrefix);
1075 return NS_OK;
1076 }
1079 NS_IMETHODIMP
1080 RDFXMLDataSourceImpl::AddXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
1081 {
1082 if (! aObserver)
1083 return NS_ERROR_NULL_POINTER;
1085 mObservers.AppendObject(aObserver);
1086 return NS_OK;
1087 }
1089 NS_IMETHODIMP
1090 RDFXMLDataSourceImpl::RemoveXMLSinkObserver(nsIRDFXMLSinkObserver* aObserver)
1091 {
1092 if (! aObserver)
1093 return NS_ERROR_NULL_POINTER;
1095 mObservers.RemoveObject(aObserver);
1097 return NS_OK;
1098 }
1101 //----------------------------------------------------------------------
1102 //
1103 // nsIRequestObserver
1104 //
1106 NS_IMETHODIMP
1107 RDFXMLDataSourceImpl::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
1108 {
1109 return mListener->OnStartRequest(request, ctxt);
1110 }
1112 NS_IMETHODIMP
1113 RDFXMLDataSourceImpl::OnStopRequest(nsIRequest *request,
1114 nsISupports *ctxt,
1115 nsresult status)
1116 {
1117 if (NS_FAILED(status)) {
1118 for (int32_t i = mObservers.Count() - 1; i >= 0; --i) {
1119 // Make sure to hold a strong reference to the observer so
1120 // that it doesn't go away in this call if it removes
1121 // itself as an observer
1122 nsCOMPtr<nsIRDFXMLSinkObserver> obs = mObservers[i];
1124 if (obs) {
1125 obs->OnError(this, status, nullptr);
1126 }
1127 }
1128 }
1130 nsresult rv;
1131 rv = mListener->OnStopRequest(request, ctxt, status);
1133 mListener = nullptr; // release the parser
1135 return rv;
1136 }
1138 //----------------------------------------------------------------------
1139 //
1140 // nsIStreamListener
1141 //
1143 NS_IMETHODIMP
1144 RDFXMLDataSourceImpl::OnDataAvailable(nsIRequest *request,
1145 nsISupports *ctxt,
1146 nsIInputStream *inStr,
1147 uint64_t sourceOffset,
1148 uint32_t count)
1149 {
1150 return mListener->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
1151 }
1153 //----------------------------------------------------------------------
1154 //
1155 // nsIRDFXMLSource
1156 //
1158 NS_IMETHODIMP
1159 RDFXMLDataSourceImpl::Serialize(nsIOutputStream* aStream)
1160 {
1161 nsresult rv;
1162 nsCOMPtr<nsIRDFXMLSerializer> serializer
1163 = do_CreateInstance("@mozilla.org/rdf/xml-serializer;1", &rv);
1165 if (! serializer)
1166 return rv;
1168 rv = serializer->Init(this);
1169 if (NS_FAILED(rv)) return rv;
1171 // Add any namespace information that we picked up when reading
1172 // the RDF/XML
1173 nsNameSpaceMap::const_iterator last = mNameSpaces.last();
1174 for (nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
1175 iter != last; ++iter) {
1176 // We might wanna change nsIRDFXMLSerializer to nsACString and
1177 // use a heap allocated buffer here in the future.
1178 NS_ConvertUTF8toUTF16 uri(iter->mURI);
1179 serializer->AddNameSpace(iter->mPrefix, uri);
1180 }
1182 // Serialize!
1183 nsCOMPtr<nsIRDFXMLSource> source = do_QueryInterface(serializer);
1184 if (! source)
1185 return NS_ERROR_FAILURE;
1187 return source->Serialize(aStream);
1188 }