• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
CFX_SAXCommentContext()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 
CFX_SAXFile()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 
~CFX_SAXFile()55 CFX_SAXFile::~CFX_SAXFile() {}
56 
StartFile(const RetainPtr<IFX_SeekableReadStream> & pFile,uint32_t dwStart,uint32_t dwLen)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 
ReadNextBlock()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 
Reset()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 
CFX_SAXReader()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 
~CFX_SAXReader()116 CFX_SAXReader::~CFX_SAXReader() {
117   Reset();
118 }
119 
Reset()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 
Push()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 
Pop()140 void CFX_SAXReader::Pop() {
141   if (!m_Stack.empty())
142     m_Stack.pop();
143 }
144 
GetCurrentItem() const145 CFX_SAXItem* CFX_SAXReader::GetCurrentItem() const {
146   return m_Stack.empty() ? nullptr : m_Stack.top().get();
147 }
148 
ClearData()149 void CFX_SAXReader::ClearData() {
150   m_Data.clear();
151   m_iEntityStart = -1;
152 }
153 
ClearName()154 void CFX_SAXReader::ClearName() {
155   m_Name.clear();
156 }
157 
AppendToData(uint8_t ch)158 void CFX_SAXReader::AppendToData(uint8_t ch) {
159   m_Data.push_back(ch);
160 }
161 
AppendToName(uint8_t ch)162 void CFX_SAXReader::AppendToName(uint8_t ch) {
163   m_Name.push_back(ch);
164 }
165 
BackUpAndReplaceDataAt(int32_t index,uint8_t ch)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 
CurrentDataIndex() const172 int32_t CFX_SAXReader::CurrentDataIndex() const {
173   return pdfium::CollectionSize<int32_t>(m_Data) - 1;
174 }
175 
IsEntityStart(uint8_t ch) const176 bool CFX_SAXReader::IsEntityStart(uint8_t ch) const {
177   return m_iEntityStart == -1 && ch == '&';
178 }
179 
IsEntityEnd(uint8_t ch) const180 bool CFX_SAXReader::IsEntityEnd(uint8_t ch) const {
181   return m_iEntityStart != -1 && ch == ';';
182 }
183 
SkipSpace(uint8_t ch)184 bool CFX_SAXReader::SkipSpace(uint8_t ch) {
185   return (m_dwParseMode & CFX_SaxParseMode_NotSkipSpace) == 0 && ch < 0x21;
186 }
187 
StartParse(const RetainPtr<IFX_SeekableReadStream> & pFile,uint32_t dwStart,uint32_t dwLen,uint32_t dwParseMode)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 
ContinueParse()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 
ParseInternal()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 
ParseChar(uint8_t ch)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 
ParseText()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 
ParseNodeStart()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 
ParseDeclOrComment()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 
ParseComment()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 
ParseCommentContent()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 
ParseDeclNode()444 void CFX_SAXReader::ParseDeclNode() {
445   SkipNode();
446 }
447 
ParseTagName()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 
ParseTagAttributeName()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 
ParseTagAttributeEqual()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 
ParseTagAttributeValue()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 
ParseMaybeClose()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 }
ParseTagClose()555 void CFX_SAXReader::ParseTagClose() {
556   m_dwNodePos = m_File.m_dwCur + m_File.m_dwBufIndex;
557   NotifyClose();
558   Pop();
559 }
ParseTagEnd()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 }
ParseTargetData()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 }
SkipNode()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 
NotifyData()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 
NotifyEnter()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 
NotifyAttribute()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 
NotifyBreak()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 
NotifyClose()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 
NotifyEnd()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 
NotifyTargetData()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 
SkipCurrentNode()740 void CFX_SAXReader::SkipCurrentNode() {
741   CFX_SAXItem* pItem = GetCurrentItem();
742   if (pItem)
743     pItem->m_bSkip = true;
744 }
745