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 "core/fxcrt/css/cfx_csssyntaxparser.h" 8 9 #include <algorithm> 10 11 #include "core/fxcrt/css/cfx_cssdatatable.h" 12 #include "core/fxcrt/css/cfx_cssdeclaration.h" 13 #include "core/fxcrt/fx_codepage.h" 14 #include "core/fxcrt/fx_extension.h" 15 #include "third_party/base/logging.h" 16 17 namespace { 18 19 bool IsSelectorStart(wchar_t wch) { 20 return wch == '.' || wch == '#' || wch == '*' || FXSYS_iswalpha(wch); 21 } 22 23 } // namespace 24 25 CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer, 26 int32_t iBufferSize) 27 : CFX_CSSSyntaxParser(pBuffer, iBufferSize, 32, false) {} 28 29 CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer, 30 int32_t iBufferSize, 31 int32_t iTextDatSize, 32 bool bOnlyDeclaration) 33 : m_iTextDataLen(0), 34 m_dwCheck(0xFFFFFFFF), 35 m_eStatus(CFX_CSSSyntaxStatus::None) { 36 ASSERT(pBuffer && iBufferSize > 0 && iTextDatSize > 0); 37 m_eMode = bOnlyDeclaration ? CFX_CSSSyntaxMode::PropertyName 38 : CFX_CSSSyntaxMode::RuleSet; 39 m_TextData.InitWithSize(iTextDatSize); 40 m_TextPlane.AttachBuffer(pBuffer, iBufferSize); 41 } 42 43 CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() {} 44 45 CFX_CSSSyntaxStatus CFX_CSSSyntaxParser::DoSyntaxParse() { 46 while (m_eStatus >= CFX_CSSSyntaxStatus::None) { 47 if (m_TextPlane.IsEOF()) { 48 if (m_eMode == CFX_CSSSyntaxMode::PropertyValue && 49 m_TextData.GetLength() > 0) { 50 SaveTextData(); 51 m_eStatus = CFX_CSSSyntaxStatus::PropertyValue; 52 return m_eStatus; 53 } 54 m_eStatus = CFX_CSSSyntaxStatus::EOS; 55 return m_eStatus; 56 } 57 wchar_t wch; 58 while (!m_TextPlane.IsEOF()) { 59 wch = m_TextPlane.GetChar(); 60 switch (m_eMode) { 61 case CFX_CSSSyntaxMode::RuleSet: 62 switch (wch) { 63 case '}': 64 m_TextPlane.MoveNext(); 65 if (RestoreMode()) 66 return CFX_CSSSyntaxStatus::DeclClose; 67 68 m_eStatus = CFX_CSSSyntaxStatus::Error; 69 return m_eStatus; 70 case '/': 71 if (m_TextPlane.GetNextChar() == '*') { 72 m_ModeStack.push(m_eMode); 73 SwitchMode(CFX_CSSSyntaxMode::Comment); 74 break; 75 } 76 default: 77 if (wch <= ' ') { 78 m_TextPlane.MoveNext(); 79 } else if (IsSelectorStart(wch)) { 80 SwitchMode(CFX_CSSSyntaxMode::Selector); 81 return CFX_CSSSyntaxStatus::StyleRule; 82 } else { 83 m_eStatus = CFX_CSSSyntaxStatus::Error; 84 return m_eStatus; 85 } 86 break; 87 } 88 break; 89 case CFX_CSSSyntaxMode::Selector: 90 switch (wch) { 91 case ',': 92 m_TextPlane.MoveNext(); 93 SwitchMode(CFX_CSSSyntaxMode::Selector); 94 if (m_iTextDataLen > 0) 95 return CFX_CSSSyntaxStatus::Selector; 96 break; 97 case '{': 98 if (m_TextData.GetLength() > 0) { 99 SaveTextData(); 100 return CFX_CSSSyntaxStatus::Selector; 101 } 102 m_TextPlane.MoveNext(); 103 m_ModeStack.push(CFX_CSSSyntaxMode::RuleSet); 104 SwitchMode(CFX_CSSSyntaxMode::PropertyName); 105 return CFX_CSSSyntaxStatus::DeclOpen; 106 case '/': 107 if (m_TextPlane.GetNextChar() == '*') { 108 if (SwitchToComment() > 0) 109 return CFX_CSSSyntaxStatus::Selector; 110 break; 111 } 112 default: 113 AppendChar(wch); 114 break; 115 } 116 break; 117 case CFX_CSSSyntaxMode::PropertyName: 118 switch (wch) { 119 case ':': 120 m_TextPlane.MoveNext(); 121 SwitchMode(CFX_CSSSyntaxMode::PropertyValue); 122 return CFX_CSSSyntaxStatus::PropertyName; 123 case '}': 124 m_TextPlane.MoveNext(); 125 if (RestoreMode()) 126 return CFX_CSSSyntaxStatus::DeclClose; 127 128 m_eStatus = CFX_CSSSyntaxStatus::Error; 129 return m_eStatus; 130 case '/': 131 if (m_TextPlane.GetNextChar() == '*') { 132 if (SwitchToComment() > 0) 133 return CFX_CSSSyntaxStatus::PropertyName; 134 break; 135 } 136 default: 137 AppendChar(wch); 138 break; 139 } 140 break; 141 case CFX_CSSSyntaxMode::PropertyValue: 142 switch (wch) { 143 case ';': 144 m_TextPlane.MoveNext(); 145 case '}': 146 SwitchMode(CFX_CSSSyntaxMode::PropertyName); 147 return CFX_CSSSyntaxStatus::PropertyValue; 148 case '/': 149 if (m_TextPlane.GetNextChar() == '*') { 150 if (SwitchToComment() > 0) 151 return CFX_CSSSyntaxStatus::PropertyValue; 152 break; 153 } 154 default: 155 AppendChar(wch); 156 break; 157 } 158 break; 159 case CFX_CSSSyntaxMode::Comment: 160 if (wch == '/' && m_TextData.GetLength() > 0 && 161 m_TextData.GetBuffer()[m_TextData.GetLength() - 1] == '*') { 162 RestoreMode(); 163 } else { 164 m_TextData.AppendChar(wch); 165 } 166 m_TextPlane.MoveNext(); 167 break; 168 case CFX_CSSSyntaxMode::UnknownRule: 169 if (wch == ';') 170 SwitchMode(CFX_CSSSyntaxMode::RuleSet); 171 m_TextPlane.MoveNext(); 172 break; 173 default: 174 NOTREACHED(); 175 break; 176 } 177 } 178 } 179 return m_eStatus; 180 } 181 182 bool CFX_CSSSyntaxParser::IsImportEnabled() const { 183 if ((m_dwCheck & CFX_CSSSYNTAXCHECK_AllowImport) == 0) 184 return false; 185 if (m_ModeStack.size() > 1) 186 return false; 187 return true; 188 } 189 190 bool CFX_CSSSyntaxParser::AppendChar(wchar_t wch) { 191 m_TextPlane.MoveNext(); 192 if (m_TextData.GetLength() > 0 || wch > ' ') { 193 m_TextData.AppendChar(wch); 194 return true; 195 } 196 return false; 197 } 198 199 int32_t CFX_CSSSyntaxParser::SaveTextData() { 200 m_iTextDataLen = m_TextData.TrimEnd(); 201 m_TextData.Clear(); 202 return m_iTextDataLen; 203 } 204 205 void CFX_CSSSyntaxParser::SwitchMode(CFX_CSSSyntaxMode eMode) { 206 m_eMode = eMode; 207 SaveTextData(); 208 } 209 210 int32_t CFX_CSSSyntaxParser::SwitchToComment() { 211 int32_t iLength = m_TextData.GetLength(); 212 m_ModeStack.push(m_eMode); 213 SwitchMode(CFX_CSSSyntaxMode::Comment); 214 return iLength; 215 } 216 217 bool CFX_CSSSyntaxParser::RestoreMode() { 218 if (m_ModeStack.empty()) 219 return false; 220 221 SwitchMode(m_ModeStack.top()); 222 m_ModeStack.pop(); 223 return true; 224 } 225 226 WideStringView CFX_CSSSyntaxParser::GetCurrentString() const { 227 return WideStringView(m_TextData.GetBuffer(), m_iTextDataLen); 228 } 229