Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=80:
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsRDFXMLSerializer.h"
10 #include "nsIAtom.h"
11 #include "nsIOutputStream.h"
12 #include "nsIRDFService.h"
13 #include "nsIRDFContainerUtils.h"
14 #include "nsIServiceManager.h"
15 #include "nsString.h"
16 #include "nsXPIDLString.h"
17 #include "nsTArray.h"
18 #include "rdf.h"
19 #include "rdfutil.h"
20 #include "mozilla/Attributes.h"
22 #include "rdfIDataSource.h"
24 int32_t nsRDFXMLSerializer::gRefCnt = 0;
25 nsIRDFContainerUtils* nsRDFXMLSerializer::gRDFC;
26 nsIRDFResource* nsRDFXMLSerializer::kRDF_instanceOf;
27 nsIRDFResource* nsRDFXMLSerializer::kRDF_type;
28 nsIRDFResource* nsRDFXMLSerializer::kRDF_nextVal;
29 nsIRDFResource* nsRDFXMLSerializer::kRDF_Bag;
30 nsIRDFResource* nsRDFXMLSerializer::kRDF_Seq;
31 nsIRDFResource* nsRDFXMLSerializer::kRDF_Alt;
33 static const char kRDFDescriptionOpen[] = " <RDF:Description";
34 static const char kIDAttr[] = " RDF:ID=\"";
35 static const char kAboutAttr[] = " RDF:about=\"";
36 static const char kRDFDescriptionClose[] = " </RDF:Description>\n";
37 static const char kRDFResource1[] = " RDF:resource=\"";
38 static const char kRDFResource2[] = "\"/>\n";
39 static const char kRDFParseTypeInteger[] = " NC:parseType=\"Integer\">";
40 static const char kRDFParseTypeDate[] = " NC:parseType=\"Date\">";
41 static const char kRDFUnknown[] = "><!-- unknown node type -->";
43 nsresult
44 nsRDFXMLSerializer::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
45 {
46 if (aOuter)
47 return NS_ERROR_NO_AGGREGATION;
49 nsCOMPtr<nsIRDFXMLSerializer> result = new nsRDFXMLSerializer();
50 if (! result)
51 return NS_ERROR_OUT_OF_MEMORY;
52 // The serializer object is here, addref gRefCnt so that the
53 // destructor can safely release it.
54 gRefCnt++;
56 nsresult rv;
57 rv = result->QueryInterface(aIID, aResult);
59 if (NS_FAILED(rv)) return rv;
61 if (gRefCnt == 1) do {
62 nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
63 if (NS_FAILED(rv)) break;
65 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
66 &kRDF_instanceOf);
67 if (NS_FAILED(rv)) break;
69 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
70 &kRDF_type);
71 if (NS_FAILED(rv)) break;
73 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
74 &kRDF_nextVal);
75 if (NS_FAILED(rv)) break;
77 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
78 &kRDF_Bag);
79 if (NS_FAILED(rv)) break;
81 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
82 &kRDF_Seq);
83 if (NS_FAILED(rv)) break;
85 rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
86 &kRDF_Alt);
87 if (NS_FAILED(rv)) break;
89 rv = CallGetService("@mozilla.org/rdf/container-utils;1", &gRDFC);
90 if (NS_FAILED(rv)) break;
91 } while (0);
93 return rv;
94 }
96 nsRDFXMLSerializer::nsRDFXMLSerializer()
97 {
98 MOZ_COUNT_CTOR(nsRDFXMLSerializer);
99 }
101 nsRDFXMLSerializer::~nsRDFXMLSerializer()
102 {
103 MOZ_COUNT_DTOR(nsRDFXMLSerializer);
105 if (--gRefCnt == 0) {
106 NS_IF_RELEASE(kRDF_Bag);
107 NS_IF_RELEASE(kRDF_Seq);
108 NS_IF_RELEASE(kRDF_Alt);
109 NS_IF_RELEASE(kRDF_instanceOf);
110 NS_IF_RELEASE(kRDF_type);
111 NS_IF_RELEASE(kRDF_nextVal);
112 NS_IF_RELEASE(gRDFC);
113 }
114 }
116 NS_IMPL_ISUPPORTS(nsRDFXMLSerializer, nsIRDFXMLSerializer, nsIRDFXMLSource)
118 NS_IMETHODIMP
119 nsRDFXMLSerializer::Init(nsIRDFDataSource* aDataSource)
120 {
121 if (! aDataSource)
122 return NS_ERROR_NULL_POINTER;
124 mDataSource = aDataSource;
125 mDataSource->GetURI(getter_Copies(mBaseURLSpec));
127 // Add the ``RDF'' prefix, by default.
128 nsCOMPtr<nsIAtom> prefix;
130 prefix = do_GetAtom("RDF");
131 AddNameSpace(prefix, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
133 prefix = do_GetAtom("NC");
134 AddNameSpace(prefix, NS_LITERAL_STRING("http://home.netscape.com/NC-rdf#"));
136 mPrefixID = 0;
138 return NS_OK;
139 }
141 NS_IMETHODIMP
142 nsRDFXMLSerializer::AddNameSpace(nsIAtom* aPrefix, const nsAString& aURI)
143 {
144 nsCOMPtr<nsIAtom> prefix = aPrefix;
145 if (!prefix) {
146 // Make up a prefix, we don't want default namespaces, so
147 // that we can use QNames for elements and attributes alike.
148 prefix = EnsureNewPrefix();
149 }
150 mNameSpaces.Put(aURI, prefix);
151 return NS_OK;
152 }
154 static nsresult
155 rdf_BlockingWrite(nsIOutputStream* stream, const char* buf, uint32_t size)
156 {
157 uint32_t written = 0;
158 uint32_t remaining = size;
159 while (remaining > 0) {
160 nsresult rv;
161 uint32_t cb;
163 if (NS_FAILED(rv = stream->Write(buf + written, remaining, &cb)))
164 return rv;
166 written += cb;
167 remaining -= cb;
168 }
169 return NS_OK;
170 }
172 static nsresult
173 rdf_BlockingWrite(nsIOutputStream* stream, const nsCSubstring& s)
174 {
175 return rdf_BlockingWrite(stream, s.BeginReading(), s.Length());
176 }
178 static nsresult
179 rdf_BlockingWrite(nsIOutputStream* stream, const nsAString& s)
180 {
181 NS_ConvertUTF16toUTF8 utf8(s);
182 return rdf_BlockingWrite(stream, utf8.get(), utf8.Length());
183 }
185 already_AddRefed<nsIAtom>
186 nsRDFXMLSerializer::EnsureNewPrefix()
187 {
188 nsAutoString qname;
189 nsCOMPtr<nsIAtom> prefix;
190 bool isNewPrefix;
191 do {
192 isNewPrefix = true;
193 qname.AssignLiteral("NS");
194 qname.AppendInt(++mPrefixID, 10);
195 prefix = do_GetAtom(qname);
196 nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
197 while (iter != mNameSpaces.last() && isNewPrefix) {
198 isNewPrefix = (iter->mPrefix != prefix);
199 ++iter;
200 }
201 } while (!isNewPrefix);
202 return prefix.forget();
203 }
205 // This converts a property resource (like
206 // "http://www.w3.org/TR/WD-rdf-syntax#Description") into a QName
207 // ("RDF:Description"), and registers the namespace, if it's made up.
209 nsresult
210 nsRDFXMLSerializer::RegisterQName(nsIRDFResource* aResource)
211 {
212 nsAutoCString uri, qname;
213 aResource->GetValueUTF8(uri);
215 nsNameSpaceMap::const_iterator iter = mNameSpaces.GetNameSpaceOf(uri);
216 if (iter != mNameSpaces.last()) {
217 NS_ENSURE_TRUE(iter->mPrefix, NS_ERROR_UNEXPECTED);
218 iter->mPrefix->ToUTF8String(qname);
219 qname.Append(':');
220 qname += StringTail(uri, uri.Length() - iter->mURI.Length());
221 mQNames.Put(aResource, qname);
222 return NS_OK;
223 }
225 // Okay, so we don't have it in our map. Try to make one up. This
226 // is very bogus.
227 int32_t i = uri.RFindChar('#'); // first try a '#'
228 if (i == -1) {
229 i = uri.RFindChar('/');
230 if (i == -1) {
231 // Okay, just punt and assume there is _no_ namespace on
232 // this thing...
233 mQNames.Put(aResource, uri);
234 return NS_OK;
235 }
236 }
238 // Take whatever is to the right of the '#' or '/' and call it the
239 // local name, make up a prefix.
240 nsCOMPtr<nsIAtom> prefix = EnsureNewPrefix();
241 mNameSpaces.Put(StringHead(uri, i+1), prefix);
242 prefix->ToUTF8String(qname);
243 qname.Append(':');
244 qname += StringTail(uri, uri.Length() - (i + 1));
246 mQNames.Put(aResource, qname);
247 return NS_OK;
248 }
250 nsresult
251 nsRDFXMLSerializer::GetQName(nsIRDFResource* aResource, nsCString& aQName)
252 {
253 return mQNames.Get(aResource, &aQName) ? NS_OK : NS_ERROR_UNEXPECTED;
254 }
256 bool
257 nsRDFXMLSerializer::IsContainerProperty(nsIRDFResource* aProperty)
258 {
259 // Return `true' if the property is an internal property related
260 // to being a container.
261 if (aProperty == kRDF_instanceOf)
262 return true;
264 if (aProperty == kRDF_nextVal)
265 return true;
267 bool isOrdinal = false;
268 gRDFC->IsOrdinalProperty(aProperty, &isOrdinal);
269 if (isOrdinal)
270 return true;
272 return false;
273 }
276 // convert '&', '<', and '>' into "&", "<", and ">", respectively.
277 static const char amp[] = "&";
278 static const char lt[] = "<";
279 static const char gt[] = ">";
280 static const char quot[] = """;
282 static void
283 rdf_EscapeAmpersandsAndAngleBrackets(nsCString& s)
284 {
285 uint32_t newLength, origLength;
286 newLength = origLength = s.Length();
288 // Compute the length of the result string.
289 const char* start = s.BeginReading();
290 const char* end = s.EndReading();
291 const char* c = start;
292 while (c != end) {
293 switch (*c) {
294 case '&' :
295 newLength += sizeof(amp) - 2;
296 break;
297 case '<':
298 case '>':
299 newLength += sizeof(gt) - 2;
300 break;
301 default:
302 break;
303 }
304 ++c;
305 }
306 if (newLength == origLength) {
307 // nothing to escape
308 return;
309 }
311 // escape the chars from the end back to the front.
312 s.SetLength(newLength);
314 // Buffer might have changed, get the pointers again
315 start = s.BeginReading(); // begin of string
316 c = start + origLength - 1; // last char in original string
317 char* w = s.EndWriting() - 1; // last char in grown buffer
318 while (c >= start) {
319 switch (*c) {
320 case '&' :
321 w -= 4;
322 nsCharTraits<char>::copy(w, amp, sizeof(amp) - 1);
323 break;
324 case '<':
325 w -= 3;
326 nsCharTraits<char>::copy(w, lt, sizeof(lt) - 1);
327 break;
328 case '>':
329 w -= 3;
330 nsCharTraits<char>::copy(w, gt, sizeof(gt) - 1);
331 break;
332 default:
333 *w = *c;
334 }
335 --w;
336 --c;
337 }
338 }
340 // convert '"' to """
341 static void
342 rdf_EscapeQuotes(nsCString& s)
343 {
344 int32_t i = 0;
345 while ((i = s.FindChar('"', i)) != -1) {
346 s.Replace(i, 1, quot, sizeof(quot) - 1);
347 i += sizeof(quot) - 2;
348 }
349 }
351 static void
352 rdf_EscapeAttributeValue(nsCString& s)
353 {
354 rdf_EscapeAmpersandsAndAngleBrackets(s);
355 rdf_EscapeQuotes(s);
356 }
359 nsresult
360 nsRDFXMLSerializer::SerializeInlineAssertion(nsIOutputStream* aStream,
361 nsIRDFResource* aResource,
362 nsIRDFResource* aProperty,
363 nsIRDFLiteral* aValue)
364 {
365 nsresult rv;
366 nsCString qname;
367 rv = GetQName(aProperty, qname);
368 NS_ENSURE_SUCCESS(rv, rv);
370 rv = rdf_BlockingWrite(aStream,
371 NS_LITERAL_CSTRING("\n "));
372 if (NS_FAILED(rv)) return rv;
374 const char16_t* value;
375 aValue->GetValueConst(&value);
376 NS_ConvertUTF16toUTF8 s(value);
378 rdf_EscapeAttributeValue(s);
380 rv = rdf_BlockingWrite(aStream, qname);
381 if (NS_FAILED(rv)) return rv;
382 rv = rdf_BlockingWrite(aStream, "=\"", 2);
383 if (NS_FAILED(rv)) return rv;
384 s.Append('"');
385 return rdf_BlockingWrite(aStream, s);
386 }
388 nsresult
389 nsRDFXMLSerializer::SerializeChildAssertion(nsIOutputStream* aStream,
390 nsIRDFResource* aResource,
391 nsIRDFResource* aProperty,
392 nsIRDFNode* aValue)
393 {
394 nsCString qname;
395 nsresult rv = GetQName(aProperty, qname);
396 NS_ENSURE_SUCCESS(rv, rv);
398 rv = rdf_BlockingWrite(aStream, " <", 5);
399 if (NS_FAILED(rv)) return rv;
400 rv = rdf_BlockingWrite(aStream, qname);
401 if (NS_FAILED(rv)) return rv;
403 nsCOMPtr<nsIRDFResource> resource;
404 nsCOMPtr<nsIRDFLiteral> literal;
405 nsCOMPtr<nsIRDFInt> number;
406 nsCOMPtr<nsIRDFDate> date;
408 if ((resource = do_QueryInterface(aValue)) != nullptr) {
409 nsAutoCString uri;
410 resource->GetValueUTF8(uri);
412 rdf_MakeRelativeRef(mBaseURLSpec, uri);
413 rdf_EscapeAttributeValue(uri);
415 rv = rdf_BlockingWrite(aStream, kRDFResource1,
416 sizeof(kRDFResource1) - 1);
417 if (NS_FAILED(rv)) return rv;
418 rv = rdf_BlockingWrite(aStream, uri);
419 if (NS_FAILED(rv)) return rv;
420 rv = rdf_BlockingWrite(aStream, kRDFResource2,
421 sizeof(kRDFResource2) - 1);
422 if (NS_FAILED(rv)) return rv;
424 goto no_close_tag;
425 }
426 else if ((literal = do_QueryInterface(aValue)) != nullptr) {
427 const char16_t *value;
428 literal->GetValueConst(&value);
429 NS_ConvertUTF16toUTF8 s(value);
431 rdf_EscapeAmpersandsAndAngleBrackets(s);
433 rv = rdf_BlockingWrite(aStream, ">", 1);
434 if (NS_FAILED(rv)) return rv;
435 rv = rdf_BlockingWrite(aStream, s);
436 if (NS_FAILED(rv)) return rv;
437 }
438 else if ((number = do_QueryInterface(aValue)) != nullptr) {
439 int32_t value;
440 number->GetValue(&value);
442 nsAutoCString n;
443 n.AppendInt(value);
445 rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
446 sizeof(kRDFParseTypeInteger) - 1);
447 if (NS_FAILED(rv)) return rv;
448 rv = rdf_BlockingWrite(aStream, n);
449 if (NS_FAILED(rv)) return rv;
450 }
451 else if ((date = do_QueryInterface(aValue)) != nullptr) {
452 PRTime value;
453 date->GetValue(&value);
455 nsAutoCString s;
456 rdf_FormatDate(value, s);
458 rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
459 sizeof(kRDFParseTypeDate) - 1);
460 if (NS_FAILED(rv)) return rv;
461 rv = rdf_BlockingWrite(aStream, s);
462 if (NS_FAILED(rv)) return rv;
463 }
464 else {
465 // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
466 // We should serialize nsIRDFInt, nsIRDFDate, etc...
467 NS_WARNING("unknown RDF node type");
469 rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
470 if (NS_FAILED(rv)) return rv;
471 }
473 rv = rdf_BlockingWrite(aStream, "</", 2);
474 if (NS_FAILED(rv)) return rv;
475 rv = rdf_BlockingWrite(aStream, qname);
476 if (NS_FAILED(rv)) return rv;
477 return rdf_BlockingWrite(aStream, ">\n", 2);
479 no_close_tag:
480 return NS_OK;
481 }
483 nsresult
484 nsRDFXMLSerializer::SerializeProperty(nsIOutputStream* aStream,
485 nsIRDFResource* aResource,
486 nsIRDFResource* aProperty,
487 bool aInline,
488 int32_t* aSkipped)
489 {
490 nsresult rv = NS_OK;
492 int32_t skipped = 0;
494 nsCOMPtr<nsISimpleEnumerator> assertions;
495 mDataSource->GetTargets(aResource, aProperty, true, getter_AddRefs(assertions));
496 if (! assertions)
497 return NS_ERROR_FAILURE;
499 // Serializing the assertion inline is ok as long as the property has
500 // only one target value, and it is a literal that doesn't include line
501 // breaks.
502 bool needsChild = false;
504 while (1) {
505 bool hasMore = false;
506 assertions->HasMoreElements(&hasMore);
507 if (! hasMore)
508 break;
510 nsCOMPtr<nsISupports> isupports;
511 assertions->GetNext(getter_AddRefs(isupports));
512 nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(isupports);
513 needsChild |= (!literal);
515 if (!needsChild) {
516 assertions->HasMoreElements(&needsChild);
517 if (!needsChild) {
518 const char16_t* literalVal = nullptr;
519 literal->GetValueConst(&literalVal);
520 if (literalVal) {
521 for (; *literalVal; literalVal++) {
522 if (*literalVal == char16_t('\n') ||
523 *literalVal == char16_t('\r')) {
524 needsChild = true;
525 break;
526 }
527 }
528 }
529 }
530 }
532 if (aInline && !needsChild) {
533 rv = SerializeInlineAssertion(aStream, aResource, aProperty, literal);
534 }
535 else if (!aInline && needsChild) {
536 nsCOMPtr<nsIRDFNode> value = do_QueryInterface(isupports);
537 rv = SerializeChildAssertion(aStream, aResource, aProperty, value);
538 }
539 else {
540 ++skipped;
541 rv = NS_OK;
542 }
544 if (NS_FAILED(rv))
545 break;
546 }
548 *aSkipped += skipped;
549 return rv;
550 }
553 nsresult
554 nsRDFXMLSerializer::SerializeDescription(nsIOutputStream* aStream,
555 nsIRDFResource* aResource)
556 {
557 nsresult rv;
559 bool isTypedNode = false;
560 nsCString typeQName;
562 nsCOMPtr<nsIRDFNode> typeNode;
563 mDataSource->GetTarget(aResource, kRDF_type, true, getter_AddRefs(typeNode));
564 if (typeNode) {
565 nsCOMPtr<nsIRDFResource> type = do_QueryInterface(typeNode, &rv);
566 if (type) {
567 // Try to get a namespace prefix. If none is available,
568 // just treat the description as if it weren't a typed node
569 // after all and emit rdf:type as a normal property. This
570 // seems preferable to using a bogus (invented) prefix.
571 isTypedNode = NS_SUCCEEDED(GetQName(type, typeQName));
572 }
573 }
575 nsAutoCString uri;
576 rv = aResource->GetValueUTF8(uri);
577 if (NS_FAILED(rv)) return rv;
579 rdf_MakeRelativeRef(mBaseURLSpec, uri);
580 rdf_EscapeAttributeValue(uri);
582 // Emit an open tag and the subject
583 if (isTypedNode) {
584 rv = rdf_BlockingWrite(aStream, NS_LITERAL_STRING(" <"));
585 if (NS_FAILED(rv)) return rv;
586 // Watch out for the default namespace!
587 rv = rdf_BlockingWrite(aStream, typeQName);
588 if (NS_FAILED(rv)) return rv;
589 }
590 else {
591 rv = rdf_BlockingWrite(aStream, kRDFDescriptionOpen,
592 sizeof(kRDFDescriptionOpen) - 1);
593 if (NS_FAILED(rv)) return rv;
594 }
595 if (uri[0] == char16_t('#')) {
596 uri.Cut(0, 1);
597 rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
598 }
599 else {
600 rv = rdf_BlockingWrite(aStream, kAboutAttr, sizeof(kAboutAttr) - 1);
601 }
602 if (NS_FAILED(rv)) return rv;
604 uri.Append('"');
605 rv = rdf_BlockingWrite(aStream, uri);
606 if (NS_FAILED(rv)) return rv;
608 // Any value that's a literal we can write out as an inline
609 // attribute on the RDF:Description
610 nsAutoTArray<nsIRDFResource*, 8> visited;
611 int32_t skipped = 0;
613 nsCOMPtr<nsISimpleEnumerator> arcs;
614 mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
616 if (arcs) {
617 // Don't re-serialize rdf:type later on
618 if (isTypedNode)
619 visited.AppendElement(kRDF_type);
621 while (1) {
622 bool hasMore = false;
623 arcs->HasMoreElements(&hasMore);
624 if (! hasMore)
625 break;
627 nsCOMPtr<nsISupports> isupports;
628 arcs->GetNext(getter_AddRefs(isupports));
630 nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
631 if (! property)
632 continue;
634 // Ignore properties that pertain to containers; we may be
635 // called from SerializeContainer() if the container resource
636 // has been assigned non-container properties.
637 if (IsContainerProperty(property))
638 continue;
640 // Only serialize values for the property once.
641 if (visited.Contains(property.get()))
642 continue;
644 visited.AppendElement(property.get());
646 SerializeProperty(aStream, aResource, property, true, &skipped);
647 }
648 }
650 if (skipped) {
651 // Close the RDF:Description tag.
652 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
653 if (NS_FAILED(rv)) return rv;
655 // Now write out resources (which might have their own
656 // substructure) as children.
657 mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
659 if (arcs) {
660 // Forget that we've visited anything
661 visited.Clear();
662 // ... except for rdf:type
663 if (isTypedNode)
664 visited.AppendElement(kRDF_type);
666 while (1) {
667 bool hasMore = false;
668 arcs->HasMoreElements(&hasMore);
669 if (! hasMore)
670 break;
672 nsCOMPtr<nsISupports> isupports;
673 arcs->GetNext(getter_AddRefs(isupports));
675 nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
676 if (! property)
677 continue;
679 // Ignore properties that pertain to containers; we may be
680 // called from SerializeContainer() if the container
681 // resource has been assigned non-container properties.
682 if (IsContainerProperty(property))
683 continue;
685 // have we already seen this property? If so, don't write it
686 // out again; serialize property will write each instance.
687 if (visited.Contains(property.get()))
688 continue;
690 visited.AppendElement(property.get());
692 SerializeProperty(aStream, aResource, property, false, &skipped);
693 }
694 }
696 // Emit a proper close-tag.
697 if (isTypedNode) {
698 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" </"));
699 if (NS_FAILED(rv)) return rv;
700 // Watch out for the default namespace!
701 rdf_BlockingWrite(aStream, typeQName);
702 if (NS_FAILED(rv)) return rv;
703 rdf_BlockingWrite(aStream, ">\n", 2);
704 if (NS_FAILED(rv)) return rv;
705 }
706 else {
707 rv = rdf_BlockingWrite(aStream, kRDFDescriptionClose,
708 sizeof(kRDFDescriptionClose) - 1);
709 if (NS_FAILED(rv)) return rv;
710 }
711 }
712 else {
713 // If we saw _no_ child properties, then we can don't need a
714 // close-tag.
715 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" />\n"));
716 if (NS_FAILED(rv)) return rv;
717 }
719 return NS_OK;
720 }
722 nsresult
723 nsRDFXMLSerializer::SerializeMember(nsIOutputStream* aStream,
724 nsIRDFResource* aContainer,
725 nsIRDFNode* aMember)
726 {
727 // If it's a resource, then output a "<RDF:li RDF:resource=... />"
728 // tag, because we'll be dumping the resource separately. (We
729 // iterate thru all the resources in the datasource,
730 // remember?) Otherwise, output the literal value.
732 nsCOMPtr<nsIRDFResource> resource;
733 nsCOMPtr<nsIRDFLiteral> literal;
734 nsCOMPtr<nsIRDFInt> number;
735 nsCOMPtr<nsIRDFDate> date;
737 static const char kRDFLIOpen[] = " <RDF:li";
738 nsresult rv = rdf_BlockingWrite(aStream, kRDFLIOpen,
739 sizeof(kRDFLIOpen) - 1);
740 if (NS_FAILED(rv)) return rv;
742 if ((resource = do_QueryInterface(aMember)) != nullptr) {
743 nsAutoCString uri;
744 resource->GetValueUTF8(uri);
746 rdf_MakeRelativeRef(mBaseURLSpec, uri);
747 rdf_EscapeAttributeValue(uri);
749 rv = rdf_BlockingWrite(aStream, kRDFResource1,
750 sizeof(kRDFResource1) - 1);
751 if (NS_FAILED(rv)) return rv;
752 rv = rdf_BlockingWrite(aStream, uri);
753 if (NS_FAILED(rv)) return rv;
754 rv = rdf_BlockingWrite(aStream, kRDFResource2,
755 sizeof(kRDFResource2) - 1);
756 if (NS_FAILED(rv)) return rv;
758 goto no_close_tag;
759 }
760 else if ((literal = do_QueryInterface(aMember)) != nullptr) {
761 const char16_t *value;
762 literal->GetValueConst(&value);
763 static const char kRDFLIOpenGT[] = ">";
764 // close the '<RDF:LI' before adding the literal
765 rv = rdf_BlockingWrite(aStream, kRDFLIOpenGT,
766 sizeof(kRDFLIOpenGT) - 1);
767 if (NS_FAILED(rv)) return rv;
769 NS_ConvertUTF16toUTF8 s(value);
770 rdf_EscapeAmpersandsAndAngleBrackets(s);
772 rv = rdf_BlockingWrite(aStream, s);
773 if (NS_FAILED(rv)) return rv;
774 }
775 else if ((number = do_QueryInterface(aMember)) != nullptr) {
776 int32_t value;
777 number->GetValue(&value);
779 nsAutoCString n;
780 n.AppendInt(value);
782 rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger,
783 sizeof(kRDFParseTypeInteger) - 1);
784 if (NS_FAILED(rv)) return rv;
785 rv = rdf_BlockingWrite(aStream, n);
786 if (NS_FAILED(rv)) return rv;
787 }
788 else if ((date = do_QueryInterface(aMember)) != nullptr) {
789 PRTime value;
790 date->GetValue(&value);
792 nsAutoCString s;
793 rdf_FormatDate(value, s);
795 rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate,
796 sizeof(kRDFParseTypeDate) - 1);
797 if (NS_FAILED(rv)) return rv;
798 rv = rdf_BlockingWrite(aStream, s);
799 if (NS_FAILED(rv)) return rv;
800 }
801 else {
802 // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
803 // We should serialize nsIRDFInt, nsIRDFDate, etc...
804 NS_WARNING("unknown RDF node type");
806 rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
807 if (NS_FAILED(rv)) return rv;
808 }
810 {
811 static const char kRDFLIClose[] = "</RDF:li>\n";
812 rv = rdf_BlockingWrite(aStream, kRDFLIClose, sizeof(kRDFLIClose) - 1);
813 if (NS_FAILED(rv)) return rv;
814 }
816 no_close_tag:
817 return NS_OK;
818 }
821 nsresult
822 nsRDFXMLSerializer::SerializeContainer(nsIOutputStream* aStream,
823 nsIRDFResource* aContainer)
824 {
825 nsresult rv;
826 nsAutoCString tag;
828 // Decide if it's a sequence, bag, or alternation, and print the
829 // appropriate tag-open sequence
831 if (IsA(mDataSource, aContainer, kRDF_Bag)) {
832 tag.AssignLiteral("RDF:Bag");
833 }
834 else if (IsA(mDataSource, aContainer, kRDF_Seq)) {
835 tag.AssignLiteral("RDF:Seq");
836 }
837 else if (IsA(mDataSource, aContainer, kRDF_Alt)) {
838 tag.AssignLiteral("RDF:Alt");
839 }
840 else {
841 NS_ASSERTION(false, "huh? this is _not_ a container.");
842 return NS_ERROR_UNEXPECTED;
843 }
845 rv = rdf_BlockingWrite(aStream, " <", 3);
846 if (NS_FAILED(rv)) return rv;
847 rv = rdf_BlockingWrite(aStream, tag);
848 if (NS_FAILED(rv)) return rv;
851 // Unfortunately, we always need to print out the identity of the
852 // resource, even if was constructed "anonymously". We need to do
853 // this because we never really know who else might be referring
854 // to it...
856 nsAutoCString uri;
857 if (NS_SUCCEEDED(aContainer->GetValueUTF8(uri))) {
858 rdf_MakeRelativeRef(mBaseURLSpec, uri);
860 rdf_EscapeAttributeValue(uri);
862 if (uri.First() == '#') {
863 // Okay, it's actually identified as an element in the
864 // current document, not trying to decorate some absolute
865 // URI. We can use the 'ID=' attribute...
867 uri.Cut(0, 1); // chop the '#'
868 rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
869 if (NS_FAILED(rv)) return rv;
870 }
871 else {
872 // We need to cheat and spit out an illegal 'about=' on
873 // the sequence.
874 rv = rdf_BlockingWrite(aStream, kAboutAttr,
875 sizeof(kAboutAttr) - 1);
876 if (NS_FAILED(rv)) return rv;
877 }
879 rv = rdf_BlockingWrite(aStream, uri);
880 if (NS_FAILED(rv)) return rv;
881 rv = rdf_BlockingWrite(aStream, "\"", 1);
882 if (NS_FAILED(rv)) return rv;
883 }
885 rv = rdf_BlockingWrite(aStream, ">\n", 2);
886 if (NS_FAILED(rv)) return rv;
888 // First iterate through each of the ordinal elements (the RDF/XML
889 // syntax doesn't allow us to place properties on RDF container
890 // elements).
891 nsCOMPtr<nsISimpleEnumerator> elements;
892 rv = NS_NewContainerEnumerator(mDataSource, aContainer, getter_AddRefs(elements));
894 if (NS_SUCCEEDED(rv)) {
895 while (1) {
896 bool hasMore;
897 rv = elements->HasMoreElements(&hasMore);
898 if (NS_FAILED(rv)) break;
900 if (! hasMore)
901 break;
903 nsCOMPtr<nsISupports> isupports;
904 elements->GetNext(getter_AddRefs(isupports));
906 nsCOMPtr<nsIRDFNode> element = do_QueryInterface(isupports);
907 NS_ASSERTION(element != nullptr, "not an nsIRDFNode");
908 if (! element)
909 continue;
911 SerializeMember(aStream, aContainer, element);
912 }
913 }
915 // close the container tag
916 rv = rdf_BlockingWrite(aStream, " </", 4);
917 if (NS_FAILED(rv)) return rv;
918 tag.Append(">\n", 2);
919 rv = rdf_BlockingWrite(aStream, tag);
920 if (NS_FAILED(rv)) return rv;
922 // Now, we iterate through _all_ of the arcs, in case someone has
923 // applied properties to the bag itself. These'll be placed in a
924 // separate RDF:Description element.
925 nsCOMPtr<nsISimpleEnumerator> arcs;
926 mDataSource->ArcLabelsOut(aContainer, getter_AddRefs(arcs));
928 bool wroteDescription = false;
929 while (! wroteDescription) {
930 bool hasMore = false;
931 rv = arcs->HasMoreElements(&hasMore);
932 if (NS_FAILED(rv)) break;
934 if (! hasMore)
935 break;
937 nsIRDFResource* property;
938 rv = arcs->GetNext((nsISupports**) &property);
939 if (NS_FAILED(rv)) break;
941 // If it's a membership property, then output a "LI"
942 // tag. Otherwise, output a property.
943 if (! IsContainerProperty(property)) {
944 rv = SerializeDescription(aStream, aContainer);
945 wroteDescription = true;
946 }
948 NS_RELEASE(property);
949 if (NS_FAILED(rv))
950 break;
951 }
953 return NS_OK;
954 }
957 nsresult
958 nsRDFXMLSerializer::SerializePrologue(nsIOutputStream* aStream)
959 {
960 static const char kXMLVersion[] = "<?xml version=\"1.0\"?>\n";
962 nsresult rv;
963 rv = rdf_BlockingWrite(aStream, kXMLVersion, sizeof(kXMLVersion) - 1);
964 if (NS_FAILED(rv)) return rv;
966 // global name space declarations
967 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("<RDF:RDF "));
968 if (NS_FAILED(rv)) return rv;
970 nsNameSpaceMap::const_iterator first = mNameSpaces.first();
971 nsNameSpaceMap::const_iterator last = mNameSpaces.last();
972 for (nsNameSpaceMap::const_iterator entry = first; entry != last; ++entry) {
973 if (entry != first) {
974 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\n "));
975 if (NS_FAILED(rv)) return rv;
976 }
977 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("xmlns"));
978 if (NS_FAILED(rv)) return rv;
980 if (entry->mPrefix) {
981 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(":"));
982 if (NS_FAILED(rv)) return rv;
983 nsAutoCString prefix;
984 entry->mPrefix->ToUTF8String(prefix);
985 rv = rdf_BlockingWrite(aStream, prefix);
986 if (NS_FAILED(rv)) return rv;
987 }
989 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("=\""));
990 if (NS_FAILED(rv)) return rv;
991 nsAutoCString uri(entry->mURI);
992 rdf_EscapeAttributeValue(uri);
993 rv = rdf_BlockingWrite(aStream, uri);
994 if (NS_FAILED(rv)) return rv;
995 rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\""));
996 if (NS_FAILED(rv)) return rv;
997 }
999 return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
1000 }
1003 nsresult
1004 nsRDFXMLSerializer::SerializeEpilogue(nsIOutputStream* aStream)
1005 {
1006 return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("</RDF:RDF>\n"));
1007 }
1009 class QNameCollector MOZ_FINAL : public rdfITripleVisitor {
1010 public:
1011 NS_DECL_ISUPPORTS
1012 NS_DECL_RDFITRIPLEVISITOR
1013 QNameCollector(nsRDFXMLSerializer* aParent)
1014 : mParent(aParent){}
1015 private:
1016 nsRDFXMLSerializer* mParent;
1017 };
1019 NS_IMPL_ISUPPORTS(QNameCollector, rdfITripleVisitor)
1020 nsresult
1021 QNameCollector::Visit(nsIRDFNode* aSubject, nsIRDFResource* aPredicate,
1022 nsIRDFNode* aObject, bool aTruthValue)
1023 {
1024 if (aPredicate == mParent->kRDF_type) {
1025 // try to get a type QName for aObject, should be a resource
1026 nsCOMPtr<nsIRDFResource> resType = do_QueryInterface(aObject);
1027 if (!resType) {
1028 // ignore error
1029 return NS_OK;
1030 }
1031 if (mParent->mQNames.Get(resType, nullptr)) {
1032 return NS_OK;
1033 }
1034 mParent->RegisterQName(resType);
1035 return NS_OK;
1036 }
1038 if (mParent->mQNames.Get(aPredicate, nullptr)) {
1039 return NS_OK;
1040 }
1041 if (aPredicate == mParent->kRDF_instanceOf ||
1042 aPredicate == mParent->kRDF_nextVal)
1043 return NS_OK;
1044 bool isOrdinal = false;
1045 mParent->gRDFC->IsOrdinalProperty(aPredicate, &isOrdinal);
1046 if (isOrdinal)
1047 return NS_OK;
1049 mParent->RegisterQName(aPredicate);
1051 return NS_OK;
1052 }
1054 nsresult
1055 nsRDFXMLSerializer::CollectNamespaces()
1056 {
1057 // Iterate over all Triples to get namespaces for subject resource types
1058 // and Predicates and cache all the QNames we want to use.
1059 nsCOMPtr<rdfITripleVisitor> collector =
1060 new QNameCollector(this);
1061 nsCOMPtr<rdfIDataSource> ds = do_QueryInterface(mDataSource); // XXX API
1062 NS_ENSURE_TRUE(collector && ds, NS_ERROR_FAILURE);
1063 return ds->VisitAllTriples(collector);
1064 }
1066 //----------------------------------------------------------------------
1068 NS_IMETHODIMP
1069 nsRDFXMLSerializer::Serialize(nsIOutputStream* aStream)
1070 {
1071 nsresult rv;
1073 rv = CollectNamespaces();
1074 if (NS_FAILED(rv)) return rv;
1076 nsCOMPtr<nsISimpleEnumerator> resources;
1077 rv = mDataSource->GetAllResources(getter_AddRefs(resources));
1078 if (NS_FAILED(rv)) return rv;
1080 rv = SerializePrologue(aStream);
1081 if (NS_FAILED(rv))
1082 return rv;
1084 while (1) {
1085 bool hasMore = false;
1086 resources->HasMoreElements(&hasMore);
1087 if (! hasMore)
1088 break;
1090 nsCOMPtr<nsISupports> isupports;
1091 resources->GetNext(getter_AddRefs(isupports));
1093 nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
1094 if (! resource)
1095 continue;
1097 if (IsA(mDataSource, resource, kRDF_Bag) ||
1098 IsA(mDataSource, resource, kRDF_Seq) ||
1099 IsA(mDataSource, resource, kRDF_Alt)) {
1100 rv = SerializeContainer(aStream, resource);
1101 }
1102 else {
1103 rv = SerializeDescription(aStream, resource);
1104 }
1106 if (NS_FAILED(rv))
1107 break;
1108 }
1110 rv = SerializeEpilogue(aStream);
1112 return rv;
1113 }
1116 bool
1117 nsRDFXMLSerializer::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
1118 {
1119 nsresult rv;
1121 bool result;
1122 rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, true, &result);
1123 if (NS_FAILED(rv)) return false;
1125 return result;
1126 }