1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcrt/xml/cfx_xmlnode.h"
8
9 #include <vector>
10
11 #include "core/fxcrt/fx_codepage.h"
12 #include "core/fxcrt/xml/cfx_xmlchardata.h"
13 #include "core/fxcrt/xml/cfx_xmlelement.h"
14 #include "core/fxcrt/xml/cfx_xmlinstruction.h"
15 #include "core/fxcrt/xml/cfx_xmltext.h"
16 #include "third_party/base/stl_util.h"
17
CFX_XMLNode()18 CFX_XMLNode::CFX_XMLNode()
19 : m_pParent(nullptr),
20 m_pChild(nullptr),
21 m_pPrior(nullptr),
22 m_pNext(nullptr) {}
23
GetType() const24 FX_XMLNODETYPE CFX_XMLNode::GetType() const {
25 return FX_XMLNODE_Unknown;
26 }
27
~CFX_XMLNode()28 CFX_XMLNode::~CFX_XMLNode() {
29 DeleteChildren();
30 }
31
DeleteChildren()32 void CFX_XMLNode::DeleteChildren() {
33 CFX_XMLNode* pChild = m_pChild;
34 while (pChild) {
35 CFX_XMLNode* pNext = pChild->m_pNext;
36 delete pChild;
37 pChild = pNext;
38 }
39 m_pChild = nullptr;
40 }
41
CountChildNodes() const42 int32_t CFX_XMLNode::CountChildNodes() const {
43 int32_t iCount = 0;
44 CFX_XMLNode* pChild = m_pChild;
45 while (pChild) {
46 iCount++;
47 pChild = pChild->m_pNext;
48 }
49 return iCount;
50 }
51
GetChildNode(int32_t index) const52 CFX_XMLNode* CFX_XMLNode::GetChildNode(int32_t index) const {
53 CFX_XMLNode* pChild = m_pChild;
54 while (pChild) {
55 if (index == 0) {
56 return pChild;
57 }
58 index--;
59 pChild = pChild->m_pNext;
60 }
61 return nullptr;
62 }
63
GetChildNodeIndex(CFX_XMLNode * pNode) const64 int32_t CFX_XMLNode::GetChildNodeIndex(CFX_XMLNode* pNode) const {
65 int32_t index = 0;
66 CFX_XMLNode* pChild = m_pChild;
67 while (pChild) {
68 if (pChild == pNode) {
69 return index;
70 }
71 index++;
72 pChild = pChild->m_pNext;
73 }
74 return -1;
75 }
76
GetPath(const wchar_t * pPath,int32_t iLength,bool bQualifiedName) const77 CFX_XMLNode* CFX_XMLNode::GetPath(const wchar_t* pPath,
78 int32_t iLength,
79 bool bQualifiedName) const {
80 ASSERT(pPath);
81 if (iLength < 0) {
82 iLength = wcslen(pPath);
83 }
84 if (iLength == 0) {
85 return nullptr;
86 }
87 WideString csPath;
88 const wchar_t* pStart = pPath;
89 const wchar_t* pEnd = pPath + iLength;
90 wchar_t ch;
91 while (pStart < pEnd) {
92 ch = *pStart++;
93 if (ch == L'/')
94 break;
95 csPath += ch;
96 }
97 iLength -= pStart - pPath;
98 CFX_XMLNode* pFind = nullptr;
99 if (csPath.GetLength() < 1) {
100 pFind = GetNodeItem(CFX_XMLNode::Root);
101 } else if (csPath.Compare(L"..") == 0) {
102 pFind = m_pParent;
103 } else if (csPath.Compare(L".") == 0) {
104 pFind = (CFX_XMLNode*)this;
105 } else {
106 WideString wsTag;
107 CFX_XMLNode* pNode = m_pChild;
108 while (pNode) {
109 if (pNode->GetType() == FX_XMLNODE_Element) {
110 if (bQualifiedName)
111 wsTag = static_cast<CFX_XMLElement*>(pNode)->GetName();
112 else
113 wsTag = static_cast<CFX_XMLElement*>(pNode)->GetLocalTagName();
114
115 if (wsTag.Compare(csPath) == 0) {
116 if (iLength < 1)
117 pFind = pNode;
118 else
119 pFind = pNode->GetPath(pStart, iLength, bQualifiedName);
120
121 if (pFind)
122 return pFind;
123 }
124 }
125 pNode = pNode->m_pNext;
126 }
127 }
128 if (!pFind || iLength < 1)
129 return pFind;
130 return pFind->GetPath(pStart, iLength, bQualifiedName);
131 }
132
InsertChildNode(CFX_XMLNode * pNode,int32_t index)133 int32_t CFX_XMLNode::InsertChildNode(CFX_XMLNode* pNode, int32_t index) {
134 pNode->m_pParent = this;
135 if (!m_pChild) {
136 m_pChild = pNode;
137 pNode->m_pPrior = nullptr;
138 pNode->m_pNext = nullptr;
139 return 0;
140 }
141 if (index == 0) {
142 pNode->m_pNext = m_pChild;
143 pNode->m_pPrior = nullptr;
144 m_pChild->m_pPrior = pNode;
145 m_pChild = pNode;
146 return 0;
147 }
148 int32_t iCount = 0;
149 CFX_XMLNode* pFind = m_pChild;
150 while (++iCount != index && pFind->m_pNext) {
151 pFind = pFind->m_pNext;
152 }
153 pNode->m_pPrior = pFind;
154 pNode->m_pNext = pFind->m_pNext;
155 if (pFind->m_pNext)
156 pFind->m_pNext->m_pPrior = pNode;
157 pFind->m_pNext = pNode;
158 return iCount;
159 }
160
RemoveChildNode(CFX_XMLNode * pNode)161 void CFX_XMLNode::RemoveChildNode(CFX_XMLNode* pNode) {
162 ASSERT(m_pChild && pNode);
163 if (m_pChild == pNode) {
164 m_pChild = pNode->m_pNext;
165 } else {
166 pNode->m_pPrior->m_pNext = pNode->m_pNext;
167 }
168 if (pNode->m_pNext)
169 pNode->m_pNext->m_pPrior = pNode->m_pPrior;
170 pNode->m_pParent = nullptr;
171 pNode->m_pNext = nullptr;
172 pNode->m_pPrior = nullptr;
173 }
174
GetNodeItem(CFX_XMLNode::NodeItem eItem) const175 CFX_XMLNode* CFX_XMLNode::GetNodeItem(CFX_XMLNode::NodeItem eItem) const {
176 switch (eItem) {
177 case CFX_XMLNode::Root: {
178 CFX_XMLNode* pParent = (CFX_XMLNode*)this;
179 while (pParent->m_pParent) {
180 pParent = pParent->m_pParent;
181 }
182 return pParent;
183 }
184 case CFX_XMLNode::Parent:
185 return m_pParent;
186 case CFX_XMLNode::FirstSibling: {
187 CFX_XMLNode* pItem = (CFX_XMLNode*)this;
188 while (pItem->m_pPrior) {
189 pItem = pItem->m_pPrior;
190 }
191 return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
192 }
193 case CFX_XMLNode::PriorSibling:
194 return m_pPrior;
195 case CFX_XMLNode::NextSibling:
196 return m_pNext;
197 case CFX_XMLNode::LastSibling: {
198 CFX_XMLNode* pItem = (CFX_XMLNode*)this;
199 while (pItem->m_pNext)
200 pItem = pItem->m_pNext;
201 return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
202 }
203 case CFX_XMLNode::FirstNeighbor: {
204 CFX_XMLNode* pParent = (CFX_XMLNode*)this;
205 while (pParent->m_pParent)
206 pParent = pParent->m_pParent;
207 return pParent == (CFX_XMLNode*)this ? nullptr : pParent;
208 }
209 case CFX_XMLNode::PriorNeighbor: {
210 if (!m_pPrior)
211 return m_pParent;
212
213 CFX_XMLNode* pItem = m_pPrior;
214 while (pItem->m_pChild) {
215 pItem = pItem->m_pChild;
216 while (pItem->m_pNext)
217 pItem = pItem->m_pNext;
218 }
219 return pItem;
220 }
221 case CFX_XMLNode::NextNeighbor: {
222 if (m_pChild)
223 return m_pChild;
224 if (m_pNext)
225 return m_pNext;
226 CFX_XMLNode* pItem = m_pParent;
227 while (pItem) {
228 if (pItem->m_pNext)
229 return pItem->m_pNext;
230 pItem = pItem->m_pParent;
231 }
232 return nullptr;
233 }
234 case CFX_XMLNode::LastNeighbor: {
235 CFX_XMLNode* pItem = (CFX_XMLNode*)this;
236 while (pItem->m_pParent) {
237 pItem = pItem->m_pParent;
238 }
239 while (true) {
240 while (pItem->m_pNext)
241 pItem = pItem->m_pNext;
242 if (!pItem->m_pChild)
243 break;
244 pItem = pItem->m_pChild;
245 }
246 return pItem == (CFX_XMLNode*)this ? nullptr : pItem;
247 }
248 case CFX_XMLNode::FirstChild:
249 return m_pChild;
250 case CFX_XMLNode::LastChild: {
251 if (!m_pChild)
252 return nullptr;
253
254 CFX_XMLNode* pChild = m_pChild;
255 while (pChild->m_pNext)
256 pChild = pChild->m_pNext;
257 return pChild;
258 }
259 default:
260 break;
261 }
262 return nullptr;
263 }
264
GetNodeLevel() const265 int32_t CFX_XMLNode::GetNodeLevel() const {
266 int32_t iLevel = 0;
267 const CFX_XMLNode* pItem = m_pParent;
268 while (pItem) {
269 iLevel++;
270 pItem = pItem->m_pParent;
271 }
272 return iLevel;
273 }
274
InsertNodeItem(CFX_XMLNode::NodeItem eItem,CFX_XMLNode * pNode)275 bool CFX_XMLNode::InsertNodeItem(CFX_XMLNode::NodeItem eItem,
276 CFX_XMLNode* pNode) {
277 switch (eItem) {
278 case CFX_XMLNode::NextSibling: {
279 pNode->m_pParent = m_pParent;
280 pNode->m_pNext = m_pNext;
281 pNode->m_pPrior = this;
282 if (m_pNext) {
283 m_pNext->m_pPrior = pNode;
284 }
285 m_pNext = pNode;
286 return true;
287 }
288 case CFX_XMLNode::PriorSibling: {
289 pNode->m_pParent = m_pParent;
290 pNode->m_pNext = this;
291 pNode->m_pPrior = m_pPrior;
292 if (m_pPrior) {
293 m_pPrior->m_pNext = pNode;
294 } else if (m_pParent) {
295 m_pParent->m_pChild = pNode;
296 }
297 m_pPrior = pNode;
298 return true;
299 }
300 default:
301 return false;
302 }
303 }
304
RemoveNodeItem(CFX_XMLNode::NodeItem eItem)305 CFX_XMLNode* CFX_XMLNode::RemoveNodeItem(CFX_XMLNode::NodeItem eItem) {
306 CFX_XMLNode* pNode = nullptr;
307 switch (eItem) {
308 case CFX_XMLNode::NextSibling:
309 if (m_pNext) {
310 pNode = m_pNext;
311 m_pNext = pNode->m_pNext;
312 if (m_pNext) {
313 m_pNext->m_pPrior = this;
314 }
315 pNode->m_pParent = nullptr;
316 pNode->m_pNext = nullptr;
317 pNode->m_pPrior = nullptr;
318 }
319 break;
320 default:
321 break;
322 }
323 return pNode;
324 }
325
Clone()326 std::unique_ptr<CFX_XMLNode> CFX_XMLNode::Clone() {
327 return nullptr;
328 }
329
SaveXMLNode(const RetainPtr<CFX_SeekableStreamProxy> & pXMLStream)330 void CFX_XMLNode::SaveXMLNode(
331 const RetainPtr<CFX_SeekableStreamProxy>& pXMLStream) {
332 CFX_XMLNode* pNode = (CFX_XMLNode*)this;
333 switch (pNode->GetType()) {
334 case FX_XMLNODE_Instruction: {
335 WideString ws;
336 CFX_XMLInstruction* pInstruction = (CFX_XMLInstruction*)pNode;
337 if (pInstruction->GetName().CompareNoCase(L"xml") == 0) {
338 ws = L"<?xml version=\"1.0\" encoding=\"";
339 uint16_t wCodePage = pXMLStream->GetCodePage();
340 if (wCodePage == FX_CODEPAGE_UTF16LE) {
341 ws += L"UTF-16";
342 } else if (wCodePage == FX_CODEPAGE_UTF16BE) {
343 ws += L"UTF-16be";
344 } else {
345 ws += L"UTF-8";
346 }
347 ws += L"\"?>";
348 pXMLStream->WriteString(ws.AsStringView());
349 } else {
350 ws = WideString::Format(L"<?%ls", pInstruction->GetName().c_str());
351 pXMLStream->WriteString(ws.AsStringView());
352
353 for (auto it : pInstruction->GetAttributes()) {
354 WideString wsValue = it.second;
355 wsValue.Replace(L"&", L"&");
356 wsValue.Replace(L"<", L"<");
357 wsValue.Replace(L">", L">");
358 wsValue.Replace(L"\'", L"'");
359 wsValue.Replace(L"\"", L""");
360
361 ws = L" ";
362 ws += it.first;
363 ws += L"=\"";
364 ws += wsValue;
365 ws += L"\"";
366 pXMLStream->WriteString(ws.AsStringView());
367 }
368
369 for (auto target : pInstruction->GetTargetData()) {
370 ws = L" \"";
371 ws += target;
372 ws += L"\"";
373 pXMLStream->WriteString(ws.AsStringView());
374 }
375 ws = L"?>";
376 pXMLStream->WriteString(ws.AsStringView());
377 }
378 break;
379 }
380 case FX_XMLNODE_Element: {
381 WideString ws;
382 ws = L"<";
383 ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
384 pXMLStream->WriteString(ws.AsStringView());
385
386 for (auto it : static_cast<CFX_XMLElement*>(pNode)->GetAttributes()) {
387 WideString wsValue = it.second;
388 wsValue.Replace(L"&", L"&");
389 wsValue.Replace(L"<", L"<");
390 wsValue.Replace(L">", L">");
391 wsValue.Replace(L"\'", L"'");
392 wsValue.Replace(L"\"", L""");
393
394 ws = L" ";
395 ws += it.first;
396 ws += L"=\"";
397 ws += wsValue;
398 ws += L"\"";
399 pXMLStream->WriteString(ws.AsStringView());
400 }
401 if (pNode->m_pChild) {
402 ws = L"\n>";
403 pXMLStream->WriteString(ws.AsStringView());
404 CFX_XMLNode* pChild = pNode->m_pChild;
405 while (pChild) {
406 pChild->SaveXMLNode(pXMLStream);
407 pChild = pChild->m_pNext;
408 }
409 ws = L"</";
410 ws += static_cast<CFX_XMLElement*>(pNode)->GetName();
411 ws += L"\n>";
412 } else {
413 ws = L"\n/>";
414 }
415 pXMLStream->WriteString(ws.AsStringView());
416 break;
417 }
418 case FX_XMLNODE_Text: {
419 WideString ws = static_cast<CFX_XMLText*>(pNode)->GetText();
420 ws.Replace(L"&", L"&");
421 ws.Replace(L"<", L"<");
422 ws.Replace(L">", L">");
423 ws.Replace(L"\'", L"'");
424 ws.Replace(L"\"", L""");
425 pXMLStream->WriteString(ws.AsStringView());
426 break;
427 }
428 case FX_XMLNODE_CharData: {
429 WideString ws = L"<![CDATA[";
430 ws += static_cast<CFX_XMLCharData*>(pNode)->GetText();
431 ws += L"]]>";
432 pXMLStream->WriteString(ws.AsStringView());
433 break;
434 }
435 case FX_XMLNODE_Unknown:
436 default:
437 break;
438 }
439 }
440