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