Home | History | Annotate | Download | only in css
      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"
     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 }
     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 }
     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 }
     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 }
     77 CFDE_CSSStyleSheet::~CFDE_CSSStyleSheet() {
     78   Reset();
     79 }
     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 }
    106 FX_DWORD CFDE_CSSStyleSheet::AddRef() {
    107   return ++m_wRefCount;
    108 }
    109 FX_DWORD CFDE_CSSStyleSheet::Release() {
    110   FX_DWORD dwRefCount = --m_wRefCount;
    111   if (dwRefCount == 0) {
    112     delete this;
    113   }
    114   return dwRefCount;
    115 }
    116 int32_t CFDE_CSSStyleSheet::CountRules() const {
    117   return m_RuleArray.GetSize();
    118 }
    119 IFDE_CSSRule* CFDE_CSSStyleSheet::GetRule(int32_t index) {
    120   return m_RuleArray.GetAt(index);
    121 }
    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 }
    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 }
    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 }
    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 }
    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 }
    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 }
    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 }
    339 FDE_CSSSYNTAXSTATUS CFDE_CSSStyleSheet::LoadPageRule(
    340     IFDE_CSSSyntaxParser* pSyntax) {
    341   return SkipRuleSet(pSyntax);
    342 }
    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 }
    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 }
    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 }
    382 inline FX_BOOL FDE_IsCSSChar(FX_WCHAR wch) {
    383   return (wch >= 'a' && wch <= 'z') || (wch >= 'A' && wch <= 'Z');
    384 }
    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 }
    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 }
    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