// 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_cssstylesheet.h" #include #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" #include "xfa/fde/css/fde_cssdatatable.h" #include "xfa/fde/css/fde_csssyntax.h" #include "xfa/fgas/crt/fgas_codepage.h" namespace { bool IsCSSChar(FX_WCHAR wch) { return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z'); } int32_t GetCSSPseudoLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) { ASSERT(*psz == ':'); const FX_WCHAR* pStart = psz; while (psz < pEnd) { FX_WCHAR wch = *psz; if (IsCSSChar(wch) || wch == ':') ++psz; else break; } return psz - pStart; } int32_t GetCSSNameLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) { const FX_WCHAR* pStart = psz; while (psz < pEnd) { FX_WCHAR wch = *psz; if (IsCSSChar(wch) || (wch >= '0' && wch <= '9') || wch == '_' || wch == '-') { ++psz; } else { break; } } return psz - pStart; } } // namespace CFDE_CSSStyleSheet::CFDE_CSSStyleSheet() : m_wCodePage(FX_CODEPAGE_UTF8), m_wRefCount(1), m_dwMediaList(FDE_CSSMEDIATYPE_ALL) { ASSERT(m_dwMediaList > 0); } CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet() { Reset(); } void CFDE_CSSStyleSheet::Reset() { m_RuleArray.clear(); m_StringCache.clear(); } uint32_t CFDE_CSSStyleSheet::Retain() { return ++m_wRefCount; } uint32_t CFDE_CSSStyleSheet::Release() { uint32_t dwRefCount = --m_wRefCount; if (dwRefCount == 0) delete this; return dwRefCount; } bool CFDE_CSSStyleSheet::GetUrl(CFX_WideString& szUrl) { szUrl = m_szUrl; return szUrl.GetLength() > 0; } uint32_t CFDE_CSSStyleSheet::GetMediaList() const { return m_dwMediaList; } uint16_t CFDE_CSSStyleSheet::GetCodePage() const { return m_wCodePage; } int32_t CFDE_CSSStyleSheet::CountRules() const { return pdfium::CollectionSize(m_RuleArray); } CFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(int32_t index) { return m_RuleArray[index].get(); } bool CFDE_CSSStyleSheet::LoadFromBuffer(const FX_WCHAR* pBuffer, int32_t iBufSize) { ASSERT(pBuffer && iBufSize > 0); m_wCodePage = FX_CODEPAGE_UTF8; m_szUrl = CFX_WideString(); auto pSyntax = pdfium::MakeUnique(); return pSyntax->Init(pBuffer, iBufSize) && LoadFromSyntax(pSyntax.get()); } bool CFDE_CSSStyleSheet::LoadFromSyntax(CFDE_CSSSyntaxParser* pSyntax) { Reset(); FDE_CSSSyntaxStatus eStatus; do { switch (eStatus = pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::StyleRule: eStatus = LoadStyleRule(pSyntax, &m_RuleArray); break; case FDE_CSSSyntaxStatus::MediaRule: eStatus = LoadMediaRule(pSyntax); break; case FDE_CSSSyntaxStatus::FontFaceRule: eStatus = LoadFontFaceRule(pSyntax, &m_RuleArray); break; case FDE_CSSSyntaxStatus::ImportRule: eStatus = LoadImportRule(pSyntax); break; case FDE_CSSSyntaxStatus::PageRule: eStatus = LoadPageRule(pSyntax); break; default: break; } } while (eStatus >= FDE_CSSSyntaxStatus::None); m_StringCache.clear(); return eStatus != FDE_CSSSyntaxStatus::Error; } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::LoadMediaRule( CFDE_CSSSyntaxParser* pSyntax) { uint32_t dwMediaList = 0; CFDE_CSSMediaRule* pMediaRule = nullptr; for (;;) { switch (pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::MediaType: { int32_t iLen; const FX_WCHAR* psz = pSyntax->GetCurrentString(iLen); const FDE_CSSMEDIATYPETABLE* pMediaType = FDE_GetCSSMediaTypeByName(CFX_WideStringC(psz, iLen)); if (pMediaType) dwMediaList |= pMediaType->wValue; } break; case FDE_CSSSyntaxStatus::StyleRule: if (pMediaRule) { FDE_CSSSyntaxStatus eStatus = LoadStyleRule(pSyntax, &pMediaRule->GetArray()); if (eStatus < FDE_CSSSyntaxStatus::None) { return eStatus; } } else { SkipRuleSet(pSyntax); } break; case FDE_CSSSyntaxStatus::DeclOpen: if ((dwMediaList & m_dwMediaList) > 0 && !pMediaRule) { m_RuleArray.push_back( pdfium::MakeUnique(dwMediaList)); pMediaRule = static_cast(m_RuleArray.back().get()); } break; case FDE_CSSSyntaxStatus::DeclClose: return FDE_CSSSyntaxStatus::None; case FDE_CSSSyntaxStatus::EOS: return FDE_CSSSyntaxStatus::EOS; case FDE_CSSSyntaxStatus::Error: default: return FDE_CSSSyntaxStatus::Error; } } } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::LoadStyleRule( CFDE_CSSSyntaxParser* pSyntax, std::vector>* ruleArray) { std::vector> selectors; CFDE_CSSStyleRule* pStyleRule = nullptr; const FX_WCHAR* pszValue = nullptr; int32_t iValueLen = 0; FDE_CSSPropertyArgs propertyArgs; propertyArgs.pStringCache = &m_StringCache; propertyArgs.pProperty = nullptr; CFX_WideString wsName; for (;;) { switch (pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::Selector: { pszValue = pSyntax->GetCurrentString(iValueLen); auto pSelector = CFDE_CSSSelector::FromString(pszValue, iValueLen); if (pSelector) selectors.push_back(std::move(pSelector)); break; } case FDE_CSSSyntaxStatus::PropertyName: pszValue = pSyntax->GetCurrentString(iValueLen); propertyArgs.pProperty = FDE_GetCSSPropertyByName(CFX_WideStringC(pszValue, iValueLen)); if (!propertyArgs.pProperty) wsName = CFX_WideStringC(pszValue, iValueLen); break; case FDE_CSSSyntaxStatus::PropertyValue: if (propertyArgs.pProperty) { pszValue = pSyntax->GetCurrentString(iValueLen); if (iValueLen > 0) { pStyleRule->GetDeclaration()->AddProperty(&propertyArgs, pszValue, iValueLen); } } else if (iValueLen > 0) { pszValue = pSyntax->GetCurrentString(iValueLen); if (iValueLen > 0) { pStyleRule->GetDeclaration()->AddProperty( &propertyArgs, wsName.c_str(), wsName.GetLength(), pszValue, iValueLen); } } break; case FDE_CSSSyntaxStatus::DeclOpen: if (!pStyleRule && !selectors.empty()) { auto rule = pdfium::MakeUnique(); pStyleRule = rule.get(); pStyleRule->SetSelector(&selectors); ruleArray->push_back(std::move(rule)); } else { SkipRuleSet(pSyntax); return FDE_CSSSyntaxStatus::None; } break; case FDE_CSSSyntaxStatus::DeclClose: if (pStyleRule && pStyleRule->GetDeclaration()->empty()) { ruleArray->pop_back(); pStyleRule = nullptr; } return FDE_CSSSyntaxStatus::None; case FDE_CSSSyntaxStatus::EOS: return FDE_CSSSyntaxStatus::EOS; case FDE_CSSSyntaxStatus::Error: default: return FDE_CSSSyntaxStatus::Error; } } } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::LoadFontFaceRule( CFDE_CSSSyntaxParser* pSyntax, std::vector>* ruleArray) { CFDE_CSSFontFaceRule* pFontFaceRule = nullptr; const FX_WCHAR* pszValue = nullptr; int32_t iValueLen = 0; FDE_CSSPropertyArgs propertyArgs; propertyArgs.pStringCache = &m_StringCache; propertyArgs.pProperty = nullptr; for (;;) { switch (pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::PropertyName: pszValue = pSyntax->GetCurrentString(iValueLen); propertyArgs.pProperty = FDE_GetCSSPropertyByName(CFX_WideStringC(pszValue, iValueLen)); break; case FDE_CSSSyntaxStatus::PropertyValue: if (propertyArgs.pProperty) { pszValue = pSyntax->GetCurrentString(iValueLen); if (iValueLen > 0) { pFontFaceRule->GetDeclaration()->AddProperty(&propertyArgs, pszValue, iValueLen); } } break; case FDE_CSSSyntaxStatus::DeclOpen: if (!pFontFaceRule) { auto rule = pdfium::MakeUnique(); pFontFaceRule = rule.get(); ruleArray->push_back(std::move(rule)); } break; case FDE_CSSSyntaxStatus::DeclClose: return FDE_CSSSyntaxStatus::None; case FDE_CSSSyntaxStatus::EOS: return FDE_CSSSyntaxStatus::EOS; case FDE_CSSSyntaxStatus::Error: default: return FDE_CSSSyntaxStatus::Error; } } } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::LoadImportRule( CFDE_CSSSyntaxParser* pSyntax) { for (;;) { switch (pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::ImportClose: return FDE_CSSSyntaxStatus::None; case FDE_CSSSyntaxStatus::URI: break; case FDE_CSSSyntaxStatus::EOS: return FDE_CSSSyntaxStatus::EOS; case FDE_CSSSyntaxStatus::Error: default: return FDE_CSSSyntaxStatus::Error; } } } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::LoadPageRule( CFDE_CSSSyntaxParser* pSyntax) { return SkipRuleSet(pSyntax); } FDE_CSSSyntaxStatus CFDE_CSSStyleSheet::SkipRuleSet( CFDE_CSSSyntaxParser* pSyntax) { for (;;) { switch (pSyntax->DoSyntaxParse()) { case FDE_CSSSyntaxStatus::Selector: case FDE_CSSSyntaxStatus::DeclOpen: case FDE_CSSSyntaxStatus::PropertyName: case FDE_CSSSyntaxStatus::PropertyValue: break; case FDE_CSSSyntaxStatus::DeclClose: return FDE_CSSSyntaxStatus::None; case FDE_CSSSyntaxStatus::EOS: return FDE_CSSSyntaxStatus::EOS; case FDE_CSSSyntaxStatus::Error: default: return FDE_CSSSyntaxStatus::Error; } } } CFDE_CSSStyleRule::CFDE_CSSStyleRule() : CFDE_CSSRule(FDE_CSSRuleType::Style) {} CFDE_CSSStyleRule::~CFDE_CSSStyleRule() {} size_t CFDE_CSSStyleRule::CountSelectorLists() const { return m_ppSelector.size(); } CFDE_CSSSelector* CFDE_CSSStyleRule::GetSelectorList(int32_t index) const { return m_ppSelector[index].get(); } CFDE_CSSDeclaration* CFDE_CSSStyleRule::GetDeclaration() { return &m_Declaration; } void CFDE_CSSStyleRule::SetSelector( std::vector>* list) { ASSERT(m_ppSelector.empty()); m_ppSelector.swap(*list); } CFDE_CSSMediaRule::CFDE_CSSMediaRule(uint32_t dwMediaList) : CFDE_CSSRule(FDE_CSSRuleType::Media), m_dwMediaList(dwMediaList) {} CFDE_CSSMediaRule::~CFDE_CSSMediaRule() {} uint32_t CFDE_CSSMediaRule::GetMediaList() const { return m_dwMediaList; } int32_t CFDE_CSSMediaRule::CountRules() const { return pdfium::CollectionSize(m_RuleArray); } CFDE_CSSRule* CFDE_CSSMediaRule::GetRule(int32_t index) { return m_RuleArray[index].get(); } CFDE_CSSSelector::CFDE_CSSSelector(FDE_CSSSelectorType eType, const FX_WCHAR* psz, int32_t iLen, bool bIgnoreCase) : m_eType(eType), m_dwHash(FX_HashCode_GetW(CFX_WideStringC(psz, iLen), bIgnoreCase)) {} CFDE_CSSSelector::~CFDE_CSSSelector() {} FDE_CSSSelectorType CFDE_CSSSelector::GetType() const { return m_eType; } uint32_t CFDE_CSSSelector::GetNameHash() const { return m_dwHash; } CFDE_CSSSelector* CFDE_CSSSelector::GetNextSelector() const { return m_pNext.get(); } std::unique_ptr CFDE_CSSSelector::ReleaseNextSelector() { return std::move(m_pNext); } std::unique_ptr CFDE_CSSSelector::FromString( const FX_WCHAR* psz, int32_t iLen) { ASSERT(psz && iLen > 0); const FX_WCHAR* pStart = psz; const FX_WCHAR* pEnd = psz + iLen; for (; psz < pEnd; ++psz) { switch (*psz) { case '>': case '[': case '+': return nullptr; } } std::unique_ptr pFirst = nullptr; CFDE_CSSSelector* pLast = nullptr; std::unique_ptr pPseudoFirst = nullptr; CFDE_CSSSelector* pPseudoLast = nullptr; for (psz = pStart; psz < pEnd;) { FX_WCHAR wch = *psz; if (wch == '.' || wch == '#') { if (psz == pStart || psz[-1] == ' ') { auto p = pdfium::MakeUnique( FDE_CSSSelectorType::Element, L"*", 1, true); if (pFirst) { pFirst->SetType(FDE_CSSSelectorType::Descendant); p->SetNext(std::move(pFirst)); } pFirst = std::move(p); pLast = pFirst.get(); } ASSERT(pLast); int32_t iNameLen = GetCSSNameLen(++psz, pEnd); if (iNameLen == 0) return nullptr; FDE_CSSSelectorType eType = wch == '.' ? FDE_CSSSelectorType::Class : FDE_CSSSelectorType::ID; auto p = pdfium::MakeUnique(eType, psz, iNameLen, false); p->SetNext(pLast->ReleaseNextSelector()); pLast->SetNext(std::move(p)); pLast = pLast->GetNextSelector(); psz += iNameLen; } else if (IsCSSChar(wch) || wch == '*') { int32_t iNameLen = wch == '*' ? 1 : GetCSSNameLen(psz, pEnd); if (iNameLen == 0) return nullptr; auto p = pdfium::MakeUnique( FDE_CSSSelectorType::Element, psz, iNameLen, true); if (pFirst) { pFirst->SetType(FDE_CSSSelectorType::Descendant); p->SetNext(std::move(pFirst)); } pFirst = std::move(p); pLast = pFirst.get(); psz += iNameLen; } else if (wch == ':') { int32_t iNameLen = GetCSSPseudoLen(psz, pEnd); if (iNameLen == 0) return nullptr; auto p = pdfium::MakeUnique(FDE_CSSSelectorType::Pseudo, psz, iNameLen, true); CFDE_CSSSelector* ptr = p.get(); if (pPseudoFirst) pPseudoLast->SetNext(std::move(p)); else pPseudoFirst = std::move(p); pPseudoLast = ptr; psz += iNameLen; } else if (wch == ' ') { psz++; } else { return nullptr; } } if (!pPseudoFirst) return pFirst; pPseudoLast->SetNext(std::move(pFirst)); return pPseudoFirst; } CFDE_CSSFontFaceRule::CFDE_CSSFontFaceRule() : CFDE_CSSRule(FDE_CSSRuleType::FontFace) {} CFDE_CSSFontFaceRule::~CFDE_CSSFontFaceRule() {}