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