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