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