1 // Copyright 2014 PDFium Authors. All rights reserved.
\r
2 // Use of this source code is governed by a BSD-style license that can be
\r
3 // found in the LICENSE file.
\r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
\r
7 #include "../../../foxitlib.h"
\r
8 #include "fde_csssyntax.h"
\r
9 #include "fde_cssdatatable.h"
\r
10 #include "fde_cssstylesheet.h"
\r
11 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadHTMLStandardStyleSheet()
\r
13 static const FX_LPCWSTR s_pStyle = (FX_LPCWSTR)L"html,address,blockquote,body,dd,div,dl,dt,fieldset,form,frame,frameset,h1,h2,h3,h4,h5,h6,noframes,ol,p,ul,center,dir,hr,menu,pre{display:block}"
\r
14 L"li{display:list-item}head{display:none}table{display:table}tr{display:table-row}thead{display:table-header-group}tbody{display:table-row-group}tfoot{display:table-footer-group}"
\r
15 L"col{display:table-column}colgroup{display:table-column-group}td,th{display:table-cell}caption{display:table-caption}th{font-weight:bolder;text-align:center}caption{text-align:center}"
\r
16 L"body{margin:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.75em 0}h3{font-size:1.17em;margin:.83em 0}h4,p,blockquote,ul,fieldset,form,ol,dl,dir,menu{margin:1.12em 0}"
\r
17 L"h5{font-size:.83em;margin:1.5em 0}h6{font-size:.75em;margin:1.67em 0}h1,h2,h3,h4,h5,h6,b,strong{font-weight:bolder}blockquote{margin-left:40px;margin-right:40px}i,cite,em,var,address{font-style:italic}"
\r
18 L"pre,tt,code,kbd,samp{font-family:monospace}pre{white-space:pre}button,textarea,input,select{display:inline-block}big{font-size:1.17em}small,sub,sup{font-size:.83em}sub{vertical-align:sub}"
\r
19 L"sup{vertical-align:super}table{border-spacing:2px}thead,tbody,tfoot{vertical-align:middle}td,th,tr{vertical-align:inherit}s,strike,del{text-decoration:line-through}hr{border:1px inset silver}"
\r
20 L"ol,ul,dir,menu,dd{margin-left:40px}ol{list-style-type:decimal}ol ul,ul ol,ul ul,ol ol{margin-top:0;margin-bottom:0}u,ins{text-decoration:underline}center{text-align:center}"
\r
21 L"ruby{display:ruby}rt{display:ruby-text;font-size:.5em}rb{display:ruby-base}rbc{display:ruby-base-group}rtc{display:ruby-text-group}"
\r
22 L"q:before{content:open-quote}q:after{content:close-quote}"
\r
23 L"rp{display:none}";
\r
24 return IFDE_CSSStyleSheet::LoadFromBuffer(CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8);
\r
26 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromStream(const CFX_WideString &szUrl, IFX_Stream *pStream, FX_WORD wCodePage, FX_DWORD dwMediaList )
\r
28 CFDE_CSSStyleSheet *pStyleSheet = FDE_New CFDE_CSSStyleSheet(dwMediaList);
\r
29 if (pStyleSheet == NULL) {
\r
32 if (!pStyleSheet->LoadFromStream(szUrl, pStream, wCodePage)) {
\r
33 pStyleSheet->Release();
\r
38 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromBuffer(const CFX_WideString &szUrl, FX_LPCWSTR pBuffer, FX_INT32 iBufSize, FX_WORD wCodePage, FX_DWORD dwMediaList )
\r
40 CFDE_CSSStyleSheet *pStyleSheet = FDE_New CFDE_CSSStyleSheet(dwMediaList);
\r
41 if (pStyleSheet == NULL) {
\r
44 if (!pStyleSheet->LoadFromBuffer(szUrl, pBuffer, iBufSize, wCodePage)) {
\r
45 pStyleSheet->Release();
\r
50 CFDE_CSSStyleSheet::CFDE_CSSStyleSheet(FX_DWORD dwMediaList)
\r
51 : m_wCodePage(FX_CODEPAGE_UTF8)
\r
53 , m_dwMediaList(dwMediaList)
\r
54 , m_pAllocator(NULL)
\r
56 FXSYS_assert(m_dwMediaList > 0);
\r
58 CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet()
\r
62 void CFDE_CSSStyleSheet::Reset()
\r
64 for (FX_INT32 i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
\r
65 IFDE_CSSRule *pRule = m_RuleArray.GetAt(i);
\r
66 switch (pRule->GetType()) {
\r
67 case FDE_CSSRULETYPE_Style:
\r
68 ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
\r
70 case FDE_CSSRULETYPE_Media:
\r
71 ((CFDE_CSSMediaRule*)pRule)->~CFDE_CSSMediaRule();
\r
73 case FDE_CSSRULETYPE_FontFace:
\r
74 ((CFDE_CSSFontFaceRule*)pRule)->~CFDE_CSSFontFaceRule();
\r
77 FXSYS_assert(FALSE);
\r
81 m_RuleArray.RemoveAll();
\r
82 m_Selectors.RemoveAll();
\r
83 m_StringCache.RemoveAll();
\r
85 m_pAllocator->Release();
\r
86 m_pAllocator = NULL;
\r
89 FX_DWORD CFDE_CSSStyleSheet::AddRef()
\r
91 return ++m_wRefCount;
\r
93 FX_DWORD CFDE_CSSStyleSheet::Release()
\r
95 FX_DWORD dwRefCount = --m_wRefCount;
\r
96 if (dwRefCount == 0) {
\r
101 FX_INT32 CFDE_CSSStyleSheet::CountRules() const
\r
103 return m_RuleArray.GetSize();
\r
105 IFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(FX_INT32 index)
\r
107 return m_RuleArray.GetAt(index);
\r
109 FX_BOOL CFDE_CSSStyleSheet::LoadFromStream(const CFX_WideString &szUrl, IFX_Stream *pStream, FX_WORD wCodePage)
\r
111 FXSYS_assert(pStream != NULL);
\r
112 IFDE_CSSSyntaxParser *pSyntax = IFDE_CSSSyntaxParser::Create();
\r
113 if (pSyntax == NULL) {
\r
116 if (pStream->GetCodePage() != wCodePage) {
\r
117 pStream->SetCodePage(wCodePage);
\r
119 FX_BOOL bRet = pSyntax->Init(pStream, 4096) && LoadFromSyntax(pSyntax);
\r
120 pSyntax->Release();
\r
121 m_wCodePage = wCodePage;
\r
125 FX_BOOL CFDE_CSSStyleSheet::LoadFromBuffer(const CFX_WideString &szUrl, FX_LPCWSTR pBuffer, FX_INT32 iBufSize, FX_WORD wCodePage)
\r
127 FXSYS_assert(pBuffer != NULL && iBufSize > 0);
\r
128 IFDE_CSSSyntaxParser *pSyntax = IFDE_CSSSyntaxParser::Create();
\r
129 if (pSyntax == NULL) {
\r
132 FX_BOOL bRet = pSyntax->Init(pBuffer, iBufSize) && LoadFromSyntax(pSyntax);
\r
133 pSyntax->Release();
\r
134 m_wCodePage = wCodePage;
\r
138 FX_BOOL CFDE_CSSStyleSheet::LoadFromSyntax(IFDE_CSSSyntaxParser *pSyntax)
\r
141 m_pAllocator = FX_CreateAllocator(FX_ALLOCTYPE_Static, 1024, 0);
\r
142 if (m_pAllocator == NULL) {
\r
145 FDE_CSSSYNTAXSTATUS eStatus;
\r
147 switch (eStatus = pSyntax->DoSyntaxParse()) {
\r
148 case FDE_CSSSYNTAXSTATUS_StyleRule:
\r
149 eStatus = LoadStyleRule(pSyntax, m_RuleArray);
\r
151 case FDE_CSSSYNTAXSTATUS_MediaRule:
\r
152 eStatus = LoadMediaRule(pSyntax);
\r
154 case FDE_CSSSYNTAXSTATUS_FontFaceRule:
\r
155 eStatus = LoadFontFaceRule(pSyntax, m_RuleArray);
\r
157 case FDE_CSSSYNTAXSTATUS_ImportRule:
\r
158 eStatus = LoadImportRule(pSyntax);
\r
160 case FDE_CSSSYNTAXSTATUS_PageRule:
\r
161 eStatus = LoadPageRule(pSyntax);
\r
166 } while (eStatus >= FDE_CSSSYNTAXSTATUS_None);
\r
167 m_Selectors.RemoveAll();
\r
168 m_StringCache.RemoveAll();
\r
169 return eStatus != FDE_CSSSYNTAXSTATUS_Error;
\r
171 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadMediaRule(IFDE_CSSSyntaxParser *pSyntax)
\r
173 FX_DWORD dwMediaList = 0;
\r
174 CFDE_CSSMediaRule *pMediaRule = NULL;
\r
176 switch (pSyntax->DoSyntaxParse()) {
\r
177 case FDE_CSSSYNTAXSTATUS_MediaType: {
\r
179 FX_LPCWSTR psz = pSyntax->GetCurrentString(iLen);
\r
180 FDE_LPCCSSMEDIATYPETABLE pMediaType = FDE_GetCSSMediaTypeByName(psz, iLen);
\r
181 if (pMediaType != NULL) {
\r
182 dwMediaList |= pMediaType->wValue;
\r
186 case FDE_CSSSYNTAXSTATUS_StyleRule:
\r
187 if (pMediaRule == NULL) {
\r
188 SkipRuleSet(pSyntax);
\r
190 FDE_CSSSYNTAXSTATUS eStatus = LoadStyleRule(pSyntax, pMediaRule->GetArray());
\r
191 if (eStatus < FDE_CSSSYNTAXSTATUS_None) {
\r
196 case FDE_CSSSYNTAXSTATUS_DeclOpen:
\r
197 if ((dwMediaList & m_dwMediaList) > 0 && pMediaRule == NULL) {
\r
198 pMediaRule = FDE_NewWith(m_pAllocator) CFDE_CSSMediaRule(dwMediaList);
\r
199 m_RuleArray.Add(pMediaRule);
\r
202 case FDE_CSSSYNTAXSTATUS_DeclClose:
\r
203 return FDE_CSSSYNTAXSTATUS_None;
\r
204 FDE_CSSSWITCHDEFAULTS();
\r
208 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadStyleRule(IFDE_CSSSyntaxParser *pSyntax, CFDE_CSSRuleArray &ruleArray)
\r
210 m_Selectors.RemoveAt(0, m_Selectors.GetSize());
\r
211 CFDE_CSSStyleRule *pStyleRule = NULL;
\r
212 FX_LPCWSTR pszValue = NULL;
\r
213 FX_INT32 iValueLen = 0;
\r
214 FDE_CSSPROPERTYARGS propertyArgs;
\r
215 propertyArgs.pStaticStore = m_pAllocator;
\r
216 propertyArgs.pStringCache = &m_StringCache;
\r
217 propertyArgs.pProperty = NULL;
\r
218 CFX_WideString wsName;
\r
220 switch (pSyntax->DoSyntaxParse()) {
\r
221 case FDE_CSSSYNTAXSTATUS_Selector: {
\r
222 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
223 IFDE_CSSSelector *pSelector = CFDE_CSSSelector::FromString(m_pAllocator, pszValue, iValueLen);
\r
224 if (pSelector != NULL) {
\r
225 m_Selectors.Add(pSelector);
\r
229 case FDE_CSSSYNTAXSTATUS_PropertyName:
\r
230 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
231 propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
\r
232 if (propertyArgs.pProperty == NULL) {
\r
233 wsName = CFX_WideStringC(pszValue, iValueLen);
\r
236 case FDE_CSSSYNTAXSTATUS_PropertyValue:
\r
237 if (propertyArgs.pProperty != NULL) {
\r
238 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
239 if (iValueLen > 0) {
\r
240 pStyleRule->GetDeclImp().AddProperty(&propertyArgs, pszValue, iValueLen);
\r
242 } else if (iValueLen > 0) {
\r
243 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
244 if (iValueLen > 0) {
\r
245 pStyleRule->GetDeclImp().AddProperty(&propertyArgs, wsName, wsName.GetLength(), pszValue, iValueLen);
\r
249 case FDE_CSSSYNTAXSTATUS_DeclOpen:
\r
250 if (pStyleRule == NULL && m_Selectors.GetSize() > 0) {
\r
251 pStyleRule = FDE_NewWith(m_pAllocator) CFDE_CSSStyleRule;
\r
252 pStyleRule->SetSelector(m_pAllocator, m_Selectors);
\r
253 ruleArray.Add(pStyleRule);
\r
255 SkipRuleSet(pSyntax);
\r
256 return FDE_CSSSYNTAXSTATUS_None;
\r
259 case FDE_CSSSYNTAXSTATUS_DeclClose:
\r
260 if (pStyleRule != NULL && pStyleRule->GetDeclImp().GetStartPosition() == NULL) {
\r
261 pStyleRule->~CFDE_CSSStyleRule();
\r
262 ruleArray.RemoveLast(1);
\r
264 return FDE_CSSSYNTAXSTATUS_None;
\r
265 FDE_CSSSWITCHDEFAULTS();
\r
269 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadFontFaceRule(IFDE_CSSSyntaxParser *pSyntax, CFDE_CSSRuleArray &ruleArray)
\r
271 CFDE_CSSFontFaceRule *pFontFaceRule = NULL;
\r
272 FX_LPCWSTR pszValue = NULL;
\r
273 FX_INT32 iValueLen = 0;
\r
274 FDE_CSSPROPERTYARGS propertyArgs;
\r
275 propertyArgs.pStaticStore = m_pAllocator;
\r
276 propertyArgs.pStringCache = &m_StringCache;
\r
277 propertyArgs.pProperty = NULL;
\r
279 switch (pSyntax->DoSyntaxParse()) {
\r
280 case FDE_CSSSYNTAXSTATUS_PropertyName:
\r
281 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
282 propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
\r
284 case FDE_CSSSYNTAXSTATUS_PropertyValue:
\r
285 if (propertyArgs.pProperty != NULL) {
\r
286 pszValue = pSyntax->GetCurrentString(iValueLen);
\r
287 if (iValueLen > 0) {
\r
288 pFontFaceRule->GetDeclImp().AddProperty(&propertyArgs, pszValue, iValueLen);
\r
292 case FDE_CSSSYNTAXSTATUS_DeclOpen:
\r
293 if (pFontFaceRule == NULL) {
\r
294 pFontFaceRule = FDE_NewWith(m_pAllocator) CFDE_CSSFontFaceRule;
\r
295 ruleArray.Add(pFontFaceRule);
\r
298 case FDE_CSSSYNTAXSTATUS_DeclClose:
\r
299 return FDE_CSSSYNTAXSTATUS_None;
\r
300 FDE_CSSSWITCHDEFAULTS();
\r
303 return FDE_CSSSYNTAXSTATUS_None;
\r
305 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadImportRule(IFDE_CSSSyntaxParser *pSyntax)
\r
308 switch (pSyntax->DoSyntaxParse()) {
\r
309 case FDE_CSSSYNTAXSTATUS_ImportClose:
\r
310 return FDE_CSSSYNTAXSTATUS_None;
\r
311 case FDE_CSSSYNTAXSTATUS_URI:
\r
313 FDE_CSSSWITCHDEFAULTS();
\r
317 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadPageRule(IFDE_CSSSyntaxParser *pSyntax)
\r
319 return SkipRuleSet(pSyntax);
\r
321 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::SkipRuleSet(IFDE_CSSSyntaxParser *pSyntax)
\r
324 switch (pSyntax->DoSyntaxParse()) {
\r
325 case FDE_CSSSYNTAXSTATUS_Selector:
\r
326 case FDE_CSSSYNTAXSTATUS_DeclOpen:
\r
327 case FDE_CSSSYNTAXSTATUS_PropertyName:
\r
328 case FDE_CSSSYNTAXSTATUS_PropertyValue:
\r
330 case FDE_CSSSYNTAXSTATUS_DeclClose:
\r
331 return FDE_CSSSYNTAXSTATUS_None;
\r
332 FDE_CSSSWITCHDEFAULTS();
\r
335 return FDE_CSSSYNTAXSTATUS_None;
\r
337 void CFDE_CSSStyleRule::SetSelector(IFX_MEMAllocator *pStaticStore, const CFDE_CSSSelectorArray &list)
\r
339 FXSYS_assert(m_ppSelector == NULL);
\r
340 m_iSelectors = list.GetSize();
\r
341 m_ppSelector = (IFDE_CSSSelector**)pStaticStore->Alloc(m_iSelectors * sizeof(IFDE_CSSSelector*));
\r
342 for (FX_INT32 i = 0; i < m_iSelectors; ++i) {
\r
343 m_ppSelector[i] = list.GetAt(i);
\r
346 CFDE_CSSMediaRule::~CFDE_CSSMediaRule()
\r
348 for (FX_INT32 i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
\r
349 IFDE_CSSRule *pRule = m_RuleArray.GetAt(i);
\r
350 switch (pRule->GetType()) {
\r
351 case FDE_CSSRULETYPE_Style:
\r
352 ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
\r
355 FXSYS_assert(FALSE);
\r
360 inline FX_BOOL FDE_IsCSSChar(FX_WCHAR wch)
\r
362 return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z');
\r
364 FX_INT32 FDE_GetCSSPersudoLen(FX_LPCWSTR psz, FX_LPCWSTR pEnd)
\r
366 FXSYS_assert(*psz == ':');
\r
367 FX_LPCWSTR pStart = psz;
\r
368 while (psz < pEnd) {
\r
369 FX_WCHAR wch = *psz;
\r
370 if (FDE_IsCSSChar(wch) || wch == ':') {
\r
376 return psz - pStart;
\r
378 FX_INT32 FDE_GetCSSNameLen(FX_LPCWSTR psz, FX_LPCWSTR pEnd)
\r
380 FX_LPCWSTR pStart = psz;
\r
381 while (psz < pEnd) {
\r
382 FX_WCHAR wch = *psz;
\r
383 if (FDE_IsCSSChar(wch)
\r
384 || (wch >= '0' && wch <= '9')
\r
385 || wch == '_' || wch == '-') {
\r
391 return psz - pStart;
\r
393 IFDE_CSSSelector* CFDE_CSSSelector::FromString(IFX_MEMAllocator *pStaticStore, FX_LPCWSTR psz, FX_INT32 iLen)
\r
395 FXSYS_assert(pStaticStore != NULL && psz != NULL && iLen > 0);
\r
396 FX_LPCWSTR pStart = psz;
\r
397 FX_LPCWSTR pEnd = psz + iLen;
\r
398 for (; psz < pEnd; ++psz) {
\r
406 CFDE_CSSSelector *pFirst = NULL, *pLast = NULL;
\r
407 CFDE_CSSSelector *pPersudoFirst = NULL, *pPersudoLast = NULL;
\r
408 for (psz = pStart ; psz < pEnd; ) {
\r
409 FX_WCHAR wch = *psz;
\r
410 if (wch == '.' || wch == '#') {
\r
411 if (psz == pStart || psz[-1] == ' ') {
\r
412 CFDE_CSSSelector *p = FDE_NewWith(pStaticStore) CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, (FX_LPCWSTR)L"*", 1, TRUE);
\r
416 if (pFirst != NULL) {
\r
417 pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
\r
418 p->SetNext(pFirst);
\r
420 pFirst = pLast = p;
\r
422 FXSYS_assert(pLast != NULL);
\r
423 FX_INT32 iNameLen = FDE_GetCSSNameLen(++psz, pEnd);
\r
424 if (iNameLen == 0) {
\r
427 FDE_CSSSELECTORTYPE eType = wch == '.' ? FDE_CSSSELECTORTYPE_Class : FDE_CSSSELECTORTYPE_ID;
\r
428 CFDE_CSSSelector *p = FDE_NewWith(pStaticStore) CFDE_CSSSelector(eType, psz, iNameLen, FALSE);
\r
432 p->SetNext(pLast->GetNextSelector());
\r
436 } else if (FDE_IsCSSChar(wch) || wch == '*') {
\r
437 FX_INT32 iNameLen = wch == '*' ? 1 : FDE_GetCSSNameLen(psz, pEnd);
\r
438 if (iNameLen == 0) {
\r
441 CFDE_CSSSelector *p = FDE_NewWith(pStaticStore) CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, psz, iNameLen, TRUE);
\r
445 if (pFirst == NULL) {
\r
446 pFirst = pLast = p;
\r
448 pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
\r
449 p->SetNext(pFirst);
\r
450 pFirst = pLast = p;
\r
453 } else if (wch == ':') {
\r
454 FX_INT32 iNameLen = FDE_GetCSSPersudoLen(psz, pEnd);
\r
455 if (iNameLen == 0) {
\r
458 CFDE_CSSSelector *p = FDE_NewWith(pStaticStore) CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Persudo, psz, iNameLen, TRUE);
\r
462 if (pPersudoFirst == NULL) {
\r
463 pPersudoFirst = pPersudoLast = p;
\r
465 pPersudoLast->SetNext(p);
\r
469 } else if (wch == ' ') {
\r
475 if (pPersudoFirst == NULL) {
\r
478 pPersudoLast->SetNext(pFirst);
\r
479 return pPersudoFirst;
\r