Fix incorrect CPDFSDK_PageView::CountAnnots().
[pdfium.git] / core / src / fxcrt / fx_xml_parser.cpp
1 // Copyright 2014 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 "../../include/fxcrt/fx_xml.h"
8 #include "xml_int.h"
9 CXML_Parser::~CXML_Parser() {
10   if (m_bOwnedStream) {
11     m_pDataAcc->Release();
12   }
13 }
14 FX_BOOL CXML_Parser::Init(uint8_t* pBuffer, size_t size) {
15   m_pDataAcc = new CXML_DataBufAcc(pBuffer, size);
16   return Init(TRUE);
17 }
18 FX_BOOL CXML_Parser::Init(IFX_FileRead* pFileRead) {
19   m_pDataAcc = new CXML_DataStmAcc(pFileRead);
20   return Init(TRUE);
21 }
22 FX_BOOL CXML_Parser::Init(IFX_BufferRead* pBuffer) {
23   if (!pBuffer) {
24     return FALSE;
25   }
26   m_pDataAcc = pBuffer;
27   return Init(FALSE);
28 }
29 FX_BOOL CXML_Parser::Init(FX_BOOL bOwndedStream) {
30   m_bOwnedStream = bOwndedStream;
31   m_nOffset = 0;
32   return ReadNextBlock();
33 }
34 FX_BOOL CXML_Parser::ReadNextBlock() {
35   if (!m_pDataAcc->ReadNextBlock()) {
36     return FALSE;
37   }
38   m_pBuffer = m_pDataAcc->GetBlockBuffer();
39   m_dwBufferSize = m_pDataAcc->GetBlockSize();
40   m_nBufferOffset = m_pDataAcc->GetBlockOffset();
41   m_dwIndex = 0;
42   return m_dwBufferSize > 0;
43 }
44 FX_BOOL CXML_Parser::IsEOF() {
45   if (!m_pDataAcc->IsEOF()) {
46     return FALSE;
47   }
48   return m_dwIndex >= m_dwBufferSize;
49 }
50 #define FXCRTM_XML_CHARTYPE_Normal 0x00
51 #define FXCRTM_XML_CHARTYPE_SpaceChar 0x01
52 #define FXCRTM_XML_CHARTYPE_Letter 0x02
53 #define FXCRTM_XML_CHARTYPE_Digital 0x04
54 #define FXCRTM_XML_CHARTYPE_NameIntro 0x08
55 #define FXCRTM_XML_CHARTYPE_NameChar 0x10
56 #define FXCRTM_XML_CHARTYPE_HexDigital 0x20
57 #define FXCRTM_XML_CHARTYPE_HexLowerLetter 0x40
58 #define FXCRTM_XML_CHARTYPE_HexUpperLetter 0x60
59 #define FXCRTM_XML_CHARTYPE_HexChar 0x60
60 uint8_t g_FXCRT_XML_ByteTypes[256] = {
61     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
62     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
63     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
64     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
65     0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x08, 0x00,
66     0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x1A,
67     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
68     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x18,
69     0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
70     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
71     0x1A, 0x1A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A,
72     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
73     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
74     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
75     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
76     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
77     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
78     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
79     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
80     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
81     0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
82     0x1A, 0x1A, 0x01, 0x01,
83 };
84 FX_BOOL g_FXCRT_XML_IsWhiteSpace(uint8_t ch) {
85   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_SpaceChar) != 0;
86 }
87 FX_BOOL g_FXCRT_XML_IsLetter(uint8_t ch) {
88   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Letter) != 0;
89 }
90 FX_BOOL g_FXCRT_XML_IsDigital(uint8_t ch) {
91   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_Digital) != 0;
92 }
93 FX_BOOL g_FXCRT_XML_IsNameIntro(uint8_t ch) {
94   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameIntro) != 0;
95 }
96 FX_BOOL g_FXCRT_XML_IsNameChar(uint8_t ch) {
97   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_NameChar) != 0;
98 }
99 FX_BOOL g_FXCRT_XML_IsHexChar(uint8_t ch) {
100   return (g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar) != 0;
101 }
102 void CXML_Parser::SkipWhiteSpaces() {
103   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
104   if (IsEOF()) {
105     return;
106   }
107   do {
108     while (m_dwIndex < m_dwBufferSize &&
109            g_FXCRT_XML_IsWhiteSpace(m_pBuffer[m_dwIndex])) {
110       m_dwIndex++;
111     }
112     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
113     if (m_dwIndex < m_dwBufferSize || IsEOF()) {
114       break;
115     }
116   } while (ReadNextBlock());
117 }
118 void CXML_Parser::GetName(CFX_ByteString& space, CFX_ByteString& name) {
119   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
120   if (IsEOF()) {
121     return;
122   }
123   CFX_ByteTextBuf buf;
124   uint8_t ch;
125   do {
126     while (m_dwIndex < m_dwBufferSize) {
127       ch = m_pBuffer[m_dwIndex];
128       if (ch == ':') {
129         space = buf.GetByteString();
130         buf.Clear();
131       } else if (g_FXCRT_XML_IsNameChar(ch)) {
132         buf.AppendChar(ch);
133       } else {
134         break;
135       }
136       m_dwIndex++;
137     }
138     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
139     if (m_dwIndex < m_dwBufferSize || IsEOF()) {
140       break;
141     }
142   } while (ReadNextBlock());
143   name = buf.GetByteString();
144 }
145 void CXML_Parser::SkipLiterals(const CFX_ByteStringC& str) {
146   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
147   if (IsEOF()) {
148     return;
149   }
150   int32_t i = 0, iLen = str.GetLength();
151   do {
152     while (m_dwIndex < m_dwBufferSize) {
153       if (str.GetAt(i) != m_pBuffer[m_dwIndex++]) {
154         i = 0;
155       } else {
156         i++;
157         if (i == iLen) {
158           break;
159         }
160       }
161     }
162     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
163     if (i == iLen) {
164       return;
165     }
166     if (m_dwIndex < m_dwBufferSize || IsEOF()) {
167       break;
168     }
169   } while (ReadNextBlock());
170   while (!m_pDataAcc->IsEOF()) {
171     ReadNextBlock();
172     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwBufferSize;
173   }
174   m_dwIndex = m_dwBufferSize;
175 }
176 FX_DWORD CXML_Parser::GetCharRef() {
177   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
178   if (IsEOF()) {
179     return 0;
180   }
181   uint8_t ch;
182   int32_t iState = 0;
183   CFX_ByteTextBuf buf;
184   FX_DWORD code = 0;
185   do {
186     while (m_dwIndex < m_dwBufferSize) {
187       ch = m_pBuffer[m_dwIndex];
188       switch (iState) {
189         case 0:
190           if (ch == '#') {
191             m_dwIndex++;
192             iState = 2;
193             break;
194           }
195           iState = 1;
196         case 1:
197           m_dwIndex++;
198           if (ch == ';') {
199             CFX_ByteStringC ref = buf.GetByteString();
200             if (ref == FX_BSTRC("gt")) {
201               code = '>';
202             } else if (ref == FX_BSTRC("lt")) {
203               code = '<';
204             } else if (ref == FX_BSTRC("amp")) {
205               code = '&';
206             } else if (ref == FX_BSTRC("apos")) {
207               code = '\'';
208             } else if (ref == FX_BSTRC("quot")) {
209               code = '"';
210             }
211             iState = 10;
212             break;
213           }
214           buf.AppendByte(ch);
215           break;
216         case 2:
217           if (ch == 'x') {
218             m_dwIndex++;
219             iState = 4;
220             break;
221           }
222           iState = 3;
223         case 3:
224           m_dwIndex++;
225           if (ch == ';') {
226             iState = 10;
227             break;
228           }
229           if (g_FXCRT_XML_IsDigital(ch)) {
230             code = code * 10 + ch - '0';
231           }
232           break;
233         case 4:
234           m_dwIndex++;
235           if (ch == ';') {
236             iState = 10;
237             break;
238           }
239           uint8_t nHex =
240               g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar;
241           if (nHex) {
242             if (nHex == FXCRTM_XML_CHARTYPE_HexDigital) {
243               code = (code << 4) + ch - '0';
244             } else if (nHex == FXCRTM_XML_CHARTYPE_HexLowerLetter) {
245               code = (code << 4) + ch - 87;
246             } else {
247               code = (code << 4) + ch - 55;
248             }
249           }
250           break;
251       }
252       if (iState == 10) {
253         break;
254       }
255     }
256     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
257     if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
258       break;
259     }
260   } while (ReadNextBlock());
261   return code;
262 }
263 void CXML_Parser::GetAttrValue(CFX_WideString& value) {
264   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
265   if (IsEOF()) {
266     return;
267   }
268   CFX_UTF8Decoder decoder;
269   uint8_t mark = 0, ch = 0;
270   do {
271     while (m_dwIndex < m_dwBufferSize) {
272       ch = m_pBuffer[m_dwIndex];
273       if (mark == 0) {
274         if (ch != '\'' && ch != '"') {
275           return;
276         }
277         mark = ch;
278         m_dwIndex++;
279         ch = 0;
280         continue;
281       }
282       m_dwIndex++;
283       if (ch == mark) {
284         break;
285       }
286       if (ch == '&') {
287         decoder.AppendChar(GetCharRef());
288         if (IsEOF()) {
289           value = decoder.GetResult();
290           return;
291         }
292       } else {
293         decoder.Input(ch);
294       }
295     }
296     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
297     if (ch == mark || m_dwIndex < m_dwBufferSize || IsEOF()) {
298       break;
299     }
300   } while (ReadNextBlock());
301   value = decoder.GetResult();
302 }
303 void CXML_Parser::GetTagName(CFX_ByteString& space,
304                              CFX_ByteString& name,
305                              FX_BOOL& bEndTag,
306                              FX_BOOL bStartTag) {
307   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
308   if (IsEOF()) {
309     return;
310   }
311   bEndTag = FALSE;
312   uint8_t ch;
313   int32_t iState = bStartTag ? 1 : 0;
314   do {
315     while (m_dwIndex < m_dwBufferSize) {
316       ch = m_pBuffer[m_dwIndex];
317       switch (iState) {
318         case 0:
319           m_dwIndex++;
320           if (ch != '<') {
321             break;
322           }
323           iState = 1;
324           break;
325         case 1:
326           if (ch == '?') {
327             m_dwIndex++;
328             SkipLiterals(FX_BSTRC("?>"));
329             iState = 0;
330             break;
331           } else if (ch == '!') {
332             m_dwIndex++;
333             SkipLiterals(FX_BSTRC("-->"));
334             iState = 0;
335             break;
336           }
337           if (ch == '/') {
338             m_dwIndex++;
339             GetName(space, name);
340             bEndTag = TRUE;
341           } else {
342             GetName(space, name);
343             bEndTag = FALSE;
344           }
345           return;
346       }
347     }
348     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
349     if (m_dwIndex < m_dwBufferSize || IsEOF()) {
350       break;
351     }
352   } while (ReadNextBlock());
353 }
354 CXML_Element* CXML_Parser::ParseElement(CXML_Element* pParent,
355                                         FX_BOOL bStartTag) {
356   m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
357   if (IsEOF()) {
358     return NULL;
359   }
360   CFX_ByteString tag_name, tag_space;
361   FX_BOOL bEndTag;
362   GetTagName(tag_space, tag_name, bEndTag, bStartTag);
363   if (tag_name.IsEmpty() || bEndTag) {
364     return NULL;
365   }
366   CXML_Element* pElement = new CXML_Element;
367   pElement->m_pParent = pParent;
368   pElement->SetTag(tag_space, tag_name);
369   do {
370     CFX_ByteString attr_space, attr_name;
371     while (m_dwIndex < m_dwBufferSize) {
372       SkipWhiteSpaces();
373       if (IsEOF()) {
374         break;
375       }
376       if (!g_FXCRT_XML_IsNameIntro(m_pBuffer[m_dwIndex])) {
377         break;
378       }
379       GetName(attr_space, attr_name);
380       SkipWhiteSpaces();
381       if (IsEOF()) {
382         break;
383       }
384       if (m_pBuffer[m_dwIndex] != '=') {
385         break;
386       }
387       m_dwIndex++;
388       SkipWhiteSpaces();
389       if (IsEOF()) {
390         break;
391       }
392       CFX_WideString attr_value;
393       GetAttrValue(attr_value);
394       pElement->m_AttrMap.SetAt(attr_space, attr_name, attr_value);
395     }
396     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
397     if (m_dwIndex < m_dwBufferSize || IsEOF()) {
398       break;
399     }
400   } while (ReadNextBlock());
401   SkipWhiteSpaces();
402   if (IsEOF()) {
403     return pElement;
404   }
405   uint8_t ch = m_pBuffer[m_dwIndex++];
406   if (ch == '/') {
407     m_dwIndex++;
408     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
409     return pElement;
410   }
411   if (ch != '>') {
412     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
413     delete pElement;
414     return NULL;
415   }
416   SkipWhiteSpaces();
417   if (IsEOF()) {
418     return pElement;
419   }
420   CFX_UTF8Decoder decoder;
421   CFX_WideTextBuf content;
422   FX_BOOL bCDATA = FALSE;
423   int32_t iState = 0;
424   do {
425     while (m_dwIndex < m_dwBufferSize) {
426       ch = m_pBuffer[m_dwIndex++];
427       switch (iState) {
428         case 0:
429           if (ch == '<') {
430             iState = 1;
431           } else if (ch == '&') {
432             decoder.ClearStatus();
433             decoder.AppendChar(GetCharRef());
434           } else {
435             decoder.Input(ch);
436           }
437           break;
438         case 1:
439           if (ch == '!') {
440             iState = 2;
441           } else if (ch == '?') {
442             SkipLiterals(FX_BSTRC("?>"));
443             SkipWhiteSpaces();
444             iState = 0;
445           } else if (ch == '/') {
446             CFX_ByteString space, name;
447             GetName(space, name);
448             SkipWhiteSpaces();
449             m_dwIndex++;
450             iState = 10;
451           } else {
452             content << decoder.GetResult();
453             CFX_WideString dataStr = content.GetWideString();
454             if (!bCDATA && !m_bSaveSpaceChars) {
455               dataStr.TrimRight(L" \t\r\n");
456             }
457             InsertContentSegment(bCDATA, dataStr, pElement);
458             content.Clear();
459             decoder.Clear();
460             bCDATA = FALSE;
461             iState = 0;
462             m_dwIndex--;
463             CXML_Element* pSubElement = ParseElement(pElement, TRUE);
464             if (pSubElement == NULL) {
465               break;
466             }
467             pSubElement->m_pParent = pElement;
468             pElement->m_Children.Add((void*)CXML_Element::Element);
469             pElement->m_Children.Add(pSubElement);
470             SkipWhiteSpaces();
471           }
472           break;
473         case 2:
474           if (ch == '[') {
475             SkipLiterals(FX_BSTRC("]]>"));
476           } else if (ch == '-') {
477             m_dwIndex++;
478             SkipLiterals(FX_BSTRC("-->"));
479           } else {
480             SkipLiterals(FX_BSTRC(">"));
481           }
482           decoder.Clear();
483           SkipWhiteSpaces();
484           iState = 0;
485           break;
486       }
487       if (iState == 10) {
488         break;
489       }
490     }
491     m_nOffset = m_nBufferOffset + (FX_FILESIZE)m_dwIndex;
492     if (iState == 10 || m_dwIndex < m_dwBufferSize || IsEOF()) {
493       break;
494     }
495   } while (ReadNextBlock());
496   content << decoder.GetResult();
497   CFX_WideString dataStr = content.GetWideString();
498   if (!m_bSaveSpaceChars) {
499     dataStr.TrimRight(L" \t\r\n");
500   }
501   InsertContentSegment(bCDATA, dataStr, pElement);
502   content.Clear();
503   decoder.Clear();
504   bCDATA = FALSE;
505   return pElement;
506 }
507 void CXML_Parser::InsertContentSegment(FX_BOOL bCDATA,
508                                        const CFX_WideStringC& content,
509                                        CXML_Element* pElement) {
510   if (content.IsEmpty()) {
511     return;
512   }
513   CXML_Content* pContent = new CXML_Content;
514   pContent->Set(bCDATA, content);
515   pElement->m_Children.Add((void*)CXML_Element::Content);
516   pElement->m_Children.Add(pContent);
517 }
518 static CXML_Element* XML_ContinueParse(CXML_Parser& parser,
519                                        FX_BOOL bSaveSpaceChars,
520                                        FX_FILESIZE* pParsedSize) {
521   parser.m_bSaveSpaceChars = bSaveSpaceChars;
522   CXML_Element* pElement = parser.ParseElement(NULL, FALSE);
523   if (pParsedSize) {
524     *pParsedSize = parser.m_nOffset;
525   }
526   return pElement;
527 }
528 CXML_Element* CXML_Element::Parse(const void* pBuffer,
529                                   size_t size,
530                                   FX_BOOL bSaveSpaceChars,
531                                   FX_FILESIZE* pParsedSize) {
532   CXML_Parser parser;
533   if (!parser.Init((uint8_t*)pBuffer, size)) {
534     return NULL;
535   }
536   return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
537 }
538 CXML_Element* CXML_Element::Parse(IFX_FileRead* pFile,
539                                   FX_BOOL bSaveSpaceChars,
540                                   FX_FILESIZE* pParsedSize) {
541   CXML_Parser parser;
542   if (!parser.Init(pFile)) {
543     return NULL;
544   }
545   return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
546 }
547 CXML_Element* CXML_Element::Parse(IFX_BufferRead* pBuffer,
548                                   FX_BOOL bSaveSpaceChars,
549                                   FX_FILESIZE* pParsedSize) {
550   CXML_Parser parser;
551   if (!parser.Init(pBuffer)) {
552     return NULL;
553   }
554   return XML_ContinueParse(parser, bSaveSpaceChars, pParsedSize);
555 }
556 CXML_Element::CXML_Element() : m_QSpaceName(), m_TagName(), m_AttrMap() {}
557 CXML_Element::CXML_Element(const CFX_ByteStringC& qSpace,
558                            const CFX_ByteStringC& tagName)
559     : m_QSpaceName(), m_TagName(), m_AttrMap() {
560   m_QSpaceName = qSpace;
561   m_TagName = tagName;
562 }
563 CXML_Element::CXML_Element(const CFX_ByteStringC& qTagName)
564     : m_pParent(NULL), m_QSpaceName(), m_TagName(), m_AttrMap() {
565   SetTag(qTagName);
566 }
567 CXML_Element::~CXML_Element() {
568   Empty();
569 }
570 void CXML_Element::Empty() {
571   RemoveChildren();
572 }
573 void CXML_Element::RemoveChildren() {
574   for (int i = 0; i < m_Children.GetSize(); i += 2) {
575     ChildType type = (ChildType)(uintptr_t)m_Children.GetAt(i);
576     if (type == Content) {
577       CXML_Content* content = (CXML_Content*)m_Children.GetAt(i + 1);
578       delete content;
579     } else if (type == Element) {
580       CXML_Element* child = (CXML_Element*)m_Children.GetAt(i + 1);
581       child->RemoveChildren();
582       delete child;
583     }
584   }
585   m_Children.RemoveAll();
586 }
587 CFX_ByteString CXML_Element::GetTagName(FX_BOOL bQualified) const {
588   if (!bQualified || m_QSpaceName.IsEmpty()) {
589     return m_TagName;
590   }
591   CFX_ByteString bsTag = m_QSpaceName;
592   bsTag += ":";
593   bsTag += m_TagName;
594   return bsTag;
595 }
596 CFX_ByteString CXML_Element::GetNamespace(FX_BOOL bQualified) const {
597   if (bQualified) {
598     return m_QSpaceName;
599   }
600   return GetNamespaceURI(m_QSpaceName);
601 }
602 CFX_ByteString CXML_Element::GetNamespaceURI(
603     const CFX_ByteStringC& qName) const {
604   const CFX_WideString* pwsSpace;
605   const CXML_Element* pElement = this;
606   do {
607     if (qName.IsEmpty()) {
608       pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC(""), FX_BSTRC("xmlns"));
609     } else {
610       pwsSpace = pElement->m_AttrMap.Lookup(FX_BSTRC("xmlns"), qName);
611     }
612     if (pwsSpace) {
613       break;
614     }
615     pElement = pElement->GetParent();
616   } while (pElement);
617   return pwsSpace ? FX_UTF8Encode(*pwsSpace) : CFX_ByteString();
618 }
619 void CXML_Element::GetAttrByIndex(int index,
620                                   CFX_ByteString& space,
621                                   CFX_ByteString& name,
622                                   CFX_WideString& value) const {
623   if (index < 0 || index >= m_AttrMap.GetSize()) {
624     return;
625   }
626   CXML_AttrItem& item = m_AttrMap.GetAt(index);
627   space = item.m_QSpaceName;
628   name = item.m_AttrName;
629   value = item.m_Value;
630 }
631 FX_BOOL CXML_Element::HasAttr(const CFX_ByteStringC& name) const {
632   CFX_ByteStringC bsSpace, bsName;
633   FX_XML_SplitQualifiedName(name, bsSpace, bsName);
634   return m_AttrMap.Lookup(bsSpace, bsName) != NULL;
635 }
636 FX_BOOL CXML_Element::GetAttrValue(const CFX_ByteStringC& name,
637                                    CFX_WideString& attribute) const {
638   CFX_ByteStringC bsSpace, bsName;
639   FX_XML_SplitQualifiedName(name, bsSpace, bsName);
640   return GetAttrValue(bsSpace, bsName, attribute);
641 }
642 FX_BOOL CXML_Element::GetAttrValue(const CFX_ByteStringC& space,
643                                    const CFX_ByteStringC& name,
644                                    CFX_WideString& attribute) const {
645   const CFX_WideString* pValue = m_AttrMap.Lookup(space, name);
646   if (pValue) {
647     attribute = *pValue;
648     return TRUE;
649   }
650   return FALSE;
651 }
652 FX_BOOL CXML_Element::GetAttrInteger(const CFX_ByteStringC& name,
653                                      int& attribute) const {
654   CFX_ByteStringC bsSpace, bsName;
655   FX_XML_SplitQualifiedName(name, bsSpace, bsName);
656   const CFX_WideString* pwsValue = m_AttrMap.Lookup(bsSpace, bsName);
657   if (pwsValue) {
658     attribute = pwsValue->GetInteger();
659     return TRUE;
660   }
661   return FALSE;
662 }
663 FX_BOOL CXML_Element::GetAttrInteger(const CFX_ByteStringC& space,
664                                      const CFX_ByteStringC& name,
665                                      int& attribute) const {
666   const CFX_WideString* pwsValue = m_AttrMap.Lookup(space, name);
667   if (pwsValue) {
668     attribute = pwsValue->GetInteger();
669     return TRUE;
670   }
671   return FALSE;
672 }
673 FX_BOOL CXML_Element::GetAttrFloat(const CFX_ByteStringC& name,
674                                    FX_FLOAT& attribute) const {
675   CFX_ByteStringC bsSpace, bsName;
676   FX_XML_SplitQualifiedName(name, bsSpace, bsName);
677   return GetAttrFloat(bsSpace, bsName, attribute);
678 }
679 FX_BOOL CXML_Element::GetAttrFloat(const CFX_ByteStringC& space,
680                                    const CFX_ByteStringC& name,
681                                    FX_FLOAT& attribute) const {
682   const CFX_WideString* pValue = m_AttrMap.Lookup(space, name);
683   if (pValue) {
684     attribute = pValue->GetFloat();
685     return TRUE;
686   }
687   return FALSE;
688 }
689 FX_DWORD CXML_Element::CountChildren() const {
690   return m_Children.GetSize() / 2;
691 }
692 CXML_Element::ChildType CXML_Element::GetChildType(FX_DWORD index) const {
693   index <<= 1;
694   if (index >= (FX_DWORD)m_Children.GetSize()) {
695     return Invalid;
696   }
697   return (ChildType)(uintptr_t)m_Children.GetAt(index);
698 }
699 CFX_WideString CXML_Element::GetContent(FX_DWORD index) const {
700   index <<= 1;
701   if (index >= (FX_DWORD)m_Children.GetSize() ||
702       (ChildType)(uintptr_t)m_Children.GetAt(index) != Content) {
703     return CFX_WideString();
704   }
705   CXML_Content* pContent = (CXML_Content*)m_Children.GetAt(index + 1);
706   if (pContent) {
707     return pContent->m_Content;
708   }
709   return CFX_WideString();
710 }
711 CXML_Element* CXML_Element::GetElement(FX_DWORD index) const {
712   index <<= 1;
713   if (index >= (FX_DWORD)m_Children.GetSize() ||
714       (ChildType)(uintptr_t)m_Children.GetAt(index) != Element) {
715     return NULL;
716   }
717   return (CXML_Element*)m_Children.GetAt(index + 1);
718 }
719 FX_DWORD CXML_Element::CountElements(const CFX_ByteStringC& space,
720                                      const CFX_ByteStringC& tag) const {
721   int count = 0;
722   for (int i = 0; i < m_Children.GetSize(); i += 2) {
723     ChildType type = (ChildType)(uintptr_t)m_Children.GetAt(i);
724     if (type != Element) {
725       continue;
726     }
727     CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
728     if ((space.IsEmpty() || pKid->m_QSpaceName == space) &&
729         pKid->m_TagName == tag) {
730       count++;
731     }
732   }
733   return count;
734 }
735 CXML_Element* CXML_Element::GetElement(const CFX_ByteStringC& space,
736                                        const CFX_ByteStringC& tag,
737                                        int index) const {
738   if (index < 0) {
739     return NULL;
740   }
741   for (int i = 0; i < m_Children.GetSize(); i += 2) {
742     ChildType type = (ChildType)(uintptr_t)m_Children.GetAt(i);
743     if (type != Element) {
744       continue;
745     }
746     CXML_Element* pKid = (CXML_Element*)m_Children.GetAt(i + 1);
747     if ((!space.IsEmpty() && pKid->m_QSpaceName != space) ||
748         pKid->m_TagName != tag) {
749       continue;
750     }
751     if (index-- == 0) {
752       return pKid;
753     }
754   }
755   return NULL;
756 }
757 FX_DWORD CXML_Element::FindElement(CXML_Element* pChild) const {
758   for (int i = 0; i < m_Children.GetSize(); i += 2) {
759     if ((ChildType)(uintptr_t)m_Children.GetAt(i) == Element &&
760         (CXML_Element*)m_Children.GetAt(i + 1) == pChild) {
761       return (FX_DWORD)(i >> 1);
762     }
763   }
764   return (FX_DWORD)-1;
765 }
766 const CFX_WideString* CXML_AttrMap::Lookup(const CFX_ByteStringC& space,
767                                            const CFX_ByteStringC& name) const {
768   if (m_pMap == NULL) {
769     return NULL;
770   }
771   for (int i = 0; i < m_pMap->GetSize(); i++) {
772     CXML_AttrItem& item = GetAt(i);
773     if ((space.IsEmpty() || item.m_QSpaceName == space) &&
774         item.m_AttrName == name) {
775       return &item.m_Value;
776     }
777   }
778   return NULL;
779 }
780 void CXML_AttrMap::SetAt(const CFX_ByteStringC& space,
781                          const CFX_ByteStringC& name,
782                          const CFX_WideStringC& value) {
783   for (int i = 0; i < GetSize(); i++) {
784     CXML_AttrItem& item = GetAt(i);
785     if ((space.IsEmpty() || item.m_QSpaceName == space) &&
786         item.m_AttrName == name) {
787       item.m_Value = value;
788       return;
789     }
790   }
791   if (!m_pMap) {
792     m_pMap = new CFX_ObjectArray<CXML_AttrItem>;
793   }
794   CXML_AttrItem* pItem = (CXML_AttrItem*)m_pMap->AddSpace();
795   if (!pItem) {
796     return;
797   }
798   pItem->m_QSpaceName = space;
799   pItem->m_AttrName = name;
800   pItem->m_Value = value;
801 }
802 void CXML_AttrMap::RemoveAt(const CFX_ByteStringC& space,
803                             const CFX_ByteStringC& name) {
804   if (m_pMap == NULL) {
805     return;
806   }
807   for (int i = 0; i < m_pMap->GetSize(); i++) {
808     CXML_AttrItem& item = GetAt(i);
809     if ((space.IsEmpty() || item.m_QSpaceName == space) &&
810         item.m_AttrName == name) {
811       m_pMap->RemoveAt(i);
812       return;
813     }
814   }
815 }
816 int CXML_AttrMap::GetSize() const {
817   return m_pMap == NULL ? 0 : m_pMap->GetSize();
818 }
819 CXML_AttrItem& CXML_AttrMap::GetAt(int index) const {
820   ASSERT(m_pMap != NULL);
821   return (*m_pMap)[index];
822 }
823 void CXML_AttrMap::RemoveAll() {
824   if (!m_pMap) {
825     return;
826   }
827   m_pMap->RemoveAll();
828   delete m_pMap;
829   m_pMap = NULL;
830 }