1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/src/foxitlib.h"
8 #include "fde_csssyntax.h"
9 #include "fde_cssdatatable.h"
10 #include "fde_cssstylesheet.h"
LoadHTMLStandardStyleSheet()11 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadHTMLStandardStyleSheet() {
12   static const FX_WCHAR* s_pStyle =
13       L"html,address,blockquote,body,dd,div,dl,dt,fieldset,form,frame,frameset,"
14       L"h1,h2,h3,h4,h5,h6,noframes,ol,p,ul,center,dir,hr,menu,pre{display:"
15       L"block}"
16       L"li{display:list-item}head{display:none}table{display:table}tr{display:"
17       L"table-row}thead{display:table-header-group}tbody{display:table-row-"
18       L"group}tfoot{display:table-footer-group}"
19       L"col{display:table-column}colgroup{display:table-column-group}td,th{"
20       L"display:table-cell}caption{display:table-caption}th{font-weight:bolder;"
21       L"text-align:center}caption{text-align:center}"
22       L"body{margin:0}h1{font-size:2em;margin:.67em "
23       L"0}h2{font-size:1.5em;margin:.75em 0}h3{font-size:1.17em;margin:.83em "
24       L"0}h4,p,blockquote,ul,fieldset,form,ol,dl,dir,menu{margin:1.12em 0}"
25       L"h5{font-size:.83em;margin:1.5em 0}h6{font-size:.75em;margin:1.67em "
26       L"0}h1,h2,h3,h4,h5,h6,b,strong{font-weight:bolder}blockquote{margin-left:"
27       L"40px;margin-right:40px}i,cite,em,var,address{font-style:italic}"
28       L"pre,tt,code,kbd,samp{font-family:monospace}pre{white-space:pre}button,"
29       L"textarea,input,select{display:inline-block}big{font-size:1.17em}small,"
30       L"sub,sup{font-size:.83em}sub{vertical-align:sub}"
31       L"sup{vertical-align:super}table{border-spacing:2px}thead,tbody,tfoot{"
32       L"vertical-align:middle}td,th,tr{vertical-align:inherit}s,strike,del{"
33       L"text-decoration:line-through}hr{border:1px inset silver}"
34       L"ol,ul,dir,menu,dd{margin-left:40px}ol{list-style-type:decimal}ol ul,ul "
35       L"ol,ul ul,ol "
36       L"ol{margin-top:0;margin-bottom:0}u,ins{text-decoration:underline}center{"
37       L"text-align:center}"
38       L"ruby{display:ruby}rt{display:ruby-text;font-size:.5em}rb{display:ruby-"
39       L"base}rbc{display:ruby-base-group}rtc{display:ruby-text-group}"
40       L"q:before{content:open-quote}q:after{content:close-quote}"
41       L"rp{display:none}";
42   return IFDE_CSSStyleSheet::LoadFromBuffer(
43       CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8);
44 }
LoadFromStream(const CFX_WideString & szUrl,IFX_Stream * pStream,FX_WORD wCodePage,FX_DWORD dwMediaList)45 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromStream(
46     const CFX_WideString& szUrl,
47     IFX_Stream* pStream,
48     FX_WORD wCodePage,
49     FX_DWORD dwMediaList) {
50   CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
51   if (!pStyleSheet->LoadFromStream(szUrl, pStream, wCodePage)) {
52     pStyleSheet->Release();
53     pStyleSheet = NULL;
54   }
55   return pStyleSheet;
56 }
LoadFromBuffer(const CFX_WideString & szUrl,const FX_WCHAR * pBuffer,int32_t iBufSize,FX_WORD wCodePage,FX_DWORD dwMediaList)57 IFDE_CSSStyleSheet* IFDE_CSSStyleSheet::LoadFromBuffer(
58     const CFX_WideString& szUrl,
59     const FX_WCHAR* pBuffer,
60     int32_t iBufSize,
61     FX_WORD wCodePage,
62     FX_DWORD dwMediaList) {
63   CFDE_CSSStyleSheet* pStyleSheet = new CFDE_CSSStyleSheet(dwMediaList);
64   if (!pStyleSheet->LoadFromBuffer(szUrl, pBuffer, iBufSize, wCodePage)) {
65     pStyleSheet->Release();
66     pStyleSheet = NULL;
67   }
68   return pStyleSheet;
69 }
CFDE_CSSStyleSheet(FX_DWORD dwMediaList)70 CFDE_CSSStyleSheet::CFDE_CSSStyleSheet(FX_DWORD dwMediaList)
71     : m_wCodePage(FX_CODEPAGE_UTF8),
72       m_wRefCount(1),
73       m_dwMediaList(dwMediaList),
74       m_pAllocator(NULL) {
75   FXSYS_assert(m_dwMediaList > 0);
76 }
~CFDE_CSSStyleSheet()77 CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet() {
78   Reset();
79 }
Reset()80 void CFDE_CSSStyleSheet::Reset() {
81   for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
82     IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
83     switch (pRule->GetType()) {
84       case FDE_CSSRULETYPE_Style:
85         ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
86         break;
87       case FDE_CSSRULETYPE_Media:
88         ((CFDE_CSSMediaRule*)pRule)->~CFDE_CSSMediaRule();
89         break;
90       case FDE_CSSRULETYPE_FontFace:
91         ((CFDE_CSSFontFaceRule*)pRule)->~CFDE_CSSFontFaceRule();
92         break;
93       default:
94         FXSYS_assert(FALSE);
95         break;
96     }
97   }
98   m_RuleArray.RemoveAll();
99   m_Selectors.RemoveAll();
100   m_StringCache.RemoveAll();
101   if (m_pAllocator) {
102     m_pAllocator->Release();
103     m_pAllocator = NULL;
104   }
105 }
AddRef()106 FX_DWORD CFDE_CSSStyleSheet::AddRef() {
107   return ++m_wRefCount;
108 }
Release()109 FX_DWORD CFDE_CSSStyleSheet::Release() {
110   FX_DWORD dwRefCount = --m_wRefCount;
111   if (dwRefCount == 0) {
112     delete this;
113   }
114   return dwRefCount;
115 }
CountRules() const116 int32_t CFDE_CSSStyleSheet::CountRules() const {
117   return m_RuleArray.GetSize();
118 }
GetRule(int32_t index)119 IFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(int32_t index) {
120   return m_RuleArray.GetAt(index);
121 }
LoadFromStream(const CFX_WideString & szUrl,IFX_Stream * pStream,FX_WORD wCodePage)122 FX_BOOL CFDE_CSSStyleSheet::LoadFromStream(const CFX_WideString& szUrl,
123                                            IFX_Stream* pStream,
124                                            FX_WORD wCodePage) {
125   FXSYS_assert(pStream != NULL);
126   IFDE_CSSSyntaxParser* pSyntax = IFDE_CSSSyntaxParser::Create();
127   if (pSyntax == NULL) {
128     return FALSE;
129   }
130   if (pStream->GetCodePage() != wCodePage) {
131     pStream->SetCodePage(wCodePage);
132   }
133   FX_BOOL bRet = pSyntax->Init(pStream, 4096) && LoadFromSyntax(pSyntax);
134   pSyntax->Release();
135   m_wCodePage = wCodePage;
136   m_szUrl = szUrl;
137   return bRet;
138 }
LoadFromBuffer(const CFX_WideString & szUrl,const FX_WCHAR * pBuffer,int32_t iBufSize,FX_WORD wCodePage)139 FX_BOOL CFDE_CSSStyleSheet::LoadFromBuffer(const CFX_WideString& szUrl,
140                                            const FX_WCHAR* pBuffer,
141                                            int32_t iBufSize,
142                                            FX_WORD wCodePage) {
143   FXSYS_assert(pBuffer != NULL && iBufSize > 0);
144   IFDE_CSSSyntaxParser* pSyntax = IFDE_CSSSyntaxParser::Create();
145   if (pSyntax == NULL) {
146     return FALSE;
147   }
148   FX_BOOL bRet = pSyntax->Init(pBuffer, iBufSize) && LoadFromSyntax(pSyntax);
149   pSyntax->Release();
150   m_wCodePage = wCodePage;
151   m_szUrl = szUrl;
152   return bRet;
153 }
LoadFromSyntax(IFDE_CSSSyntaxParser * pSyntax)154 FX_BOOL CFDE_CSSStyleSheet::LoadFromSyntax(IFDE_CSSSyntaxParser* pSyntax) {
155   Reset();
156   m_pAllocator = FX_CreateAllocator(FX_ALLOCTYPE_Static, 1024, 0);
157   if (m_pAllocator == NULL) {
158     return FALSE;
159   }
160   FDE_CSSSYNTAXSTATUS eStatus;
161   do {
162     switch (eStatus = pSyntax->DoSyntaxParse()) {
163       case FDE_CSSSYNTAXSTATUS_StyleRule:
164         eStatus = LoadStyleRule(pSyntax, m_RuleArray);
165         break;
166       case FDE_CSSSYNTAXSTATUS_MediaRule:
167         eStatus = LoadMediaRule(pSyntax);
168         break;
169       case FDE_CSSSYNTAXSTATUS_FontFaceRule:
170         eStatus = LoadFontFaceRule(pSyntax, m_RuleArray);
171         break;
172       case FDE_CSSSYNTAXSTATUS_ImportRule:
173         eStatus = LoadImportRule(pSyntax);
174         break;
175       case FDE_CSSSYNTAXSTATUS_PageRule:
176         eStatus = LoadPageRule(pSyntax);
177         break;
178       default:
179         break;
180     }
181   } while (eStatus >= FDE_CSSSYNTAXSTATUS_None);
182   m_Selectors.RemoveAll();
183   m_StringCache.RemoveAll();
184   return eStatus != FDE_CSSSYNTAXSTATUS_Error;
185 }
LoadMediaRule(IFDE_CSSSyntaxParser * pSyntax)186 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadMediaRule(
187     IFDE_CSSSyntaxParser* pSyntax) {
188   FX_DWORD dwMediaList = 0;
189   CFDE_CSSMediaRule* pMediaRule = NULL;
190   for (;;) {
191     switch (pSyntax->DoSyntaxParse()) {
192       case FDE_CSSSYNTAXSTATUS_MediaType: {
193         int32_t iLen;
194         const FX_WCHAR* psz = pSyntax->GetCurrentString(iLen);
195         FDE_LPCCSSMEDIATYPETABLE pMediaType =
196             FDE_GetCSSMediaTypeByName(psz, iLen);
197         if (pMediaType != NULL) {
198           dwMediaList |= pMediaType->wValue;
199         }
200       } break;
201       case FDE_CSSSYNTAXSTATUS_StyleRule:
202         if (pMediaRule == NULL) {
203           SkipRuleSet(pSyntax);
204         } else {
205           FDE_CSSSYNTAXSTATUS eStatus =
206               LoadStyleRule(pSyntax, pMediaRule->GetArray());
207           if (eStatus < FDE_CSSSYNTAXSTATUS_None) {
208             return eStatus;
209           }
210         }
211         break;
212       case FDE_CSSSYNTAXSTATUS_DeclOpen:
213         if ((dwMediaList & m_dwMediaList) > 0 && pMediaRule == NULL) {
214           pMediaRule = FDE_NewWith(m_pAllocator) CFDE_CSSMediaRule(dwMediaList);
215           m_RuleArray.Add(pMediaRule);
216         }
217         break;
218       case FDE_CSSSYNTAXSTATUS_DeclClose:
219         return FDE_CSSSYNTAXSTATUS_None;
220         FDE_CSSSWITCHDEFAULTS();
221     }
222   }
223 }
LoadStyleRule(IFDE_CSSSyntaxParser * pSyntax,CFDE_CSSRuleArray & ruleArray)224 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadStyleRule(
225     IFDE_CSSSyntaxParser* pSyntax,
226     CFDE_CSSRuleArray& ruleArray) {
227   m_Selectors.RemoveAt(0, m_Selectors.GetSize());
228   CFDE_CSSStyleRule* pStyleRule = NULL;
229   const FX_WCHAR* pszValue = NULL;
230   int32_t iValueLen = 0;
231   FDE_CSSPROPERTYARGS propertyArgs;
232   propertyArgs.pStaticStore = m_pAllocator;
233   propertyArgs.pStringCache = &m_StringCache;
234   propertyArgs.pProperty = NULL;
235   CFX_WideString wsName;
236   for (;;) {
237     switch (pSyntax->DoSyntaxParse()) {
238       case FDE_CSSSYNTAXSTATUS_Selector: {
239         pszValue = pSyntax->GetCurrentString(iValueLen);
240         IFDE_CSSSelector* pSelector =
241             CFDE_CSSSelector::FromString(m_pAllocator, pszValue, iValueLen);
242         if (pSelector != NULL) {
243           m_Selectors.Add(pSelector);
244         }
245       } break;
246       case FDE_CSSSYNTAXSTATUS_PropertyName:
247         pszValue = pSyntax->GetCurrentString(iValueLen);
248         propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
249         if (propertyArgs.pProperty == NULL) {
250           wsName = CFX_WideStringC(pszValue, iValueLen);
251         }
252         break;
253       case FDE_CSSSYNTAXSTATUS_PropertyValue:
254         if (propertyArgs.pProperty != NULL) {
255           pszValue = pSyntax->GetCurrentString(iValueLen);
256           if (iValueLen > 0) {
257             pStyleRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
258                                                  iValueLen);
259           }
260         } else if (iValueLen > 0) {
261           pszValue = pSyntax->GetCurrentString(iValueLen);
262           if (iValueLen > 0) {
263             pStyleRule->GetDeclImp().AddProperty(
264                 &propertyArgs, wsName, wsName.GetLength(), pszValue, iValueLen);
265           }
266         }
267         break;
268       case FDE_CSSSYNTAXSTATUS_DeclOpen:
269         if (pStyleRule == NULL && m_Selectors.GetSize() > 0) {
270           pStyleRule = FDE_NewWith(m_pAllocator) CFDE_CSSStyleRule;
271           pStyleRule->SetSelector(m_pAllocator, m_Selectors);
272           ruleArray.Add(pStyleRule);
273         } else {
274           SkipRuleSet(pSyntax);
275           return FDE_CSSSYNTAXSTATUS_None;
276         }
277         break;
278       case FDE_CSSSYNTAXSTATUS_DeclClose:
279         if (pStyleRule != NULL &&
280             pStyleRule->GetDeclImp().GetStartPosition() == NULL) {
281           pStyleRule->~CFDE_CSSStyleRule();
282           ruleArray.RemoveLast(1);
283         }
284         return FDE_CSSSYNTAXSTATUS_None;
285         FDE_CSSSWITCHDEFAULTS();
286     }
287   }
288 }
LoadFontFaceRule(IFDE_CSSSyntaxParser * pSyntax,CFDE_CSSRuleArray & ruleArray)289 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadFontFaceRule(
290     IFDE_CSSSyntaxParser* pSyntax,
291     CFDE_CSSRuleArray& ruleArray) {
292   CFDE_CSSFontFaceRule* pFontFaceRule = NULL;
293   const FX_WCHAR* pszValue = NULL;
294   int32_t iValueLen = 0;
295   FDE_CSSPROPERTYARGS propertyArgs;
296   propertyArgs.pStaticStore = m_pAllocator;
297   propertyArgs.pStringCache = &m_StringCache;
298   propertyArgs.pProperty = NULL;
299   for (;;) {
300     switch (pSyntax->DoSyntaxParse()) {
301       case FDE_CSSSYNTAXSTATUS_PropertyName:
302         pszValue = pSyntax->GetCurrentString(iValueLen);
303         propertyArgs.pProperty = FDE_GetCSSPropertyByName(pszValue, iValueLen);
304         break;
305       case FDE_CSSSYNTAXSTATUS_PropertyValue:
306         if (propertyArgs.pProperty != NULL) {
307           pszValue = pSyntax->GetCurrentString(iValueLen);
308           if (iValueLen > 0) {
309             pFontFaceRule->GetDeclImp().AddProperty(&propertyArgs, pszValue,
310                                                     iValueLen);
311           }
312         }
313         break;
314       case FDE_CSSSYNTAXSTATUS_DeclOpen:
315         if (pFontFaceRule == NULL) {
316           pFontFaceRule = FDE_NewWith(m_pAllocator) CFDE_CSSFontFaceRule;
317           ruleArray.Add(pFontFaceRule);
318         }
319         break;
320       case FDE_CSSSYNTAXSTATUS_DeclClose:
321         return FDE_CSSSYNTAXSTATUS_None;
322         FDE_CSSSWITCHDEFAULTS();
323     }
324   }
325   return FDE_CSSSYNTAXSTATUS_None;
326 }
LoadImportRule(IFDE_CSSSyntaxParser * pSyntax)327 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadImportRule(
328     IFDE_CSSSyntaxParser* pSyntax) {
329   for (;;) {
330     switch (pSyntax->DoSyntaxParse()) {
331       case FDE_CSSSYNTAXSTATUS_ImportClose:
332         return FDE_CSSSYNTAXSTATUS_None;
333       case FDE_CSSSYNTAXSTATUS_URI:
334         break;
335         FDE_CSSSWITCHDEFAULTS();
336     }
337   }
338 }
LoadPageRule(IFDE_CSSSyntaxParser * pSyntax)339 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadPageRule(
340     IFDE_CSSSyntaxParser* pSyntax) {
341   return SkipRuleSet(pSyntax);
342 }
SkipRuleSet(IFDE_CSSSyntaxParser * pSyntax)343 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::SkipRuleSet(
344     IFDE_CSSSyntaxParser* pSyntax) {
345   for (;;) {
346     switch (pSyntax->DoSyntaxParse()) {
347       case FDE_CSSSYNTAXSTATUS_Selector:
348       case FDE_CSSSYNTAXSTATUS_DeclOpen:
349       case FDE_CSSSYNTAXSTATUS_PropertyName:
350       case FDE_CSSSYNTAXSTATUS_PropertyValue:
351         break;
352       case FDE_CSSSYNTAXSTATUS_DeclClose:
353         return FDE_CSSSYNTAXSTATUS_None;
354         FDE_CSSSWITCHDEFAULTS();
355     }
356   }
357   return FDE_CSSSYNTAXSTATUS_None;
358 }
SetSelector(IFX_MEMAllocator * pStaticStore,const CFDE_CSSSelectorArray & list)359 void CFDE_CSSStyleRule::SetSelector(IFX_MEMAllocator* pStaticStore,
360                                     const CFDE_CSSSelectorArray& list) {
361   FXSYS_assert(m_ppSelector == NULL);
362   m_iSelectors = list.GetSize();
363   m_ppSelector = (IFDE_CSSSelector**)pStaticStore->Alloc(
364       m_iSelectors * sizeof(IFDE_CSSSelector*));
365   for (int32_t i = 0; i < m_iSelectors; ++i) {
366     m_ppSelector[i] = list.GetAt(i);
367   }
368 }
~CFDE_CSSMediaRule()369 CFDE_CSSMediaRule::~CFDE_CSSMediaRule() {
370   for (int32_t i = m_RuleArray.GetSize() - 1; i >= 0; --i) {
371     IFDE_CSSRule* pRule = m_RuleArray.GetAt(i);
372     switch (pRule->GetType()) {
373       case FDE_CSSRULETYPE_Style:
374         ((CFDE_CSSStyleRule*)pRule)->~CFDE_CSSStyleRule();
375         break;
376       default:
377         FXSYS_assert(FALSE);
378         break;
379     }
380   }
381 }
FDE_IsCSSChar(FX_WCHAR wch)382 inline FX_BOOL FDE_IsCSSChar(FX_WCHAR wch) {
383   return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z');
384 }
FDE_GetCSSPersudoLen(const FX_WCHAR * psz,const FX_WCHAR * pEnd)385 int32_t FDE_GetCSSPersudoLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
386   FXSYS_assert(*psz == ':');
387   const FX_WCHAR* pStart = psz;
388   while (psz < pEnd) {
389     FX_WCHAR wch = *psz;
390     if (FDE_IsCSSChar(wch) || wch == ':') {
391       ++psz;
392     } else {
393       break;
394     }
395   }
396   return psz - pStart;
397 }
FDE_GetCSSNameLen(const FX_WCHAR * psz,const FX_WCHAR * pEnd)398 int32_t FDE_GetCSSNameLen(const FX_WCHAR* psz, const FX_WCHAR* pEnd) {
399   const FX_WCHAR* pStart = psz;
400   while (psz < pEnd) {
401     FX_WCHAR wch = *psz;
402     if (FDE_IsCSSChar(wch) || (wch >= '0' && wch <= '9') || wch == '_' ||
403         wch == '-') {
404       ++psz;
405     } else {
406       break;
407     }
408   }
409   return psz - pStart;
410 }
FromString(IFX_MEMAllocator * pStaticStore,const FX_WCHAR * psz,int32_t iLen)411 IFDE_CSSSelector* CFDE_CSSSelector::FromString(IFX_MEMAllocator* pStaticStore,
412                                                const FX_WCHAR* psz,
413                                                int32_t iLen) {
414   FXSYS_assert(pStaticStore != NULL && psz != NULL && iLen > 0);
415   const FX_WCHAR* pStart = psz;
416   const FX_WCHAR* pEnd = psz + iLen;
417   for (; psz < pEnd; ++psz) {
418     switch (*psz) {
419       case '>':
420       case '[':
421       case '+':
422         return NULL;
423     }
424   }
425   CFDE_CSSSelector *pFirst = NULL, *pLast = NULL;
426   CFDE_CSSSelector *pPersudoFirst = NULL, *pPersudoLast = NULL;
427   for (psz = pStart; psz < pEnd;) {
428     FX_WCHAR wch = *psz;
429     if (wch == '.' || wch == '#') {
430       if (psz == pStart || psz[-1] == ' ') {
431         CFDE_CSSSelector* p = FDE_NewWith(pStaticStore)
432             CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, L"*", 1, TRUE);
433         if (p == NULL) {
434           return NULL;
435         }
436         if (pFirst != NULL) {
437           pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
438           p->SetNext(pFirst);
439         }
440         pFirst = pLast = p;
441       }
442       FXSYS_assert(pLast != NULL);
443       int32_t iNameLen = FDE_GetCSSNameLen(++psz, pEnd);
444       if (iNameLen == 0) {
445         return NULL;
446       }
447       FDE_CSSSELECTORTYPE eType =
448           wch == '.' ? FDE_CSSSELECTORTYPE_Class : FDE_CSSSELECTORTYPE_ID;
449       CFDE_CSSSelector* p = FDE_NewWith(pStaticStore)
450           CFDE_CSSSelector(eType, psz, iNameLen, FALSE);
451       if (p == NULL) {
452         return NULL;
453       }
454       p->SetNext(pLast->GetNextSelector());
455       pLast->SetNext(p);
456       pLast = p;
457       psz += iNameLen;
458     } else if (FDE_IsCSSChar(wch) || wch == '*') {
459       int32_t iNameLen = wch == '*' ? 1 : FDE_GetCSSNameLen(psz, pEnd);
460       if (iNameLen == 0) {
461         return NULL;
462       }
463       CFDE_CSSSelector* p = FDE_NewWith(pStaticStore)
464           CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Element, psz, iNameLen, TRUE);
465       if (p == NULL) {
466         return NULL;
467       }
468       if (pFirst == NULL) {
469         pFirst = pLast = p;
470       } else {
471         pFirst->SetType(FDE_CSSSELECTORTYPE_Descendant);
472         p->SetNext(pFirst);
473         pFirst = pLast = p;
474       }
475       psz += iNameLen;
476     } else if (wch == ':') {
477       int32_t iNameLen = FDE_GetCSSPersudoLen(psz, pEnd);
478       if (iNameLen == 0) {
479         return NULL;
480       }
481       CFDE_CSSSelector* p = FDE_NewWith(pStaticStore)
482           CFDE_CSSSelector(FDE_CSSSELECTORTYPE_Persudo, psz, iNameLen, TRUE);
483       if (p == NULL) {
484         return NULL;
485       }
486       if (pPersudoFirst == NULL) {
487         pPersudoFirst = pPersudoLast = p;
488       } else {
489         pPersudoLast->SetNext(p);
490         pPersudoLast = p;
491       }
492       psz += iNameLen;
493     } else if (wch == ' ') {
494       psz++;
495     } else {
496       return NULL;
497     }
498   }
499   if (pPersudoFirst == NULL) {
500     return pFirst;
501   } else {
502     pPersudoLast->SetNext(pFirst);
503     return pPersudoFirst;
504   }
505 }
506