Home | History | Annotate | Download | only in xml
      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 "xfa/fde/xml/cfx_saxreader.h"
      8 
      9 #include <algorithm>
     10 #include <utility>
     11 
     12 #include "third_party/base/ptr_util.h"
     13 #include "xfa/fxfa/xfa_checksum.h"
     14 
     15 enum class CFX_SaxMode {
     16   Text = 0,
     17   NodeStart,
     18   DeclOrComment,
     19   DeclNode,
     20   Comment,
     21   CommentContent,
     22   TagName,
     23   TagAttributeName,
     24   TagAttributeEqual,
     25   TagAttributeValue,
     26   TagMaybeClose,
     27   TagClose,
     28   TagEnd,
     29   TargetData,
     30   MAX
     31 };
     32 
     33 class CFX_SAXCommentContext {
     34  public:
     35   CFX_SAXCommentContext() : m_iHeaderCount(0), m_iTailCount(0) {}
     36   int32_t m_iHeaderCount;
     37   int32_t m_iTailCount;
     38 };
     39 
     40 namespace {
     41 
     42 const uint32_t kSaxFileBufSize = 32768;
     43 
     44 typedef void (CFX_SAXReader::*FX_SAXReader_LPFParse)();
     45 static const FX_SAXReader_LPFParse
     46     g_FX_SAXReader_LPFParse[static_cast<int>(CFX_SaxMode::MAX)] = {
     47         &CFX_SAXReader::ParseText,
     48         &CFX_SAXReader::ParseNodeStart,
     49         &CFX_SAXReader::ParseDeclOrComment,
     50         &CFX_SAXReader::ParseDeclNode,
     51         &CFX_SAXReader::ParseComment,
     52         &CFX_SAXReader::ParseCommentContent,
     53         &CFX_SAXReader::ParseTagName,
     54         &CFX_SAXReader::ParseTagAttributeName,
     55         &CFX_SAXReader::ParseTagAttributeEqual,
     56         &CFX_SAXReader::ParseTagAttributeValue,
     57         &CFX_SAXReader::ParseMaybeClose,
     58         &CFX_SAXReader::ParseTagClose,
     59         &CFX_SAXReader::ParseTagEnd,
     60         &CFX_SAXReader::ParseTargetData,
     61 };
     62 
     63 }  // namespace
     64 
     65 CFX_SAXFile::CFX_SAXFile()
     66     : m_dwStart(0),
     67       m_dwEnd(0),
     68       m_dwCur(0),
     69       m_pBuf(nullptr),
     70       m_dwBufSize(0),
     71       m_dwBufIndex(0) {}
     72 
     73 CFX_SAXFile::~CFX_SAXFile() {}
     74 
     75 bool CFX_SAXFile::StartFile(const CFX_RetainPtr<IFX_SeekableReadStream>& pFile,
     76                             uint32_t dwStart,
     77                             uint32_t dwLen) {
     78   ASSERT(!m_pFile && pFile);
     79   uint32_t dwSize = pFile->GetSize();
     80   if (dwStart >= dwSize)
     81     return false;
     82 
     83   if (dwLen == static_cast<uint32_t>(-1) || dwStart + dwLen > dwSize)
     84     dwLen = dwSize - dwStart;
     85 
     86   if (dwLen == 0)
     87     return false;
     88 
     89   m_dwBufSize = std::min(dwLen, kSaxFileBufSize);
     90   m_pBuf = FX_Alloc(uint8_t, m_dwBufSize);
     91   if (!pFile->ReadBlock(m_pBuf, dwStart, m_dwBufSize))
     92     return false;
     93 
     94   m_dwStart = dwStart;
     95   m_dwEnd = dwStart + dwLen;
     96   m_dwCur = dwStart;
     97   m_pFile = pFile;
     98   m_dwBufIndex = 0;
     99   return true;
    100 }
    101 
    102 bool CFX_SAXFile::ReadNextBlock() {
    103   ASSERT(m_pFile);
    104   uint32_t dwSize = m_dwEnd - m_dwCur;
    105   if (dwSize == 0) {
    106     return false;
    107   }
    108   m_dwBufSize = std::min(dwSize, kSaxFileBufSize);
    109   if (!m_pFile->ReadBlock(m_pBuf, m_dwCur, m_dwBufSize)) {
    110     return false;
    111   }
    112   m_dwBufIndex = 0;
    113   return true;
    114 }
    115 
    116 void CFX_SAXFile::Reset() {
    117   if (m_pBuf) {
    118     FX_Free(m_pBuf);
    119     m_pBuf = nullptr;
    120   }
    121   m_pFile = nullptr;
    122 }
    123 
    124 CFX_SAXReader::CFX_SAXReader()
    125     : m_File(),
    126       m_pHandler(nullptr),
    127       m_iState(-1),
    128       m_dwItemID(0),
    129       m_iDataSize(256),
    130       m_iNameSize(256),
    131       m_dwParseMode(0),
    132       m_pCommentContext(nullptr) {
    133   m_pszData = FX_Alloc(uint8_t, m_iDataSize);
    134   m_pszName = FX_Alloc(uint8_t, m_iNameSize);
    135 }
    136 CFX_SAXReader::~CFX_SAXReader() {
    137   Reset();
    138   if (m_pszData) {
    139     FX_Free(m_pszData);
    140     m_pszData = nullptr;
    141   }
    142   if (m_pszName) {
    143     FX_Free(m_pszName);
    144     m_pszName = nullptr;
    145   }
    146 }
    147 
    148 void CFX_SAXReader::Reset() {
    149   m_File.Reset();
    150   while (!m_Stack.empty())
    151     m_Stack.pop();
    152 
    153   m_dwItemID = 0;
    154   m_SkipStack.RemoveAll();
    155   m_SkipChar = 0;
    156   m_iDataLength = 0;
    157   m_iEntityStart = -1;
    158   m_iNameLength = 0;
    159   m_iDataPos = 0;
    160   delete m_pCommentContext;
    161   m_pCommentContext = nullptr;
    162 }
    163 
    164 void CFX_SAXReader::Push() {
    165   std::unique_ptr<CFX_SAXItem> pNew =
    166       pdfium::MakeUnique<CFX_SAXItem>(++m_dwItemID);
    167   if (!m_Stack.empty())
    168     pNew->m_bSkip = m_Stack.top()->m_bSkip;
    169   m_Stack.push(std::move(pNew));
    170 }
    171 
    172 void CFX_SAXReader::Pop() {
    173   if (!m_Stack.empty())
    174     m_Stack.pop();
    175 }
    176 
    177 CFX_SAXItem* CFX_SAXReader::GetCurrentItem() const {
    178   return m_Stack.empty() ? nullptr : m_Stack.top().get();
    179 }
    180 
    181 void CFX_SAXReader::AppendData(uint8_t ch) {
    182   ReallocDataBuffer();
    183   m_pszData[m_iDataPos++] = ch;
    184 }
    185 
    186 void CFX_SAXReader::AppendName(uint8_t ch) {
    187   ReallocNameBuffer();
    188   m_pszName[m_iDataPos++] = ch;
    189 }
    190 
    191 void CFX_SAXReader::ReallocDataBuffer() {
    192   if (m_iDataPos < m_iDataSize) {
    193     return;
    194   }
    195   if (m_iDataSize <= 1024 * 1024) {
    196     m_iDataSize *= 2;
    197   } else {
    198     m_iDataSize += 1024 * 1024;
    199   }
    200   m_pszData = (uint8_t*)FX_Realloc(uint8_t, m_pszData, m_iDataSize);
    201 }
    202 
    203 void CFX_SAXReader::ReallocNameBuffer() {
    204   if (m_iDataPos < m_iNameSize) {
    205     return;
    206   }
    207   if (m_iNameSize <= 1024 * 1024) {
    208     m_iNameSize *= 2;
    209   } else {
    210     m_iNameSize += 1024 * 1024;
    211   }
    212   m_pszName = (uint8_t*)FX_Realloc(uint8_t, m_pszName, m_iNameSize);
    213 }
    214 
    215 bool CFX_SAXReader::SkipSpace(uint8_t ch) {
    216   return (m_dwParseMode & CFX_SaxParseMode_NotSkipSpace) == 0 && ch < 0x21;
    217 }
    218 
    219 int32_t CFX_SAXReader::StartParse(
    220     const CFX_RetainPtr<IFX_SeekableReadStream>& pFile,
    221     uint32_t dwStart,
    222     uint32_t dwLen,
    223     uint32_t dwParseMode) {
    224   m_iState = -1;
    225   Reset();
    226   if (!m_File.StartFile(pFile, dwStart, dwLen))
    227     return -1;
    228 
    229   m_iState = 0;
    230   m_eMode = CFX_SaxMode::Text;
    231   m_ePrevMode = CFX_SaxMode::Text;
    232   m_bCharData = false;
    233   m_dwDataOffset = 0;
    234   m_dwParseMode = dwParseMode;
    235   m_Stack.emplace(new CFX_SAXItem(++m_dwItemID));
    236   return 0;
    237 }
    238 
    239 int32_t CFX_SAXReader::ContinueParse(IFX_Pause* pPause) {
    240   if (m_iState < 0 || m_iState > 99) {
    241     return m_iState;
    242   }
    243   while (m_File.m_dwCur < m_File.m_dwEnd) {
    244     uint32_t& index = m_File.m_dwBufIndex;
    245     uint32_t size = m_File.m_dwBufSize;
    246     const uint8_t* pBuf = m_File.m_pBuf;
    247     while (index < size) {
    248       m_CurByte = pBuf[index];
    249       (this->*g_FX_SAXReader_LPFParse[static_cast<int>(m_eMode)])();
    250       index++;
    251     }
    252     m_File.m_dwCur += index;
    253     m_iState = (m_File.m_dwCur - m_File.m_dwStart) * 100 /
    254                (m_File.m_dwEnd - m_File.m_dwStart);
    255     if (m_File.m_dwCur >= m_File.m_dwEnd) {
    256       break;
    257     }
    258     if (!m_File.ReadNextBlock()) {
    259       m_iState = -2;
    260       break;
    261     }
    262     m_dwDataOffset = 0;
    263     if (pPause && pPause->NeedToPauseNow()) {
    264       break;
    265     }
    266   }
    267   return m_iState;
    268 }
    269 void CFX_SAXReader::ParseChar(uint8_t ch) {
    270   ReallocDataBuffer();
    271   m_pszData[m_iDataPos] = ch;
    272   if (m_iEntityStart > -1 && ch == ';') {
    273     int32_t iSaveEntityStart = m_iEntityStart;
    274     CFX_ByteString csEntity(m_pszData + m_iEntityStart + 1,
    275                             m_iDataPos - m_iEntityStart - 1);
    276     int32_t iLen = csEntity.GetLength();
    277     if (iLen > 0) {
    278       if (csEntity[0] == '#') {
    279         if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_sharp) == 0) {
    280           ch = 0;
    281           uint8_t w;
    282           if (iLen > 1 && csEntity[1] == 'x') {
    283             for (int32_t i = 2; i < iLen; i++) {
    284               w = csEntity[i];
    285               if (w >= '0' && w <= '9') {
    286                 ch = (ch << 4) + w - '0';
    287               } else if (w >= 'A' && w <= 'F') {
    288                 ch = (ch << 4) + w - 55;
    289               } else if (w >= 'a' && w <= 'f') {
    290                 ch = (ch << 4) + w - 87;
    291               } else {
    292                 break;
    293               }
    294             }
    295           } else {
    296             for (int32_t i = 1; i < iLen; i++) {
    297               w = csEntity[i];
    298               if (w < '0' || w > '9') {
    299                 break;
    300               }
    301               ch = ch * 10 + w - '0';
    302             }
    303           }
    304           if (ch != 0) {
    305             m_pszData[m_iEntityStart++] = ch;
    306           }
    307         }
    308       } else {
    309         if (csEntity.Compare("amp") == 0) {
    310           if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_amp) == 0) {
    311             m_pszData[m_iEntityStart++] = '&';
    312           }
    313         } else if (csEntity.Compare("lt") == 0) {
    314           if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_lt) == 0) {
    315             m_pszData[m_iEntityStart++] = '<';
    316           }
    317         } else if (csEntity.Compare("gt") == 0) {
    318           if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_gt) == 0) {
    319             m_pszData[m_iEntityStart++] = '>';
    320           }
    321         } else if (csEntity.Compare("apos") == 0) {
    322           if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_apos) == 0) {
    323             m_pszData[m_iEntityStart++] = '\'';
    324           }
    325         } else if (csEntity.Compare("quot") == 0) {
    326           if ((m_dwParseMode & CFX_SaxParseMode_NotConvert_quot) == 0) {
    327             m_pszData[m_iEntityStart++] = '\"';
    328           }
    329         }
    330       }
    331     }
    332     if (iSaveEntityStart != m_iEntityStart) {
    333       m_iDataPos = m_iEntityStart;
    334       m_iEntityStart = -1;
    335     } else {
    336       m_iDataPos++;
    337       m_iEntityStart = -1;
    338     }
    339   } else {
    340     if (m_iEntityStart < 0 && ch == '&') {
    341       m_iEntityStart = m_iDataPos;
    342     }
    343     m_iDataPos++;
    344   }
    345 }
    346 
    347 void CFX_SAXReader::ParseText() {
    348   if (m_CurByte == '<') {
    349     if (m_iDataPos > 0) {
    350       m_iDataLength = m_iDataPos;
    351       m_iDataPos = 0;
    352       if (m_pHandler) {
    353         NotifyData();
    354       }
    355     }
    356     Push();
    357     m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
    358     m_eMode = CFX_SaxMode::NodeStart;
    359     return;
    360   }
    361   if (m_iDataPos < 1 && SkipSpace(m_CurByte)) {
    362     return;
    363   }
    364   ParseChar(m_CurByte);
    365 }
    366 
    367 void CFX_SAXReader::ParseNodeStart() {
    368   if (m_CurByte == '?') {
    369     GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Instruction;
    370     m_eMode = CFX_SaxMode::TagName;
    371     return;
    372   }
    373   if (m_CurByte == '!') {
    374     m_eMode = CFX_SaxMode::DeclOrComment;
    375     return;
    376   }
    377   if (m_CurByte == '/') {
    378     m_eMode = CFX_SaxMode::TagEnd;
    379     return;
    380   }
    381   if (m_CurByte == '>') {
    382     Pop();
    383     m_eMode = CFX_SaxMode::Text;
    384     return;
    385   }
    386   if (m_CurByte > 0x20) {
    387     m_dwDataOffset = m_File.m_dwBufIndex;
    388     GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Tag;
    389     m_eMode = CFX_SaxMode::TagName;
    390     AppendData(m_CurByte);
    391   }
    392 }
    393 
    394 void CFX_SAXReader::ParseDeclOrComment() {
    395   if (m_CurByte == '-') {
    396     m_eMode = CFX_SaxMode::Comment;
    397     GetCurrentItem()->m_eNode = CFX_SAXItem::Type::Comment;
    398     if (!m_pCommentContext)
    399       m_pCommentContext = new CFX_SAXCommentContext;
    400 
    401     m_pCommentContext->m_iHeaderCount = 1;
    402     m_pCommentContext->m_iTailCount = 0;
    403   } else {
    404     m_eMode = CFX_SaxMode::DeclNode;
    405     m_dwDataOffset = m_File.m_dwBufIndex;
    406     m_SkipChar = '>';
    407     m_SkipStack.Add('>');
    408     SkipNode();
    409   }
    410 }
    411 void CFX_SAXReader::ParseComment() {
    412   m_pCommentContext->m_iHeaderCount = 2;
    413   m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
    414   m_eMode = CFX_SaxMode::CommentContent;
    415 }
    416 void CFX_SAXReader::ParseCommentContent() {
    417   if (m_CurByte == '-') {
    418     m_pCommentContext->m_iTailCount++;
    419   } else if (m_CurByte == '>' && m_pCommentContext->m_iTailCount == 2) {
    420     m_iDataLength = m_iDataPos;
    421     m_iDataPos = 0;
    422     if (m_pHandler) {
    423       NotifyTargetData();
    424     }
    425     Pop();
    426     m_eMode = CFX_SaxMode::Text;
    427   } else {
    428     while (m_pCommentContext->m_iTailCount > 0) {
    429       AppendData('-');
    430       m_pCommentContext->m_iTailCount--;
    431     }
    432     AppendData(m_CurByte);
    433   }
    434 }
    435 void CFX_SAXReader::ParseDeclNode() {
    436   SkipNode();
    437 }
    438 void CFX_SAXReader::ParseTagName() {
    439   if (m_CurByte < 0x21 || m_CurByte == '/' || m_CurByte == '>' ||
    440       m_CurByte == '?') {
    441     m_iDataLength = m_iDataPos;
    442     m_iDataPos = 0;
    443     if (m_pHandler) {
    444       NotifyEnter();
    445     }
    446     if (m_CurByte < 0x21) {
    447       m_eMode = CFX_SaxMode::TagAttributeName;
    448     } else if (m_CurByte == '/' || m_CurByte == '?') {
    449       m_ePrevMode = m_eMode;
    450       m_eMode = CFX_SaxMode::TagMaybeClose;
    451     } else {
    452       if (m_pHandler) {
    453         NotifyBreak();
    454       }
    455       m_eMode = CFX_SaxMode::Text;
    456     }
    457   } else {
    458     AppendData(m_CurByte);
    459   }
    460 }
    461 void CFX_SAXReader::ParseTagAttributeName() {
    462   if (m_CurByte < 0x21 || m_CurByte == '=') {
    463     if (m_iDataPos < 1 && m_CurByte < 0x21) {
    464       return;
    465     }
    466     m_iNameLength = m_iDataPos;
    467     m_iDataPos = 0;
    468     m_SkipChar = 0;
    469     m_eMode = m_CurByte == '=' ? CFX_SaxMode::TagAttributeValue
    470                                : CFX_SaxMode::TagAttributeEqual;
    471     return;
    472   }
    473   if (m_CurByte == '/' || m_CurByte == '>' || m_CurByte == '?') {
    474     if (m_CurByte == '/' || m_CurByte == '?') {
    475       m_ePrevMode = m_eMode;
    476       m_eMode = CFX_SaxMode::TagMaybeClose;
    477     } else {
    478       if (m_pHandler) {
    479         NotifyBreak();
    480       }
    481       m_eMode = CFX_SaxMode::Text;
    482     }
    483     return;
    484   }
    485   if (m_iDataPos < 1) {
    486     m_dwDataOffset = m_File.m_dwBufIndex;
    487   }
    488   AppendName(m_CurByte);
    489 }
    490 
    491 void CFX_SAXReader::ParseTagAttributeEqual() {
    492   if (m_CurByte == '=') {
    493     m_SkipChar = 0;
    494     m_eMode = CFX_SaxMode::TagAttributeValue;
    495     return;
    496   }
    497   if (GetCurrentItem()->m_eNode == CFX_SAXItem::Type::Instruction) {
    498     m_iDataPos = m_iNameLength;
    499     AppendName(0x20);
    500     m_eMode = CFX_SaxMode::TargetData;
    501     ParseTargetData();
    502   }
    503 }
    504 
    505 void CFX_SAXReader::ParseTagAttributeValue() {
    506   if (m_SkipChar) {
    507     if (m_SkipChar == m_CurByte) {
    508       {
    509         m_iDataLength = m_iDataPos;
    510         m_iDataPos = 0;
    511         if (m_pHandler) {
    512           NotifyAttribute();
    513         }
    514       }
    515       m_SkipChar = 0;
    516       m_eMode = CFX_SaxMode::TagAttributeName;
    517       return;
    518     }
    519     ParseChar(m_CurByte);
    520     return;
    521   }
    522   if (m_CurByte < 0x21) {
    523     return;
    524   }
    525   if (m_iDataPos < 1) {
    526     if (m_CurByte == '\'' || m_CurByte == '\"') {
    527       m_SkipChar = m_CurByte;
    528     }
    529   }
    530 }
    531 
    532 void CFX_SAXReader::ParseMaybeClose() {
    533   if (m_CurByte == '>') {
    534     if (GetCurrentItem()->m_eNode == CFX_SAXItem::Type::Instruction) {
    535       m_iNameLength = m_iDataPos;
    536       m_iDataPos = 0;
    537       if (m_pHandler) {
    538         NotifyTargetData();
    539       }
    540     }
    541     ParseTagClose();
    542     m_eMode = CFX_SaxMode::Text;
    543   } else if (m_ePrevMode == CFX_SaxMode::TagName) {
    544     AppendData('/');
    545     m_eMode = CFX_SaxMode::TagName;
    546     m_ePrevMode = CFX_SaxMode::Text;
    547     ParseTagName();
    548   } else if (m_ePrevMode == CFX_SaxMode::TagAttributeName) {
    549     AppendName('/');
    550     m_eMode = CFX_SaxMode::TagAttributeName;
    551     m_ePrevMode = CFX_SaxMode::Text;
    552     ParseTagAttributeName();
    553   } else if (m_ePrevMode == CFX_SaxMode::TargetData) {
    554     AppendName('?');
    555     m_eMode = CFX_SaxMode::TargetData;
    556     m_ePrevMode = CFX_SaxMode::Text;
    557     ParseTargetData();
    558   }
    559 }
    560 void CFX_SAXReader::ParseTagClose() {
    561   m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
    562   if (m_pHandler) {
    563     NotifyClose();
    564   }
    565   Pop();
    566 }
    567 void CFX_SAXReader::ParseTagEnd() {
    568   if (m_CurByte < 0x21) {
    569     return;
    570   }
    571   if (m_CurByte == '>') {
    572     Pop();
    573     m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
    574     m_iDataLength = m_iDataPos;
    575     m_iDataPos = 0;
    576     if (m_pHandler) {
    577       NotifyEnd();
    578     }
    579     Pop();
    580     m_eMode = CFX_SaxMode::Text;
    581   } else {
    582     ParseChar(m_CurByte);
    583   }
    584 }
    585 void CFX_SAXReader::ParseTargetData() {
    586   if (m_CurByte == '?') {
    587     m_ePrevMode = m_eMode;
    588     m_eMode = CFX_SaxMode::TagMaybeClose;
    589   } else {
    590     AppendName(m_CurByte);
    591   }
    592 }
    593 void CFX_SAXReader::SkipNode() {
    594   int32_t iLen = m_SkipStack.GetSize();
    595   if (m_SkipChar == '\'' || m_SkipChar == '\"') {
    596     if (m_CurByte != m_SkipChar) {
    597       return;
    598     }
    599     iLen--;
    600     ASSERT(iLen > -1);
    601     m_SkipStack.RemoveAt(iLen, 1);
    602     m_SkipChar = iLen ? m_SkipStack[iLen - 1] : 0;
    603     return;
    604   }
    605   switch (m_CurByte) {
    606     case '<':
    607       m_SkipChar = '>';
    608       m_SkipStack.Add('>');
    609       break;
    610     case '[':
    611       m_SkipChar = ']';
    612       m_SkipStack.Add(']');
    613       break;
    614     case '(':
    615       m_SkipChar = ')';
    616       m_SkipStack.Add(')');
    617       break;
    618     case '\'':
    619       m_SkipChar = '\'';
    620       m_SkipStack.Add('\'');
    621       break;
    622     case '\"':
    623       m_SkipChar = '\"';
    624       m_SkipStack.Add('\"');
    625       break;
    626     default:
    627       if (m_CurByte == m_SkipChar) {
    628         iLen--;
    629         m_SkipStack.RemoveAt(iLen, 1);
    630         m_SkipChar = iLen ? m_SkipStack[iLen - 1] : 0;
    631         if (iLen == 0 && m_CurByte == '>') {
    632           m_iDataLength = m_iDataPos;
    633           m_iDataPos = 0;
    634           if (m_iDataLength >= 9 &&
    635               FXSYS_memcmp(m_pszData, "[CDATA[", 7 * sizeof(uint8_t)) == 0 &&
    636               FXSYS_memcmp(m_pszData + m_iDataLength - 2, "]]",
    637                            2 * sizeof(uint8_t)) == 0) {
    638             Pop();
    639             m_iDataLength -= 9;
    640             m_dwDataOffset += 7;
    641             FXSYS_memmove(m_pszData, m_pszData + 7,
    642                           m_iDataLength * sizeof(uint8_t));
    643             m_bCharData = true;
    644             if (m_pHandler) {
    645               NotifyData();
    646             }
    647             m_bCharData = false;
    648           } else {
    649             Pop();
    650           }
    651           m_eMode = CFX_SaxMode::Text;
    652         }
    653       }
    654       break;
    655   }
    656   if (iLen > 0) {
    657     ParseChar(m_CurByte);
    658   }
    659 }
    660 
    661 void CFX_SAXReader::NotifyData() {
    662   CFX_SAXItem* pItem = GetCurrentItem();
    663   if (!pItem)
    664     return;
    665 
    666   if (pItem->m_eNode == CFX_SAXItem::Type::Tag)
    667     m_pHandler->OnTagData(
    668         pItem->m_pNode,
    669         m_bCharData ? CFX_SAXItem::Type::CharData : CFX_SAXItem::Type::Text,
    670         CFX_ByteStringC(m_pszData, m_iDataLength),
    671         m_File.m_dwCur + m_dwDataOffset);
    672 }
    673 
    674 void CFX_SAXReader::NotifyEnter() {
    675   CFX_SAXItem* pItem = GetCurrentItem();
    676   if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
    677       pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
    678     pItem->m_pNode = m_pHandler->OnTagEnter(
    679         CFX_ByteStringC(m_pszData, m_iDataLength), pItem->m_eNode, m_dwNodePos);
    680   }
    681 }
    682 
    683 void CFX_SAXReader::NotifyAttribute() {
    684   CFX_SAXItem* pItem = GetCurrentItem();
    685   if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
    686       pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
    687     m_pHandler->OnTagAttribute(pItem->m_pNode,
    688                                CFX_ByteStringC(m_pszName, m_iNameLength),
    689                                CFX_ByteStringC(m_pszData, m_iDataLength));
    690   }
    691 }
    692 
    693 void CFX_SAXReader::NotifyBreak() {
    694   CFX_SAXItem* pItem = GetCurrentItem();
    695   if (pItem->m_eNode == CFX_SAXItem::Type::Tag)
    696     m_pHandler->OnTagBreak(pItem->m_pNode);
    697 }
    698 
    699 void CFX_SAXReader::NotifyClose() {
    700   CFX_SAXItem* pItem = GetCurrentItem();
    701   if (pItem->m_eNode == CFX_SAXItem::Type::Tag ||
    702       pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
    703     m_pHandler->OnTagClose(pItem->m_pNode, m_dwNodePos);
    704   }
    705 }
    706 
    707 void CFX_SAXReader::NotifyEnd() {
    708   CFX_SAXItem* pItem = GetCurrentItem();
    709   if (!pItem || pItem->m_eNode != CFX_SAXItem::Type::Tag)
    710     return;
    711 
    712   m_pHandler->OnTagEnd(pItem->m_pNode,
    713                        CFX_ByteStringC(m_pszData, m_iDataLength), m_dwNodePos);
    714 }
    715 
    716 void CFX_SAXReader::NotifyTargetData() {
    717   CFX_SAXItem* pItem = GetCurrentItem();
    718   if (pItem->m_eNode == CFX_SAXItem::Type::Instruction) {
    719     m_pHandler->OnTargetData(pItem->m_pNode, pItem->m_eNode,
    720                              CFX_ByteStringC(m_pszName, m_iNameLength),
    721                              m_dwNodePos);
    722   } else if (pItem->m_eNode == CFX_SAXItem::Type::Comment) {
    723     m_pHandler->OnTargetData(pItem->m_pNode, pItem->m_eNode,
    724                              CFX_ByteStringC(m_pszData, m_iDataLength),
    725                              m_dwNodePos);
    726   }
    727 }
    728 
    729 void CFX_SAXReader::SkipCurrentNode() {
    730   CFX_SAXItem* pItem = GetCurrentItem();
    731   if (!pItem)
    732     return;
    733 
    734   pItem->m_bSkip = true;
    735 }
    736 
    737 void CFX_SAXReader::SetHandler(CXFA_SAXReaderHandler* pHandler) {
    738   m_pHandler = pHandler;
    739 }
    740