blob: c2b0265a6ee6a63cfdbf086d2e6b024c7978b977 [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
thestigcfb77cc2016-06-10 12:21:53 -070012namespace {
Dan Sinclair1770c022016-03-14 14:14:16 -040013
thestigcfb77cc2016-06-10 12:21:53 -070014bool FDE_IsSelectorStart(FX_WCHAR wch) {
Dan Sinclair1770c022016-03-14 14:14:16 -040015 return wch == '.' || wch == '#' || wch == '*' || (wch >= 'a' && wch <= 'z') ||
16 (wch >= 'A' && wch <= 'Z');
17}
18
thestigcfb77cc2016-06-10 12:21:53 -070019} // namespace
Dan Sinclair1770c022016-03-14 14:14:16 -040020
Dan Sinclair1770c022016-03-14 14:14:16 -040021CFDE_CSSSyntaxParser::CFDE_CSSSyntaxParser()
thestigcfb77cc2016-06-10 12:21:53 -070022 : m_pStream(nullptr),
Dan Sinclair1770c022016-03-14 14:14:16 -040023 m_iStreamPos(0),
24 m_iPlaneSize(0),
25 m_iTextDatLen(0),
tsepez736f28a2016-03-25 14:19:51 -070026 m_dwCheck((uint32_t)-1),
Dan Sinclair1770c022016-03-14 14:14:16 -040027 m_eMode(FDE_CSSSYNTAXMODE_RuleSet),
dsinclair34965452016-07-18 13:14:49 -070028 m_eStatus(FDE_CSSSYNTAXSTATUS_None),
29 m_ModeStack(100) {}
30
Dan Sinclair1770c022016-03-14 14:14:16 -040031CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() {
32 m_TextData.Reset();
33 m_TextPlane.Reset();
34}
dsinclair34965452016-07-18 13:14:49 -070035
Dan Sinclair1770c022016-03-14 14:14:16 -040036FX_BOOL CFDE_CSSSyntaxParser::Init(IFX_Stream* pStream,
37 int32_t iCSSPlaneSize,
38 int32_t iTextDataSize,
39 FX_BOOL bOnlyDeclaration) {
thestigcfb77cc2016-06-10 12:21:53 -070040 ASSERT(pStream && iCSSPlaneSize > 0 && iTextDataSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -040041 Reset(bOnlyDeclaration);
42 if (!m_TextData.EstimateSize(iTextDataSize)) {
43 return FALSE;
44 }
45 uint8_t bom[4];
46 m_pStream = pStream;
47 m_iStreamPos = m_pStream->GetBOM(bom);
48 m_iPlaneSize = iCSSPlaneSize;
49 return TRUE;
50}
51FX_BOOL CFDE_CSSSyntaxParser::Init(const FX_WCHAR* pBuffer,
52 int32_t iBufferSize,
53 int32_t iTextDatSize,
54 FX_BOOL bOnlyDeclaration) {
thestigcfb77cc2016-06-10 12:21:53 -070055 ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -040056 Reset(bOnlyDeclaration);
57 if (!m_TextData.EstimateSize(iTextDatSize)) {
58 return FALSE;
59 }
60 return m_TextPlane.AttachBuffer(pBuffer, iBufferSize);
61}
62void CFDE_CSSSyntaxParser::Reset(FX_BOOL bOnlyDeclaration) {
63 m_TextPlane.Reset();
64 m_TextData.Reset();
thestigcfb77cc2016-06-10 12:21:53 -070065 m_pStream = nullptr;
Dan Sinclair1770c022016-03-14 14:14:16 -040066 m_iStreamPos = 0;
67 m_iTextDatLen = 0;
tsepez736f28a2016-03-25 14:19:51 -070068 m_dwCheck = (uint32_t)-1;
Dan Sinclair1770c022016-03-14 14:14:16 -040069 m_eStatus = FDE_CSSSYNTAXSTATUS_None;
70 m_eMode = bOnlyDeclaration ? FDE_CSSSYNTAXMODE_PropertyName
71 : FDE_CSSSYNTAXMODE_RuleSet;
72}
73FDE_CSSSYNTAXSTATUS CFDE_CSSSyntaxParser::DoSyntaxParse() {
74 while (m_eStatus >= FDE_CSSSYNTAXSTATUS_None) {
75 if (m_TextPlane.IsEOF()) {
thestigcfb77cc2016-06-10 12:21:53 -070076 if (!m_pStream) {
Dan Sinclair1770c022016-03-14 14:14:16 -040077 if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
78 m_TextData.GetLength() > 0) {
79 SaveTextData();
80 return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
81 }
82 return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
83 }
84 FX_BOOL bEOS;
85 int32_t iLen = m_TextPlane.LoadFromStream(m_pStream, m_iStreamPos,
86 m_iPlaneSize, bEOS);
87 m_iStreamPos = m_pStream->GetPosition();
88 if (iLen < 1) {
89 if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
90 m_TextData.GetLength() > 0) {
91 SaveTextData();
92 return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
93 }
94 return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
95 }
96 }
97 FX_WCHAR wch;
98 while (!m_TextPlane.IsEOF()) {
99 wch = m_TextPlane.GetChar();
100 switch (m_eMode) {
101 case FDE_CSSSYNTAXMODE_RuleSet:
102 switch (wch) {
103 case '@':
104 m_TextPlane.MoveNext();
105 SwitchMode(FDE_CSSSYNTAXMODE_AtRule);
106 break;
107 case '}':
108 m_TextPlane.MoveNext();
109 if (RestoreMode()) {
110 return FDE_CSSSYNTAXSTATUS_DeclClose;
111 } else {
112 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
113 }
114 break;
115 case '/':
116 if (m_TextPlane.GetNextChar() == '*') {
117 m_ModeStack.Push(m_eMode);
118 SwitchMode(FDE_CSSSYNTAXMODE_Comment);
119 break;
120 }
121 default:
122 if (wch <= ' ') {
123 m_TextPlane.MoveNext();
124 } else if (FDE_IsSelectorStart(wch)) {
125 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
126 return FDE_CSSSYNTAXSTATUS_StyleRule;
127 } else {
128 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
129 }
130 break;
131 }
132 break;
133 case FDE_CSSSYNTAXMODE_Selector:
134 switch (wch) {
135 case ',':
136 m_TextPlane.MoveNext();
137 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
138 if (m_iTextDatLen > 0) {
139 return FDE_CSSSYNTAXSTATUS_Selector;
140 }
141 break;
142 case '{':
143 if (m_TextData.GetLength() > 0) {
144 SaveTextData();
145 return FDE_CSSSYNTAXSTATUS_Selector;
146 } else {
147 m_TextPlane.MoveNext();
148 m_ModeStack.Push(FDE_CSSSYNTAXMODE_RuleSet);
149 SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
150 return FDE_CSSSYNTAXSTATUS_DeclOpen;
151 }
152 break;
153 case '/':
154 if (m_TextPlane.GetNextChar() == '*') {
155 if (SwitchToComment() > 0) {
156 return FDE_CSSSYNTAXSTATUS_Selector;
157 }
158 break;
159 }
160 default:
161 AppendChar(wch);
162 break;
163 }
164 break;
165 case FDE_CSSSYNTAXMODE_PropertyName:
166 switch (wch) {
167 case ':':
168 m_TextPlane.MoveNext();
169 SwitchMode(FDE_CSSSYNTAXMODE_PropertyValue);
170 return FDE_CSSSYNTAXSTATUS_PropertyName;
171 case '}':
172 m_TextPlane.MoveNext();
173 if (RestoreMode()) {
174 return FDE_CSSSYNTAXSTATUS_DeclClose;
175 } else {
176 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
177 }
178 break;
179 case '/':
180 if (m_TextPlane.GetNextChar() == '*') {
181 if (SwitchToComment() > 0) {
182 return FDE_CSSSYNTAXSTATUS_PropertyName;
183 }
184 break;
185 }
186 default:
187 AppendChar(wch);
188 break;
189 }
190 break;
191 case FDE_CSSSYNTAXMODE_PropertyValue:
192 switch (wch) {
193 case ';':
194 m_TextPlane.MoveNext();
195 case '}':
196 SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
197 return FDE_CSSSYNTAXSTATUS_PropertyValue;
198 case '/':
199 if (m_TextPlane.GetNextChar() == '*') {
200 if (SwitchToComment() > 0) {
201 return FDE_CSSSYNTAXSTATUS_PropertyValue;
202 }
203 break;
204 }
205 default:
206 AppendChar(wch);
207 break;
208 }
209 break;
210 case FDE_CSSSYNTAXMODE_Comment:
211 if (wch == '/' && m_TextData.GetLength() > 0 &&
212 m_TextData.GetAt(m_TextData.GetLength() - 1) == '*') {
213 RestoreMode();
214 } else {
215 m_TextData.AppendChar(wch);
216 }
217 m_TextPlane.MoveNext();
218 break;
219 case FDE_CSSSYNTAXMODE_MediaType:
220 switch (wch) {
221 case ',':
222 m_TextPlane.MoveNext();
223 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
224 if (m_iTextDatLen > 0) {
225 return FDE_CSSSYNTAXSTATUS_MediaType;
226 }
227 break;
228 case '{': {
229 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700230 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_MediaRule)
Dan Sinclair1770c022016-03-14 14:14:16 -0400231 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700232
Dan Sinclair1770c022016-03-14 14:14:16 -0400233 if (m_TextData.GetLength() > 0) {
234 SaveTextData();
235 return FDE_CSSSYNTAXSTATUS_MediaType;
236 } else {
237 m_TextPlane.MoveNext();
238 *pMode = FDE_CSSSYNTAXMODE_RuleSet;
239 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
240 return FDE_CSSSYNTAXSTATUS_DeclOpen;
241 }
242 } break;
243 case ';': {
244 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700245 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_Import)
Dan Sinclair1770c022016-03-14 14:14:16 -0400246 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700247
Dan Sinclair1770c022016-03-14 14:14:16 -0400248 if (m_TextData.GetLength() > 0) {
249 SaveTextData();
250 if (IsImportEnabled()) {
251 return FDE_CSSSYNTAXSTATUS_MediaType;
252 }
253 } else {
254 FX_BOOL bEnabled = IsImportEnabled();
255 m_TextPlane.MoveNext();
256 m_ModeStack.Pop();
257 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
258 if (bEnabled) {
259 DisableImport();
260 return FDE_CSSSYNTAXSTATUS_ImportClose;
261 }
262 }
263 } break;
264 case '/':
265 if (m_TextPlane.GetNextChar() == '*') {
266 if (SwitchToComment() > 0) {
267 return FDE_CSSSYNTAXSTATUS_MediaType;
268 }
269 break;
270 }
271 default:
272 AppendChar(wch);
273 break;
274 }
275 break;
276 case FDE_CSSSYNTAXMODE_URI: {
277 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700278 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_Import)
Dan Sinclair1770c022016-03-14 14:14:16 -0400279 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700280
Dan Sinclair1770c022016-03-14 14:14:16 -0400281 if (wch <= ' ' || wch == ';') {
282 int32_t iURIStart, iURILength = m_TextData.GetLength();
283 if (iURILength > 0 &&
284 FDE_ParseCSSURI(m_TextData.GetBuffer(), iURILength, iURIStart,
285 iURILength)) {
286 m_TextData.Subtract(iURIStart, iURILength);
287 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
288 if (IsImportEnabled()) {
289 return FDE_CSSSYNTAXSTATUS_URI;
290 } else {
291 break;
292 }
293 }
294 }
295 AppendChar(wch);
296 } break;
297 case FDE_CSSSYNTAXMODE_AtRule:
298 if (wch > ' ') {
299 AppendChar(wch);
300 } else {
301 int32_t iLen = m_TextData.GetLength();
302 const FX_WCHAR* psz = m_TextData.GetBuffer();
303 if (FXSYS_wcsncmp(L"charset", psz, iLen) == 0) {
304 SwitchMode(FDE_CSSSYNTAXMODE_Charset);
305 } else if (FXSYS_wcsncmp(L"import", psz, iLen) == 0) {
306 m_ModeStack.Push(FDE_CSSSYNTAXMODE_Import);
307 SwitchMode(FDE_CSSSYNTAXMODE_URI);
308 if (IsImportEnabled()) {
309 return FDE_CSSSYNTAXSTATUS_ImportRule;
310 } else {
311 break;
312 }
313 } else if (FXSYS_wcsncmp(L"media", psz, iLen) == 0) {
314 m_ModeStack.Push(FDE_CSSSYNTAXMODE_MediaRule);
315 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
316 return FDE_CSSSYNTAXSTATUS_MediaRule;
317 } else if (FXSYS_wcsncmp(L"font-face", psz, iLen) == 0) {
318 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
319 return FDE_CSSSYNTAXSTATUS_FontFaceRule;
320 } else if (FXSYS_wcsncmp(L"page", psz, iLen) == 0) {
321 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
322 return FDE_CSSSYNTAXSTATUS_PageRule;
323 } else {
324 SwitchMode(FDE_CSSSYNTAXMODE_UnknownRule);
325 }
326 }
327 break;
328 case FDE_CSSSYNTAXMODE_Charset:
329 if (wch == ';') {
330 m_TextPlane.MoveNext();
331 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
332 if (IsCharsetEnabled()) {
333 DisableCharset();
334 if (m_iTextDatLen > 0) {
thestigcfb77cc2016-06-10 12:21:53 -0700335 if (m_pStream) {
dsinclairfc2cdf82016-05-19 18:07:11 -0700336 uint16_t wCodePage = FX_GetCodePageFromStringW(
Dan Sinclair1770c022016-03-14 14:14:16 -0400337 m_TextData.GetBuffer(), m_iTextDatLen);
338 if (wCodePage < 0xFFFF &&
339 m_pStream->GetCodePage() != wCodePage) {
340 m_pStream->SetCodePage(wCodePage);
341 }
342 }
343 return FDE_CSSSYNTAXSTATUS_Charset;
344 }
345 }
346 } else {
347 AppendChar(wch);
348 }
349 break;
350 case FDE_CSSSYNTAXMODE_UnknownRule:
351 if (wch == ';') {
352 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
353 }
354 m_TextPlane.MoveNext();
355 break;
356 default:
dsinclair43854a52016-04-27 12:26:00 -0700357 ASSERT(FALSE);
Dan Sinclair1770c022016-03-14 14:14:16 -0400358 break;
359 }
360 }
361 }
362 return m_eStatus;
363}
364FX_BOOL CFDE_CSSSyntaxParser::IsImportEnabled() const {
365 if ((m_dwCheck & FDE_CSSSYNTAXCHECK_AllowImport) == 0) {
366 return FALSE;
367 }
368 if (m_ModeStack.GetSize() > 1) {
369 return FALSE;
370 }
371 return TRUE;
372}
thestigcfb77cc2016-06-10 12:21:53 -0700373FX_BOOL CFDE_CSSSyntaxParser::AppendChar(FX_WCHAR wch) {
Dan Sinclair1770c022016-03-14 14:14:16 -0400374 m_TextPlane.MoveNext();
375 if (m_TextData.GetLength() > 0 || wch > ' ') {
376 m_TextData.AppendChar(wch);
377 return TRUE;
378 }
379 return FALSE;
380}
thestigcfb77cc2016-06-10 12:21:53 -0700381int32_t CFDE_CSSSyntaxParser::SaveTextData() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400382 m_iTextDatLen = m_TextData.TrimEnd();
383 m_TextData.Clear();
384 return m_iTextDatLen;
385}
thestigcfb77cc2016-06-10 12:21:53 -0700386void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) {
Dan Sinclair1770c022016-03-14 14:14:16 -0400387 m_eMode = eMode;
388 SaveTextData();
389}
thestigcfb77cc2016-06-10 12:21:53 -0700390int32_t CFDE_CSSSyntaxParser::SwitchToComment() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400391 int32_t iLength = m_TextData.GetLength();
392 m_ModeStack.Push(m_eMode);
393 SwitchMode(FDE_CSSSYNTAXMODE_Comment);
394 return iLength;
395}
thestigcfb77cc2016-06-10 12:21:53 -0700396FX_BOOL CFDE_CSSSyntaxParser::RestoreMode() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400397 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700398 if (!pMode)
Dan Sinclair1770c022016-03-14 14:14:16 -0400399 return FALSE;
thestigcfb77cc2016-06-10 12:21:53 -0700400
Dan Sinclair1770c022016-03-14 14:14:16 -0400401 SwitchMode(*pMode);
402 m_ModeStack.Pop();
403 return TRUE;
404}
405const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const {
406 iLength = m_iTextDatLen;
407 return m_TextData.GetBuffer();
408}
409CFDE_CSSTextBuf::CFDE_CSSTextBuf()
410 : m_bExtBuf(FALSE),
thestigcfb77cc2016-06-10 12:21:53 -0700411 m_pBuffer(nullptr),
Dan Sinclair1770c022016-03-14 14:14:16 -0400412 m_iBufLen(0),
413 m_iDatLen(0),
414 m_iDatPos(0) {}
415CFDE_CSSTextBuf::~CFDE_CSSTextBuf() {
416 Reset();
417}
418void CFDE_CSSTextBuf::Reset() {
419 if (!m_bExtBuf) {
420 FX_Free(m_pBuffer);
thestigcfb77cc2016-06-10 12:21:53 -0700421 m_pBuffer = nullptr;
Dan Sinclair1770c022016-03-14 14:14:16 -0400422 }
423 m_iDatPos = m_iDatLen = m_iBufLen;
424}
425FX_BOOL CFDE_CSSTextBuf::AttachBuffer(const FX_WCHAR* pBuffer,
426 int32_t iBufLen) {
427 Reset();
brucedawson2f109ab2016-05-27 16:13:13 -0700428 m_pBuffer = const_cast<FX_WCHAR*>(pBuffer);
Dan Sinclair1770c022016-03-14 14:14:16 -0400429 m_iDatLen = m_iBufLen = iBufLen;
430 return m_bExtBuf = TRUE;
431}
432FX_BOOL CFDE_CSSTextBuf::EstimateSize(int32_t iAllocSize) {
dsinclair43854a52016-04-27 12:26:00 -0700433 ASSERT(iAllocSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400434 Clear();
435 m_bExtBuf = FALSE;
436 return ExpandBuf(iAllocSize);
437}
438int32_t CFDE_CSSTextBuf::LoadFromStream(IFX_Stream* pTxtStream,
439 int32_t iStreamOffset,
440 int32_t iMaxChars,
441 FX_BOOL& bEOS) {
dsinclair43854a52016-04-27 12:26:00 -0700442 ASSERT(iStreamOffset >= 0 && iMaxChars > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400443 Clear();
444 m_bExtBuf = FALSE;
445 if (!ExpandBuf(iMaxChars)) {
446 return 0;
447 }
448 if (pTxtStream->GetPosition() != iStreamOffset) {
449 pTxtStream->Seek(FX_STREAMSEEK_Begin, iStreamOffset);
450 }
451 m_iDatLen = pTxtStream->ReadString(m_pBuffer, iMaxChars, bEOS);
452 return m_iDatLen;
453}
454FX_BOOL CFDE_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) {
455 if (m_bExtBuf) {
456 return FALSE;
457 }
458 if (!m_pBuffer) {
459 m_pBuffer = FX_Alloc(FX_WCHAR, iDesiredSize);
460 } else if (m_iBufLen != iDesiredSize) {
461 m_pBuffer = FX_Realloc(FX_WCHAR, m_pBuffer, iDesiredSize);
462 } else {
463 return TRUE;
464 }
465 if (!m_pBuffer) {
466 m_iBufLen = 0;
467 return FALSE;
468 }
469 m_iBufLen = iDesiredSize;
470 return TRUE;
471}
472void CFDE_CSSTextBuf::Subtract(int32_t iStart, int32_t iLength) {
dsinclair43854a52016-04-27 12:26:00 -0700473 ASSERT(iStart >= 0 && iLength > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400474 if (iLength > m_iDatLen - iStart) {
475 iLength = m_iDatLen - iStart;
476 }
477 if (iLength < 0) {
478 iLength = 0;
479 } else {
480 FXSYS_memmove(m_pBuffer, m_pBuffer + iStart, iLength * sizeof(FX_WCHAR));
481 }
482 m_iDatLen = iLength;
483}