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 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 20 IFDE_CSSSyntaxParser* IFDE_CSSSyntaxParser::Create() { 21 return new CFDE_CSSSyntaxParser; 22 } 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) {} 31 CFDE_CSSSyntaxParser::~CFDE_CSSSyntaxParser() { 32 m_TextData.Reset(); 33 m_TextPlane.Reset(); 34 } 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 } 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 } 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 } 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 } 363 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 } 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 } 380 inline int32_t CFDE_CSSSyntaxParser::SaveTextData() { 381 m_iTextDatLen = m_TextData.TrimEnd(); 382 m_TextData.Clear(); 383 return m_iTextDatLen; 384 } 385 inline void CFDE_CSSSyntaxParser::SwitchMode(FDE_CSSSYNTAXMODE eMode) { 386 m_eMode = eMode; 387 SaveTextData(); 388 } 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 } 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 } 404 const FX_WCHAR* CFDE_CSSSyntaxParser::GetCurrentString(int32_t& iLength) const { 405 iLength = m_iTextDatLen; 406 return m_TextData.GetBuffer(); 407 } 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) {} 414 CFDE_CSSTextBuf::~CFDE_CSSTextBuf() { 415 Reset(); 416 } 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 } 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 } 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 } 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 } 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 } 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