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