From 332139df2c3c0826069fa61bcd436309fcdf5a6f Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Thu, 12 Apr 2018 13:15:39 +0000 Subject: Merge CFX_XMLParser and CFX_XMLSyntaxParser The CFX_XMLParser was a wrapper around the CFX_XMLSyntaxParser. This CL merges the SyntaxParser into protected/private methods if the XMLParser. Change-Id: If1519b5de55866ed14359dffd64dc12c36ee0244 Reviewed-on: https://pdfium-review.googlesource.com/30171 Reviewed-by: Ryan Harrison Commit-Queue: dsinclair --- BUILD.gn | 4 +- core/fxcrt/xml/cfx_xmlparser.cpp | 709 +++++++++++++++++++++++- core/fxcrt/xml/cfx_xmlparser.h | 112 +++- core/fxcrt/xml/cfx_xmlparser_unittest.cpp | 580 +++++++++++++++++++ core/fxcrt/xml/cfx_xmlsyntaxparser.cpp | 694 ----------------------- core/fxcrt/xml/cfx_xmlsyntaxparser.h | 130 ----- core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp | 542 ------------------ 7 files changed, 1384 insertions(+), 1387 deletions(-) create mode 100644 core/fxcrt/xml/cfx_xmlparser_unittest.cpp delete mode 100644 core/fxcrt/xml/cfx_xmlsyntaxparser.cpp delete mode 100644 core/fxcrt/xml/cfx_xmlsyntaxparser.h delete mode 100644 core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp diff --git a/BUILD.gn b/BUILD.gn index 2a239699e0..0a4b251f5c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -963,8 +963,6 @@ jumbo_static_library("fxcrt") { "core/fxcrt/xml/cfx_xmlnode.h", "core/fxcrt/xml/cfx_xmlparser.cpp", "core/fxcrt/xml/cfx_xmlparser.h", - "core/fxcrt/xml/cfx_xmlsyntaxparser.cpp", - "core/fxcrt/xml/cfx_xmlsyntaxparser.h", "core/fxcrt/xml/cfx_xmltext.cpp", "core/fxcrt/xml/cfx_xmltext.h", ] @@ -2925,7 +2923,7 @@ test("pdfium_unittests") { "core/fxcrt/css/cfx_cssdeclaration_unittest.cpp", "core/fxcrt/css/cfx_cssstylesheet_unittest.cpp", "core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp", - "core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp", + "core/fxcrt/xml/cfx_xmlparser_unittest.cpp", "fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp", "fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp", "fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp", diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp index 76ea32df6c..39196d0264 100644 --- a/core/fxcrt/xml/cfx_xmlparser.cpp +++ b/core/fxcrt/xml/cfx_xmlparser.cpp @@ -6,6 +6,12 @@ #include "core/fxcrt/xml/cfx_xmlparser.h" +#include +#include +#include + +#include "core/fxcrt/fx_extension.h" +#include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/xml/cfx_xmlchardata.h" #include "core/fxcrt/xml/cfx_xmlelement.h" #include "core/fxcrt/xml/cfx_xmlinstruction.h" @@ -13,13 +19,115 @@ #include "core/fxcrt/xml/cfx_xmltext.h" #include "third_party/base/ptr_util.h" +namespace { + +const uint32_t kMaxCharRange = 0x10ffff; + +bool IsXMLWhiteSpace(wchar_t ch) { + return ch == L' ' || ch == 0x0A || ch == 0x0D || ch == 0x09; +} + +struct FX_XMLNAMECHAR { + uint16_t wStart; + uint16_t wEnd; + bool bStartChar; +}; + +const FX_XMLNAMECHAR g_XMLNameChars[] = { + {L'-', L'.', false}, {L'0', L'9', false}, {L':', L':', false}, + {L'A', L'Z', true}, {L'_', L'_', true}, {L'a', L'z', true}, + {0xB7, 0xB7, false}, {0xC0, 0xD6, true}, {0xD8, 0xF6, true}, + {0xF8, 0x02FF, true}, {0x0300, 0x036F, false}, {0x0370, 0x037D, true}, + {0x037F, 0x1FFF, true}, {0x200C, 0x200D, true}, {0x203F, 0x2040, false}, + {0x2070, 0x218F, true}, {0x2C00, 0x2FEF, true}, {0x3001, 0xD7FF, true}, + {0xF900, 0xFDCF, true}, {0xFDF0, 0xFFFD, true}, +}; + +int32_t GetUTF8EncodeLength(const std::vector& src, + FX_FILESIZE iSrcLen) { + uint32_t unicode = 0; + int32_t iDstNum = 0; + const wchar_t* pSrc = src.data(); + while (iSrcLen-- > 0) { + unicode = *pSrc++; + int nbytes = 0; + if ((uint32_t)unicode < 0x80) { + nbytes = 1; + } else if ((uint32_t)unicode < 0x800) { + nbytes = 2; + } else if ((uint32_t)unicode < 0x10000) { + nbytes = 3; + } else if ((uint32_t)unicode < 0x200000) { + nbytes = 4; + } else if ((uint32_t)unicode < 0x4000000) { + nbytes = 5; + } else { + nbytes = 6; + } + iDstNum += nbytes; + } + return iDstNum; +} + +} // namespace + +// static +bool CFX_XMLParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) { + auto* it = std::lower_bound( + std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch, + [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; }); + return it != std::end(g_XMLNameChars) && ch >= it->wStart && + (!bFirstChar || it->bStartChar); +} + CFX_XMLParser::CFX_XMLParser(CFX_XMLNode* pParent, const RetainPtr& pStream) - : m_pParser(pdfium::MakeUnique(pStream)), - m_pParent(pParent), - m_pChild(nullptr) { - ASSERT(m_pParent && pStream); + : m_pParent(pParent), + m_pChild(nullptr), + m_pStream(pStream), + m_iXMLPlaneSize(1024), + m_iCurrentPos(0), + m_iCurrentNodeNum(-1), + m_iLastNodeNum(-1), + m_iParsedBytes(0), + m_ParsedChars(0), + m_iBufferChars(0), + m_bEOS(false), + m_Start(0), + m_End(0), + m_iAllocStep(m_BlockBuffer.GetAllocStep()), + m_pCurrentBlock(nullptr), + m_iIndexInBlock(0), + m_iTextDataLength(0), + m_syntaxParserResult(FX_XmlSyntaxResult::None), + m_syntaxParserState(FDE_XmlSyntaxState::Text), + m_wQuotationMark(0), + m_iEntityStart(-1) { + ASSERT(m_pParent); + ASSERT(pStream); + m_NodeStack.push(m_pParent); + + m_CurNode.iNodeNum = -1; + m_CurNode.eNodeType = FX_XMLNODE_Unknown; + + m_iXMLPlaneSize = + std::min(m_iXMLPlaneSize, + pdfium::base::checked_cast(m_pStream->GetLength())); + m_iCurrentPos = m_pStream->GetBOMLength(); + + FX_SAFE_SIZE_T alloc_size_safe = m_iXMLPlaneSize; + alloc_size_safe += 1; // For NUL. + if (!alloc_size_safe.IsValid() || alloc_size_safe.ValueOrDie() <= 0) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return; + } + + m_Buffer.resize(pdfium::base::ValueOrDieForType(alloc_size_safe)); + + m_BlockBuffer.InitBuffer(); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); } CFX_XMLParser::~CFX_XMLParser() {} @@ -27,7 +135,7 @@ CFX_XMLParser::~CFX_XMLParser() {} int32_t CFX_XMLParser::Parse() { int32_t iCount = 0; while (true) { - FX_XmlSyntaxResult result = m_pParser->DoSyntaxParse(); + FX_XmlSyntaxResult result = DoSyntaxParse(); if (result == FX_XmlSyntaxResult::Error) return -1; if (result == FX_XmlSyntaxResult::EndOfString) @@ -44,7 +152,7 @@ int32_t CFX_XMLParser::Parse() { if (m_pChild->GetType() != FX_XMLNODE_Element) return -1; - m_ws1 = m_pParser->GetTagName(); + m_ws1 = GetTagName(); if (m_ws1.GetLength() > 0 && m_ws1 != static_cast(m_pChild)->GetName()) { return -1; @@ -60,7 +168,7 @@ int32_t CFX_XMLParser::Parse() { iCount++; break; case FX_XmlSyntaxResult::TargetName: - m_ws1 = m_pParser->GetTargetName(); + m_ws1 = GetTargetName(); if (m_ws1 == L"originalXFAVersion" || m_ws1 == L"acrobat") { m_pChild = new CFX_XMLInstruction(m_ws1); m_pParent->AppendChild(m_pChild); @@ -70,30 +178,30 @@ int32_t CFX_XMLParser::Parse() { m_ws1.clear(); break; case FX_XmlSyntaxResult::TagName: - m_ws1 = m_pParser->GetTagName(); + m_ws1 = GetTagName(); m_pChild = new CFX_XMLElement(m_ws1); m_pParent->AppendChild(m_pChild); m_NodeStack.push(m_pChild); m_pParent = m_pChild; break; case FX_XmlSyntaxResult::AttriName: - m_ws1 = m_pParser->GetAttributeName(); + m_ws1 = GetAttributeName(); break; case FX_XmlSyntaxResult::AttriValue: if (m_pChild && m_pChild->GetType() == FX_XMLNODE_Element) { - static_cast(m_pChild)->SetString( - m_ws1, m_pParser->GetAttributeName()); + static_cast(m_pChild)->SetString(m_ws1, + GetAttributeName()); } m_ws1.clear(); break; case FX_XmlSyntaxResult::Text: - m_ws1 = m_pParser->GetTextData(); + m_ws1 = GetTextData(); m_pChild = new CFX_XMLText(m_ws1); m_pParent->AppendChild(m_pChild); m_pChild = m_pParent; break; case FX_XmlSyntaxResult::CData: - m_ws1 = m_pParser->GetTextData(); + m_ws1 = GetTextData(); m_pChild = new CFX_XMLCharData(m_ws1); m_pParent->AppendChild(m_pChild); m_pChild = m_pParent; @@ -107,7 +215,7 @@ int32_t CFX_XMLParser::Parse() { if (!m_ws1.IsEmpty()) instruction->AppendData(m_ws1); - instruction->AppendData(m_pParser->GetTargetData()); + instruction->AppendData(GetTargetData()); } m_ws1.clear(); break; @@ -118,5 +226,576 @@ int32_t CFX_XMLParser::Parse() { break; } } - return m_NodeStack.size() != 1 ? -1 : m_pParser->GetStatus(); + return m_NodeStack.size() != 1 ? -1 : GetStatus(); +} + +FX_XmlSyntaxResult CFX_XMLParser::DoSyntaxParse() { + if (m_syntaxParserResult == FX_XmlSyntaxResult::Error || + m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) { + return m_syntaxParserResult; + } + + FX_FILESIZE iStreamLength = m_pStream->GetLength(); + FX_FILESIZE iPos; + + FX_XmlSyntaxResult syntaxParserResult = FX_XmlSyntaxResult::None; + while (true) { + if (m_Start >= m_End) { + if (m_bEOS || m_iCurrentPos >= iStreamLength) { + m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString; + return m_syntaxParserResult; + } + m_ParsedChars += m_End; + m_iParsedBytes = m_iCurrentPos; + if (m_pStream->GetPosition() != m_iCurrentPos) + m_pStream->Seek(CFX_SeekableStreamProxy::From::Begin, m_iCurrentPos); + + m_iBufferChars = + m_pStream->ReadString(m_Buffer.data(), m_iXMLPlaneSize, &m_bEOS); + iPos = m_pStream->GetPosition(); + if (m_iBufferChars < 1) { + m_iCurrentPos = iStreamLength; + m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString; + return m_syntaxParserResult; + } + m_iCurrentPos = iPos; + m_Start = 0; + m_End = m_iBufferChars; + } + + while (m_Start < m_End) { + wchar_t ch = m_Buffer[m_Start]; + switch (m_syntaxParserState) { + case FDE_XmlSyntaxState::Text: + if (ch == L'<') { + if (!m_BlockBuffer.IsEmpty()) { + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_iEntityStart = -1; + syntaxParserResult = FX_XmlSyntaxResult::Text; + } else { + m_Start++; + m_syntaxParserState = FDE_XmlSyntaxState::Node; + } + } else { + ParseTextChar(ch); + } + break; + case FDE_XmlSyntaxState::Node: + if (ch == L'!') { + m_Start++; + m_syntaxParserState = FDE_XmlSyntaxState::SkipCommentOrDecl; + } else if (ch == L'/') { + m_Start++; + m_syntaxParserState = FDE_XmlSyntaxState::CloseElement; + } else if (ch == L'?') { + m_iLastNodeNum++; + m_iCurrentNodeNum = m_iLastNodeNum; + m_CurNode.iNodeNum = m_iLastNodeNum; + m_CurNode.eNodeType = FX_XMLNODE_Instruction; + m_XMLNodeStack.push(m_CurNode); + m_Start++; + m_syntaxParserState = FDE_XmlSyntaxState::Target; + syntaxParserResult = FX_XmlSyntaxResult::InstructionOpen; + } else { + m_iLastNodeNum++; + m_iCurrentNodeNum = m_iLastNodeNum; + m_CurNode.iNodeNum = m_iLastNodeNum; + m_CurNode.eNodeType = FX_XMLNODE_Element; + m_XMLNodeStack.push(m_CurNode); + m_syntaxParserState = FDE_XmlSyntaxState::Tag; + syntaxParserResult = FX_XmlSyntaxResult::ElementOpen; + } + break; + case FDE_XmlSyntaxState::Target: + case FDE_XmlSyntaxState::Tag: + if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { + if (m_BlockBuffer.IsEmpty()) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (m_syntaxParserState != FDE_XmlSyntaxState::Target) + syntaxParserResult = FX_XmlSyntaxResult::TagName; + else + syntaxParserResult = FX_XmlSyntaxResult::TargetName; + + m_syntaxParserState = FDE_XmlSyntaxState::AttriName; + } else { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + m_Start++; + } + break; + case FDE_XmlSyntaxState::AttriName: + if (m_BlockBuffer.IsEmpty() && IsXMLWhiteSpace(ch)) { + m_Start++; + break; + } + if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { + if (m_BlockBuffer.IsEmpty()) { + if (m_CurNode.eNodeType == FX_XMLNODE_Element) { + if (ch == L'>' || ch == L'/') { + m_syntaxParserState = FDE_XmlSyntaxState::BreakElement; + break; + } + } else if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { + if (ch == L'?') { + m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction; + m_Start++; + } else { + m_syntaxParserState = FDE_XmlSyntaxState::TargetData; + } + break; + } + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } else { + if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { + if (ch != '=' && !IsXMLWhiteSpace(ch)) { + m_syntaxParserState = FDE_XmlSyntaxState::TargetData; + break; + } + } + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_syntaxParserState = FDE_XmlSyntaxState::AttriEqualSign; + syntaxParserResult = FX_XmlSyntaxResult::AttriName; + } + } else { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + m_Start++; + } + break; + case FDE_XmlSyntaxState::AttriEqualSign: + if (IsXMLWhiteSpace(ch)) { + m_Start++; + break; + } + if (ch != L'=') { + if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { + m_syntaxParserState = FDE_XmlSyntaxState::TargetData; + break; + } + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } else { + m_syntaxParserState = FDE_XmlSyntaxState::AttriQuotation; + m_Start++; + } + break; + case FDE_XmlSyntaxState::AttriQuotation: + if (IsXMLWhiteSpace(ch)) { + m_Start++; + break; + } + if (ch != L'\"' && ch != L'\'') { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } else { + m_wQuotationMark = ch; + m_syntaxParserState = FDE_XmlSyntaxState::AttriValue; + m_Start++; + } + break; + case FDE_XmlSyntaxState::AttriValue: + if (ch == m_wQuotationMark) { + if (m_iEntityStart > -1) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_wQuotationMark = 0; + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_Start++; + m_syntaxParserState = FDE_XmlSyntaxState::AttriName; + syntaxParserResult = FX_XmlSyntaxResult::AttriValue; + } else { + ParseTextChar(ch); + } + break; + case FDE_XmlSyntaxState::CloseInstruction: + if (ch != L'>') { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + m_syntaxParserState = FDE_XmlSyntaxState::TargetData; + } else if (!m_BlockBuffer.IsEmpty()) { + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + syntaxParserResult = FX_XmlSyntaxResult::TargetData; + } else { + m_Start++; + if (m_XMLNodeStack.empty()) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + m_XMLNodeStack.pop(); + if (!m_XMLNodeStack.empty()) { + m_CurNode = m_XMLNodeStack.top(); + } else { + m_CurNode.iNodeNum = -1; + m_CurNode.eNodeType = FX_XMLNODE_Unknown; + } + m_iCurrentNodeNum = m_CurNode.iNodeNum; + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_syntaxParserState = FDE_XmlSyntaxState::Text; + syntaxParserResult = FX_XmlSyntaxResult::InstructionClose; + } + break; + case FDE_XmlSyntaxState::BreakElement: + if (ch == L'>') { + m_syntaxParserState = FDE_XmlSyntaxState::Text; + syntaxParserResult = FX_XmlSyntaxResult::ElementBreak; + } else if (ch == L'/') { + m_syntaxParserState = FDE_XmlSyntaxState::CloseElement; + } else { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + m_Start++; + break; + case FDE_XmlSyntaxState::CloseElement: + if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { + if (ch == L'>') { + if (m_XMLNodeStack.empty()) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + m_XMLNodeStack.pop(); + if (!m_XMLNodeStack.empty()) { + m_CurNode = m_XMLNodeStack.top(); + } else { + m_CurNode.iNodeNum = -1; + m_CurNode.eNodeType = FX_XMLNODE_Unknown; + } + m_iCurrentNodeNum = m_CurNode.iNodeNum; + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_syntaxParserState = FDE_XmlSyntaxState::Text; + syntaxParserResult = FX_XmlSyntaxResult::ElementClose; + } else if (!IsXMLWhiteSpace(ch)) { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + } else { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + } + m_Start++; + break; + case FDE_XmlSyntaxState::SkipCommentOrDecl: + if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"--", 2) == 0) { + m_Start += 2; + m_syntaxParserState = FDE_XmlSyntaxState::SkipComment; + } else if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"[CDATA[", 7) == + 0) { + m_Start += 7; + m_syntaxParserState = FDE_XmlSyntaxState::SkipCData; + } else { + m_syntaxParserState = FDE_XmlSyntaxState::SkipDeclNode; + m_SkipChar = L'>'; + m_SkipStack.push(L'>'); + } + break; + case FDE_XmlSyntaxState::SkipCData: { + if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"]]>", 3) == 0) { + m_Start += 3; + syntaxParserResult = FX_XmlSyntaxResult::CData; + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_syntaxParserState = FDE_XmlSyntaxState::Text; + } else { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) + return FX_XmlSyntaxResult::Error; + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + m_Start++; + } + break; + } + case FDE_XmlSyntaxState::SkipDeclNode: + if (m_SkipChar == L'\'' || m_SkipChar == L'\"') { + m_Start++; + if (ch != m_SkipChar) + break; + + m_SkipStack.pop(); + if (m_SkipStack.empty()) + m_syntaxParserState = FDE_XmlSyntaxState::Text; + else + m_SkipChar = m_SkipStack.top(); + } else { + switch (ch) { + case L'<': + m_SkipChar = L'>'; + m_SkipStack.push(L'>'); + break; + case L'[': + m_SkipChar = L']'; + m_SkipStack.push(L']'); + break; + case L'(': + m_SkipChar = L')'; + m_SkipStack.push(L')'); + break; + case L'\'': + m_SkipChar = L'\''; + m_SkipStack.push(L'\''); + break; + case L'\"': + m_SkipChar = L'\"'; + m_SkipStack.push(L'\"'); + break; + default: + if (ch == m_SkipChar) { + m_SkipStack.pop(); + if (m_SkipStack.empty()) { + if (m_BlockBuffer.GetDataLength() >= 9) + (void)m_BlockBuffer.GetTextData(0, 7); + + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_syntaxParserState = FDE_XmlSyntaxState::Text; + } else { + m_SkipChar = m_SkipStack.top(); + } + } + break; + } + if (!m_SkipStack.empty()) { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + } + m_Start++; + } + break; + case FDE_XmlSyntaxState::SkipComment: + if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"-->", 3) == 0) { + m_Start += 2; + m_syntaxParserState = FDE_XmlSyntaxState::Text; + } + + m_Start++; + break; + case FDE_XmlSyntaxState::TargetData: + if (IsXMLWhiteSpace(ch)) { + if (m_BlockBuffer.IsEmpty()) { + m_Start++; + break; + } + if (m_wQuotationMark == 0) { + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_wQuotationMark = 0; + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_Start++; + syntaxParserResult = FX_XmlSyntaxResult::TargetData; + break; + } + } + if (ch == '?') { + m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction; + m_Start++; + } else if (ch == '\"') { + if (m_wQuotationMark == 0) { + m_wQuotationMark = ch; + m_Start++; + } else if (ch == m_wQuotationMark) { + m_iTextDataLength = m_BlockBuffer.GetDataLength(); + m_wQuotationMark = 0; + m_BlockBuffer.Reset(true); + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_Start++; + syntaxParserResult = FX_XmlSyntaxResult::TargetData; + } else { + m_syntaxParserResult = FX_XmlSyntaxResult::Error; + return m_syntaxParserResult; + } + } else { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) { + return FX_XmlSyntaxResult::Error; + } + } + m_pCurrentBlock[m_iIndexInBlock++] = ch; + m_BlockBuffer.IncrementDataLength(); + m_Start++; + } + break; + default: + break; + } + if (syntaxParserResult != FX_XmlSyntaxResult::None) + return syntaxParserResult; + } + } + return FX_XmlSyntaxResult::Text; +} + +int32_t CFX_XMLParser::GetStatus() const { + if (!m_pStream) + return -1; + + int32_t iStreamLength = m_pStream->GetLength(); + if (iStreamLength < 1) + return 100; + + if (m_syntaxParserResult == FX_XmlSyntaxResult::Error) + return -1; + + if (m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) + return 100; + return m_iParsedBytes * 100 / iStreamLength; +} + +FX_FILESIZE CFX_XMLParser::GetCurrentBinaryPos() const { + if (!m_pStream) + return 0; + + int32_t nDstLen = GetUTF8EncodeLength(m_Buffer, m_Start); + return m_iParsedBytes + nDstLen; +} + +void CFX_XMLParser::ParseTextChar(wchar_t character) { + if (m_iIndexInBlock == m_iAllocStep) { + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + if (!m_pCurrentBlock) + return; + } + + m_pCurrentBlock[m_iIndexInBlock++] = character; + m_BlockBuffer.IncrementDataLength(); + if (m_iEntityStart > -1 && character == L';') { + WideString csEntity = m_BlockBuffer.GetTextData( + m_iEntityStart + 1, + m_BlockBuffer.GetDataLength() - 1 - m_iEntityStart - 1); + int32_t iLen = csEntity.GetLength(); + if (iLen > 0) { + if (csEntity[0] == L'#') { + uint32_t ch = 0; + wchar_t w; + if (iLen > 1 && csEntity[1] == L'x') { + for (int32_t i = 2; i < iLen; i++) { + w = csEntity[i]; + if (std::iswdigit(w)) + ch = (ch << 4) + w - L'0'; + else if (w >= L'A' && w <= L'F') + ch = (ch << 4) + w - 55; + else if (w >= L'a' && w <= L'f') + ch = (ch << 4) + w - 87; + else + break; + } + } else { + for (int32_t i = 1; i < iLen; i++) { + w = csEntity[i]; + if (!std::iswdigit(w)) + break; + ch = ch * 10 + w - L'0'; + } + } + if (ch > kMaxCharRange) + ch = ' '; + + character = static_cast(ch); + if (character != 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, character); + m_iEntityStart++; + } + } else { + if (csEntity.Compare(L"amp") == 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, L'&'); + m_iEntityStart++; + } else if (csEntity.Compare(L"lt") == 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, L'<'); + m_iEntityStart++; + } else if (csEntity.Compare(L"gt") == 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, L'>'); + m_iEntityStart++; + } else if (csEntity.Compare(L"apos") == 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, L'\''); + m_iEntityStart++; + } else if (csEntity.Compare(L"quot") == 0) { + m_BlockBuffer.SetTextChar(m_iEntityStart, L'\"'); + m_iEntityStart++; + } + } + } + if (m_iEntityStart >= 0 && + m_BlockBuffer.GetDataLength() > static_cast(m_iEntityStart)) { + m_BlockBuffer.DeleteTextChars(m_BlockBuffer.GetDataLength() - + m_iEntityStart); + } + std::tie(m_pCurrentBlock, m_iIndexInBlock) = + m_BlockBuffer.GetAvailableBlock(); + m_iEntityStart = -1; + } else if (m_iEntityStart < 0 && character == L'&') { + m_iEntityStart = m_BlockBuffer.GetDataLength() - 1; + } + m_Start++; } diff --git a/core/fxcrt/xml/cfx_xmlparser.h b/core/fxcrt/xml/cfx_xmlparser.h index 8c4c354699..c7587e9151 100644 --- a/core/fxcrt/xml/cfx_xmlparser.h +++ b/core/fxcrt/xml/cfx_xmlparser.h @@ -9,29 +9,135 @@ #include #include +#include +#include "core/fxcrt/cfx_blockbuffer.h" +#include "core/fxcrt/cfx_seekablestreamproxy.h" #include "core/fxcrt/fx_string.h" #include "core/fxcrt/retain_ptr.h" -#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h" +#include "core/fxcrt/xml/cfx_xmlnode.h" class CFX_XMLElement; class CFX_XMLNode; class CFX_SeekableStreamProxy; +enum class FX_XmlSyntaxResult { + None, + InstructionOpen, + InstructionClose, + ElementOpen, + ElementBreak, + ElementClose, + TargetName, + TagName, + AttriName, + AttriValue, + Text, + CData, + TargetData, + Error, + EndOfString +}; + class CFX_XMLParser { public: + static bool IsXMLNameChar(wchar_t ch, bool bFirstChar); + CFX_XMLParser(CFX_XMLNode* pParent, const RetainPtr& pStream); - ~CFX_XMLParser(); + virtual ~CFX_XMLParser(); int32_t Parse(); + protected: + FX_XmlSyntaxResult DoSyntaxParse(); + + WideString GetTagName() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + + WideString GetAttributeName() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + + WideString GetAttributeValue() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + + WideString GetTextData() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + private: - std::unique_ptr m_pParser; + enum class FDE_XmlSyntaxState { + Text, + Node, + Target, + Tag, + AttriName, + AttriEqualSign, + AttriQuotation, + AttriValue, + Entity, + EntityDecimal, + EntityHex, + CloseInstruction, + BreakElement, + CloseElement, + SkipDeclNode, + DeclCharData, + SkipComment, + SkipCommentOrDecl, + SkipCData, + TargetData + }; + + void ParseTextChar(wchar_t ch); + + int32_t GetStatus() const; + FX_FILESIZE GetCurrentPos() const { return m_ParsedChars + m_Start; } + FX_FILESIZE GetCurrentBinaryPos() const; + int32_t GetCurrentNodeNumber() const { return m_iCurrentNodeNum; } + int32_t GetLastNodeNumber() const { return m_iLastNodeNum; } + + WideString GetTargetName() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + + WideString GetTargetData() const { + return m_BlockBuffer.GetTextData(0, m_iTextDataLength); + } + CFX_XMLNode* m_pParent; CFX_XMLNode* m_pChild; std::stack m_NodeStack; WideString m_ws1; + + RetainPtr m_pStream; + size_t m_iXMLPlaneSize; + FX_FILESIZE m_iCurrentPos; + int32_t m_iCurrentNodeNum; + int32_t m_iLastNodeNum; + int32_t m_iParsedBytes; + FX_FILESIZE m_ParsedChars; + std::vector m_Buffer; + size_t m_iBufferChars; + bool m_bEOS; + FX_FILESIZE m_Start; // Start position in m_Buffer + FX_FILESIZE m_End; // End position in m_Buffer + FX_XMLNODE m_CurNode; + std::stack m_XMLNodeStack; + CFX_BlockBuffer m_BlockBuffer; + int32_t m_iAllocStep; + wchar_t* m_pCurrentBlock; // Pointer into CFX_BlockBuffer + int32_t m_iIndexInBlock; + int32_t m_iTextDataLength; + FX_XmlSyntaxResult m_syntaxParserResult; + FDE_XmlSyntaxState m_syntaxParserState; + wchar_t m_wQuotationMark; + int32_t m_iEntityStart; + std::stack m_SkipStack; + wchar_t m_SkipChar; }; #endif // CORE_FXCRT_XML_CFX_XMLPARSER_H_ diff --git a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp new file mode 100644 index 0000000000..d22925f797 --- /dev/null +++ b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp @@ -0,0 +1,580 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/fxcrt/xml/cfx_xmlparser.h" + +#include + +#include "core/fxcrt/cfx_seekablestreamproxy.h" +#include "core/fxcrt/fx_codepage.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/test_support.h" + +class CFX_XMLTestParser : public CFX_XMLParser { + public: + CFX_XMLTestParser(CFX_XMLNode* pParent, + const RetainPtr& pStream) + : CFX_XMLParser(pParent, pStream) {} + + ~CFX_XMLTestParser() override = default; + + FX_XmlSyntaxResult DoSyntaxParse() { return CFX_XMLParser::DoSyntaxParse(); } + + WideString GetTagName() const { return CFX_XMLParser::GetTagName(); } + + WideString GetAttributeName() const { + return CFX_XMLParser::GetAttributeName(); + } + + WideString GetAttributeValue() const { + return CFX_XMLParser::GetAttributeValue(); + } + + WideString GetTextData() const { return CFX_XMLParser::GetTextData(); } +}; + +TEST(CFX_XMLParserTest, CData) { + const char* input = + ""; + + const wchar_t* cdata = + L"\n" + L" if (a[1] < 3)\n" + L" app.alert(\"Tclams\");\n" + L" "; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); + ASSERT_EQ(cdata, parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, CDataWithInnerScript) { + const char* input = + "\n" + " ]]>\n" + ""; + + const wchar_t* cdata = + L"\n" + L" if (a[1] < 3)\n" + L" app.alert(\"Tclams\");\n" + L" \n" + L" "; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); + ASSERT_EQ(cdata, parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, ArrowBangArrow) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, ArrowBangBracketArrow) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + // Parser walks to end of input. + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, IncompleteCData) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + // Parser walks to end of input. + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, UnClosedCData) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + // Parser walks to end of input. + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, EmptyCData) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); + ASSERT_EQ(L"", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, Comment) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, IncorrectCommentStart) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, CommentEmpty) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, CommentThreeDash) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, CommentTwoDash) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"\n ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, Entities) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L"BTH\xab48", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, EntityOverflowHex) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L" ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, EntityOverflowDecimal) { + const char* input = + ""; + + RetainPtr stream = + pdfium::MakeRetain( + reinterpret_cast(const_cast(input)), strlen(input)); + stream->SetCodePage(FX_CODEPAGE_UTF8); + + auto root = pdfium::MakeUnique(); + CFX_XMLTestParser parser(root.get(), stream); + ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); + ASSERT_EQ(L"contentType", parser.GetAttributeName()); + ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); + ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); + ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); + ASSERT_EQ(L" ", parser.GetTextData()); + + ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); + ASSERT_EQ(L"script", parser.GetTagName()); + + ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); +} + +TEST(CFX_XMLParserTest, IsXMLNameChar) { + EXPECT_FALSE(CFX_XMLTestParser::IsXMLNameChar(L'-', true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(L'-', false)); + + EXPECT_FALSE(CFX_XMLTestParser::IsXMLNameChar(0x2069, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0x2070, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0x2073, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0x218F, true)); + EXPECT_FALSE(CFX_XMLTestParser::IsXMLNameChar(0x2190, true)); + + EXPECT_FALSE(CFX_XMLTestParser::IsXMLNameChar(0xFDEF, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0xFDF0, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0xFDF1, true)); + EXPECT_TRUE(CFX_XMLTestParser::IsXMLNameChar(0xFFFD, true)); + EXPECT_FALSE(CFX_XMLTestParser::IsXMLNameChar(0xFFFE, true)); +} diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp b/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp deleted file mode 100644 index 1fb51b7d52..0000000000 --- a/core/fxcrt/xml/cfx_xmlsyntaxparser.cpp +++ /dev/null @@ -1,694 +0,0 @@ -// Copyright 2017 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h" - -#include -#include -#include - -#include "core/fxcrt/fx_extension.h" -#include "core/fxcrt/fx_safe_types.h" - -namespace { - -const uint32_t kMaxCharRange = 0x10ffff; - -bool IsXMLWhiteSpace(wchar_t ch) { - return ch == L' ' || ch == 0x0A || ch == 0x0D || ch == 0x09; -} - -struct FX_XMLNAMECHAR { - uint16_t wStart; - uint16_t wEnd; - bool bStartChar; -}; - -const FX_XMLNAMECHAR g_XMLNameChars[] = { - {L'-', L'.', false}, {L'0', L'9', false}, {L':', L':', false}, - {L'A', L'Z', true}, {L'_', L'_', true}, {L'a', L'z', true}, - {0xB7, 0xB7, false}, {0xC0, 0xD6, true}, {0xD8, 0xF6, true}, - {0xF8, 0x02FF, true}, {0x0300, 0x036F, false}, {0x0370, 0x037D, true}, - {0x037F, 0x1FFF, true}, {0x200C, 0x200D, true}, {0x203F, 0x2040, false}, - {0x2070, 0x218F, true}, {0x2C00, 0x2FEF, true}, {0x3001, 0xD7FF, true}, - {0xF900, 0xFDCF, true}, {0xFDF0, 0xFFFD, true}, -}; - - -int32_t GetUTF8EncodeLength(const std::vector& src, - FX_FILESIZE iSrcLen) { - uint32_t unicode = 0; - int32_t iDstNum = 0; - const wchar_t* pSrc = src.data(); - while (iSrcLen-- > 0) { - unicode = *pSrc++; - int nbytes = 0; - if ((uint32_t)unicode < 0x80) { - nbytes = 1; - } else if ((uint32_t)unicode < 0x800) { - nbytes = 2; - } else if ((uint32_t)unicode < 0x10000) { - nbytes = 3; - } else if ((uint32_t)unicode < 0x200000) { - nbytes = 4; - } else if ((uint32_t)unicode < 0x4000000) { - nbytes = 5; - } else { - nbytes = 6; - } - iDstNum += nbytes; - } - return iDstNum; -} - -} // namespace - -// static -bool CFX_XMLSyntaxParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) { - auto* it = std::lower_bound( - std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch, - [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; }); - return it != std::end(g_XMLNameChars) && ch >= it->wStart && - (!bFirstChar || it->bStartChar); -} - -CFX_XMLSyntaxParser::CFX_XMLSyntaxParser( - const RetainPtr& pStream) - : m_pStream(pStream), - m_iXMLPlaneSize(1024), - m_iCurrentPos(0), - m_iCurrentNodeNum(-1), - m_iLastNodeNum(-1), - m_iParsedBytes(0), - m_ParsedChars(0), - m_iBufferChars(0), - m_bEOS(false), - m_Start(0), - m_End(0), - m_iAllocStep(m_BlockBuffer.GetAllocStep()), - m_pCurrentBlock(nullptr), - m_iIndexInBlock(0), - m_iTextDataLength(0), - m_syntaxParserResult(FX_XmlSyntaxResult::None), - m_syntaxParserState(FDE_XmlSyntaxState::Text), - m_wQuotationMark(0), - m_iEntityStart(-1) { - ASSERT(pStream); - - m_CurNode.iNodeNum = -1; - m_CurNode.eNodeType = FX_XMLNODE_Unknown; - - m_iXMLPlaneSize = - std::min(m_iXMLPlaneSize, - pdfium::base::checked_cast(m_pStream->GetLength())); - m_iCurrentPos = m_pStream->GetBOMLength(); - - FX_SAFE_SIZE_T alloc_size_safe = m_iXMLPlaneSize; - alloc_size_safe += 1; // For NUL. - if (!alloc_size_safe.IsValid() || alloc_size_safe.ValueOrDie() <= 0) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return; - } - - m_Buffer.resize(pdfium::base::ValueOrDieForType(alloc_size_safe)); - - m_BlockBuffer.InitBuffer(); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); -} - -CFX_XMLSyntaxParser::~CFX_XMLSyntaxParser() {} - -FX_XmlSyntaxResult CFX_XMLSyntaxParser::DoSyntaxParse() { - if (m_syntaxParserResult == FX_XmlSyntaxResult::Error || - m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) { - return m_syntaxParserResult; - } - - FX_FILESIZE iStreamLength = m_pStream->GetLength(); - FX_FILESIZE iPos; - - FX_XmlSyntaxResult syntaxParserResult = FX_XmlSyntaxResult::None; - while (true) { - if (m_Start >= m_End) { - if (m_bEOS || m_iCurrentPos >= iStreamLength) { - m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString; - return m_syntaxParserResult; - } - m_ParsedChars += m_End; - m_iParsedBytes = m_iCurrentPos; - if (m_pStream->GetPosition() != m_iCurrentPos) - m_pStream->Seek(CFX_SeekableStreamProxy::From::Begin, m_iCurrentPos); - - m_iBufferChars = - m_pStream->ReadString(m_Buffer.data(), m_iXMLPlaneSize, &m_bEOS); - iPos = m_pStream->GetPosition(); - if (m_iBufferChars < 1) { - m_iCurrentPos = iStreamLength; - m_syntaxParserResult = FX_XmlSyntaxResult::EndOfString; - return m_syntaxParserResult; - } - m_iCurrentPos = iPos; - m_Start = 0; - m_End = m_iBufferChars; - } - - while (m_Start < m_End) { - wchar_t ch = m_Buffer[m_Start]; - switch (m_syntaxParserState) { - case FDE_XmlSyntaxState::Text: - if (ch == L'<') { - if (!m_BlockBuffer.IsEmpty()) { - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_iEntityStart = -1; - syntaxParserResult = FX_XmlSyntaxResult::Text; - } else { - m_Start++; - m_syntaxParserState = FDE_XmlSyntaxState::Node; - } - } else { - ParseTextChar(ch); - } - break; - case FDE_XmlSyntaxState::Node: - if (ch == L'!') { - m_Start++; - m_syntaxParserState = FDE_XmlSyntaxState::SkipCommentOrDecl; - } else if (ch == L'/') { - m_Start++; - m_syntaxParserState = FDE_XmlSyntaxState::CloseElement; - } else if (ch == L'?') { - m_iLastNodeNum++; - m_iCurrentNodeNum = m_iLastNodeNum; - m_CurNode.iNodeNum = m_iLastNodeNum; - m_CurNode.eNodeType = FX_XMLNODE_Instruction; - m_XMLNodeStack.push(m_CurNode); - m_Start++; - m_syntaxParserState = FDE_XmlSyntaxState::Target; - syntaxParserResult = FX_XmlSyntaxResult::InstructionOpen; - } else { - m_iLastNodeNum++; - m_iCurrentNodeNum = m_iLastNodeNum; - m_CurNode.iNodeNum = m_iLastNodeNum; - m_CurNode.eNodeType = FX_XMLNODE_Element; - m_XMLNodeStack.push(m_CurNode); - m_syntaxParserState = FDE_XmlSyntaxState::Tag; - syntaxParserResult = FX_XmlSyntaxResult::ElementOpen; - } - break; - case FDE_XmlSyntaxState::Target: - case FDE_XmlSyntaxState::Tag: - if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { - if (m_BlockBuffer.IsEmpty()) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (m_syntaxParserState != FDE_XmlSyntaxState::Target) - syntaxParserResult = FX_XmlSyntaxResult::TagName; - else - syntaxParserResult = FX_XmlSyntaxResult::TargetName; - - m_syntaxParserState = FDE_XmlSyntaxState::AttriName; - } else { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - m_Start++; - } - break; - case FDE_XmlSyntaxState::AttriName: - if (m_BlockBuffer.IsEmpty() && IsXMLWhiteSpace(ch)) { - m_Start++; - break; - } - if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { - if (m_BlockBuffer.IsEmpty()) { - if (m_CurNode.eNodeType == FX_XMLNODE_Element) { - if (ch == L'>' || ch == L'/') { - m_syntaxParserState = FDE_XmlSyntaxState::BreakElement; - break; - } - } else if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { - if (ch == L'?') { - m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction; - m_Start++; - } else { - m_syntaxParserState = FDE_XmlSyntaxState::TargetData; - } - break; - } - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } else { - if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { - if (ch != '=' && !IsXMLWhiteSpace(ch)) { - m_syntaxParserState = FDE_XmlSyntaxState::TargetData; - break; - } - } - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_syntaxParserState = FDE_XmlSyntaxState::AttriEqualSign; - syntaxParserResult = FX_XmlSyntaxResult::AttriName; - } - } else { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - m_Start++; - } - break; - case FDE_XmlSyntaxState::AttriEqualSign: - if (IsXMLWhiteSpace(ch)) { - m_Start++; - break; - } - if (ch != L'=') { - if (m_CurNode.eNodeType == FX_XMLNODE_Instruction) { - m_syntaxParserState = FDE_XmlSyntaxState::TargetData; - break; - } - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } else { - m_syntaxParserState = FDE_XmlSyntaxState::AttriQuotation; - m_Start++; - } - break; - case FDE_XmlSyntaxState::AttriQuotation: - if (IsXMLWhiteSpace(ch)) { - m_Start++; - break; - } - if (ch != L'\"' && ch != L'\'') { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } else { - m_wQuotationMark = ch; - m_syntaxParserState = FDE_XmlSyntaxState::AttriValue; - m_Start++; - } - break; - case FDE_XmlSyntaxState::AttriValue: - if (ch == m_wQuotationMark) { - if (m_iEntityStart > -1) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_wQuotationMark = 0; - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_Start++; - m_syntaxParserState = FDE_XmlSyntaxState::AttriName; - syntaxParserResult = FX_XmlSyntaxResult::AttriValue; - } else { - ParseTextChar(ch); - } - break; - case FDE_XmlSyntaxState::CloseInstruction: - if (ch != L'>') { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - m_syntaxParserState = FDE_XmlSyntaxState::TargetData; - } else if (!m_BlockBuffer.IsEmpty()) { - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - syntaxParserResult = FX_XmlSyntaxResult::TargetData; - } else { - m_Start++; - if (m_XMLNodeStack.empty()) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - m_XMLNodeStack.pop(); - if (!m_XMLNodeStack.empty()) { - m_CurNode = m_XMLNodeStack.top(); - } else { - m_CurNode.iNodeNum = -1; - m_CurNode.eNodeType = FX_XMLNODE_Unknown; - } - m_iCurrentNodeNum = m_CurNode.iNodeNum; - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_syntaxParserState = FDE_XmlSyntaxState::Text; - syntaxParserResult = FX_XmlSyntaxResult::InstructionClose; - } - break; - case FDE_XmlSyntaxState::BreakElement: - if (ch == L'>') { - m_syntaxParserState = FDE_XmlSyntaxState::Text; - syntaxParserResult = FX_XmlSyntaxResult::ElementBreak; - } else if (ch == L'/') { - m_syntaxParserState = FDE_XmlSyntaxState::CloseElement; - } else { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - m_Start++; - break; - case FDE_XmlSyntaxState::CloseElement: - if (!IsXMLNameChar(ch, m_BlockBuffer.IsEmpty())) { - if (ch == L'>') { - if (m_XMLNodeStack.empty()) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - m_XMLNodeStack.pop(); - if (!m_XMLNodeStack.empty()) { - m_CurNode = m_XMLNodeStack.top(); - } else { - m_CurNode.iNodeNum = -1; - m_CurNode.eNodeType = FX_XMLNODE_Unknown; - } - m_iCurrentNodeNum = m_CurNode.iNodeNum; - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_syntaxParserState = FDE_XmlSyntaxState::Text; - syntaxParserResult = FX_XmlSyntaxResult::ElementClose; - } else if (!IsXMLWhiteSpace(ch)) { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - } else { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - } - m_Start++; - break; - case FDE_XmlSyntaxState::SkipCommentOrDecl: - if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"--", 2) == 0) { - m_Start += 2; - m_syntaxParserState = FDE_XmlSyntaxState::SkipComment; - } else if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"[CDATA[", 7) == - 0) { - m_Start += 7; - m_syntaxParserState = FDE_XmlSyntaxState::SkipCData; - } else { - m_syntaxParserState = FDE_XmlSyntaxState::SkipDeclNode; - m_SkipChar = L'>'; - m_SkipStack.push(L'>'); - } - break; - case FDE_XmlSyntaxState::SkipCData: { - if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"]]>", 3) == 0) { - m_Start += 3; - syntaxParserResult = FX_XmlSyntaxResult::CData; - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_syntaxParserState = FDE_XmlSyntaxState::Text; - } else { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) - return FX_XmlSyntaxResult::Error; - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - m_Start++; - } - break; - } - case FDE_XmlSyntaxState::SkipDeclNode: - if (m_SkipChar == L'\'' || m_SkipChar == L'\"') { - m_Start++; - if (ch != m_SkipChar) - break; - - m_SkipStack.pop(); - if (m_SkipStack.empty()) - m_syntaxParserState = FDE_XmlSyntaxState::Text; - else - m_SkipChar = m_SkipStack.top(); - } else { - switch (ch) { - case L'<': - m_SkipChar = L'>'; - m_SkipStack.push(L'>'); - break; - case L'[': - m_SkipChar = L']'; - m_SkipStack.push(L']'); - break; - case L'(': - m_SkipChar = L')'; - m_SkipStack.push(L')'); - break; - case L'\'': - m_SkipChar = L'\''; - m_SkipStack.push(L'\''); - break; - case L'\"': - m_SkipChar = L'\"'; - m_SkipStack.push(L'\"'); - break; - default: - if (ch == m_SkipChar) { - m_SkipStack.pop(); - if (m_SkipStack.empty()) { - if (m_BlockBuffer.GetDataLength() >= 9) - (void)m_BlockBuffer.GetTextData(0, 7); - - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_syntaxParserState = FDE_XmlSyntaxState::Text; - } else { - m_SkipChar = m_SkipStack.top(); - } - } - break; - } - if (!m_SkipStack.empty()) { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - } - m_Start++; - } - break; - case FDE_XmlSyntaxState::SkipComment: - if (FXSYS_wcsnicmp(m_Buffer.data() + m_Start, L"-->", 3) == 0) { - m_Start += 2; - m_syntaxParserState = FDE_XmlSyntaxState::Text; - } - - m_Start++; - break; - case FDE_XmlSyntaxState::TargetData: - if (IsXMLWhiteSpace(ch)) { - if (m_BlockBuffer.IsEmpty()) { - m_Start++; - break; - } - if (m_wQuotationMark == 0) { - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_wQuotationMark = 0; - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_Start++; - syntaxParserResult = FX_XmlSyntaxResult::TargetData; - break; - } - } - if (ch == '?') { - m_syntaxParserState = FDE_XmlSyntaxState::CloseInstruction; - m_Start++; - } else if (ch == '\"') { - if (m_wQuotationMark == 0) { - m_wQuotationMark = ch; - m_Start++; - } else if (ch == m_wQuotationMark) { - m_iTextDataLength = m_BlockBuffer.GetDataLength(); - m_wQuotationMark = 0; - m_BlockBuffer.Reset(true); - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_Start++; - syntaxParserResult = FX_XmlSyntaxResult::TargetData; - } else { - m_syntaxParserResult = FX_XmlSyntaxResult::Error; - return m_syntaxParserResult; - } - } else { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) { - return FX_XmlSyntaxResult::Error; - } - } - m_pCurrentBlock[m_iIndexInBlock++] = ch; - m_BlockBuffer.IncrementDataLength(); - m_Start++; - } - break; - default: - break; - } - if (syntaxParserResult != FX_XmlSyntaxResult::None) - return syntaxParserResult; - } - } - return FX_XmlSyntaxResult::Text; -} - -int32_t CFX_XMLSyntaxParser::GetStatus() const { - if (!m_pStream) - return -1; - - int32_t iStreamLength = m_pStream->GetLength(); - if (iStreamLength < 1) - return 100; - - if (m_syntaxParserResult == FX_XmlSyntaxResult::Error) - return -1; - - if (m_syntaxParserResult == FX_XmlSyntaxResult::EndOfString) - return 100; - return m_iParsedBytes * 100 / iStreamLength; -} - -FX_FILESIZE CFX_XMLSyntaxParser::GetCurrentBinaryPos() const { - if (!m_pStream) - return 0; - - int32_t nDstLen = GetUTF8EncodeLength(m_Buffer, m_Start); - return m_iParsedBytes + nDstLen; -} - -void CFX_XMLSyntaxParser::ParseTextChar(wchar_t character) { - if (m_iIndexInBlock == m_iAllocStep) { - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - if (!m_pCurrentBlock) - return; - } - - m_pCurrentBlock[m_iIndexInBlock++] = character; - m_BlockBuffer.IncrementDataLength(); - if (m_iEntityStart > -1 && character == L';') { - WideString csEntity = m_BlockBuffer.GetTextData( - m_iEntityStart + 1, - m_BlockBuffer.GetDataLength() - 1 - m_iEntityStart - 1); - int32_t iLen = csEntity.GetLength(); - if (iLen > 0) { - if (csEntity[0] == L'#') { - uint32_t ch = 0; - wchar_t w; - if (iLen > 1 && csEntity[1] == L'x') { - for (int32_t i = 2; i < iLen; i++) { - w = csEntity[i]; - if (std::iswdigit(w)) - ch = (ch << 4) + w - L'0'; - else if (w >= L'A' && w <= L'F') - ch = (ch << 4) + w - 55; - else if (w >= L'a' && w <= L'f') - ch = (ch << 4) + w - 87; - else - break; - } - } else { - for (int32_t i = 1; i < iLen; i++) { - w = csEntity[i]; - if (!std::iswdigit(w)) - break; - ch = ch * 10 + w - L'0'; - } - } - if (ch > kMaxCharRange) - ch = ' '; - - character = static_cast(ch); - if (character != 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, character); - m_iEntityStart++; - } - } else { - if (csEntity.Compare(L"amp") == 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, L'&'); - m_iEntityStart++; - } else if (csEntity.Compare(L"lt") == 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, L'<'); - m_iEntityStart++; - } else if (csEntity.Compare(L"gt") == 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, L'>'); - m_iEntityStart++; - } else if (csEntity.Compare(L"apos") == 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, L'\''); - m_iEntityStart++; - } else if (csEntity.Compare(L"quot") == 0) { - m_BlockBuffer.SetTextChar(m_iEntityStart, L'\"'); - m_iEntityStart++; - } - } - } - if (m_iEntityStart >= 0 && - m_BlockBuffer.GetDataLength() > static_cast(m_iEntityStart)) { - m_BlockBuffer.DeleteTextChars(m_BlockBuffer.GetDataLength() - - m_iEntityStart); - } - std::tie(m_pCurrentBlock, m_iIndexInBlock) = - m_BlockBuffer.GetAvailableBlock(); - m_iEntityStart = -1; - } else if (m_iEntityStart < 0 && character == L'&') { - m_iEntityStart = m_BlockBuffer.GetDataLength() - 1; - } - m_Start++; -} diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser.h b/core/fxcrt/xml/cfx_xmlsyntaxparser.h deleted file mode 100644 index b93bbb6801..0000000000 --- a/core/fxcrt/xml/cfx_xmlsyntaxparser.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2017 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com - -#ifndef CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_ -#define CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_ - -#include -#include - -#include "core/fxcrt/cfx_blockbuffer.h" -#include "core/fxcrt/cfx_seekablestreamproxy.h" -#include "core/fxcrt/fx_string.h" -#include "core/fxcrt/retain_ptr.h" -#include "core/fxcrt/xml/cfx_xmlnode.h" - -enum class FX_XmlSyntaxResult { - None, - InstructionOpen, - InstructionClose, - ElementOpen, - ElementBreak, - ElementClose, - TargetName, - TagName, - AttriName, - AttriValue, - Text, - CData, - TargetData, - Error, - EndOfString -}; - -class CFX_XMLSyntaxParser { - public: - static bool IsXMLNameChar(wchar_t ch, bool bFirstChar); - - explicit CFX_XMLSyntaxParser( - const RetainPtr& pStream); - ~CFX_XMLSyntaxParser(); - - FX_XmlSyntaxResult DoSyntaxParse(); - - int32_t GetStatus() const; - FX_FILESIZE GetCurrentPos() const { return m_ParsedChars + m_Start; } - FX_FILESIZE GetCurrentBinaryPos() const; - int32_t GetCurrentNodeNumber() const { return m_iCurrentNodeNum; } - int32_t GetLastNodeNumber() const { return m_iLastNodeNum; } - - WideString GetTargetName() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - WideString GetTagName() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - WideString GetAttributeName() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - WideString GetAttributeValue() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - WideString GetTextData() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - WideString GetTargetData() const { - return m_BlockBuffer.GetTextData(0, m_iTextDataLength); - } - - protected: - enum class FDE_XmlSyntaxState { - Text, - Node, - Target, - Tag, - AttriName, - AttriEqualSign, - AttriQuotation, - AttriValue, - Entity, - EntityDecimal, - EntityHex, - CloseInstruction, - BreakElement, - CloseElement, - SkipDeclNode, - DeclCharData, - SkipComment, - SkipCommentOrDecl, - SkipCData, - TargetData - }; - - void ParseTextChar(wchar_t ch); - - RetainPtr m_pStream; - size_t m_iXMLPlaneSize; - FX_FILESIZE m_iCurrentPos; - int32_t m_iCurrentNodeNum; - int32_t m_iLastNodeNum; - int32_t m_iParsedBytes; - FX_FILESIZE m_ParsedChars; - std::vector m_Buffer; - size_t m_iBufferChars; - bool m_bEOS; - FX_FILESIZE m_Start; // Start position in m_Buffer - FX_FILESIZE m_End; // End position in m_Buffer - FX_XMLNODE m_CurNode; - std::stack m_XMLNodeStack; - CFX_BlockBuffer m_BlockBuffer; - int32_t m_iAllocStep; - wchar_t* m_pCurrentBlock; // Pointer into CFX_BlockBuffer - int32_t m_iIndexInBlock; - int32_t m_iTextDataLength; - FX_XmlSyntaxResult m_syntaxParserResult; - FDE_XmlSyntaxState m_syntaxParserState; - wchar_t m_wQuotationMark; - int32_t m_iEntityStart; - std::stack m_SkipStack; - wchar_t m_SkipChar; -}; - -#endif // CORE_FXCRT_XML_CFX_XMLSYNTAXPARSER_H_ diff --git a/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp deleted file mode 100644 index b7f7c416b1..0000000000 --- a/core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2016 PDFium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "core/fxcrt/xml/cfx_xmlsyntaxparser.h" - -#include - -#include "core/fxcrt/cfx_seekablestreamproxy.h" -#include "core/fxcrt/fx_codepage.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/test_support.h" - -TEST(CFX_XMLSyntaxParserTest, CData) { - const char* input = - ""; - - const wchar_t* cdata = - L"\n" - L" if (a[1] < 3)\n" - L" app.alert(\"Tclams\");\n" - L" "; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); - ASSERT_EQ(cdata, parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, CDataWithInnerScript) { - const char* input = - "\n" - " ]]>\n" - ""; - - const wchar_t* cdata = - L"\n" - L" if (a[1] < 3)\n" - L" app.alert(\"Tclams\");\n" - L" \n" - L" "; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); - ASSERT_EQ(cdata, parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, ArrowBangArrow) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, ArrowBangBracketArrow) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - // Parser walks to end of input. - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, IncompleteCData) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - // Parser walks to end of input. - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, UnClosedCData) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - // Parser walks to end of input. - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, EmptyCData) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::CData, parser.DoSyntaxParse()); - ASSERT_EQ(L"", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, Comment) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, IncorrectCommentStart) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, CommentEmpty) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, CommentThreeDash) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, CommentTwoDash) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"\n ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, Entities) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L"BTH\xab48", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, EntityOverflowHex) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L" ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, EntityOverflowDecimal) { - const char* input = - ""; - - RetainPtr stream = - pdfium::MakeRetain( - reinterpret_cast(const_cast(input)), strlen(input)); - stream->SetCodePage(FX_CODEPAGE_UTF8); - - CFX_XMLSyntaxParser parser(stream); - ASSERT_EQ(FX_XmlSyntaxResult::ElementOpen, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::TagName, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::AttriName, parser.DoSyntaxParse()); - ASSERT_EQ(L"contentType", parser.GetAttributeName()); - ASSERT_EQ(FX_XmlSyntaxResult::AttriValue, parser.DoSyntaxParse()); - ASSERT_EQ(L"application/x-javascript", parser.GetAttributeValue()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementBreak, parser.DoSyntaxParse()); - ASSERT_EQ(FX_XmlSyntaxResult::Text, parser.DoSyntaxParse()); - ASSERT_EQ(L" ", parser.GetTextData()); - - ASSERT_EQ(FX_XmlSyntaxResult::ElementClose, parser.DoSyntaxParse()); - ASSERT_EQ(L"script", parser.GetTagName()); - - ASSERT_EQ(FX_XmlSyntaxResult::EndOfString, parser.DoSyntaxParse()); -} - -TEST(CFX_XMLSyntaxParserTest, IsXMLNameChar) { - EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(L'-', true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(L'-', false)); - - EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2069, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2070, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2073, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0x218F, true)); - EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0x2190, true)); - - EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDEF, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDF0, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFDF1, true)); - EXPECT_TRUE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFFFD, true)); - EXPECT_FALSE(CFX_XMLSyntaxParser::IsXMLNameChar(0xFFFE, true)); -} -- cgit v1.2.3