• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)17 bool IsSelectorStart(wchar_t wch) {
18   return wch == '.' || wch == '#' || wch == '*' ||
19          (isascii(wch) && isalpha(wch));
20 }
21 
22 }  // namespace
23 
CFX_CSSSyntaxParser(WideStringView str)24 CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(WideStringView str) : m_Input(str) {}
25 
26 CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() = default;
27 
SetParseOnlyDeclarations()28 void CFX_CSSSyntaxParser::SetParseOnlyDeclarations() {
29   m_eMode = Mode::kPropertyName;
30 }
31 
DoSyntaxParse()32 CFX_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)163 void CFX_CSSSyntaxParser::SaveMode(Mode mode) {
164   m_ModeStack.push(mode);
165 }
166 
RestoreMode()167 bool 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() const177 WideStringView CFX_CSSSyntaxParser::GetCurrentString() const {
178   return m_Output.GetTrailingBlankTrimmedString();
179 }
180