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