blob: 4595396c132a6365398593b64f4340fa31cf5c0c [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),
28 m_eStatus(FDE_CSSSYNTAXSTATUS_None) {}
29CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() {
30 m_TextData.Reset();
31 m_TextPlane.Reset();
32}
33FX_BOOL CFDE_CSSSyntaxParser::Init(IFX_Stream* pStream,
34 int32_t iCSSPlaneSize,
35 int32_t iTextDataSize,
36 FX_BOOL bOnlyDeclaration) {
thestigcfb77cc2016-06-10 12:21:53 -070037 ASSERT(pStream && iCSSPlaneSize > 0 && iTextDataSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -040038 Reset(bOnlyDeclaration);
39 if (!m_TextData.EstimateSize(iTextDataSize)) {
40 return FALSE;
41 }
42 uint8_t bom[4];
43 m_pStream = pStream;
44 m_iStreamPos = m_pStream->GetBOM(bom);
45 m_iPlaneSize = iCSSPlaneSize;
46 return TRUE;
47}
48FX_BOOL CFDE_CSSSyntaxParser::Init(const FX_WCHAR* pBuffer,
49 int32_t iBufferSize,
50 int32_t iTextDatSize,
51 FX_BOOL bOnlyDeclaration) {
thestigcfb77cc2016-06-10 12:21:53 -070052 ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -040053 Reset(bOnlyDeclaration);
54 if (!m_TextData.EstimateSize(iTextDatSize)) {
55 return FALSE;
56 }
57 return m_TextPlane.AttachBuffer(pBuffer, iBufferSize);
58}
59void CFDE_CSSSyntaxParser::Reset(FX_BOOL bOnlyDeclaration) {
60 m_TextPlane.Reset();
61 m_TextData.Reset();
thestigcfb77cc2016-06-10 12:21:53 -070062 m_pStream = nullptr;
Dan Sinclair1770c022016-03-14 14:14:16 -040063 m_iStreamPos = 0;
64 m_iTextDatLen = 0;
tsepez736f28a2016-03-25 14:19:51 -070065 m_dwCheck = (uint32_t)-1;
Dan Sinclair1770c022016-03-14 14:14:16 -040066 m_eStatus = FDE_CSSSYNTAXSTATUS_None;
67 m_eMode = bOnlyDeclaration ? FDE_CSSSYNTAXMODE_PropertyName
68 : FDE_CSSSYNTAXMODE_RuleSet;
69}
70FDE_CSSSYNTAXSTATUS CFDE_CSSSyntaxParser::DoSyntaxParse() {
71 while (m_eStatus >= FDE_CSSSYNTAXSTATUS_None) {
72 if (m_TextPlane.IsEOF()) {
thestigcfb77cc2016-06-10 12:21:53 -070073 if (!m_pStream) {
Dan Sinclair1770c022016-03-14 14:14:16 -040074 if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
75 m_TextData.GetLength() > 0) {
76 SaveTextData();
77 return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
78 }
79 return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
80 }
81 FX_BOOL bEOS;
82 int32_t iLen = m_TextPlane.LoadFromStream(m_pStream, m_iStreamPos,
83 m_iPlaneSize, bEOS);
84 m_iStreamPos = m_pStream->GetPosition();
85 if (iLen < 1) {
86 if (m_eMode == FDE_CSSSYNTAXMODE_PropertyValue &&
87 m_TextData.GetLength() > 0) {
88 SaveTextData();
89 return m_eStatus = FDE_CSSSYNTAXSTATUS_PropertyValue;
90 }
91 return m_eStatus = FDE_CSSSYNTAXSTATUS_EOS;
92 }
93 }
94 FX_WCHAR wch;
95 while (!m_TextPlane.IsEOF()) {
96 wch = m_TextPlane.GetChar();
97 switch (m_eMode) {
98 case FDE_CSSSYNTAXMODE_RuleSet:
99 switch (wch) {
100 case '@':
101 m_TextPlane.MoveNext();
102 SwitchMode(FDE_CSSSYNTAXMODE_AtRule);
103 break;
104 case '}':
105 m_TextPlane.MoveNext();
106 if (RestoreMode()) {
107 return FDE_CSSSYNTAXSTATUS_DeclClose;
108 } else {
109 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
110 }
111 break;
112 case '/':
113 if (m_TextPlane.GetNextChar() == '*') {
114 m_ModeStack.Push(m_eMode);
115 SwitchMode(FDE_CSSSYNTAXMODE_Comment);
116 break;
117 }
118 default:
119 if (wch <= ' ') {
120 m_TextPlane.MoveNext();
121 } else if (FDE_IsSelectorStart(wch)) {
122 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
123 return FDE_CSSSYNTAXSTATUS_StyleRule;
124 } else {
125 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
126 }
127 break;
128 }
129 break;
130 case FDE_CSSSYNTAXMODE_Selector:
131 switch (wch) {
132 case ',':
133 m_TextPlane.MoveNext();
134 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
135 if (m_iTextDatLen > 0) {
136 return FDE_CSSSYNTAXSTATUS_Selector;
137 }
138 break;
139 case '{':
140 if (m_TextData.GetLength() > 0) {
141 SaveTextData();
142 return FDE_CSSSYNTAXSTATUS_Selector;
143 } else {
144 m_TextPlane.MoveNext();
145 m_ModeStack.Push(FDE_CSSSYNTAXMODE_RuleSet);
146 SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
147 return FDE_CSSSYNTAXSTATUS_DeclOpen;
148 }
149 break;
150 case '/':
151 if (m_TextPlane.GetNextChar() == '*') {
152 if (SwitchToComment() > 0) {
153 return FDE_CSSSYNTAXSTATUS_Selector;
154 }
155 break;
156 }
157 default:
158 AppendChar(wch);
159 break;
160 }
161 break;
162 case FDE_CSSSYNTAXMODE_PropertyName:
163 switch (wch) {
164 case ':':
165 m_TextPlane.MoveNext();
166 SwitchMode(FDE_CSSSYNTAXMODE_PropertyValue);
167 return FDE_CSSSYNTAXSTATUS_PropertyName;
168 case '}':
169 m_TextPlane.MoveNext();
170 if (RestoreMode()) {
171 return FDE_CSSSYNTAXSTATUS_DeclClose;
172 } else {
173 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
174 }
175 break;
176 case '/':
177 if (m_TextPlane.GetNextChar() == '*') {
178 if (SwitchToComment() > 0) {
179 return FDE_CSSSYNTAXSTATUS_PropertyName;
180 }
181 break;
182 }
183 default:
184 AppendChar(wch);
185 break;
186 }
187 break;
188 case FDE_CSSSYNTAXMODE_PropertyValue:
189 switch (wch) {
190 case ';':
191 m_TextPlane.MoveNext();
192 case '}':
193 SwitchMode(FDE_CSSSYNTAXMODE_PropertyName);
194 return FDE_CSSSYNTAXSTATUS_PropertyValue;
195 case '/':
196 if (m_TextPlane.GetNextChar() == '*') {
197 if (SwitchToComment() > 0) {
198 return FDE_CSSSYNTAXSTATUS_PropertyValue;
199 }
200 break;
201 }
202 default:
203 AppendChar(wch);
204 break;
205 }
206 break;
207 case FDE_CSSSYNTAXMODE_Comment:
208 if (wch == '/' && m_TextData.GetLength() > 0 &&
209 m_TextData.GetAt(m_TextData.GetLength() - 1) == '*') {
210 RestoreMode();
211 } else {
212 m_TextData.AppendChar(wch);
213 }
214 m_TextPlane.MoveNext();
215 break;
216 case FDE_CSSSYNTAXMODE_MediaType:
217 switch (wch) {
218 case ',':
219 m_TextPlane.MoveNext();
220 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
221 if (m_iTextDatLen > 0) {
222 return FDE_CSSSYNTAXSTATUS_MediaType;
223 }
224 break;
225 case '{': {
226 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700227 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_MediaRule)
Dan Sinclair1770c022016-03-14 14:14:16 -0400228 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700229
Dan Sinclair1770c022016-03-14 14:14:16 -0400230 if (m_TextData.GetLength() > 0) {
231 SaveTextData();
232 return FDE_CSSSYNTAXSTATUS_MediaType;
233 } else {
234 m_TextPlane.MoveNext();
235 *pMode = FDE_CSSSYNTAXMODE_RuleSet;
236 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
237 return FDE_CSSSYNTAXSTATUS_DeclOpen;
238 }
239 } break;
240 case ';': {
241 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700242 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_Import)
Dan Sinclair1770c022016-03-14 14:14:16 -0400243 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700244
Dan Sinclair1770c022016-03-14 14:14:16 -0400245 if (m_TextData.GetLength() > 0) {
246 SaveTextData();
247 if (IsImportEnabled()) {
248 return FDE_CSSSYNTAXSTATUS_MediaType;
249 }
250 } else {
251 FX_BOOL bEnabled = IsImportEnabled();
252 m_TextPlane.MoveNext();
253 m_ModeStack.Pop();
254 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
255 if (bEnabled) {
256 DisableImport();
257 return FDE_CSSSYNTAXSTATUS_ImportClose;
258 }
259 }
260 } break;
261 case '/':
262 if (m_TextPlane.GetNextChar() == '*') {
263 if (SwitchToComment() > 0) {
264 return FDE_CSSSYNTAXSTATUS_MediaType;
265 }
266 break;
267 }
268 default:
269 AppendChar(wch);
270 break;
271 }
272 break;
273 case FDE_CSSSYNTAXMODE_URI: {
274 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700275 if (!pMode || *pMode != FDE_CSSSYNTAXMODE_Import)
Dan Sinclair1770c022016-03-14 14:14:16 -0400276 return m_eStatus = FDE_CSSSYNTAXSTATUS_Error;
thestigcfb77cc2016-06-10 12:21:53 -0700277
Dan Sinclair1770c022016-03-14 14:14:16 -0400278 if (wch <= ' ' || wch == ';') {
279 int32_t iURIStart, iURILength = m_TextData.GetLength();
280 if (iURILength > 0 &&
281 FDE_ParseCSSURI(m_TextData.GetBuffer(), iURILength, iURIStart,
282 iURILength)) {
283 m_TextData.Subtract(iURIStart, iURILength);
284 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
285 if (IsImportEnabled()) {
286 return FDE_CSSSYNTAXSTATUS_URI;
287 } else {
288 break;
289 }
290 }
291 }
292 AppendChar(wch);
293 } break;
294 case FDE_CSSSYNTAXMODE_AtRule:
295 if (wch > ' ') {
296 AppendChar(wch);
297 } else {
298 int32_t iLen = m_TextData.GetLength();
299 const FX_WCHAR* psz = m_TextData.GetBuffer();
300 if (FXSYS_wcsncmp(L"charset", psz, iLen) == 0) {
301 SwitchMode(FDE_CSSSYNTAXMODE_Charset);
302 } else if (FXSYS_wcsncmp(L"import", psz, iLen) == 0) {
303 m_ModeStack.Push(FDE_CSSSYNTAXMODE_Import);
304 SwitchMode(FDE_CSSSYNTAXMODE_URI);
305 if (IsImportEnabled()) {
306 return FDE_CSSSYNTAXSTATUS_ImportRule;
307 } else {
308 break;
309 }
310 } else if (FXSYS_wcsncmp(L"media", psz, iLen) == 0) {
311 m_ModeStack.Push(FDE_CSSSYNTAXMODE_MediaRule);
312 SwitchMode(FDE_CSSSYNTAXMODE_MediaType);
313 return FDE_CSSSYNTAXSTATUS_MediaRule;
314 } else if (FXSYS_wcsncmp(L"font-face", psz, iLen) == 0) {
315 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
316 return FDE_CSSSYNTAXSTATUS_FontFaceRule;
317 } else if (FXSYS_wcsncmp(L"page", psz, iLen) == 0) {
318 SwitchMode(FDE_CSSSYNTAXMODE_Selector);
319 return FDE_CSSSYNTAXSTATUS_PageRule;
320 } else {
321 SwitchMode(FDE_CSSSYNTAXMODE_UnknownRule);
322 }
323 }
324 break;
325 case FDE_CSSSYNTAXMODE_Charset:
326 if (wch == ';') {
327 m_TextPlane.MoveNext();
328 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
329 if (IsCharsetEnabled()) {
330 DisableCharset();
331 if (m_iTextDatLen > 0) {
thestigcfb77cc2016-06-10 12:21:53 -0700332 if (m_pStream) {
dsinclairfc2cdf82016-05-19 18:07:11 -0700333 uint16_t wCodePage = FX_GetCodePageFromStringW(
Dan Sinclair1770c022016-03-14 14:14:16 -0400334 m_TextData.GetBuffer(), m_iTextDatLen);
335 if (wCodePage < 0xFFFF &&
336 m_pStream->GetCodePage() != wCodePage) {
337 m_pStream->SetCodePage(wCodePage);
338 }
339 }
340 return FDE_CSSSYNTAXSTATUS_Charset;
341 }
342 }
343 } else {
344 AppendChar(wch);
345 }
346 break;
347 case FDE_CSSSYNTAXMODE_UnknownRule:
348 if (wch == ';') {
349 SwitchMode(FDE_CSSSYNTAXMODE_RuleSet);
350 }
351 m_TextPlane.MoveNext();
352 break;
353 default:
dsinclair43854a52016-04-27 12:26:00 -0700354 ASSERT(FALSE);
Dan Sinclair1770c022016-03-14 14:14:16 -0400355 break;
356 }
357 }
358 }
359 return m_eStatus;
360}
361FX_BOOL CFDE_CSSSyntaxParser::IsImportEnabled() const {
362 if ((m_dwCheck & FDE_CSSSYNTAXCHECK_AllowImport) == 0) {
363 return FALSE;
364 }
365 if (m_ModeStack.GetSize() > 1) {
366 return FALSE;
367 }
368 return TRUE;
369}
thestigcfb77cc2016-06-10 12:21:53 -0700370FX_BOOL CFDE_CSSSyntaxParser::AppendChar(FX_WCHAR wch) {
Dan Sinclair1770c022016-03-14 14:14:16 -0400371 m_TextPlane.MoveNext();
372 if (m_TextData.GetLength() > 0 || wch > ' ') {
373 m_TextData.AppendChar(wch);
374 return TRUE;
375 }
376 return FALSE;
377}
thestigcfb77cc2016-06-10 12:21:53 -0700378int32_t CFDE_CSSSyntaxParser::SaveTextData() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400379 m_iTextDatLen = m_TextData.TrimEnd();
380 m_TextData.Clear();
381 return m_iTextDatLen;
382}
thestigcfb77cc2016-06-10 12:21:53 -0700383void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) {
Dan Sinclair1770c022016-03-14 14:14:16 -0400384 m_eMode = eMode;
385 SaveTextData();
386}
thestigcfb77cc2016-06-10 12:21:53 -0700387int32_t CFDE_CSSSyntaxParser::SwitchToComment() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400388 int32_t iLength = m_TextData.GetLength();
389 m_ModeStack.Push(m_eMode);
390 SwitchMode(FDE_CSSSYNTAXMODE_Comment);
391 return iLength;
392}
thestigcfb77cc2016-06-10 12:21:53 -0700393FX_BOOL CFDE_CSSSyntaxParser::RestoreMode() {
Dan Sinclair1770c022016-03-14 14:14:16 -0400394 FDE_CSSSYNTAXMODE* pMode = m_ModeStack.GetTopElement();
thestigcfb77cc2016-06-10 12:21:53 -0700395 if (!pMode)
Dan Sinclair1770c022016-03-14 14:14:16 -0400396 return FALSE;
thestigcfb77cc2016-06-10 12:21:53 -0700397
Dan Sinclair1770c022016-03-14 14:14:16 -0400398 SwitchMode(*pMode);
399 m_ModeStack.Pop();
400 return TRUE;
401}
402const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const {
403 iLength = m_iTextDatLen;
404 return m_TextData.GetBuffer();
405}
406CFDE_CSSTextBuf::CFDE_CSSTextBuf()
407 : m_bExtBuf(FALSE),
thestigcfb77cc2016-06-10 12:21:53 -0700408 m_pBuffer(nullptr),
Dan Sinclair1770c022016-03-14 14:14:16 -0400409 m_iBufLen(0),
410 m_iDatLen(0),
411 m_iDatPos(0) {}
412CFDE_CSSTextBuf::~CFDE_CSSTextBuf() {
413 Reset();
414}
415void CFDE_CSSTextBuf::Reset() {
416 if (!m_bExtBuf) {
417 FX_Free(m_pBuffer);
thestigcfb77cc2016-06-10 12:21:53 -0700418 m_pBuffer = nullptr;
Dan Sinclair1770c022016-03-14 14:14:16 -0400419 }
420 m_iDatPos = m_iDatLen = m_iBufLen;
421}
422FX_BOOL CFDE_CSSTextBuf::AttachBuffer(const FX_WCHAR* pBuffer,
423 int32_t iBufLen) {
424 Reset();
brucedawson2f109ab2016-05-27 16:13:13 -0700425 m_pBuffer = const_cast<FX_WCHAR*>(pBuffer);
Dan Sinclair1770c022016-03-14 14:14:16 -0400426 m_iDatLen = m_iBufLen = iBufLen;
427 return m_bExtBuf = TRUE;
428}
429FX_BOOL CFDE_CSSTextBuf::EstimateSize(int32_t iAllocSize) {
dsinclair43854a52016-04-27 12:26:00 -0700430 ASSERT(iAllocSize > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400431 Clear();
432 m_bExtBuf = FALSE;
433 return ExpandBuf(iAllocSize);
434}
435int32_t CFDE_CSSTextBuf::LoadFromStream(IFX_Stream* pTxtStream,
436 int32_t iStreamOffset,
437 int32_t iMaxChars,
438 FX_BOOL& bEOS) {
dsinclair43854a52016-04-27 12:26:00 -0700439 ASSERT(iStreamOffset >= 0 && iMaxChars > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400440 Clear();
441 m_bExtBuf = FALSE;
442 if (!ExpandBuf(iMaxChars)) {
443 return 0;
444 }
445 if (pTxtStream->GetPosition() != iStreamOffset) {
446 pTxtStream->Seek(FX_STREAMSEEK_Begin, iStreamOffset);
447 }
448 m_iDatLen = pTxtStream->ReadString(m_pBuffer, iMaxChars, bEOS);
449 return m_iDatLen;
450}
451FX_BOOL CFDE_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) {
452 if (m_bExtBuf) {
453 return FALSE;
454 }
455 if (!m_pBuffer) {
456 m_pBuffer = FX_Alloc(FX_WCHAR, iDesiredSize);
457 } else if (m_iBufLen != iDesiredSize) {
458 m_pBuffer = FX_Realloc(FX_WCHAR, m_pBuffer, iDesiredSize);
459 } else {
460 return TRUE;
461 }
462 if (!m_pBuffer) {
463 m_iBufLen = 0;
464 return FALSE;
465 }
466 m_iBufLen = iDesiredSize;
467 return TRUE;
468}
469void CFDE_CSSTextBuf::Subtract(int32_t iStart, int32_t iLength) {
dsinclair43854a52016-04-27 12:26:00 -0700470 ASSERT(iStart >= 0 && iLength > 0);
Dan Sinclair1770c022016-03-14 14:14:16 -0400471 if (iLength > m_iDatLen - iStart) {
472 iLength = m_iDatLen - iStart;
473 }
474 if (iLength < 0) {
475 iLength = 0;
476 } else {
477 FXSYS_memmove(m_pBuffer, m_pBuffer + iStart, iLength * sizeof(FX_WCHAR));
478 }
479 m_iDatLen = iLength;
480}