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