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