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