1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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  * XML utility classes
8  */
9 
10 #include "txXMLUtils.h"
11 #include "nsString.h"
12 #include "nsReadableUtils.h"
13 #include "nsGkAtoms.h"
14 #include "txStringUtils.h"
15 #include "txNamespaceMap.h"
16 #include "txXPathTreeWalker.h"
17 #include "nsContentUtils.h"
18 
19 //------------------------------/
20 //- Implementation of XMLUtils -/
21 //------------------------------/
22 
23 // static
splitExpatName(const char16_t * aExpatName,nsAtom ** aPrefix,nsAtom ** aLocalName,int32_t * aNameSpaceID)24 nsresult XMLUtils::splitExpatName(const char16_t* aExpatName, nsAtom** aPrefix,
25                                   nsAtom** aLocalName, int32_t* aNameSpaceID) {
26   /**
27    *  Expat can send the following:
28    *    localName
29    *    namespaceURI<separator>localName
30    *    namespaceURI<separator>localName<separator>prefix
31    */
32 
33   const char16_t* uriEnd = nullptr;
34   const char16_t* nameEnd = nullptr;
35   const char16_t* pos;
36   for (pos = aExpatName; *pos; ++pos) {
37     if (*pos == kExpatSeparatorChar) {
38       if (uriEnd) {
39         nameEnd = pos;
40       } else {
41         uriEnd = pos;
42       }
43     }
44   }
45 
46   const char16_t* nameStart;
47   if (uriEnd) {
48     *aNameSpaceID = txNamespaceManager::getNamespaceID(
49         nsDependentSubstring(aExpatName, uriEnd));
50     if (*aNameSpaceID == kNameSpaceID_Unknown) {
51       return NS_ERROR_FAILURE;
52     }
53 
54     nameStart = (uriEnd + 1);
55     if (nameEnd) {
56       const char16_t* prefixStart = nameEnd + 1;
57       *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take();
58       if (!*aPrefix) {
59         return NS_ERROR_OUT_OF_MEMORY;
60       }
61     } else {
62       nameEnd = pos;
63       *aPrefix = nullptr;
64     }
65   } else {
66     *aNameSpaceID = kNameSpaceID_None;
67     nameStart = aExpatName;
68     nameEnd = pos;
69     *aPrefix = nullptr;
70   }
71 
72   *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take();
73 
74   return *aLocalName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
75 }
76 
splitQName(const nsAString & aName,nsAtom ** aPrefix,nsAtom ** aLocalName)77 nsresult XMLUtils::splitQName(const nsAString& aName, nsAtom** aPrefix,
78                               nsAtom** aLocalName) {
79   const char16_t* colon;
80   bool valid = XMLUtils::isValidQName(aName, &colon);
81   if (!valid) {
82     return NS_ERROR_FAILURE;
83   }
84 
85   if (colon) {
86     const char16_t* end;
87     aName.EndReading(end);
88 
89     *aPrefix = NS_Atomize(Substring(aName.BeginReading(), colon)).take();
90     *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
91   } else {
92     *aPrefix = nullptr;
93     *aLocalName = NS_Atomize(aName).take();
94   }
95 
96   return NS_OK;
97 }
98 
99 /**
100  * Returns true if the given string has only whitespace characters
101  */
isWhitespace(const nsAString & aText)102 bool XMLUtils::isWhitespace(const nsAString& aText) {
103   nsString::const_char_iterator start, end;
104   aText.BeginReading(start);
105   aText.EndReading(end);
106   for (; start != end; ++start) {
107     if (!isWhitespace(*start)) {
108       return false;
109     }
110   }
111   return true;
112 }
113 
114 /**
115  * Normalizes the value of a XML processing instruction
116  **/
normalizePIValue(nsAString & piValue)117 void XMLUtils::normalizePIValue(nsAString& piValue) {
118   nsAutoString origValue(piValue);
119   uint32_t origLength = origValue.Length();
120   uint32_t conversionLoop = 0;
121   char16_t prevCh = 0;
122   piValue.Truncate();
123 
124   while (conversionLoop < origLength) {
125     char16_t ch = origValue.CharAt(conversionLoop);
126     switch (ch) {
127       case '>': {
128         if (prevCh == '?') {
129           piValue.Append(char16_t(' '));
130         }
131         break;
132       }
133       default: {
134         break;
135       }
136     }
137     piValue.Append(ch);
138     prevCh = ch;
139     ++conversionLoop;
140   }
141 }
142 
143 // static
isValidQName(const nsAString & aQName,const char16_t ** aColon)144 bool XMLUtils::isValidQName(const nsAString& aQName, const char16_t** aColon) {
145   return NS_SUCCEEDED(nsContentUtils::CheckQName(aQName, true, aColon));
146 }
147 
148 // static
getXMLSpacePreserve(const txXPathNode & aNode)149 bool XMLUtils::getXMLSpacePreserve(const txXPathNode& aNode) {
150   nsAutoString value;
151   txXPathTreeWalker walker(aNode);
152   do {
153     if (walker.getAttr(nsGkAtoms::space, kNameSpaceID_XML, value)) {
154       if (TX_StringEqualsAtom(value, nsGkAtoms::preserve)) {
155         return true;
156       }
157       if (TX_StringEqualsAtom(value, nsGkAtoms::_default)) {
158         return false;
159       }
160     }
161   } while (walker.moveToParent());
162 
163   return false;
164 }
165