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 "core/fxcrt/css/cfx_csssyntaxparser.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "core/fxcrt/css/cfx_cssdatatable.h"
     12 #include "core/fxcrt/css/cfx_cssdeclaration.h"
     13 #include "core/fxcrt/fx_codepage.h"
     14 #include "core/fxcrt/fx_extension.h"
     15 #include "third_party/base/logging.h"
     16 
     17 namespace {
     18 
     19 bool IsSelectorStart(wchar_t wch) {
     20   return wch == '.' || wch == '#' || wch == '*' || FXSYS_iswalpha(wch);
     21 }
     22 
     23 }  // namespace
     24 
     25 CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer,
     26                                          int32_t iBufferSize)
     27     : CFX_CSSSyntaxParser(pBuffer, iBufferSize, 32, false) {}
     28 
     29 CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer,
     30                                          int32_t iBufferSize,
     31                                          int32_t iTextDatSize,
     32                                          bool bOnlyDeclaration)
     33     : m_iTextDataLen(0),
     34       m_dwCheck(0xFFFFFFFF),
     35       m_eStatus(CFX_CSSSyntaxStatus::None) {
     36   ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0);
     37   m_eMode = bOnlyDeclaration ? CFX_CSSSyntaxMode::PropertyName
     38                              : CFX_CSSSyntaxMode::RuleSet;
     39   m_TextData.InitWithSize(iTextDatSize);
     40   m_TextPlane.AttachBuffer(pBuffer, iBufferSize);
     41 }
     42 
     43 CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() {}
     44 
     45 CFX_CSSSyntaxStatus CFX_CSSSyntaxParser::DoSyntaxParse() {
     46   while (m_eStatus >= CFX_CSSSyntaxStatus::None) {
     47     if (m_TextPlane.IsEOF()) {
     48       if (m_eMode == CFX_CSSSyntaxMode::PropertyValue &&
     49           m_TextData.GetLength() > 0) {
     50         SaveTextData();
     51         m_eStatus = CFX_CSSSyntaxStatus::PropertyValue;
     52         return m_eStatus;
     53       }
     54       m_eStatus = CFX_CSSSyntaxStatus::EOS;
     55       return m_eStatus;
     56     }
     57     wchar_t wch;
     58     while (!m_TextPlane.IsEOF()) {
     59       wch = m_TextPlane.GetChar();
     60       switch (m_eMode) {
     61         case CFX_CSSSyntaxMode::RuleSet:
     62           switch (wch) {
     63             case '}':
     64               m_TextPlane.MoveNext();
     65               if (RestoreMode())
     66                 return CFX_CSSSyntaxStatus::DeclClose;
     67 
     68               m_eStatus = CFX_CSSSyntaxStatus::Error;
     69               return m_eStatus;
     70             case '/':
     71               if (m_TextPlane.GetNextChar() == '*') {
     72                 m_ModeStack.push(m_eMode);
     73                 SwitchMode(CFX_CSSSyntaxMode::Comment);
     74                 break;
     75               }
     76             default:
     77               if (wch <= ' ') {
     78                 m_TextPlane.MoveNext();
     79               } else if (IsSelectorStart(wch)) {
     80                 SwitchMode(CFX_CSSSyntaxMode::Selector);
     81                 return CFX_CSSSyntaxStatus::StyleRule;
     82               } else {
     83                 m_eStatus = CFX_CSSSyntaxStatus::Error;
     84                 return m_eStatus;
     85               }
     86               break;
     87           }
     88           break;
     89         case CFX_CSSSyntaxMode::Selector:
     90           switch (wch) {
     91             case ',':
     92               m_TextPlane.MoveNext();
     93               SwitchMode(CFX_CSSSyntaxMode::Selector);
     94               if (m_iTextDataLen > 0)
     95                 return CFX_CSSSyntaxStatus::Selector;
     96               break;
     97             case '{':
     98               if (m_TextData.GetLength() > 0) {
     99                 SaveTextData();
    100                 return CFX_CSSSyntaxStatus::Selector;
    101               }
    102               m_TextPlane.MoveNext();
    103               m_ModeStack.push(CFX_CSSSyntaxMode::RuleSet);
    104               SwitchMode(CFX_CSSSyntaxMode::PropertyName);
    105               return CFX_CSSSyntaxStatus::DeclOpen;
    106             case '/':
    107               if (m_TextPlane.GetNextChar() == '*') {
    108                 if (SwitchToComment() > 0)
    109                   return CFX_CSSSyntaxStatus::Selector;
    110                 break;
    111               }
    112             default:
    113               AppendChar(wch);
    114               break;
    115           }
    116           break;
    117         case CFX_CSSSyntaxMode::PropertyName:
    118           switch (wch) {
    119             case ':':
    120               m_TextPlane.MoveNext();
    121               SwitchMode(CFX_CSSSyntaxMode::PropertyValue);
    122               return CFX_CSSSyntaxStatus::PropertyName;
    123             case '}':
    124               m_TextPlane.MoveNext();
    125               if (RestoreMode())
    126                 return CFX_CSSSyntaxStatus::DeclClose;
    127 
    128               m_eStatus = CFX_CSSSyntaxStatus::Error;
    129               return m_eStatus;
    130             case '/':
    131               if (m_TextPlane.GetNextChar() == '*') {
    132                 if (SwitchToComment() > 0)
    133                   return CFX_CSSSyntaxStatus::PropertyName;
    134                 break;
    135               }
    136             default:
    137               AppendChar(wch);
    138               break;
    139           }
    140           break;
    141         case CFX_CSSSyntaxMode::PropertyValue:
    142           switch (wch) {
    143             case ';':
    144               m_TextPlane.MoveNext();
    145             case '}':
    146               SwitchMode(CFX_CSSSyntaxMode::PropertyName);
    147               return CFX_CSSSyntaxStatus::PropertyValue;
    148             case '/':
    149               if (m_TextPlane.GetNextChar() == '*') {
    150                 if (SwitchToComment() > 0)
    151                   return CFX_CSSSyntaxStatus::PropertyValue;
    152                 break;
    153               }
    154             default:
    155               AppendChar(wch);
    156               break;
    157           }
    158           break;
    159         case CFX_CSSSyntaxMode::Comment:
    160           if (wch == '/' && m_TextData.GetLength() > 0 &&
    161               m_TextData.GetBuffer()[m_TextData.GetLength() - 1] == '*') {
    162             RestoreMode();
    163           } else {
    164             m_TextData.AppendChar(wch);
    165           }
    166           m_TextPlane.MoveNext();
    167           break;
    168         case CFX_CSSSyntaxMode::UnknownRule:
    169           if (wch == ';')
    170             SwitchMode(CFX_CSSSyntaxMode::RuleSet);
    171           m_TextPlane.MoveNext();
    172           break;
    173         default:
    174           NOTREACHED();
    175           break;
    176       }
    177     }
    178   }
    179   return m_eStatus;
    180 }
    181 
    182 bool CFX_CSSSyntaxParser::IsImportEnabled() const {
    183   if ((m_dwCheck & CFX_CSSSYNTAXCHECK_AllowImport) == 0)
    184     return false;
    185   if (m_ModeStack.size() > 1)
    186     return false;
    187   return true;
    188 }
    189 
    190 bool CFX_CSSSyntaxParser::AppendChar(wchar_t wch) {
    191   m_TextPlane.MoveNext();
    192   if (m_TextData.GetLength() > 0 || wch > ' ') {
    193     m_TextData.AppendChar(wch);
    194     return true;
    195   }
    196   return false;
    197 }
    198 
    199 int32_t CFX_CSSSyntaxParser::SaveTextData() {
    200   m_iTextDataLen = m_TextData.TrimEnd();
    201   m_TextData.Clear();
    202   return m_iTextDataLen;
    203 }
    204 
    205 void CFX_CSSSyntaxParser::SwitchMode(CFX_CSSSyntaxMode eMode) {
    206   m_eMode = eMode;
    207   SaveTextData();
    208 }
    209 
    210 int32_t CFX_CSSSyntaxParser::SwitchToComment() {
    211   int32_t iLength = m_TextData.GetLength();
    212   m_ModeStack.push(m_eMode);
    213   SwitchMode(CFX_CSSSyntaxMode::Comment);
    214   return iLength;
    215 }
    216 
    217 bool CFX_CSSSyntaxParser::RestoreMode() {
    218   if (m_ModeStack.empty())
    219     return false;
    220 
    221   SwitchMode(m_ModeStack.top());
    222   m_ModeStack.pop();
    223   return true;
    224 }
    225 
    226 WideStringView CFX_CSSSyntaxParser::GetCurrentString() const {
    227   return WideStringView(m_TextData.GetBuffer(), m_iTextDataLen);
    228 }
    229