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