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