| // Copyright 2014 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 "xfa/fde/css/cfde_csssyntaxparser.h" |
| |
| #include <algorithm> |
| |
| #include "core/fxcrt/fx_codepage.h" |
| #include "core/fxcrt/fx_extension.h" |
| #include "third_party/base/logging.h" |
| #include "xfa/fde/css/cfde_cssdeclaration.h" |
| #include "xfa/fde/css/fde_cssdatatable.h" |
| |
| namespace { |
| |
| bool IsSelectorStart(wchar_t wch) { |
| return wch == '.' || wch == '#' || wch == '*' || FXSYS_iswalpha(wch); |
| } |
| |
| } // namespace |
| |
| CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser(const wchar_t* pBuffer, |
| int32_t iBufferSize) |
| : CFDE_CSSSyntaxParser(pBuffer, iBufferSize, 32, false) {} |
| |
| CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser(const wchar_t* pBuffer, |
| int32_t iBufferSize, |
| int32_t iTextDatSize, |
| bool bOnlyDeclaration) |
| : m_iTextDataLen(0), |
| m_dwCheck(0xFFFFFFFF), |
| m_eStatus(FDE_CSSSyntaxStatus::None) { |
| ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0); |
| m_eMode = bOnlyDeclaration ? FDE_CSSSyntaxMode::PropertyName |
| : FDE_CSSSyntaxMode::RuleSet; |
| m_TextData.InitWithSize(iTextDatSize); |
| m_TextPlane.AttachBuffer(pBuffer, iBufferSize); |
| } |
| |
| CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() {} |
| |
| FDE_CSSSyntaxStatus CFDE_CSSSyntaxParser::DoSyntaxParse() { |
| while (m_eStatus >= FDE_CSSSyntaxStatus::None) { |
| if (m_TextPlane.IsEOF()) { |
| if (m_eMode == FDE_CSSSyntaxMode::PropertyValue && |
| m_TextData.GetLength() > 0) { |
| SaveTextData(); |
| m_eStatus = FDE_CSSSyntaxStatus::PropertyValue; |
| return m_eStatus; |
| } |
| m_eStatus = FDE_CSSSyntaxStatus::EOS; |
| return m_eStatus; |
| } |
| wchar_t wch; |
| while (!m_TextPlane.IsEOF()) { |
| wch = m_TextPlane.GetChar(); |
| switch (m_eMode) { |
| case FDE_CSSSyntaxMode::RuleSet: |
| switch (wch) { |
| case '}': |
| m_TextPlane.MoveNext(); |
| if (RestoreMode()) |
| return FDE_CSSSyntaxStatus::DeclClose; |
| |
| m_eStatus = FDE_CSSSyntaxStatus::Error; |
| return m_eStatus; |
| case '/': |
| if (m_TextPlane.GetNextChar() == '*') { |
| m_ModeStack.push(m_eMode); |
| SwitchMode(FDE_CSSSyntaxMode::Comment); |
| break; |
| } |
| default: |
| if (wch <= ' ') { |
| m_TextPlane.MoveNext(); |
| } else if (IsSelectorStart(wch)) { |
| SwitchMode(FDE_CSSSyntaxMode::Selector); |
| return FDE_CSSSyntaxStatus::StyleRule; |
| } else { |
| m_eStatus = FDE_CSSSyntaxStatus::Error; |
| return m_eStatus; |
| } |
| break; |
| } |
| break; |
| case FDE_CSSSyntaxMode::Selector: |
| switch (wch) { |
| case ',': |
| m_TextPlane.MoveNext(); |
| SwitchMode(FDE_CSSSyntaxMode::Selector); |
| if (m_iTextDataLen > 0) |
| return FDE_CSSSyntaxStatus::Selector; |
| break; |
| case '{': |
| if (m_TextData.GetLength() > 0) { |
| SaveTextData(); |
| return FDE_CSSSyntaxStatus::Selector; |
| } |
| m_TextPlane.MoveNext(); |
| m_ModeStack.push(FDE_CSSSyntaxMode::RuleSet); |
| SwitchMode(FDE_CSSSyntaxMode::PropertyName); |
| return FDE_CSSSyntaxStatus::DeclOpen; |
| case '/': |
| if (m_TextPlane.GetNextChar() == '*') { |
| if (SwitchToComment() > 0) |
| return FDE_CSSSyntaxStatus::Selector; |
| break; |
| } |
| default: |
| AppendChar(wch); |
| break; |
| } |
| break; |
| case FDE_CSSSyntaxMode::PropertyName: |
| switch (wch) { |
| case ':': |
| m_TextPlane.MoveNext(); |
| SwitchMode(FDE_CSSSyntaxMode::PropertyValue); |
| return FDE_CSSSyntaxStatus::PropertyName; |
| case '}': |
| m_TextPlane.MoveNext(); |
| if (RestoreMode()) |
| return FDE_CSSSyntaxStatus::DeclClose; |
| |
| m_eStatus = FDE_CSSSyntaxStatus::Error; |
| return m_eStatus; |
| case '/': |
| if (m_TextPlane.GetNextChar() == '*') { |
| if (SwitchToComment() > 0) |
| return FDE_CSSSyntaxStatus::PropertyName; |
| break; |
| } |
| default: |
| AppendChar(wch); |
| break; |
| } |
| break; |
| case FDE_CSSSyntaxMode::PropertyValue: |
| switch (wch) { |
| case ';': |
| m_TextPlane.MoveNext(); |
| case '}': |
| SwitchMode(FDE_CSSSyntaxMode::PropertyName); |
| return FDE_CSSSyntaxStatus::PropertyValue; |
| case '/': |
| if (m_TextPlane.GetNextChar() == '*') { |
| if (SwitchToComment() > 0) |
| return FDE_CSSSyntaxStatus::PropertyValue; |
| break; |
| } |
| default: |
| AppendChar(wch); |
| break; |
| } |
| break; |
| case FDE_CSSSyntaxMode::Comment: |
| if (wch == '/' && m_TextData.GetLength() > 0 && |
| m_TextData.GetBuffer()[m_TextData.GetLength() - 1] == '*') { |
| RestoreMode(); |
| } else { |
| m_TextData.AppendChar(wch); |
| } |
| m_TextPlane.MoveNext(); |
| break; |
| case FDE_CSSSyntaxMode::UnknownRule: |
| if (wch == ';') |
| SwitchMode(FDE_CSSSyntaxMode::RuleSet); |
| m_TextPlane.MoveNext(); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| } |
| return m_eStatus; |
| } |
| |
| bool CFDE_CSSSyntaxParser::IsImportEnabled() const { |
| if ((m_dwCheck & FDE_CSSSYNTAXCHECK_AllowImport) == 0) |
| return false; |
| if (m_ModeStack.size() > 1) |
| return false; |
| return true; |
| } |
| |
| bool CFDE_CSSSyntaxParser::AppendChar(wchar_t wch) { |
| m_TextPlane.MoveNext(); |
| if (m_TextData.GetLength() > 0 || wch > ' ') { |
| m_TextData.AppendChar(wch); |
| return true; |
| } |
| return false; |
| } |
| |
| int32_t CFDE_CSSSyntaxParser::SaveTextData() { |
| m_iTextDataLen = m_TextData.TrimEnd(); |
| m_TextData.Clear(); |
| return m_iTextDataLen; |
| } |
| |
| void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSyntaxMode eMode) { |
| m_eMode = eMode; |
| SaveTextData(); |
| } |
| |
| int32_t CFDE_CSSSyntaxParser::SwitchToComment() { |
| int32_t iLength = m_TextData.GetLength(); |
| m_ModeStack.push(m_eMode); |
| SwitchMode(FDE_CSSSyntaxMode::Comment); |
| return iLength; |
| } |
| |
| bool CFDE_CSSSyntaxParser::RestoreMode() { |
| if (m_ModeStack.empty()) |
| return false; |
| |
| SwitchMode(m_ModeStack.top()); |
| m_ModeStack.pop(); |
| return true; |
| } |
| |
| CFX_WideStringC CFDE_CSSSyntaxParser::GetCurrentString() const { |
| return CFX_WideStringC(m_TextData.GetBuffer(), m_iTextDataLen); |
| } |