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
FDE_IsSelectorStart(FX_WCHAR wch)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
Create()20 IFDE_CSSSyntaxParser* IFDE_CSSSyntaxParser::Create() {
21   return new CFDE_CSSSyntaxParser;
22 }
CFDE_CSSSyntaxParser()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) {}
~CFDE_CSSSyntaxParser()31 CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() {
32   m_TextData.Reset();
33   m_TextPlane.Reset();
34 }
Init(IFX_Stream * pStream,int32_t iCSSPlaneSize,int32_t iTextDataSize,FX_BOOL bOnlyDeclaration)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 }
Init(const FX_WCHAR * pBuffer,int32_t iBufferSize,int32_t iTextDatSize,FX_BOOL bOnlyDeclaration)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 }
Reset(FX_BOOL bOnlyDeclaration)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 }
DoSyntaxParse()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 }
IsImportEnabled() const363 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 }
AppendChar(FX_WCHAR wch)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 }
SaveTextData()380 inline int32_t CFDE_CSSSyntaxParser::SaveTextData() {
381   m_iTextDatLen = m_TextData.TrimEnd();
382   m_TextData.Clear();
383   return m_iTextDatLen;
384 }
SwitchMode(FDE_CSSSYNTAXMODE eMode)385 inline void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) {
386   m_eMode = eMode;
387   SaveTextData();
388 }
SwitchToComment()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 }
RestoreMode()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 }
GetCurrentString(int32_t & iLength) const404 const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const {
405   iLength = m_iTextDatLen;
406   return m_TextData.GetBuffer();
407 }
CFDE_CSSTextBuf()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) {}
~CFDE_CSSTextBuf()414 CFDE_CSSTextBuf::~CFDE_CSSTextBuf() {
415   Reset();
416 }
Reset()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 }
AttachBuffer(const FX_WCHAR * pBuffer,int32_t iBufLen)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 }
EstimateSize(int32_t iAllocSize)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 }
LoadFromStream(IFX_Stream * pTxtStream,int32_t iStreamOffset,int32_t iMaxChars,FX_BOOL & bEOS)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 }
ExpandBuf(int32_t iDesiredSize)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 }
Subtract(int32_t iStart,int32_t iLength)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