1 // Copyright 2014 The PDFium Authors 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 "core/fxcrt/css/cfx_cssdata.h" 10 #include "core/fxcrt/css/cfx_cssdeclaration.h" 11 #include "core/fxcrt/fx_codepage.h" 12 #include "core/fxcrt/fx_extension.h" 13 #include "third_party/base/notreached.h" 14 15 namespace { 16 IsSelectorStart(wchar_t wch)17bool IsSelectorStart(wchar_t wch) { 18 return wch == '.' || wch == '#' || wch == '*' || 19 (isascii(wch) && isalpha(wch)); 20 } 21 22 } // namespace 23 CFX_CSSSyntaxParser(WideStringView str)24CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(WideStringView str) : m_Input(str) {} 25 26 CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() = default; 27 SetParseOnlyDeclarations()28void CFX_CSSSyntaxParser::SetParseOnlyDeclarations() { 29 m_eMode = Mode::kPropertyName; 30 } 31 DoSyntaxParse()32CFX_CSSSyntaxParser::Status CFX_CSSSyntaxParser::DoSyntaxParse() { 33 m_Output.Clear(); 34 if (m_bHasError) 35 return Status::kError; 36 37 while (!m_Input.IsEOF()) { 38 wchar_t wch = m_Input.GetChar(); 39 switch (m_eMode) { 40 case Mode::kRuleSet: 41 switch (wch) { 42 case '}': 43 m_bHasError = true; 44 return Status::kError; 45 case '/': 46 if (m_Input.GetNextChar() == '*') { 47 SaveMode(Mode::kRuleSet); 48 m_eMode = Mode::kComment; 49 break; 50 } 51 [[fallthrough]]; 52 default: 53 if (wch <= ' ') { 54 m_Input.MoveNext(); 55 } else if (IsSelectorStart(wch)) { 56 m_eMode = Mode::kSelector; 57 return Status::kStyleRule; 58 } else { 59 m_bHasError = true; 60 return Status::kError; 61 } 62 break; 63 } 64 break; 65 case Mode::kSelector: 66 switch (wch) { 67 case ',': 68 m_Input.MoveNext(); 69 if (!m_Output.IsEmpty()) 70 return Status::kSelector; 71 break; 72 case '{': 73 if (!m_Output.IsEmpty()) 74 return Status::kSelector; 75 m_Input.MoveNext(); 76 SaveMode(Mode::kRuleSet); // Back to validate ruleset again. 77 m_eMode = Mode::kPropertyName; 78 return Status::kDeclOpen; 79 case '/': 80 if (m_Input.GetNextChar() == '*') { 81 SaveMode(Mode::kSelector); 82 m_eMode = Mode::kComment; 83 if (!m_Output.IsEmpty()) 84 return Status::kSelector; 85 break; 86 } 87 [[fallthrough]]; 88 default: 89 m_Output.AppendCharIfNotLeadingBlank(wch); 90 m_Input.MoveNext(); 91 break; 92 } 93 break; 94 case Mode::kPropertyName: 95 switch (wch) { 96 case ':': 97 m_Input.MoveNext(); 98 m_eMode = Mode::kPropertyValue; 99 return Status::kPropertyName; 100 case '}': 101 m_Input.MoveNext(); 102 if (!RestoreMode()) 103 return Status::kError; 104 105 return Status::kDeclClose; 106 case '/': 107 if (m_Input.GetNextChar() == '*') { 108 SaveMode(Mode::kPropertyName); 109 m_eMode = Mode::kComment; 110 if (!m_Output.IsEmpty()) 111 return Status::kPropertyName; 112 break; 113 } 114 [[fallthrough]]; 115 default: 116 m_Output.AppendCharIfNotLeadingBlank(wch); 117 m_Input.MoveNext(); 118 break; 119 } 120 break; 121 case Mode::kPropertyValue: 122 switch (wch) { 123 case ';': 124 m_Input.MoveNext(); 125 [[fallthrough]]; 126 case '}': 127 m_eMode = Mode::kPropertyName; 128 return Status::kPropertyValue; 129 case '/': 130 if (m_Input.GetNextChar() == '*') { 131 SaveMode(Mode::kPropertyValue); 132 m_eMode = Mode::kComment; 133 if (!m_Output.IsEmpty()) 134 return Status::kPropertyValue; 135 break; 136 } 137 [[fallthrough]]; 138 default: 139 m_Output.AppendCharIfNotLeadingBlank(wch); 140 m_Input.MoveNext(); 141 break; 142 } 143 break; 144 case Mode::kComment: 145 if (wch == '*' && m_Input.GetNextChar() == '/') { 146 if (!RestoreMode()) 147 return Status::kError; 148 m_Input.MoveNext(); 149 } 150 m_Input.MoveNext(); 151 break; 152 default: 153 NOTREACHED(); 154 break; 155 } 156 } 157 if (m_eMode == Mode::kPropertyValue && !m_Output.IsEmpty()) 158 return Status::kPropertyValue; 159 160 return Status::kEOS; 161 } 162 SaveMode(Mode mode)163void CFX_CSSSyntaxParser::SaveMode(Mode mode) { 164 m_ModeStack.push(mode); 165 } 166 RestoreMode()167bool CFX_CSSSyntaxParser::RestoreMode() { 168 if (m_ModeStack.empty()) { 169 m_bHasError = true; 170 return false; 171 } 172 m_eMode = m_ModeStack.top(); 173 m_ModeStack.pop(); 174 return true; 175 } 176 GetCurrentString() const177WideStringView CFX_CSSSyntaxParser::GetCurrentString() const { 178 return m_Output.GetTrailingBlankTrimmedString(); 179 } 180