| // 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/fde_csssyntax.h" |
| |
| #include "xfa/fde/css/fde_cssdatatable.h" |
| #include "xfa/fgas/crt/fgas_codepage.h" |
| |
| namespace { |
| |
| bool FDE_IsSelectorStart(FX_WCHAR wch) { |
| return wch == '.' || wch == '#' || wch == '*' || (wch >= 'a' && wch <= 'z') || |
| (wch >= 'A' && wch <= 'Z'); |
| } |
| |
| } // namespace |
| |
| CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser() |
| : m_pStream(nullptr), |
| m_iStreamPos(0), |
| m_iPlaneSize(0), |
| m_iTextDatLen(0), |
| m_dwCheck((uint32_t)-1), |
| m_eMode(FDE_CSSSYNTAXMODE_RuleSet), |
| m_eStatus(FDE_CSSSYNTAXSTATUS_None), |
| m_ModeStack(100) {} |
| |
| 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) { |
| ASSERT(pStream && 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) { |
| ASSERT(pBuffer && 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 = nullptr; |
| m_iStreamPos = 0; |
| m_iTextDatLen = 0; |
| m_dwCheck = (uint32_t)-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) { |
| 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 || *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 || *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 || *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) { |
| uint16_t wCodePage = FX_GetCodePageFromStringW( |
| 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: |
| 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; |
| } |
| 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; |
| } |
| int32_t CFDE_CSSSyntaxParser::SaveTextData() { |
| m_iTextDatLen = m_TextData.TrimEnd(); |
| m_TextData.Clear(); |
| return m_iTextDatLen; |
| } |
| 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; |
| } |
| FX_BOOL CFDE_CSSSyntaxParser::RestoreMode() { |
| FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement(); |
| if (!pMode) |
| 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(nullptr), |
| 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 = nullptr; |
| } |
| m_iDatPos = m_iDatLen = m_iBufLen; |
| } |
| FX_BOOL CFDE_CSSTextBuf::AttachBuffer(const FX_WCHAR* pBuffer, |
| int32_t iBufLen) { |
| Reset(); |
| m_pBuffer = const_cast<FX_WCHAR*>(pBuffer); |
| m_iDatLen = m_iBufLen = iBufLen; |
| return m_bExtBuf = TRUE; |
| } |
| FX_BOOL CFDE_CSSTextBuf::EstimateSize(int32_t iAllocSize) { |
| 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) { |
| ASSERT(iStreamOffset >= 0 && iMaxChars > 0); |
| Clear(); |
| m_bExtBuf = FALSE; |
| if (!ExpandBuf(iMaxChars)) { |
| return 0; |
| } |
| if (pTxtStream->GetPosition() != iStreamOffset) { |
| pTxtStream->Seek(FX_STREAMSEEK_Begin, iStreamOffset); |
| } |
| m_iDatLen = pTxtStream->ReadString(m_pBuffer, iMaxChars, bEOS); |
| 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) { |
| 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; |
| } |