Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 An implementation for an NGLayout-style content sink that knows how
9 to build an RDF content model from XML-serialized RDF.
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.
17 Open Issues ------------------
19 1) factoring code with nsXMLContentSink - There's some amount of
20 common code between this and the HTML content sink. This will
21 increase as we support more and more HTML elements. How can code
22 from XML/HTML be factored?
24 2) We don't support the `parseType' attribute on the Description
25 tag; therefore, it is impossible to "inline" raw XML in this
26 implemenation.
28 3) We don't build the reifications at parse time due to the
29 footprint overhead it would incur for large RDF documents. (It
30 may be possible to attach a "reification" wrapper datasource that
31 would present this information at query-time.) Because of this,
32 the `bagID' attribute is not processed correctly.
34 4) No attempt is made to `resolve URIs' to a canonical form (the
35 specification hints that an implementation should do this). This
36 is omitted for the obvious reason that we can ill afford to
37 resolve each URI reference.
39 */
41 #include "nsCOMPtr.h"
42 #include "nsInterfaceHashtable.h"
43 #include "nsIContentSink.h"
44 #include "nsIRDFContainer.h"
45 #include "nsIRDFContainerUtils.h"
46 #include "nsIRDFContentSink.h"
47 #include "nsIRDFNode.h"
48 #include "nsIRDFService.h"
49 #include "nsIRDFXMLSink.h"
50 #include "nsIServiceManager.h"
51 #include "nsIURL.h"
52 #include "nsIXMLContentSink.h"
53 #include "nsRDFCID.h"
54 #include "nsTArray.h"
55 #include "nsXPIDLString.h"
56 #include "prlog.h"
57 #include "rdf.h"
58 #include "rdfutil.h"
59 #include "nsReadableUtils.h"
60 #include "nsIExpatSink.h"
61 #include "nsCRT.h"
62 #include "nsIAtom.h"
63 #include "nsStaticAtom.h"
64 #include "nsIScriptError.h"
65 #include "nsIDTD.h"
67 using namespace mozilla;
69 ///////////////////////////////////////////////////////////////////////
71 enum RDFContentSinkState {
72 eRDFContentSinkState_InProlog,
73 eRDFContentSinkState_InDocumentElement,
74 eRDFContentSinkState_InDescriptionElement,
75 eRDFContentSinkState_InContainerElement,
76 eRDFContentSinkState_InPropertyElement,
77 eRDFContentSinkState_InMemberElement,
78 eRDFContentSinkState_InEpilog
79 };
81 enum RDFContentSinkParseMode {
82 eRDFContentSinkParseMode_Resource,
83 eRDFContentSinkParseMode_Literal,
84 eRDFContentSinkParseMode_Int,
85 eRDFContentSinkParseMode_Date
86 };
88 typedef
89 NS_STDCALL_FUNCPROTO(nsresult,
90 nsContainerTestFn,
91 nsIRDFContainerUtils, IsAlt,
92 (nsIRDFDataSource*, nsIRDFResource*, bool*));
94 typedef
95 NS_STDCALL_FUNCPROTO(nsresult,
96 nsMakeContainerFn,
97 nsIRDFContainerUtils, MakeAlt,
98 (nsIRDFDataSource*, nsIRDFResource*, nsIRDFContainer**));
100 class RDFContentSinkImpl : public nsIRDFContentSink,
101 public nsIExpatSink
102 {
103 public:
104 RDFContentSinkImpl();
105 virtual ~RDFContentSinkImpl();
107 // nsISupports
108 NS_DECL_ISUPPORTS
109 NS_DECL_NSIEXPATSINK
111 // nsIContentSink
112 NS_IMETHOD WillParse(void);
113 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
114 NS_IMETHOD DidBuildModel(bool aTerminated);
115 NS_IMETHOD WillInterrupt(void);
116 NS_IMETHOD WillResume(void);
117 NS_IMETHOD SetParser(nsParserBase* aParser);
118 virtual void FlushPendingNotifications(mozFlushType aType) { }
119 NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
120 virtual nsISupports *GetTarget() { return nullptr; }
122 // nsIRDFContentSink
123 NS_IMETHOD Init(nsIURI* aURL);
124 NS_IMETHOD SetDataSource(nsIRDFDataSource* aDataSource);
125 NS_IMETHOD GetDataSource(nsIRDFDataSource*& aDataSource);
127 // pseudo constants
128 static int32_t gRefCnt;
129 static nsIRDFService* gRDFService;
130 static nsIRDFContainerUtils* gRDFContainerUtils;
131 static nsIRDFResource* kRDF_type;
132 static nsIRDFResource* kRDF_instanceOf; // XXX should be RDF:type
133 static nsIRDFResource* kRDF_Alt;
134 static nsIRDFResource* kRDF_Bag;
135 static nsIRDFResource* kRDF_Seq;
136 static nsIRDFResource* kRDF_nextVal;
138 #define RDF_ATOM(name_, value_) static nsIAtom* name_;
139 #include "nsRDFContentSinkAtomList.h"
140 #undef RDF_ATOM
142 typedef struct ContainerInfo {
143 nsIRDFResource** mType;
144 nsContainerTestFn mTestFn;
145 nsMakeContainerFn mMakeFn;
146 } ContainerInfo;
148 protected:
149 // Text management
150 void ParseText(nsIRDFNode **aResult);
152 nsresult FlushText();
153 nsresult AddText(const char16_t* aText, int32_t aLength);
155 // RDF-specific parsing
156 nsresult OpenRDF(const char16_t* aName);
157 nsresult OpenObject(const char16_t* aName ,const char16_t** aAttributes);
158 nsresult OpenProperty(const char16_t* aName, const char16_t** aAttributes);
159 nsresult OpenMember(const char16_t* aName, const char16_t** aAttributes);
160 nsresult OpenValue(const char16_t* aName, const char16_t** aAttributes);
162 nsresult GetIdAboutAttribute(const char16_t** aAttributes, nsIRDFResource** aResource, bool* aIsAnonymous = nullptr);
163 nsresult GetResourceAttribute(const char16_t** aAttributes, nsIRDFResource** aResource);
164 nsresult AddProperties(const char16_t** aAttributes, nsIRDFResource* aSubject, int32_t* aCount = nullptr);
165 void SetParseMode(const char16_t **aAttributes);
167 char16_t* mText;
168 int32_t mTextLength;
169 int32_t mTextSize;
171 /**
172 * From the set of given attributes, this method extracts the
173 * namespace definitions and feeds them to the datasource.
174 * These can then be suggested to the serializer to be used again.
175 * Hopefully, this will keep namespace definitions intact in a
176 * parse - serialize cycle.
177 */
178 void RegisterNamespaces(const char16_t **aAttributes);
180 /**
181 * Extracts the localname from aExpatName, the name that the Expat parser
182 * passes us.
183 * aLocalName will contain the localname in aExpatName.
184 * The return value is a dependent string containing just the namespace.
185 */
186 const nsDependentSubstring SplitExpatName(const char16_t *aExpatName,
187 nsIAtom **aLocalName);
189 enum eContainerType { eBag, eSeq, eAlt };
190 nsresult InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
191 nsresult ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer);
193 // The datasource in which we're assigning assertions
194 nsCOMPtr<nsIRDFDataSource> mDataSource;
196 // A hash of all the node IDs referred to
197 nsInterfaceHashtable<nsStringHashKey, nsIRDFResource> mNodeIDMap;
199 // The current state of the content sink
200 RDFContentSinkState mState;
201 RDFContentSinkParseMode mParseMode;
203 // content stack management
204 int32_t
205 PushContext(nsIRDFResource *aContext,
206 RDFContentSinkState aState,
207 RDFContentSinkParseMode aParseMode);
209 nsresult
210 PopContext(nsIRDFResource *&aContext,
211 RDFContentSinkState &aState,
212 RDFContentSinkParseMode &aParseMode);
214 nsIRDFResource* GetContextElement(int32_t ancestor = 0);
217 struct RDFContextStackElement {
218 nsCOMPtr<nsIRDFResource> mResource;
219 RDFContentSinkState mState;
220 RDFContentSinkParseMode mParseMode;
221 };
223 nsAutoTArray<RDFContextStackElement, 8>* mContextStack;
225 nsIURI* mDocumentURL;
227 private:
228 #ifdef PR_LOGGING
229 static PRLogModuleInfo* gLog;
230 #endif
231 };
233 int32_t RDFContentSinkImpl::gRefCnt = 0;
234 nsIRDFService* RDFContentSinkImpl::gRDFService;
235 nsIRDFContainerUtils* RDFContentSinkImpl::gRDFContainerUtils;
236 nsIRDFResource* RDFContentSinkImpl::kRDF_type;
237 nsIRDFResource* RDFContentSinkImpl::kRDF_instanceOf;
238 nsIRDFResource* RDFContentSinkImpl::kRDF_Alt;
239 nsIRDFResource* RDFContentSinkImpl::kRDF_Bag;
240 nsIRDFResource* RDFContentSinkImpl::kRDF_Seq;
241 nsIRDFResource* RDFContentSinkImpl::kRDF_nextVal;
243 #ifdef PR_LOGGING
244 PRLogModuleInfo* RDFContentSinkImpl::gLog;
245 #endif
247 ////////////////////////////////////////////////////////////////////////
249 #define RDF_ATOM(name_, value_) nsIAtom* RDFContentSinkImpl::name_;
250 #include "nsRDFContentSinkAtomList.h"
251 #undef RDF_ATOM
253 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
254 #include "nsRDFContentSinkAtomList.h"
255 #undef RDF_ATOM
257 static const nsStaticAtom rdf_atoms[] = {
258 #define RDF_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &RDFContentSinkImpl::name_),
259 #include "nsRDFContentSinkAtomList.h"
260 #undef RDF_ATOM
261 };
263 RDFContentSinkImpl::RDFContentSinkImpl()
264 : mText(nullptr),
265 mTextLength(0),
266 mTextSize(0),
267 mState(eRDFContentSinkState_InProlog),
268 mParseMode(eRDFContentSinkParseMode_Literal),
269 mContextStack(nullptr),
270 mDocumentURL(nullptr)
271 {
272 if (gRefCnt++ == 0) {
273 NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
274 nsresult rv = CallGetService(kRDFServiceCID, &gRDFService);
276 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
277 if (NS_SUCCEEDED(rv)) {
278 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
279 &kRDF_type);
280 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
281 &kRDF_instanceOf);
282 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
283 &kRDF_Alt);
284 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
285 &kRDF_Bag);
286 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
287 &kRDF_Seq);
288 rv = gRDFService->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
289 &kRDF_nextVal);
290 }
292 NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
293 rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
295 NS_RegisterStaticAtoms(rdf_atoms);
296 }
298 #ifdef PR_LOGGING
299 if (! gLog)
300 gLog = PR_NewLogModule("nsRDFContentSink");
301 #endif
302 }
305 RDFContentSinkImpl::~RDFContentSinkImpl()
306 {
307 #ifdef DEBUG_REFS
308 --gInstanceCount;
309 fprintf(stdout, "%d - RDF: RDFContentSinkImpl\n", gInstanceCount);
310 #endif
312 NS_IF_RELEASE(mDocumentURL);
314 if (mContextStack) {
315 PR_LOG(gLog, PR_LOG_WARNING,
316 ("rdfxml: warning! unclosed tag"));
318 // XXX we should never need to do this, but, we'll write the
319 // code all the same. If someone left the content stack dirty,
320 // pop all the elements off the stack and release them.
321 int32_t i = mContextStack->Length();
322 while (0 < i--) {
323 nsIRDFResource* resource = nullptr;
324 RDFContentSinkState state;
325 RDFContentSinkParseMode parseMode;
326 PopContext(resource, state, parseMode);
328 #ifdef PR_LOGGING
329 // print some fairly useless debugging info
330 // XXX we should save line numbers on the context stack: this'd
331 // be about 1000x more helpful.
332 if (resource) {
333 nsXPIDLCString uri;
334 resource->GetValue(getter_Copies(uri));
335 PR_LOG(gLog, PR_LOG_NOTICE,
336 ("rdfxml: uri=%s", (const char*) uri));
337 }
338 #endif
340 NS_IF_RELEASE(resource);
341 }
343 delete mContextStack;
344 }
345 moz_free(mText);
348 if (--gRefCnt == 0) {
349 NS_IF_RELEASE(gRDFService);
350 NS_IF_RELEASE(gRDFContainerUtils);
351 NS_IF_RELEASE(kRDF_type);
352 NS_IF_RELEASE(kRDF_instanceOf);
353 NS_IF_RELEASE(kRDF_Alt);
354 NS_IF_RELEASE(kRDF_Bag);
355 NS_IF_RELEASE(kRDF_Seq);
356 NS_IF_RELEASE(kRDF_nextVal);
357 }
358 }
360 ////////////////////////////////////////////////////////////////////////
361 // nsISupports interface
363 NS_IMPL_ADDREF(RDFContentSinkImpl)
364 NS_IMPL_RELEASE(RDFContentSinkImpl)
366 NS_IMETHODIMP
367 RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
368 {
369 NS_PRECONDITION(result, "null ptr");
370 if (! result)
371 return NS_ERROR_NULL_POINTER;
373 NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID);
374 NS_DEFINE_IID(kIExpatSinkIID, NS_IEXPATSINK_IID);
375 NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
376 NS_DEFINE_IID(kIXMLContentSinkIID, NS_IXMLCONTENT_SINK_IID);
377 NS_DEFINE_IID(kIRDFContentSinkIID, NS_IRDFCONTENTSINK_IID);
379 *result = nullptr;
380 if (iid.Equals(kIRDFContentSinkIID) ||
381 iid.Equals(kIXMLContentSinkIID) ||
382 iid.Equals(kIContentSinkIID) ||
383 iid.Equals(kISupportsIID)) {
384 *result = static_cast<nsIXMLContentSink*>(this);
385 AddRef();
386 return NS_OK;
387 }
388 else if (iid.Equals(kIExpatSinkIID)) {
389 *result = static_cast<nsIExpatSink*>(this);
390 AddRef();
391 return NS_OK;
392 }
393 return NS_NOINTERFACE;
394 }
396 NS_IMETHODIMP
397 RDFContentSinkImpl::HandleStartElement(const char16_t *aName,
398 const char16_t **aAtts,
399 uint32_t aAttsCount,
400 int32_t aIndex,
401 uint32_t aLineNumber)
402 {
403 FlushText();
405 nsresult rv = NS_ERROR_UNEXPECTED; // XXX
407 RegisterNamespaces(aAtts);
409 switch (mState) {
410 case eRDFContentSinkState_InProlog:
411 rv = OpenRDF(aName);
412 break;
414 case eRDFContentSinkState_InDocumentElement:
415 rv = OpenObject(aName,aAtts);
416 break;
418 case eRDFContentSinkState_InDescriptionElement:
419 rv = OpenProperty(aName,aAtts);
420 break;
422 case eRDFContentSinkState_InContainerElement:
423 rv = OpenMember(aName,aAtts);
424 break;
426 case eRDFContentSinkState_InPropertyElement:
427 case eRDFContentSinkState_InMemberElement:
428 rv = OpenValue(aName,aAtts);
429 break;
431 case eRDFContentSinkState_InEpilog:
432 PR_LOG(gLog, PR_LOG_WARNING,
433 ("rdfxml: unexpected content in epilog at line %d",
434 aLineNumber));
435 break;
436 }
438 return rv;
439 }
441 NS_IMETHODIMP
442 RDFContentSinkImpl::HandleEndElement(const char16_t *aName)
443 {
444 FlushText();
446 nsIRDFResource* resource;
447 if (NS_FAILED(PopContext(resource, mState, mParseMode))) {
448 // XXX parser didn't catch unmatched tags?
449 #ifdef PR_LOGGING
450 if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) {
451 nsAutoString tagStr(aName);
452 char* tagCStr = ToNewCString(tagStr);
454 PR_LogPrint
455 ("rdfxml: extra close tag '%s' at line %d",
456 tagCStr, 0/*XXX fix me */);
458 NS_Free(tagCStr);
459 }
460 #endif
462 return NS_ERROR_UNEXPECTED; // XXX
463 }
465 // If we've just popped a member or property element, _now_ is the
466 // time to add that element to the graph.
467 switch (mState) {
468 case eRDFContentSinkState_InMemberElement:
469 {
470 nsCOMPtr<nsIRDFContainer> container;
471 NS_NewRDFContainer(getter_AddRefs(container));
472 container->Init(mDataSource, GetContextElement(1));
473 container->AppendElement(resource);
474 }
475 break;
477 case eRDFContentSinkState_InPropertyElement:
478 {
479 mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true);
480 } break;
481 default:
482 break;
483 }
485 if (mContextStack->IsEmpty())
486 mState = eRDFContentSinkState_InEpilog;
488 NS_IF_RELEASE(resource);
489 return NS_OK;
490 }
492 NS_IMETHODIMP
493 RDFContentSinkImpl::HandleComment(const char16_t *aName)
494 {
495 return NS_OK;
496 }
498 NS_IMETHODIMP
499 RDFContentSinkImpl::HandleCDataSection(const char16_t *aData,
500 uint32_t aLength)
501 {
502 return aData ? AddText(aData, aLength) : NS_OK;
503 }
505 NS_IMETHODIMP
506 RDFContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
507 const nsAString & aName,
508 const nsAString & aSystemId,
509 const nsAString & aPublicId,
510 nsISupports* aCatalogData)
511 {
512 return NS_OK;
513 }
515 NS_IMETHODIMP
516 RDFContentSinkImpl::HandleCharacterData(const char16_t *aData,
517 uint32_t aLength)
518 {
519 return aData ? AddText(aData, aLength) : NS_OK;
520 }
522 NS_IMETHODIMP
523 RDFContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget,
524 const char16_t *aData)
525 {
526 return NS_OK;
527 }
529 NS_IMETHODIMP
530 RDFContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion,
531 const char16_t *aEncoding,
532 int32_t aStandalone)
533 {
534 return NS_OK;
535 }
537 NS_IMETHODIMP
538 RDFContentSinkImpl::ReportError(const char16_t* aErrorText,
539 const char16_t* aSourceText,
540 nsIScriptError *aError,
541 bool *_retval)
542 {
543 NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
545 // The expat driver should report the error.
546 *_retval = true;
547 return NS_OK;
548 }
550 ////////////////////////////////////////////////////////////////////////
551 // nsIContentSink interface
553 NS_IMETHODIMP
554 RDFContentSinkImpl::WillParse(void)
555 {
556 return NS_OK;
557 }
560 NS_IMETHODIMP
561 RDFContentSinkImpl::WillBuildModel(nsDTDMode)
562 {
563 if (mDataSource) {
564 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
565 if (sink)
566 return sink->BeginLoad();
567 }
568 return NS_OK;
569 }
571 NS_IMETHODIMP
572 RDFContentSinkImpl::DidBuildModel(bool aTerminated)
573 {
574 if (mDataSource) {
575 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
576 if (sink)
577 return sink->EndLoad();
578 }
579 return NS_OK;
580 }
582 NS_IMETHODIMP
583 RDFContentSinkImpl::WillInterrupt(void)
584 {
585 if (mDataSource) {
586 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
587 if (sink)
588 return sink->Interrupt();
589 }
590 return NS_OK;
591 }
593 NS_IMETHODIMP
594 RDFContentSinkImpl::WillResume(void)
595 {
596 if (mDataSource) {
597 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
598 if (sink)
599 return sink->Resume();
600 }
601 return NS_OK;
602 }
604 NS_IMETHODIMP
605 RDFContentSinkImpl::SetParser(nsParserBase* aParser)
606 {
607 return NS_OK;
608 }
610 ////////////////////////////////////////////////////////////////////////
611 // nsIRDFContentSink interface
613 NS_IMETHODIMP
614 RDFContentSinkImpl::Init(nsIURI* aURL)
615 {
616 NS_PRECONDITION(aURL != nullptr, "null ptr");
617 if (! aURL)
618 return NS_ERROR_NULL_POINTER;
620 mDocumentURL = aURL;
621 NS_ADDREF(aURL);
623 mState = eRDFContentSinkState_InProlog;
624 return NS_OK;
625 }
627 NS_IMETHODIMP
628 RDFContentSinkImpl::SetDataSource(nsIRDFDataSource* aDataSource)
629 {
630 NS_PRECONDITION(aDataSource != nullptr, "SetDataSource null ptr");
631 mDataSource = aDataSource;
632 NS_ASSERTION(mDataSource != nullptr,"Couldn't QI RDF DataSource");
633 return NS_OK;
634 }
637 NS_IMETHODIMP
638 RDFContentSinkImpl::GetDataSource(nsIRDFDataSource*& aDataSource)
639 {
640 aDataSource = mDataSource;
641 NS_IF_ADDREF(aDataSource);
642 return NS_OK;
643 }
645 ////////////////////////////////////////////////////////////////////////
646 // Text buffering
648 static bool
649 rdf_IsDataInBuffer(char16_t* buffer, int32_t length)
650 {
651 for (int32_t i = 0; i < length; ++i) {
652 if (buffer[i] == ' ' ||
653 buffer[i] == '\t' ||
654 buffer[i] == '\n' ||
655 buffer[i] == '\r')
656 continue;
658 return true;
659 }
660 return false;
661 }
663 void
664 RDFContentSinkImpl::ParseText(nsIRDFNode **aResult)
665 {
666 // XXXwaterson wasteful, but we'd need to make a copy anyway to be
667 // able to call nsIRDFService::Get[Resource|Literal|...]().
668 nsAutoString value;
669 value.Append(mText, mTextLength);
670 value.Trim(" \t\n\r");
672 switch (mParseMode) {
673 case eRDFContentSinkParseMode_Literal:
674 {
675 nsIRDFLiteral *result;
676 gRDFService->GetLiteral(value.get(), &result);
677 *aResult = result;
678 }
679 break;
681 case eRDFContentSinkParseMode_Resource:
682 {
683 nsIRDFResource *result;
684 gRDFService->GetUnicodeResource(value, &result);
685 *aResult = result;
686 }
687 break;
689 case eRDFContentSinkParseMode_Int:
690 {
691 nsresult err;
692 int32_t i = value.ToInteger(&err);
693 nsIRDFInt *result;
694 gRDFService->GetIntLiteral(i, &result);
695 *aResult = result;
696 }
697 break;
699 case eRDFContentSinkParseMode_Date:
700 {
701 PRTime t = rdf_ParseDate(nsDependentCString(NS_LossyConvertUTF16toASCII(value).get(), value.Length()));
702 nsIRDFDate *result;
703 gRDFService->GetDateLiteral(t, &result);
704 *aResult = result;
705 }
706 break;
708 default:
709 NS_NOTREACHED("unknown parse type");
710 break;
711 }
712 }
714 nsresult
715 RDFContentSinkImpl::FlushText()
716 {
717 nsresult rv = NS_OK;
718 if (0 != mTextLength) {
719 if (rdf_IsDataInBuffer(mText, mTextLength)) {
720 // XXX if there's anything but whitespace, then we'll
721 // create a text node.
723 switch (mState) {
724 case eRDFContentSinkState_InMemberElement: {
725 nsCOMPtr<nsIRDFNode> node;
726 ParseText(getter_AddRefs(node));
728 nsCOMPtr<nsIRDFContainer> container;
729 NS_NewRDFContainer(getter_AddRefs(container));
730 container->Init(mDataSource, GetContextElement(1));
732 container->AppendElement(node);
733 } break;
735 case eRDFContentSinkState_InPropertyElement: {
736 nsCOMPtr<nsIRDFNode> node;
737 ParseText(getter_AddRefs(node));
739 mDataSource->Assert(GetContextElement(1), GetContextElement(0), node, true);
740 } break;
742 default:
743 // just ignore it
744 break;
745 }
746 }
747 mTextLength = 0;
748 }
749 return rv;
750 }
753 nsresult
754 RDFContentSinkImpl::AddText(const char16_t* aText, int32_t aLength)
755 {
756 // Create buffer when we first need it
757 if (0 == mTextSize) {
758 mText = (char16_t *) moz_malloc(sizeof(char16_t) * 4096);
759 if (!mText) {
760 return NS_ERROR_OUT_OF_MEMORY;
761 }
762 mTextSize = 4096;
763 }
765 // Copy data from string into our buffer; grow the buffer as needed.
766 // It never shrinks, but since the content sink doesn't stick around,
767 // this shouldn't be a bloat issue.
768 int32_t amount = mTextSize - mTextLength;
769 if (amount < aLength) {
770 // Grow the buffer by at least a factor of two to prevent thrashing.
771 // Since PR_REALLOC will leave mText intact if the call fails,
772 // don't clobber mText or mTextSize until the new mem is allocated.
773 int32_t newSize = (2 * mTextSize > (mTextSize + aLength)) ?
774 (2 * mTextSize) : (mTextSize + aLength);
775 char16_t* newText =
776 (char16_t *) moz_realloc(mText, sizeof(char16_t) * newSize);
777 if (!newText)
778 return NS_ERROR_OUT_OF_MEMORY;
779 mTextSize = newSize;
780 mText = newText;
781 }
782 memcpy(&mText[mTextLength], aText, sizeof(char16_t) * aLength);
783 mTextLength += aLength;
785 return NS_OK;
786 }
788 bool
789 rdf_RequiresAbsoluteURI(const nsString& uri)
790 {
791 // cheap shot at figuring out if this requires an absolute url translation
792 return !(StringBeginsWith(uri, NS_LITERAL_STRING("urn:")) ||
793 StringBeginsWith(uri, NS_LITERAL_STRING("chrome:")));
794 }
796 nsresult
797 RDFContentSinkImpl::GetIdAboutAttribute(const char16_t** aAttributes,
798 nsIRDFResource** aResource,
799 bool* aIsAnonymous)
800 {
801 // This corresponds to the dirty work of production [6.5]
802 nsresult rv = NS_OK;
804 nsAutoString nodeID;
806 nsCOMPtr<nsIAtom> localName;
807 for (; *aAttributes; aAttributes += 2) {
808 const nsDependentSubstring& nameSpaceURI =
809 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
811 // We'll accept either `ID' or `rdf:ID' (ibid with `about' or
812 // `rdf:about') in the spirit of being liberal towards the
813 // input that we receive.
814 if (!nameSpaceURI.IsEmpty() &&
815 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
816 continue;
817 }
819 // XXX you can't specify both, but we'll just pick up the
820 // first thing that was specified and ignore the other.
822 if (localName == kAboutAtom) {
823 if (aIsAnonymous)
824 *aIsAnonymous = false;
826 nsAutoString relURI(aAttributes[1]);
827 if (rdf_RequiresAbsoluteURI(relURI)) {
828 nsAutoCString uri;
829 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
830 if (NS_FAILED(rv)) return rv;
832 return gRDFService->GetResource(uri,
833 aResource);
834 }
835 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
836 aResource);
837 }
838 else if (localName == kIdAtom) {
839 if (aIsAnonymous)
840 *aIsAnonymous = false;
841 // In the spirit of leniency, we do not bother trying to
842 // enforce that this be a valid "XML Name" (see
843 // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
844 // 6.21. If we wanted to, this would be where to do it.
846 // Construct an in-line resource whose URI is the
847 // document's URI plus the XML name specified in the ID
848 // attribute.
849 nsAutoCString name;
850 nsAutoCString ref('#');
851 AppendUTF16toUTF8(aAttributes[1], ref);
853 rv = mDocumentURL->Resolve(ref, name);
854 if (NS_FAILED(rv)) return rv;
856 return gRDFService->GetResource(name, aResource);
857 }
858 else if (localName == kNodeIdAtom) {
859 nodeID.Assign(aAttributes[1]);
860 }
861 else if (localName == kAboutEachAtom) {
862 // XXX we don't deal with aboutEach...
863 //PR_LOG(gLog, PR_LOG_WARNING,
864 // ("rdfxml: ignoring aboutEach at line %d",
865 // aNode.GetSourceLineNumber()));
866 }
867 }
869 // Otherwise, we couldn't find anything, so just gensym one...
870 if (aIsAnonymous)
871 *aIsAnonymous = true;
873 // If nodeID is present, check if we already know about it. If we've seen
874 // the nodeID before, use the same resource, otherwise generate a new one.
875 if (!nodeID.IsEmpty()) {
876 mNodeIDMap.Get(nodeID,aResource);
878 if (!*aResource) {
879 rv = gRDFService->GetAnonymousResource(aResource);
880 mNodeIDMap.Put(nodeID,*aResource);
881 }
882 }
883 else {
884 rv = gRDFService->GetAnonymousResource(aResource);
885 }
887 return rv;
888 }
890 nsresult
891 RDFContentSinkImpl::GetResourceAttribute(const char16_t** aAttributes,
892 nsIRDFResource** aResource)
893 {
894 nsCOMPtr<nsIAtom> localName;
896 nsAutoString nodeID;
898 for (; *aAttributes; aAttributes += 2) {
899 const nsDependentSubstring& nameSpaceURI =
900 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
902 // We'll accept `resource' or `rdf:resource', under the spirit
903 // that we should be liberal towards the input that we
904 // receive.
905 if (!nameSpaceURI.IsEmpty() &&
906 !nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
907 continue;
908 }
910 // XXX you can't specify both, but we'll just pick up the
911 // first thing that was specified and ignore the other.
913 if (localName == kResourceAtom) {
914 // XXX Take the URI and make it fully qualified by
915 // sticking it into the document's URL. This may not be
916 // appropriate...
917 nsAutoString relURI(aAttributes[1]);
918 if (rdf_RequiresAbsoluteURI(relURI)) {
919 nsresult rv;
920 nsAutoCString uri;
922 rv = mDocumentURL->Resolve(NS_ConvertUTF16toUTF8(aAttributes[1]), uri);
923 if (NS_FAILED(rv)) return rv;
925 return gRDFService->GetResource(uri, aResource);
926 }
927 return gRDFService->GetResource(NS_ConvertUTF16toUTF8(aAttributes[1]),
928 aResource);
929 }
930 else if (localName == kNodeIdAtom) {
931 nodeID.Assign(aAttributes[1]);
932 }
933 }
935 // If nodeID is present, check if we already know about it. If we've seen
936 // the nodeID before, use the same resource, otherwise generate a new one.
937 if (!nodeID.IsEmpty()) {
938 mNodeIDMap.Get(nodeID,aResource);
940 if (!*aResource) {
941 nsresult rv;
942 rv = gRDFService->GetAnonymousResource(aResource);
943 if (NS_FAILED(rv)) {
944 return rv;
945 }
946 mNodeIDMap.Put(nodeID,*aResource);
947 }
948 return NS_OK;
949 }
951 return NS_ERROR_FAILURE;
952 }
954 nsresult
955 RDFContentSinkImpl::AddProperties(const char16_t** aAttributes,
956 nsIRDFResource* aSubject,
957 int32_t* aCount)
958 {
959 if (aCount)
960 *aCount = 0;
962 nsCOMPtr<nsIAtom> localName;
963 for (; *aAttributes; aAttributes += 2) {
964 const nsDependentSubstring& nameSpaceURI =
965 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
967 // skip 'xmlns' directives, these are "meta" information
968 if (nameSpaceURI.EqualsLiteral("http://www.w3.org/2000/xmlns/")) {
969 continue;
970 }
972 // skip `about', `ID', `resource', and 'nodeID' attributes (either with or
973 // without the `rdf:' prefix); these are all "special" and
974 // should've been dealt with by the caller.
975 if (localName == kAboutAtom || localName == kIdAtom ||
976 localName == kResourceAtom || localName == kNodeIdAtom) {
977 if (nameSpaceURI.IsEmpty() ||
978 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI))
979 continue;
980 }
982 // Skip `parseType', `RDF:parseType', and `NC:parseType'. This
983 // is meta-information that will be handled in SetParseMode.
984 if (localName == kParseTypeAtom) {
985 if (nameSpaceURI.IsEmpty() ||
986 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
987 nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
988 continue;
989 }
990 }
992 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
993 propertyStr.Append(nsAtomCString(localName));
995 // Add the assertion to RDF
996 nsCOMPtr<nsIRDFResource> property;
997 gRDFService->GetResource(propertyStr, getter_AddRefs(property));
999 nsCOMPtr<nsIRDFLiteral> target;
1000 gRDFService->GetLiteral(aAttributes[1],
1001 getter_AddRefs(target));
1003 mDataSource->Assert(aSubject, property, target, true);
1004 }
1005 return NS_OK;
1006 }
1008 void
1009 RDFContentSinkImpl::SetParseMode(const char16_t **aAttributes)
1010 {
1011 nsCOMPtr<nsIAtom> localName;
1012 for (; *aAttributes; aAttributes += 2) {
1013 const nsDependentSubstring& nameSpaceURI =
1014 SplitExpatName(aAttributes[0], getter_AddRefs(localName));
1016 if (localName == kParseTypeAtom) {
1017 nsDependentString v(aAttributes[1]);
1019 if (nameSpaceURI.IsEmpty() ||
1020 nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
1021 if (v.EqualsLiteral("Resource"))
1022 mParseMode = eRDFContentSinkParseMode_Resource;
1024 break;
1025 }
1026 else if (nameSpaceURI.EqualsLiteral(NC_NAMESPACE_URI)) {
1027 if (v.EqualsLiteral("Date"))
1028 mParseMode = eRDFContentSinkParseMode_Date;
1029 else if (v.EqualsLiteral("Integer"))
1030 mParseMode = eRDFContentSinkParseMode_Int;
1032 break;
1033 }
1034 }
1035 }
1036 }
1038 ////////////////////////////////////////////////////////////////////////
1039 // RDF-specific routines used to build the model
1041 nsresult
1042 RDFContentSinkImpl::OpenRDF(const char16_t* aName)
1043 {
1044 // ensure that we're actually reading RDF by making sure that the
1045 // opening tag is <rdf:RDF>, where "rdf:" corresponds to whatever
1046 // they've declared the standard RDF namespace to be.
1047 nsCOMPtr<nsIAtom> localName;
1048 const nsDependentSubstring& nameSpaceURI =
1049 SplitExpatName(aName, getter_AddRefs(localName));
1051 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) || localName != kRDFAtom) {
1052 // PR_LOG(gLog, PR_LOG_ALWAYS,
1053 // ("rdfxml: expected RDF:RDF at line %d",
1054 // aNode.GetSourceLineNumber()));
1056 return NS_ERROR_UNEXPECTED;
1057 }
1059 PushContext(nullptr, mState, mParseMode);
1060 mState = eRDFContentSinkState_InDocumentElement;
1061 return NS_OK;
1062 }
1064 nsresult
1065 RDFContentSinkImpl::OpenObject(const char16_t* aName,
1066 const char16_t** aAttributes)
1067 {
1068 // an "object" non-terminal is either a "description", a "typed
1069 // node", or a "container", so this change the content sink's
1070 // state appropriately.
1071 nsCOMPtr<nsIAtom> localName;
1072 const nsDependentSubstring& nameSpaceURI =
1073 SplitExpatName(aName, getter_AddRefs(localName));
1075 // Figure out the URI of this object, and create an RDF node for it.
1076 nsCOMPtr<nsIRDFResource> source;
1077 GetIdAboutAttribute(aAttributes, getter_AddRefs(source));
1079 // If there is no `ID' or `about', then there's not much we can do.
1080 if (! source)
1081 return NS_ERROR_FAILURE;
1083 // Push the element onto the context stack
1084 PushContext(source, mState, mParseMode);
1086 // Now figure out what kind of state transition we need to
1087 // make. We'll either be going into a mode where we parse a
1088 // description or a container.
1089 bool isaTypedNode = true;
1091 if (nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI)) {
1092 isaTypedNode = false;
1094 if (localName == kDescriptionAtom) {
1095 // it's a description
1096 mState = eRDFContentSinkState_InDescriptionElement;
1097 }
1098 else if (localName == kBagAtom) {
1099 // it's a bag container
1100 InitContainer(kRDF_Bag, source);
1101 mState = eRDFContentSinkState_InContainerElement;
1102 }
1103 else if (localName == kSeqAtom) {
1104 // it's a seq container
1105 InitContainer(kRDF_Seq, source);
1106 mState = eRDFContentSinkState_InContainerElement;
1107 }
1108 else if (localName == kAltAtom) {
1109 // it's an alt container
1110 InitContainer(kRDF_Alt, source);
1111 mState = eRDFContentSinkState_InContainerElement;
1112 }
1113 else {
1114 // heh, that's not *in* the RDF namespace: just treat it
1115 // like a typed node
1116 isaTypedNode = true;
1117 }
1118 }
1120 if (isaTypedNode) {
1121 NS_ConvertUTF16toUTF8 typeStr(nameSpaceURI);
1122 typeStr.Append(nsAtomCString(localName));
1124 nsCOMPtr<nsIRDFResource> type;
1125 nsresult rv = gRDFService->GetResource(typeStr, getter_AddRefs(type));
1126 if (NS_FAILED(rv)) return rv;
1128 rv = mDataSource->Assert(source, kRDF_type, type, true);
1129 if (NS_FAILED(rv)) return rv;
1131 mState = eRDFContentSinkState_InDescriptionElement;
1132 }
1134 AddProperties(aAttributes, source);
1135 return NS_OK;
1136 }
1138 nsresult
1139 RDFContentSinkImpl::OpenProperty(const char16_t* aName, const char16_t** aAttributes)
1140 {
1141 nsresult rv;
1143 // an "object" non-terminal is either a "description", a "typed
1144 // node", or a "container", so this change the content sink's
1145 // state appropriately.
1146 nsCOMPtr<nsIAtom> localName;
1147 const nsDependentSubstring& nameSpaceURI =
1148 SplitExpatName(aName, getter_AddRefs(localName));
1150 NS_ConvertUTF16toUTF8 propertyStr(nameSpaceURI);
1151 propertyStr.Append(nsAtomCString(localName));
1153 nsCOMPtr<nsIRDFResource> property;
1154 rv = gRDFService->GetResource(propertyStr, getter_AddRefs(property));
1155 if (NS_FAILED(rv)) return rv;
1157 // See if they've specified a 'resource' attribute, in which case
1158 // they mean *that* to be the object of this property.
1159 nsCOMPtr<nsIRDFResource> target;
1160 GetResourceAttribute(aAttributes, getter_AddRefs(target));
1162 bool isAnonymous = false;
1164 if (! target) {
1165 // See if an 'ID' attribute has been specified, in which case
1166 // this corresponds to the fourth form of [6.12].
1168 // XXX strictly speaking, we should reject the RDF/XML as
1169 // invalid if they've specified both an 'ID' and a 'resource'
1170 // attribute. Bah.
1172 // XXX strictly speaking, 'about=' isn't allowed here, but
1173 // what the hell.
1174 GetIdAboutAttribute(aAttributes, getter_AddRefs(target), &isAnonymous);
1175 }
1177 if (target) {
1178 // They specified an inline resource for the value of this
1179 // property. Create an RDF resource for the inline resource
1180 // URI, add the properties to it, and attach the inline
1181 // resource to its parent.
1182 int32_t count;
1183 rv = AddProperties(aAttributes, target, &count);
1184 NS_ASSERTION(NS_SUCCEEDED(rv), "problem adding properties");
1185 if (NS_FAILED(rv)) return rv;
1187 if (count || !isAnonymous) {
1188 // If the resource was "anonymous" (i.e., they hadn't
1189 // explicitly set an ID or resource attribute), then we'll
1190 // only assert this property from the context element *if*
1191 // there were properties specified on the anonymous
1192 // resource.
1193 rv = mDataSource->Assert(GetContextElement(0), property, target, true);
1194 if (NS_FAILED(rv)) return rv;
1195 }
1197 // XXX Technically, we should _not_ fall through here and push
1198 // the element onto the stack: this is supposed to be a closed
1199 // node. But right now I'm lazy and the code will just Do The
1200 // Right Thing so long as the RDF is well-formed.
1201 }
1203 // Push the element onto the context stack and change state.
1204 PushContext(property, mState, mParseMode);
1205 mState = eRDFContentSinkState_InPropertyElement;
1206 SetParseMode(aAttributes);
1208 return NS_OK;
1209 }
1211 nsresult
1212 RDFContentSinkImpl::OpenMember(const char16_t* aName,
1213 const char16_t** aAttributes)
1214 {
1215 // ensure that we're actually reading a member element by making
1216 // sure that the opening tag is <rdf:li>, where "rdf:" corresponds
1217 // to whatever they've declared the standard RDF namespace to be.
1218 nsresult rv;
1220 nsCOMPtr<nsIAtom> localName;
1221 const nsDependentSubstring& nameSpaceURI =
1222 SplitExpatName(aName, getter_AddRefs(localName));
1224 if (!nameSpaceURI.EqualsLiteral(RDF_NAMESPACE_URI) ||
1225 localName != kLiAtom) {
1226 PR_LOG(gLog, PR_LOG_ALWAYS,
1227 ("rdfxml: expected RDF:li at line %d",
1228 -1)); // XXX pass in line number
1230 return NS_ERROR_UNEXPECTED;
1231 }
1233 // The parent element is the container.
1234 nsIRDFResource* container = GetContextElement(0);
1235 if (! container)
1236 return NS_ERROR_NULL_POINTER;
1238 nsIRDFResource* resource;
1239 if (NS_SUCCEEDED(rv = GetResourceAttribute(aAttributes, &resource))) {
1240 // Okay, this node has an RDF:resource="..." attribute. That
1241 // means that it's a "referenced item," as covered in [6.29].
1242 nsCOMPtr<nsIRDFContainer> c;
1243 NS_NewRDFContainer(getter_AddRefs(c));
1244 c->Init(mDataSource, container);
1245 c->AppendElement(resource);
1247 // XXX Technically, we should _not_ fall through here and push
1248 // the element onto the stack: this is supposed to be a closed
1249 // node. But right now I'm lazy and the code will just Do The
1250 // Right Thing so long as the RDF is well-formed.
1251 NS_RELEASE(resource);
1252 }
1254 // Change state. Pushing a null context element is a bit weird,
1255 // but the idea is that there really is _no_ context "property".
1256 // The contained element will use nsIRDFContainer::AppendElement() to add
1257 // the element to the container, which requires only the container
1258 // and the element to be added.
1259 PushContext(nullptr, mState, mParseMode);
1260 mState = eRDFContentSinkState_InMemberElement;
1261 SetParseMode(aAttributes);
1263 return NS_OK;
1264 }
1267 nsresult
1268 RDFContentSinkImpl::OpenValue(const char16_t* aName, const char16_t** aAttributes)
1269 {
1270 // a "value" can either be an object or a string: we'll only get
1271 // *here* if it's an object, as raw text is added as a leaf.
1272 return OpenObject(aName,aAttributes);
1273 }
1275 ////////////////////////////////////////////////////////////////////////
1276 // namespace resolution
1277 void
1278 RDFContentSinkImpl::RegisterNamespaces(const char16_t **aAttributes)
1279 {
1280 nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(mDataSource);
1281 if (!sink) {
1282 return;
1283 }
1284 NS_NAMED_LITERAL_STRING(xmlns, "http://www.w3.org/2000/xmlns/");
1285 for (; *aAttributes; aAttributes += 2) {
1286 // check the namespace
1287 const char16_t* attr = aAttributes[0];
1288 const char16_t* xmlnsP = xmlns.BeginReading();
1289 while (*attr == *xmlnsP) {
1290 ++attr;
1291 ++xmlnsP;
1292 }
1293 if (*attr != 0xFFFF ||
1294 xmlnsP != xmlns.EndReading()) {
1295 continue;
1296 }
1297 // get the localname (or "xmlns" for the default namespace)
1298 const char16_t* endLocal = ++attr;
1299 while (*endLocal && *endLocal != 0xFFFF) {
1300 ++endLocal;
1301 }
1302 nsDependentSubstring lname(attr, endLocal);
1303 nsCOMPtr<nsIAtom> preferred = do_GetAtom(lname);
1304 if (preferred == kXMLNSAtom) {
1305 preferred = nullptr;
1306 }
1307 sink->AddNameSpace(preferred, nsDependentString(aAttributes[1]));
1308 }
1309 }
1311 ////////////////////////////////////////////////////////////////////////
1312 // Qualified name resolution
1314 const nsDependentSubstring
1315 RDFContentSinkImpl::SplitExpatName(const char16_t *aExpatName,
1316 nsIAtom **aLocalName)
1317 {
1318 /**
1319 * Expat can send the following:
1320 * localName
1321 * namespaceURI<separator>localName
1322 * namespaceURI<separator>localName<separator>prefix
1323 *
1324 * and we use 0xFFFF for the <separator>.
1325 *
1326 */
1328 const char16_t *uriEnd = aExpatName;
1329 const char16_t *nameStart = aExpatName;
1330 const char16_t *pos;
1331 for (pos = aExpatName; *pos; ++pos) {
1332 if (*pos == 0xFFFF) {
1333 if (uriEnd != aExpatName) {
1334 break;
1335 }
1337 uriEnd = pos;
1338 nameStart = pos + 1;
1339 }
1340 }
1342 const nsDependentSubstring& nameSpaceURI = Substring(aExpatName, uriEnd);
1343 *aLocalName = NS_NewAtom(Substring(nameStart, pos)).take();
1344 return nameSpaceURI;
1345 }
1347 nsresult
1348 RDFContentSinkImpl::InitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
1349 {
1350 // Do the right kind of initialization based on the container
1351 // 'type' resource, and the state of the container (i.e., 'make' a
1352 // new container vs. 'reinitialize' the container).
1353 nsresult rv;
1355 static const ContainerInfo gContainerInfo[] = {
1356 { &RDFContentSinkImpl::kRDF_Alt, &nsIRDFContainerUtils::IsAlt, &nsIRDFContainerUtils::MakeAlt },
1357 { &RDFContentSinkImpl::kRDF_Bag, &nsIRDFContainerUtils::IsBag, &nsIRDFContainerUtils::MakeBag },
1358 { &RDFContentSinkImpl::kRDF_Seq, &nsIRDFContainerUtils::IsSeq, &nsIRDFContainerUtils::MakeSeq },
1359 { 0, 0, 0 },
1360 };
1362 for (const ContainerInfo* info = gContainerInfo; info->mType != 0; ++info) {
1363 if (*info->mType != aContainerType)
1364 continue;
1366 bool isContainer;
1367 rv = (gRDFContainerUtils->*(info->mTestFn))(mDataSource, aContainer, &isContainer);
1368 if (isContainer) {
1369 rv = ReinitContainer(aContainerType, aContainer);
1370 }
1371 else {
1372 rv = (gRDFContainerUtils->*(info->mMakeFn))(mDataSource, aContainer, nullptr);
1373 }
1374 return rv;
1375 }
1377 NS_NOTREACHED("not an RDF container type");
1378 return NS_ERROR_FAILURE;
1379 }
1383 nsresult
1384 RDFContentSinkImpl::ReinitContainer(nsIRDFResource* aContainerType, nsIRDFResource* aContainer)
1385 {
1386 // Mega-kludge to deal with the fact that Make[Seq|Alt|Bag] is
1387 // idempotent, and as such, containers will have state (e.g.,
1388 // RDF:nextVal) maintained in the graph across loads. This
1389 // re-initializes each container's RDF:nextVal to '1', and 'marks'
1390 // the container as such.
1391 nsresult rv;
1393 nsCOMPtr<nsIRDFLiteral> one;
1394 rv = gRDFService->GetLiteral(MOZ_UTF16("1"), getter_AddRefs(one));
1395 if (NS_FAILED(rv)) return rv;
1397 // Re-initialize the 'nextval' property
1398 nsCOMPtr<nsIRDFNode> nextval;
1399 rv = mDataSource->GetTarget(aContainer, kRDF_nextVal, true, getter_AddRefs(nextval));
1400 if (NS_FAILED(rv)) return rv;
1402 rv = mDataSource->Change(aContainer, kRDF_nextVal, nextval, one);
1403 if (NS_FAILED(rv)) return rv;
1405 // Re-mark as a container. XXX should be kRDF_type
1406 rv = mDataSource->Assert(aContainer, kRDF_instanceOf, aContainerType, true);
1407 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to mark container as such");
1408 if (NS_FAILED(rv)) return rv;
1410 return NS_OK;
1411 }
1413 ////////////////////////////////////////////////////////////////////////
1414 // Content stack management
1416 nsIRDFResource*
1417 RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */)
1418 {
1419 if ((nullptr == mContextStack) ||
1420 (uint32_t(ancestor) >= mContextStack->Length())) {
1421 return nullptr;
1422 }
1424 return mContextStack->ElementAt(
1425 mContextStack->Length()-ancestor-1).mResource;
1426 }
1428 int32_t
1429 RDFContentSinkImpl::PushContext(nsIRDFResource *aResource,
1430 RDFContentSinkState aState,
1431 RDFContentSinkParseMode aParseMode)
1432 {
1433 if (! mContextStack) {
1434 mContextStack = new nsAutoTArray<RDFContextStackElement, 8>();
1435 if (! mContextStack)
1436 return 0;
1437 }
1439 RDFContextStackElement* e = mContextStack->AppendElement();
1440 if (! e)
1441 return mContextStack->Length();
1443 e->mResource = aResource;
1444 e->mState = aState;
1445 e->mParseMode = aParseMode;
1447 return mContextStack->Length();
1448 }
1450 nsresult
1451 RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource,
1452 RDFContentSinkState &aState,
1453 RDFContentSinkParseMode &aParseMode)
1454 {
1455 if ((nullptr == mContextStack) ||
1456 (mContextStack->IsEmpty())) {
1457 return NS_ERROR_NULL_POINTER;
1458 }
1460 uint32_t i = mContextStack->Length() - 1;
1461 RDFContextStackElement &e = mContextStack->ElementAt(i);
1463 aResource = e.mResource;
1464 NS_IF_ADDREF(aResource);
1465 aState = e.mState;
1466 aParseMode = e.mParseMode;
1468 mContextStack->RemoveElementAt(i);
1469 return NS_OK;
1470 }
1473 ////////////////////////////////////////////////////////////////////////
1475 nsresult
1476 NS_NewRDFContentSink(nsIRDFContentSink** aResult)
1477 {
1478 NS_PRECONDITION(aResult != nullptr, "null ptr");
1479 if (! aResult)
1480 return NS_ERROR_NULL_POINTER;
1482 RDFContentSinkImpl* sink = new RDFContentSinkImpl();
1483 if (! sink)
1484 return NS_ERROR_OUT_OF_MEMORY;
1486 NS_ADDREF(sink);
1487 *aResult = sink;
1488 return NS_OK;
1489 }