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