blob: 30352a5a2043aa2da4a066add098b16a4768494d [file] [log] [blame]
// 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 <algorithm>
#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);
}