blob: db01274d6a5d8d66aa3ef5baff1b3e7c3f2c3bba [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 "xfa/fde/css/fde_cssstylesheet.h"
#include "xfa/fde/css/fde_cssdatatable.h"
#include "xfa/fde/css/fde_csssyntax.h"
#include "xfa/fgas/crt/fgas_codepage.h"
IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadHTMLStandardStyleSheet() {
static const FX_WCHAR s_pStyle[] =
L"html,address,blockquote,body,dd,div,dl,dt,fieldset,form,frame,frameset,"
L"h1,h2,h3,h4,h5,h6,noframes,ol,p,ul,center,dir,hr,menu,pre{display:"
L"block}"
L"li{display:list-item}head{display:none}table{display:table}tr{display:"
L"table-row}thead{display:table-header-group}tbody{display:table-row-"
L"group}tfoot{display:table-footer-group}"
L"col{display:table-column}colgroup{display:table-column-group}td,th{"
L"display:table-cell}caption{display:table-caption}th{font-weight:bolder;"
L"text-align:center}caption{text-align:center}"
L"body{margin:0}h1{font-size:2em;margin:.67em "
L"0}h2{font-size:1.5em;margin:.75em 0}h3{font-size:1.17em;margin:.83em "
L"0}h4,p,blockquote,ul,fieldset,form,ol,dl,dir,menu{margin:1.12em 0}"
L"h5{font-size:.83em;margin:1.5em 0}h6{font-size:.75em;margin:1.67em "
L"0}h1,h2,h3,h4,h5,h6,b,strong{font-weight:bolder}blockquote{margin-left:"
L"40px;margin-right:40px}i,cite,em,var,address{font-style:italic}"
L"pre,tt,code,kbd,samp{font-family:monospace}pre{white-space:pre}button,"
L"textarea,input,select{display:inline-block}big{font-size:1.17em}small,"
L"sub,sup{font-size:.83em}sub{vertical-align:sub}"
L"sup{vertical-align:super}table{border-spacing:2px}thead,tbody,tfoot{"
L"vertical-align:middle}td,th,tr{vertical-align:inherit}s,strike,del{"
L"text-decoration:line-through}hr{border:1px inset silver}"
L"ol,ul,dir,menu,dd{margin-left:40px}ol{list-style-type:decimal}ol ul,ul "
L"ol,ul ul,ol "
L"ol{margin-top:0;margin-bottom:0}u,ins{text-decoration:underline}center{"
L"text-align:center}"
L"ruby{display:ruby}rt{display:ruby-text;font-size:.5em}rb{display:ruby-"
L"base}rbc{display:ruby-base-group}rtc{display:ruby-text-group}"
L"q:before{content:open-quote}q:after{content:close-quote}"
L"rp{display:none}";
return IFDE_CSSStyleSheet::LoadFromBuffer(
CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8);
}
IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromStream(
const CFX_WideString& szUrl,
IFX_Stream* pStream,
uint16_t wCodePage,
uint32_t dwMediaList) {
CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
if (!pStyleSheet->LoadFromStream(szUrl, pStream, wCodePage)) {
pStyleSheet->Release();
pStyleSheet = NULL;
}
return pStyleSheet;
}
IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromBuffer(
const CFX_WideString& szUrl,
const FX_WCHAR* pBuffer,
int32_t iBufSize,
uint16_t wCodePage,
uint32_t dwMediaList) {
CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
if (!pStyleSheet->LoadFromBuffer(szUrl, pBuffer, iBufSize, wCodePage)) {
pStyleSheet->Release();
pStyleSheet = NULL;
}
return pStyleSheet;
}
CFDE_CSSStyleSheet::CFDE_CSSStyleSheet(uint32_t dwMediaList)
: m_wCodePage(FX_CODEPAGE_UTF8),
m_wRefCount(1),
m_dwMediaList(dwMediaList),
m_pAllocator(NULL) {
ASSERT(m_dwMediaList > 0);
}
CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet() {
Reset();
}
void CFDE_CSSStyleSheet::Reset() {
for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
switch (pRule->GetType()) {
case FDE_CSSRULETYPE_Style:
((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
break;
case FDE_CSSRULETYPE_Media:
((CFDE_CSSMediaRule*)pRule)->~CFDE_CSSMediaRule();
break;
case FDE_CSSRULETYPE_FontFace:
((CFDE_CSSFontFaceRule*)pRule)->~CFDE_CSSFontFaceRule();
break;
default:
ASSERT(FALSE);
break;
}
}
m_RuleArray.RemoveAll();
m_Selectors.RemoveAll();
m_StringCache.RemoveAll();
if (m_pAllocator) {
m_pAllocator->Release();
m_pAllocator = NULL;
}
}
uint32_t CFDE_CSSStyleSheet::AddRef() {
return ++m_wRefCount;
}
uint32_t CFDE_CSSStyleSheet::Release() {
uint32_t dwRefCount = --m_wRefCount;
if (dwRefCount == 0) {
delete this;
}
return dwRefCount;
}
int32_t CFDE_CSSStyleSheet::CountRules() const {
return m_RuleArray.GetSize();
}
IFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(int32_t index) {
return m_RuleArray.GetAt(index);
}
FX_BOOL CFDE_CSSStyleSheet::LoadFromStream(const CFX_WideString& szUrl,
IFX_Stream* pStream,
uint16_t wCodePage) {
CFDE_CSSSyntaxParser* pSyntax = new CFDE_CSSSyntaxParser;
if (pStream->GetCodePage() != wCodePage) {
pStream->SetCodePage(wCodePage);
}
FX_BOOL bRet = pSyntax->Init(pStream, 4096) && LoadFromSyntax(pSyntax);
pSyntax->Release();
m_wCodePage = wCodePage;
m_szUrl = szUrl;
return bRet;
}
FX_BOOL CFDE_CSSStyleSheet::LoadFromBuffer(const CFX_WideString& szUrl,
const FX_WCHAR* pBuffer,
int32_t iBufSize,
uint16_t wCodePage) {
ASSERT(pBuffer && iBufSize > 0);
CFDE_CSSSyntaxParser* pSyntax = new CFDE_CSSSyntaxParser;
FX_BOOL bRet = pSyntax->Init(pBuffer, iBufSize) && LoadFromSyntax(pSyntax);
pSyntax->Release();
m_wCodePage = wCodePage;
m_szUrl = szUrl;
return bRet;
}
FX_BOOL CFDE_CSSStyleSheet::LoadFromSyntax(CFDE_CSSSyntaxParser* pSyntax) {
Reset();
m_pAllocator = IFX_MemoryAllocator::Create(FX_ALLOCTYPE_Static, 1024, 0);
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_Selectors.RemoveAll();
m_StringCache.RemoveAll();
return eStatus != FDE_CSSSYNTAXSTATUS_Error;
}
FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadMediaRule(
CFDE_CSSSyntaxParser* pSyntax) {
uint32_t dwMediaList = 0;
CFDE_CSSMediaRule* pMediaRule = NULL;
for (;;) {
switch (pSyntax->DoSyntaxParse()) {
case FDE_CSSSYNTAXSTATUS_MediaType: {
int32_t iLen;
const FX_WCHAR* psz = pSyntax->GetCurrentString(iLen);
FDE_LPCCSSMEDIATYPETABLE pMediaType =
FDE_GetCSSMediaTypeByName(CFX_WideStringC(psz, iLen));
if (pMediaType != NULL) {
dwMediaList |= pMediaType->wValue;
}
} break;
case FDE_CSSSYNTAXSTATUS_StyleRule:
if (pMediaRule == NULL) {
SkipRuleSet(pSyntax);
} else {
FDE_CSSSYNTAXSTATUS eStatus =
LoadStyleRule(pSyntax, pMediaRule->GetArray());
if (eStatus < FDE_CSSSYNTAXSTATUS_None) {
return eStatus;
}
}
break;
case FDE_CSSSYNTAXSTATUS_DeclOpen:
if ((dwMediaList & m_dwMediaList) > 0 && pMediaRule == NULL) {
pMediaRule =
FXTARGET_NewWith(m_pAllocator) CFDE_CSSMediaRule(dwMediaList);
m_RuleArray.Add(pMediaRule);
}
break;
case FDE_CSSSYNTAXSTATUS_DeclClose:
return FDE_CSSSYNTAXSTATUS_None;
FDE_CSSSWITCHDEFAULTS();
}
}
}
FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadStyleRule(
CFDE_CSSSyntaxParser* pSyntax,
CFDE_CSSRuleArray& ruleArray) {
m_Selectors.RemoveAt(0, m_Selectors.GetSize());
CFDE_CSSStyleRule* pStyleRule = NULL;
const FX_WCHAR* pszValue = NULL;
int32_t iValueLen = 0;
FDE_CSSPROPERTYARGS propertyArgs;
propertyArgs.pStaticStore = m_pAllocator;
propertyArgs.pStringCache = &m_StringCache;
propertyArgs.pProperty = NULL;
CFX_WideString wsName;
for (;;) {
switch (pSyntax->DoSyntaxParse()) {
case FDE_CSSSYNTAXSTATUS_Selector: {
pszValue = pSyntax->GetCurrentString(iValueLen);
CFDE_CSSSelector* pSelector =
CFDE_CSSSelector::FromString(m_pAllocator, pszValue, iValueLen);
if (pSelector != NULL) {
m_Selectors.Add(pSelector);
}
} break;
case FDE_CSSSYNTAXSTATUS_PropertyName:
pszValue = pSyntax->GetCurrentString(iValueLen);
propertyArgs.pProperty =
FDE_GetCSSPropertyByName(CFX_WideStringC(pszValue, iValueLen));
if (propertyArgs.pProperty == NULL) {
wsName = CFX_WideStringC(pszValue, iValueLen);
}
break;
case FDE_CSSSYNTAXSTATUS_PropertyValue:
if (propertyArgs.pProperty != NULL) {
pszValue = pSyntax->GetCurrentString(iValueLen);
if (iValueLen > 0) {
pStyleRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
iValueLen);
}
} else if (iValueLen > 0) {
pszValue = pSyntax->GetCurrentString(iValueLen);
if (iValueLen > 0) {
pStyleRule->GetDeclImp().AddProperty(&propertyArgs, wsName.c_str(),
wsName.GetLength(), pszValue,
iValueLen);
}
}
break;
case FDE_CSSSYNTAXSTATUS_DeclOpen:
if (pStyleRule == NULL && m_Selectors.GetSize() > 0) {
pStyleRule = FXTARGET_NewWith(m_pAllocator) CFDE_CSSStyleRule;
pStyleRule->SetSelector(m_pAllocator, m_Selectors);
ruleArray.Add(pStyleRule);
} else {
SkipRuleSet(pSyntax);
return FDE_CSSSYNTAXSTATUS_None;
}
break;
case FDE_CSSSYNTAXSTATUS_DeclClose:
if (pStyleRule != NULL &&
pStyleRule->GetDeclImp().GetStartPosition() == NULL) {
pStyleRule->~CFDE_CSSStyleRule();
ruleArray.RemoveLast(1);
}
return FDE_CSSSYNTAXSTATUS_None;
FDE_CSSSWITCHDEFAULTS();
}
}
}
FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadFontFaceRule(
CFDE_CSSSyntaxParser* pSyntax,
CFDE_CSSRuleArray& ruleArray) {
CFDE_CSSFontFaceRule* pFontFaceRule = NULL;
const FX_WCHAR* pszValue = NULL;
int32_t iValueLen = 0;
FDE_CSSPROPERTYARGS propertyArgs;
propertyArgs.pStaticStore = m_pAllocator;
propertyArgs.pStringCache = &m_StringCache;
propertyArgs.pProperty = NULL;
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 != NULL) {
pszValue = pSyntax->GetCurrentString(iValueLen);
if (iValueLen > 0) {
pFontFaceRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
iValueLen);
}
}
break;
case FDE_CSSSYNTAXSTATUS_DeclOpen:
if (pFontFaceRule == NULL) {
pFontFaceRule = FXTARGET_NewWith(m_pAllocator) CFDE_CSSFontFaceRule;
ruleArray.Add(pFontFaceRule);
}
break;
case FDE_CSSSYNTAXSTATUS_DeclClose:
return FDE_CSSSYNTAXSTATUS_None;
FDE_CSSSWITCHDEFAULTS();
}
}
return FDE_CSSSYNTAXSTATUS_None;
}
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;
FDE_CSSSWITCHDEFAULTS();
}
}
}
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;
FDE_CSSSWITCHDEFAULTS();
}
}
return FDE_CSSSYNTAXSTATUS_None;
}
void CFDE_CSSStyleRule::SetSelector(IFX_MemoryAllocator* pStaticStore,
const CFDE_CSSSelectorArray& list) {
ASSERT(m_ppSelector == NULL);
m_iSelectors = list.GetSize();
m_ppSelector = static_cast<CFDE_CSSSelector**>(
pStaticStore->Alloc(m_iSelectors * sizeof(CFDE_CSSSelector*)));
for (int32_t i = 0; i < m_iSelectors; ++i) {
m_ppSelector[i] = list.GetAt(i);
}
}
CFDE_CSSMediaRule::~CFDE_CSSMediaRule() {
for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
switch (pRule->GetType()) {
case FDE_CSSRULETYPE_Style:
((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
break;
default:
ASSERT(FALSE);
break;
}
}
}
inline FX_BOOL FDE_IsCSSChar(FX_WCHAR wch) {
return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z');
}
int32_t FDE_GetCSSPersudoLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
ASSERT(*psz == ':');
const FX_WCHAR* pStart = psz;
while (psz < pEnd) {
FX_WCHAR wch = *psz;
if (FDE_IsCSSChar(wch) || wch == ':') {
++psz;
} else {
break;
}
}
return psz - pStart;
}
int32_t FDE_GetCSSNameLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
const FX_WCHAR* pStart = psz;
while (psz < pEnd) {
FX_WCHAR wch = *psz;
if (FDE_IsCSSChar(wch) || (wch >= '0' && wch <= '9') || wch == '_' ||
wch == '-') {
++psz;
} else {
break;
}
}
return psz - pStart;
}
CFDE_CSSSelector* CFDE_CSSSelector::FromString(
IFX_MemoryAllocator* pStaticStore,
const FX_WCHAR* psz,
int32_t iLen) {
ASSERT(pStaticStore != NULL && psz != NULL && iLen > 0);
const FX_WCHAR* pStart = psz;
const FX_WCHAR* pEnd = psz + iLen;
for (; psz < pEnd; ++psz) {
switch (*psz) {
case '>':
case '[':
case '+':
return NULL;
}
}
CFDE_CSSSelector *pFirst = NULL, *pLast = NULL;
CFDE_CSSSelector *pPersudoFirst = NULL, *pPersudoLast = NULL;
for (psz = pStart; psz < pEnd;) {
FX_WCHAR wch = *psz;
if (wch == '.' || wch == '#') {
if (psz == pStart || psz[-1] == ' ') {
CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, L"*", 1, true);
if (p == NULL) {
return NULL;
}
if (pFirst != NULL) {
pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
p->SetNext(pFirst);
}
pFirst = pLast = p;
}
ASSERT(pLast != NULL);
int32_t iNameLen = FDE_GetCSSNameLen(++psz, pEnd);
if (iNameLen == 0) {
return NULL;
}
FDE_CSSSELECTORTYPE eType =
wch == '.' ? FDE_CSSSELECTORTYPE_Class : FDE_CSSSELECTORTYPE_ID;
CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
CFDE_CSSSelector(eType, psz, iNameLen, false);
if (p == NULL) {
return NULL;
}
p->SetNext(pLast->GetNextSelector());
pLast->SetNext(p);
pLast = p;
psz += iNameLen;
} else if (FDE_IsCSSChar(wch) || wch == '*') {
int32_t iNameLen = wch == '*' ? 1 : FDE_GetCSSNameLen(psz, pEnd);
if (iNameLen == 0) {
return NULL;
}
CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, psz, iNameLen, true);
if (p == NULL) {
return NULL;
}
if (pFirst == NULL) {
pFirst = pLast = p;
} else {
pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
p->SetNext(pFirst);
pFirst = pLast = p;
}
psz += iNameLen;
} else if (wch == ':') {
int32_t iNameLen = FDE_GetCSSPersudoLen(psz, pEnd);
if (iNameLen == 0) {
return NULL;
}
CFDE_CSSSelector* p = FXTARGET_NewWith(pStaticStore)
CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Persudo, psz, iNameLen, true);
if (p == NULL) {
return NULL;
}
if (pPersudoFirst == NULL) {
pPersudoFirst = pPersudoLast = p;
} else {
pPersudoLast->SetNext(p);
pPersudoLast = p;
}
psz += iNameLen;
} else if (wch == ' ') {
psz++;
} else {
return NULL;
}
}
if (pPersudoFirst == NULL) {
return pFirst;
} else {
pPersudoLast->SetNext(pFirst);
return pPersudoFirst;
}
}