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 #ifdef _cplusplus
     11 extern "C" {
     12 #endif
     13 inline FX_BOOL FDE_IsSelectorStart(FX_WCHAR wch) {
     14   return wch == '.' || wch == '#' || wch == '*' || (wch >= 'a' && wch <= 'z') ||
     15          (wch >= 'A' && wch <= 'Z');
     16 }
     17 #ifdef _cplusplus
     18 };
     19 #endif
     20 IFDE_CSSSyntaxParser* IFDE_CSSSyntaxParser::Create() {
     21   return new CFDE_CSSSyntaxParser;
     22 }
     23 CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser()
     24     : m_pStream(NULL),
     25       m_iStreamPos(0),
     26       m_iPlaneSize(0),
     27       m_iTextDatLen(0),
     28       m_dwCheck((FX_DWORD)-1),
     29       m_eMode(FDE_CSSSYNTAXMODE_RuleSet),
     30       m_eStatus(FDE_CSSSYNTAXSTATUS_None) {}
     31 CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() {
     32   m_TextData.Reset();
     33   m_TextPlane.Reset();
     34 }
     35 FX_BOOL CFDE_CSSSyntaxParser::Init(IFX_Stream* pStream,
     36                                    int32_t iCSSPlaneSize,
     37                                    int32_t iTextDataSize,
     38                                    FX_BOOL bOnlyDeclaration) {
     39   FXSYS_assert(pStream != NULL && iCSSPlaneSize > 0 && iTextDataSize > 0);
     40   Reset(bOnlyDeclaration);
     41   if (!m_TextData.EstimateSize(iTextDataSize)) {
     42     return FALSE;
     43   }
     44   uint8_t bom[4];
     45   m_pStream = pStream;
     46   m_iStreamPos = m_pStream->GetBOM(bom);
     47   m_iPlaneSize = iCSSPlaneSize;
     48   return TRUE;
     49 }
     50 FX_BOOL CFDE_CSSSyntaxParser::Init(const FX_WCHAR* pBuffer,
     51                                    int32_t iBufferSize,
     52                                    int32_t iTextDatSize,
     53                                    FX_BOOL bOnlyDeclaration) {
     54   FXSYS_assert(pBuffer != NULL && iBufferSize > 0 && iTextDatSize > 0);
     55   Reset(bOnlyDeclaration);
     56   if (!m_TextData.EstimateSize(iTextDatSize)) {
     57     return FALSE;
     58   }
     59   return m_TextPlane.AttachBuffer(pBuffer, iBufferSize);
     60 }
     61 void CFDE_CSSSyntaxParser::Reset(FX_BOOL bOnlyDeclaration) {
     62   m_TextPlane.Reset();
     63   m_TextData.Reset();
     64   m_pStream = NULL;
     65   m_iStreamPos = 0;
     66   m_iTextDatLen = 0;
     67   m_dwCheck = (FX_DWORD)-1;
     68   m_eStatus = FDE_CSSSYNTAXSTATUS_None;
     69   m_eMode = bOnlyDeclaration ? FDE_CSSSYNTAXMODE_PropertyName
     70                              : FDE_CSSSYNTAXMODE_RuleSet;
     71 }
     72 FDE_CSSSYNTAXSTATUS CFDE_CSSSyntaxParser::DoSyntaxParse() {
     73   while (m_eStatus >= FDE_CSSSYNTAXSTATUS_None) {
     74     if (m_TextPlane.IsEOF()) {
     75       if (m_pStream == NULL) {
     76         if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
     77             m_TextData.GetLength() > 0) {
     78           SaveTextData();
     79           return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
     80         }
     81         return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
     82       }
     83       FX_BOOL bEOS;
     84       int32_t iLen = m_TextPlane.LoadFromStream(m_pStream, m_iStreamPos,
     85                                                 m_iPlaneSize, bEOS);
     86       m_iStreamPos = m_pStream->GetPosition();
     87       if (iLen < 1) {
     88         if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
     89             m_TextData.GetLength() > 0) {
     90           SaveTextData();
     91           return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
     92         }
     93         return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
     94       }
     95     }
     96     FX_WCHAR wch;
     97     while (!m_TextPlane.IsEOF()) {
     98       wch = m_TextPlane.GetChar();
     99       switch (m_eMode) {
    100         case FDE_CSSSYNTAXMODE_RuleSet:
    101           switch (wch) {
    102             case '@':
    103               m_TextPlane.MoveNext();
    104               SwitchMode(FDE_CSSSYNTAXMODE_AtRule);
    105               break;
    106             case '}':
    107               m_TextPlane.MoveNext();
    108               if (RestoreMode()) {
    109                 return FDE_CSSSYNTAXSTATUS_DeclClose;
    110               } else {
    111                 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    112               }
    113               break;
    114             case '/':
    115               if (m_TextPlane.GetNextChar() == '*') {
    116                 m_ModeStack.Push(m_eMode);
    117                 SwitchMode(FDE_CSSSYNTAXMODE_Comment);
    118                 break;
    119               }
    120             default:
    121               if (wch <= ' ') {
    122                 m_TextPlane.MoveNext();
    123               } else if (FDE_IsSelectorStart(wch)) {
    124                 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
    125                 return FDE_CSSSYNTAXSTATUS_StyleRule;
    126               } else {
    127                 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    128               }
    129               break;
    130           }
    131           break;
    132         case FDE_CSSSYNTAXMODE_Selector:
    133           switch (wch) {
    134             case ',':
    135               m_TextPlane.MoveNext();
    136               SwitchMode(FDE_CSSSYNTAXMODE_Selector);
    137               if (m_iTextDatLen > 0) {
    138                 return FDE_CSSSYNTAXSTATUS_Selector;
    139               }
    140               break;
    141             case '{':
    142               if (m_TextData.GetLength() > 0) {
    143                 SaveTextData();
    144                 return FDE_CSSSYNTAXSTATUS_Selector;
    145               } else {
    146                 m_TextPlane.MoveNext();
    147                 m_ModeStack.Push(FDE_CSSSYNTAXMODE_RuleSet);
    148                 SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
    149                 return FDE_CSSSYNTAXSTATUS_DeclOpen;
    150               }
    151               break;
    152             case '/':
    153               if (m_TextPlane.GetNextChar() == '*') {
    154                 if (SwitchToComment() > 0) {
    155                   return FDE_CSSSYNTAXSTATUS_Selector;
    156                 }
    157                 break;
    158               }
    159             default:
    160               AppendChar(wch);
    161               break;
    162           }
    163           break;
    164         case FDE_CSSSYNTAXMODE_PropertyName:
    165           switch (wch) {
    166             case ':':
    167               m_TextPlane.MoveNext();
    168               SwitchMode(FDE_CSSSYNTAXMODE_PropertyValue);
    169               return FDE_CSSSYNTAXSTATUS_PropertyName;
    170             case '}':
    171               m_TextPlane.MoveNext();
    172               if (RestoreMode()) {
    173                 return FDE_CSSSYNTAXSTATUS_DeclClose;
    174               } else {
    175                 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    176               }
    177               break;
    178             case '/':
    179               if (m_TextPlane.GetNextChar() == '*') {
    180                 if (SwitchToComment() > 0) {
    181                   return FDE_CSSSYNTAXSTATUS_PropertyName;
    182                 }
    183                 break;
    184               }
    185             default:
    186               AppendChar(wch);
    187               break;
    188           }
    189           break;
    190         case FDE_CSSSYNTAXMODE_PropertyValue:
    191           switch (wch) {
    192             case ';':
    193               m_TextPlane.MoveNext();
    194             case '}':
    195               SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
    196               return FDE_CSSSYNTAXSTATUS_PropertyValue;
    197             case '/':
    198               if (m_TextPlane.GetNextChar() == '*') {
    199                 if (SwitchToComment() > 0) {
    200                   return FDE_CSSSYNTAXSTATUS_PropertyValue;
    201                 }
    202                 break;
    203               }
    204             default:
    205               AppendChar(wch);
    206               break;
    207           }
    208           break;
    209         case FDE_CSSSYNTAXMODE_Comment:
    210           if (wch == '/' && m_TextData.GetLength() > 0 &&
    211               m_TextData.GetAt(m_TextData.GetLength() - 1) == '*') {
    212             RestoreMode();
    213           } else {
    214             m_TextData.AppendChar(wch);
    215           }
    216           m_TextPlane.MoveNext();
    217           break;
    218         case FDE_CSSSYNTAXMODE_MediaType:
    219           switch (wch) {
    220             case ',':
    221               m_TextPlane.MoveNext();
    222               SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
    223               if (m_iTextDatLen > 0) {
    224                 return FDE_CSSSYNTAXSTATUS_MediaType;
    225               }
    226               break;
    227             case '{': {
    228               FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
    229               if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_MediaRule) {
    230                 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    231               }
    232               if (m_TextData.GetLength() > 0) {
    233                 SaveTextData();
    234                 return FDE_CSSSYNTAXSTATUS_MediaType;
    235               } else {
    236                 m_TextPlane.MoveNext();
    237                 *pMode = FDE_CSSSYNTAXMODE_RuleSet;
    238                 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
    239                 return FDE_CSSSYNTAXSTATUS_DeclOpen;
    240               }
    241             } break;
    242             case ';': {
    243               FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
    244               if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_Import) {
    245                 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    246               }
    247               if (m_TextData.GetLength() > 0) {
    248                 SaveTextData();
    249                 if (IsImportEnabled()) {
    250                   return FDE_CSSSYNTAXSTATUS_MediaType;
    251                 }
    252               } else {
    253                 FX_BOOL bEnabled = IsImportEnabled();
    254                 m_TextPlane.MoveNext();
    255                 m_ModeStack.Pop();
    256                 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
    257                 if (bEnabled) {
    258                   DisableImport();
    259                   return FDE_CSSSYNTAXSTATUS_ImportClose;
    260                 }
    261               }
    262             } break;
    263             case '/':
    264               if (m_TextPlane.GetNextChar() == '*') {
    265                 if (SwitchToComment() > 0) {
    266                   return FDE_CSSSYNTAXSTATUS_MediaType;
    267                 }
    268                 break;
    269               }
    270             default:
    271               AppendChar(wch);
    272               break;
    273           }
    274           break;
    275         case FDE_CSSSYNTAXMODE_URI: {
    276           FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
    277           if (pMode == NULL || *pMode != FDE_CSSSYNTAXMODE_Import) {
    278             return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
    279           }
    280           if (wch <= ' ' || wch == ';') {
    281             int32_t iURIStart, iURILength = m_TextData.GetLength();
    282             if (iURILength > 0 &&
    283                 FDE_ParseCSSURI(m_TextData.GetBuffer(), iURILength, iURIStart,
    284                                 iURILength)) {
    285               m_TextData.Subtract(iURIStart, iURILength);
    286               SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
    287               if (IsImportEnabled()) {
    288                 return FDE_CSSSYNTAXSTATUS_URI;
    289               } else {
    290                 break;
    291               }
    292             }
    293           }
    294           AppendChar(wch);
    295         } break;
    296         case FDE_CSSSYNTAXMODE_AtRule:
    297           if (wch > ' ') {
    298             AppendChar(wch);
    299           } else {
    300             int32_t iLen = m_TextData.GetLength();
    301             const FX_WCHAR* psz = m_TextData.GetBuffer();
    302             if (FXSYS_wcsncmp(L"charset", psz, iLen) == 0) {
    303               SwitchMode(FDE_CSSSYNTAXMODE_Charset);
    304             } else if (FXSYS_wcsncmp(L"import", psz, iLen) == 0) {
    305               m_ModeStack.Push(FDE_CSSSYNTAXMODE_Import);
    306               SwitchMode(FDE_CSSSYNTAXMODE_URI);
    307               if (IsImportEnabled()) {
    308                 return FDE_CSSSYNTAXSTATUS_ImportRule;
    309               } else {
    310                 break;
    311               }
    312             } else if (FXSYS_wcsncmp(L"media", psz, iLen) == 0) {
    313               m_ModeStack.Push(FDE_CSSSYNTAXMODE_MediaRule);
    314               SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
    315               return FDE_CSSSYNTAXSTATUS_MediaRule;
    316             } else if (FXSYS_wcsncmp(L"font-face", psz, iLen) == 0) {
    317               SwitchMode(FDE_CSSSYNTAXMODE_Selector);
    318               return FDE_CSSSYNTAXSTATUS_FontFaceRule;
    319             } else if (FXSYS_wcsncmp(L"page", psz, iLen) == 0) {
    320               SwitchMode(FDE_CSSSYNTAXMODE_Selector);
    321               return FDE_CSSSYNTAXSTATUS_PageRule;
    322             } else {
    323               SwitchMode(FDE_CSSSYNTAXMODE_UnknownRule);
    324             }
    325           }
    326           break;
    327         case FDE_CSSSYNTAXMODE_Charset:
    328           if (wch == ';') {
    329             m_TextPlane.MoveNext();
    330             SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
    331             if (IsCharsetEnabled()) {
    332               DisableCharset();
    333               if (m_iTextDatLen > 0) {
    334                 if (m_pStream != NULL) {
    335                   FX_WORD wCodePage = FX_GetCodePageFormStringW(
    336                       m_TextData.GetBuffer(), m_iTextDatLen);
    337                   if (wCodePage < 0xFFFF &&
    338                       m_pStream->GetCodePage() != wCodePage) {
    339                     m_pStream->SetCodePage(wCodePage);
    340                   }
    341                 }
    342                 return FDE_CSSSYNTAXSTATUS_Charset;
    343               }
    344             }
    345           } else {
    346             AppendChar(wch);
    347           }
    348           break;
    349         case FDE_CSSSYNTAXMODE_UnknownRule:
    350           if (wch == ';') {
    351             SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
    352           }
    353           m_TextPlane.MoveNext();
    354           break;
    355         default:
    356           FXSYS_assert(FALSE);
    357           break;
    358       }
    359     }
    360   }
    361   return m_eStatus;
    362 }
    363 FX_BOOL CFDE_CSSSyntaxParser::IsImportEnabled() const {
    364   if ((m_dwCheck & FDE_CSSSYNTAXCHECK_AllowImport) == 0) {
    365     return FALSE;
    366   }
    367   if (m_ModeStack.GetSize() > 1) {
    368     return FALSE;
    369   }
    370   return TRUE;
    371 }
    372 inline FX_BOOL CFDE_CSSSyntaxParser::AppendChar(FX_WCHAR wch) {
    373   m_TextPlane.MoveNext();
    374   if (m_TextData.GetLength() > 0 || wch > ' ') {
    375     m_TextData.AppendChar(wch);
    376     return TRUE;
    377   }
    378   return FALSE;
    379 }
    380 inline int32_t CFDE_CSSSyntaxParser::SaveTextData() {
    381   m_iTextDatLen = m_TextData.TrimEnd();
    382   m_TextData.Clear();
    383   return m_iTextDatLen;
    384 }
    385 inline void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) {
    386   m_eMode = eMode;
    387   SaveTextData();
    388 }
    389 inline int32_t CFDE_CSSSyntaxParser::SwitchToComment() {
    390   int32_t iLength = m_TextData.GetLength();
    391   m_ModeStack.Push(m_eMode);
    392   SwitchMode(FDE_CSSSYNTAXMODE_Comment);
    393   return iLength;
    394 }
    395 inline FX_BOOL CFDE_CSSSyntaxParser::RestoreMode() {
    396   FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
    397   if (pMode == NULL) {
    398     return FALSE;
    399   }
    400   SwitchMode(*pMode);
    401   m_ModeStack.Pop();
    402   return TRUE;
    403 }
    404 const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const {
    405   iLength = m_iTextDatLen;
    406   return m_TextData.GetBuffer();
    407 }
    408 CFDE_CSSTextBuf::CFDE_CSSTextBuf()
    409     : m_bExtBuf(FALSE),
    410       m_pBuffer(NULL),
    411       m_iBufLen(0),
    412       m_iDatLen(0),
    413       m_iDatPos(0) {}
    414 CFDE_CSSTextBuf::~CFDE_CSSTextBuf() {
    415   Reset();
    416 }
    417 void CFDE_CSSTextBuf::Reset() {
    418   if (!m_bExtBuf) {
    419     FX_Free(m_pBuffer);
    420     m_pBuffer = NULL;
    421   }
    422   m_iDatPos = m_iDatLen = m_iBufLen;
    423 }
    424 FX_BOOL CFDE_CSSTextBuf::AttachBuffer(const FX_WCHAR* pBuffer,
    425                                       int32_t iBufLen) {
    426   Reset();
    427   m_pBuffer = (FX_WCHAR*)pBuffer;
    428   m_iDatLen = m_iBufLen = iBufLen;
    429   return m_bExtBuf = TRUE;
    430 }
    431 FX_BOOL CFDE_CSSTextBuf::EstimateSize(int32_t iAllocSize) {
    432   FXSYS_assert(iAllocSize > 0);
    433   Clear();
    434   m_bExtBuf = FALSE;
    435   return ExpandBuf(iAllocSize);
    436 }
    437 int32_t CFDE_CSSTextBuf::LoadFromStream(IFX_Stream* pTxtStream,
    438                                         int32_t iStreamOffset,
    439                                         int32_t iMaxChars,
    440                                         FX_BOOL& bEOS) {
    441   FXSYS_assert(iStreamOffset >= 0 && iMaxChars > 0);
    442   Clear();
    443   m_bExtBuf = FALSE;
    444   if (!ExpandBuf(iMaxChars)) {
    445     return 0;
    446   }
    447   pTxtStream->Lock();
    448   if (pTxtStream->GetPosition() != iStreamOffset) {
    449     pTxtStream->Seek(FX_STREAMSEEK_Begin, iStreamOffset);
    450   }
    451   m_iDatLen = pTxtStream->ReadString(m_pBuffer, iMaxChars, bEOS);
    452   pTxtStream->Unlock();
    453   return m_iDatLen;
    454 }
    455 FX_BOOL CFDE_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) {
    456   if (m_bExtBuf) {
    457     return FALSE;
    458   }
    459   if (!m_pBuffer) {
    460     m_pBuffer = FX_Alloc(FX_WCHAR, iDesiredSize);
    461   } else if (m_iBufLen != iDesiredSize) {
    462     m_pBuffer = FX_Realloc(FX_WCHAR, m_pBuffer, iDesiredSize);
    463   } else {
    464     return TRUE;
    465   }
    466   if (!m_pBuffer) {
    467     m_iBufLen = 0;
    468     return FALSE;
    469   }
    470   m_iBufLen = iDesiredSize;
    471   return TRUE;
    472 }
    473 void CFDE_CSSTextBuf::Subtract(int32_t iStart, int32_t iLength) {
    474   FXSYS_assert(iStart >= 0 && iLength > 0);
    475   if (iLength > m_iDatLen - iStart) {
    476     iLength = m_iDatLen - iStart;
    477   }
    478   if (iLength < 0) {
    479     iLength = 0;
    480   } else {
    481     FXSYS_memmove(m_pBuffer, m_pBuffer + iStart, iLength * sizeof(FX_WCHAR));
    482   }
    483   m_iDatLen = iLength;
    484 }
    485