// 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/src/foxitlib.h" | |
#include "fde_csssyntax.h" | |
#include "fde_cssdatatable.h" | |
#ifdef _cplusplus | |
extern "C" { | |
#endif | |
inline FX_BOOL FDE_IsSelectorStart(FX_WCHAR wch) { | |
return wch == '.' || wch == '#' || wch == '*' || (wch >= 'a' && wch <= 'z') || | |
(wch >= 'A' && wch <= 'Z'); | |
} | |
#ifdef _cplusplus | |
}; | |
#endif | |
IFDE_CSSSyntaxParser* IFDE_CSSSyntaxParser::Create() { | |
return new CFDE_CSSSyntaxParser; | |
} | |
CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser() | |
: m_pStream(NULL), | |
m_iStreamPos(0), | |
m_iPlaneSize(0), | |
m_iTextDatLen(0), | |
m_dwCheck((FX_DWORD)-1), | |
m_eMode(FDE_CSSSYNTAXMODE_RuleSet), | |
m_eStatus(FDE_CSSSYNTAXSTATUS_None) {} | |
CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() { | |
m_TextData.Reset(); | |
m_TextPlane.Reset(); | |
} | |
FX_BOOL CFDE_CSSSyntaxParser::Init(IFX_Stream* pStream, | |
int32_t iCSSPlaneSize, | |
int32_t iTextDataSize, | |
FX_BOOL bOnlyDeclaration) { | |
FXSYS_assert(pStream != NULL && iCSSPlaneSize > 0 && iTextDataSize > 0); | |
Reset(bOnlyDeclaration); | |
if (!m_TextData.EstimateSize(iTextDataSize)) { | |
return FALSE; | |
} | |
uint8_t bom[4]; | |
m_pStream = pStream; | |
m_iStreamPos = m_pStream->GetBOM(bom); | |
m_iPlaneSize = iCSSPlaneSize; | |
return TRUE; | |
} | |
FX_BOOL CFDE_CSSSyntaxParser::Init(const FX_WCHAR* pBuffer, | |
int32_t iBufferSize, | |
int32_t iTextDatSize, | |
FX_BOOL bOnlyDeclaration) { | |
FXSYS_assert(pBuffer != NULL && iBufferSize > 0 && iTextDatSize > 0); | |
Reset(bOnlyDeclaration); | |
if (!m_TextData.EstimateSize(iTextDatSize)) { | |
return FALSE; | |
} | |
return m_TextPlane.AttachBuffer(pBuffer, iBufferSize); | |
} | |
void CFDE_CSSSyntaxParser::Reset(FX_BOOL bOnlyDeclaration) { | |
m_TextPlane.Reset(); | |
m_TextData.Reset(); | |
m_pStream = NULL; | |
m_iStreamPos = 0; | |
m_iTextDatLen = 0; | |
m_dwCheck = (FX_DWORD)-1; | |
m_eStatus = FDE_CSSSYNTAXSTATUS_None; | |
m_eMode = bOnlyDeclaration ? FDE_CSSSYNTAXMODE_PropertyName | |
: FDE_CSSSYNTAXMODE_RuleSet; | |
} | |
FDE_CSSSYNTAXSTATUS CFDE_CSSSyntaxParser::DoSyntaxParse() { | |
while (m_eStatus >= FDE_CSSSYNTAXSTATUS_None) { | |
if (m_TextPlane.IsEOF()) { | |
if (m_pStream == NULL) { | |
if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue && | |
m_TextData.GetLength() > 0) { | |
SaveTextData(); | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue; | |
} | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS; | |
} | |
FX_BOOL bEOS; | |
int32_t iLen = m_TextPlane.LoadFromStream(m_pStream, m_iStreamPos, | |
m_iPlaneSize, bEOS); | |
m_iStreamPos = m_pStream->GetPosition(); | |
if (iLen < 1) { | |
if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue && | |
m_TextData.GetLength() > 0) { | |
SaveTextData(); | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue; | |
} | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS; | |
} | |
} | |
FX_WCHAR wch; | |
while (!m_TextPlane.IsEOF()) { | |
wch = m_TextPlane.GetChar(); | |
switch (m_eMode) { | |
case FDE_CSSSYNTAXMODE_RuleSet: | |
switch (wch) { | |
case '@': | |
m_TextPlane.MoveNext(); | |
SwitchMode(FDE_CSSSYNTAXMODE_AtRule); | |
break; | |
case '}': | |
m_TextPlane.MoveNext(); | |
if (RestoreMode()) { | |
return FDE_CSSSYNTAXSTATUS_DeclClose; | |
} else { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
break; | |
case '/': | |
if (m_TextPlane.GetNextChar() == '*') { | |
m_ModeStack.Push(m_eMode); | |
SwitchMode(FDE_CSSSYNTAXMODE_Comment); | |
break; | |
} | |
default: | |
if (wch <= ' ') { | |
m_TextPlane.MoveNext(); | |
} else if (FDE_IsSelectorStart(wch)) { | |
SwitchMode(FDE_CSSSYNTAXMODE_Selector); | |
return FDE_CSSSYNTAXSTATUS_StyleRule; | |
} else { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
break; | |
} | |
break; | |
case FDE_CSSSYNTAXMODE_Selector: | |
switch (wch) { | |
case ',': | |
m_TextPlane.MoveNext(); | |
SwitchMode(FDE_CSSSYNTAXMODE_Selector); | |
if (m_iTextDatLen > 0) { | |
return FDE_CSSSYNTAXSTATUS_Selector; | |
} | |
break; | |
case '{': | |
if (m_TextData.GetLength() > 0) { | |
SaveTextData(); | |
return FDE_CSSSYNTAXSTATUS_Selector; | |
} else { | |
m_TextPlane.MoveNext(); | |
m_ModeStack.Push(FDE_CSSSYNTAXMODE_RuleSet); | |
SwitchMode(FDE_CSSSYNTAXMODE_PropertyName); | |
return FDE_CSSSYNTAXSTATUS_DeclOpen; | |
} | |
break; | |
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; | |
} else { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
break; | |
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.GetAt(m_TextData.GetLength() - 1) == '*') { | |
RestoreMode(); | |
} else { | |
m_TextData.AppendChar(wch); | |
} | |
m_TextPlane.MoveNext(); | |
break; | |
case FDE_CSSSYNTAXMODE_MediaType: | |
switch (wch) { | |
case ',': | |
m_TextPlane.MoveNext(); | |
SwitchMode(FDE_CSSSYNTAXMODE_MediaType); | |
if (m_iTextDatLen > 0) { | |
return FDE_CSSSYNTAXSTATUS_MediaType; | |
} | |
break; | |
case '{': { | |
FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement(); | |
if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_MediaRule) { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
if (m_TextData.GetLength() > 0) { | |
SaveTextData(); | |
return FDE_CSSSYNTAXSTATUS_MediaType; | |
} else { | |
m_TextPlane.MoveNext(); | |
*pMode = FDE_CSSSYNTAXMODE_RuleSet; | |
SwitchMode(FDE_CSSSYNTAXMODE_RuleSet); | |
return FDE_CSSSYNTAXSTATUS_DeclOpen; | |
} | |
} break; | |
case ';': { | |
FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement(); | |
if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_Import) { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
if (m_TextData.GetLength() > 0) { | |
SaveTextData(); | |
if (IsImportEnabled()) { | |
return FDE_CSSSYNTAXSTATUS_MediaType; | |
} | |
} else { | |
FX_BOOL bEnabled = IsImportEnabled(); | |
m_TextPlane.MoveNext(); | |
m_ModeStack.Pop(); | |
SwitchMode(FDE_CSSSYNTAXMODE_RuleSet); | |
if (bEnabled) { | |
DisableImport(); | |
return FDE_CSSSYNTAXSTATUS_ImportClose; | |
} | |
} | |
} break; | |
case '/': | |
if (m_TextPlane.GetNextChar() == '*') { | |
if (SwitchToComment() > 0) { | |
return FDE_CSSSYNTAXSTATUS_MediaType; | |
} | |
break; | |
} | |
default: | |
AppendChar(wch); | |
break; | |
} | |
break; | |
case FDE_CSSSYNTAXMODE_URI: { | |
FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement(); | |
if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_Import) { | |
return m_eStatus = FDE_CSSSYNTAXSTATUS_Error; | |
} | |
if (wch <= ' ' || wch == ';') { | |
int32_t iURIStart, iURILength = m_TextData.GetLength(); | |
if (iURILength > 0 && | |
FDE_ParseCSSURI(m_TextData.GetBuffer(), iURILength, iURIStart, | |
iURILength)) { | |
m_TextData.Subtract(iURIStart, iURILength); | |
SwitchMode(FDE_CSSSYNTAXMODE_MediaType); | |
if (IsImportEnabled()) { | |
return FDE_CSSSYNTAXSTATUS_URI; | |
} else { | |
break; | |
} | |
} | |
} | |
AppendChar(wch); | |
} break; | |
case FDE_CSSSYNTAXMODE_AtRule: | |
if (wch > ' ') { | |
AppendChar(wch); | |
} else { | |
int32_t iLen = m_TextData.GetLength(); | |
const FX_WCHAR* psz = m_TextData.GetBuffer(); | |
if (FXSYS_wcsncmp(L"charset", psz, iLen) == 0) { | |
SwitchMode(FDE_CSSSYNTAXMODE_Charset); | |
} else if (FXSYS_wcsncmp(L"import", psz, iLen) == 0) { | |
m_ModeStack.Push(FDE_CSSSYNTAXMODE_Import); | |
SwitchMode(FDE_CSSSYNTAXMODE_URI); | |
if (IsImportEnabled()) { | |
return FDE_CSSSYNTAXSTATUS_ImportRule; | |
} else { | |
break; | |
} | |
} else if (FXSYS_wcsncmp(L"media", psz, iLen) == 0) { | |
m_ModeStack.Push(FDE_CSSSYNTAXMODE_MediaRule); | |
SwitchMode(FDE_CSSSYNTAXMODE_MediaType); | |
return FDE_CSSSYNTAXSTATUS_MediaRule; | |
} else if (FXSYS_wcsncmp(L"font-face", psz, iLen) == 0) { | |
SwitchMode(FDE_CSSSYNTAXMODE_Selector); | |
return FDE_CSSSYNTAXSTATUS_FontFaceRule; | |
} else if (FXSYS_wcsncmp(L"page", psz, iLen) == 0) { | |
SwitchMode(FDE_CSSSYNTAXMODE_Selector); | |
return FDE_CSSSYNTAXSTATUS_PageRule; | |
} else { | |
SwitchMode(FDE_CSSSYNTAXMODE_UnknownRule); | |
} | |
} | |
break; | |
case FDE_CSSSYNTAXMODE_Charset: | |
if (wch == ';') { | |
m_TextPlane.MoveNext(); | |
SwitchMode(FDE_CSSSYNTAXMODE_RuleSet); | |
if (IsCharsetEnabled()) { | |
DisableCharset(); | |
if (m_iTextDatLen > 0) { | |
if (m_pStream != NULL) { | |
FX_WORD wCodePage = FX_GetCodePageFormStringW( | |
m_TextData.GetBuffer(), m_iTextDatLen); | |
if (wCodePage < 0xFFFF && | |
m_pStream->GetCodePage() != wCodePage) { | |
m_pStream->SetCodePage(wCodePage); | |
} | |
} | |
return FDE_CSSSYNTAXSTATUS_Charset; | |
} | |
} | |
} else { | |
AppendChar(wch); | |
} | |
break; | |
case FDE_CSSSYNTAXMODE_UnknownRule: | |
if (wch == ';') { | |
SwitchMode(FDE_CSSSYNTAXMODE_RuleSet); | |
} | |
m_TextPlane.MoveNext(); | |
break; | |
default: | |
FXSYS_assert(FALSE); | |
break; | |
} | |
} | |
} | |
return m_eStatus; | |
} | |
FX_BOOL CFDE_CSSSyntaxParser::IsImportEnabled() const { | |
if ((m_dwCheck & FDE_CSSSYNTAXCHECK_AllowImport) == 0) { | |
return FALSE; | |
} | |
if (m_ModeStack.GetSize() > 1) { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
inline FX_BOOL CFDE_CSSSyntaxParser::AppendChar(FX_WCHAR wch) { | |
m_TextPlane.MoveNext(); | |
if (m_TextData.GetLength() > 0 || wch > ' ') { | |
m_TextData.AppendChar(wch); | |
return TRUE; | |
} | |
return FALSE; | |
} | |
inline int32_t CFDE_CSSSyntaxParser::SaveTextData() { | |
m_iTextDatLen = m_TextData.TrimEnd(); | |
m_TextData.Clear(); | |
return m_iTextDatLen; | |
} | |
inline void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) { | |
m_eMode = eMode; | |
SaveTextData(); | |
} | |
inline int32_t CFDE_CSSSyntaxParser::SwitchToComment() { | |
int32_t iLength = m_TextData.GetLength(); | |
m_ModeStack.Push(m_eMode); | |
SwitchMode(FDE_CSSSYNTAXMODE_Comment); | |
return iLength; | |
} | |
inline FX_BOOL CFDE_CSSSyntaxParser::RestoreMode() { | |
FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement(); | |
if (pMode == NULL) { | |
return FALSE; | |
} | |
SwitchMode(*pMode); | |
m_ModeStack.Pop(); | |
return TRUE; | |
} | |
const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const { | |
iLength = m_iTextDatLen; | |
return m_TextData.GetBuffer(); | |
} | |
CFDE_CSSTextBuf::CFDE_CSSTextBuf() | |
: m_bExtBuf(FALSE), | |
m_pBuffer(NULL), | |
m_iBufLen(0), | |
m_iDatLen(0), | |
m_iDatPos(0) {} | |
CFDE_CSSTextBuf::~CFDE_CSSTextBuf() { | |
Reset(); | |
} | |
void CFDE_CSSTextBuf::Reset() { | |
if (!m_bExtBuf) { | |
FX_Free(m_pBuffer); | |
m_pBuffer = NULL; | |
} | |
m_iDatPos = m_iDatLen = m_iBufLen; | |
} | |
FX_BOOL CFDE_CSSTextBuf::AttachBuffer(const FX_WCHAR* pBuffer, | |
int32_t iBufLen) { | |
Reset(); | |
m_pBuffer = (FX_WCHAR*)pBuffer; | |
m_iDatLen = m_iBufLen = iBufLen; | |
return m_bExtBuf = TRUE; | |
} | |
FX_BOOL CFDE_CSSTextBuf::EstimateSize(int32_t iAllocSize) { | |
FXSYS_assert(iAllocSize > 0); | |
Clear(); | |
m_bExtBuf = FALSE; | |
return ExpandBuf(iAllocSize); | |
} | |
int32_t CFDE_CSSTextBuf::LoadFromStream(IFX_Stream* pTxtStream, | |
int32_t iStreamOffset, | |
int32_t iMaxChars, | |
FX_BOOL& bEOS) { | |
FXSYS_assert(iStreamOffset >= 0 && iMaxChars > 0); | |
Clear(); | |
m_bExtBuf = FALSE; | |
if (!ExpandBuf(iMaxChars)) { | |
return 0; | |
} | |
pTxtStream->Lock(); | |
if (pTxtStream->GetPosition() != iStreamOffset) { | |
pTxtStream->Seek(FX_STREAMSEEK_Begin, iStreamOffset); | |
} | |
m_iDatLen = pTxtStream->ReadString(m_pBuffer, iMaxChars, bEOS); | |
pTxtStream->Unlock(); | |
return m_iDatLen; | |
} | |
FX_BOOL CFDE_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) { | |
if (m_bExtBuf) { | |
return FALSE; | |
} | |
if (!m_pBuffer) { | |
m_pBuffer = FX_Alloc(FX_WCHAR, iDesiredSize); | |
} else if (m_iBufLen != iDesiredSize) { | |
m_pBuffer = FX_Realloc(FX_WCHAR, m_pBuffer, iDesiredSize); | |
} else { | |
return TRUE; | |
} | |
if (!m_pBuffer) { | |
m_iBufLen = 0; | |
return FALSE; | |
} | |
m_iBufLen = iDesiredSize; | |
return TRUE; | |
} | |
void CFDE_CSSTextBuf::Subtract(int32_t iStart, int32_t iLength) { | |
FXSYS_assert(iStart >= 0 && iLength > 0); | |
if (iLength > m_iDatLen - iStart) { | |
iLength = m_iDatLen - iStart; | |
} | |
if (iLength < 0) { | |
iLength = 0; | |
} else { | |
FXSYS_memmove(m_pBuffer, m_pBuffer + iStart, iLength * sizeof(FX_WCHAR)); | |
} | |
m_iDatLen = iLength; | |
} |