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