fix bug 4376, 'Litehtml breaks locale'
[claws.git] / src / plugins / litehtml_viewer / litehtml / css_selector.cpp
1 #include "html.h"
2 #include "css_selector.h"
3 #include "document.h"
4
5 void litehtml::css_element_selector::parse( const tstring& txt )
6 {
7         tstring::size_type el_end = txt.find_first_of(_t(".#[:"));
8         m_tag = txt.substr(0, el_end);
9         litehtml::lcase(m_tag);
10         while(el_end != tstring::npos)
11         {
12                 if(txt[el_end] == _t('.'))
13                 {
14                         css_attribute_selector attribute;
15
16                         tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1);
17                         attribute.val           = txt.substr(el_end + 1, pos - el_end - 1);
18                         split_string( attribute.val, attribute.class_val, _t(" ") );
19                         attribute.condition     = select_equal;
20                         attribute.attribute     = _t("class");
21                         m_attrs.push_back(attribute);
22                         el_end = pos;
23                 } else if(txt[el_end] == _t(':'))
24                 {
25                         css_attribute_selector attribute;
26
27                         if(txt[el_end + 1] == _t(':'))
28                         {
29                                 tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 2);
30                                 attribute.val           = txt.substr(el_end + 2, pos - el_end - 2);
31                                 attribute.condition     = select_pseudo_element;
32                                 litehtml::lcase(attribute.val);
33                                 attribute.attribute     = _t("pseudo-el");
34                                 m_attrs.push_back(attribute);
35                                 el_end = pos;
36                         } else
37                         {
38                                 tstring::size_type pos = txt.find_first_of(_t(".#[:("), el_end + 1);
39                                 if(pos != tstring::npos && txt.at(pos) == _t('('))
40                                 {
41                                         pos = find_close_bracket(txt, pos);
42                                         if(pos != tstring::npos)
43                                         {
44                                                 pos++;
45                                         } else
46                                         {
47                                                 int iii = 0;
48                                                 iii++;
49                                         }
50                                 }
51                                 if(pos != tstring::npos)
52                                 {
53                                         attribute.val           = txt.substr(el_end + 1, pos - el_end - 1);
54                                 } else
55                                 {
56                                         attribute.val           = txt.substr(el_end + 1);
57                                 }
58                                 litehtml::lcase(attribute.val);
59                                 if(attribute.val == _t("after") || attribute.val == _t("before"))
60                                 {
61                                         attribute.condition     = select_pseudo_element;
62                                 } else
63                                 {
64                                         attribute.condition     = select_pseudo_class;
65                                 }
66                                 attribute.attribute     = _t("pseudo");
67                                 m_attrs.push_back(attribute);
68                                 el_end = pos;
69                         }
70                 } else if(txt[el_end] == _t('#'))
71                 {
72                         css_attribute_selector attribute;
73
74                         tstring::size_type pos = txt.find_first_of(_t(".#[:"), el_end + 1);
75                         attribute.val           = txt.substr(el_end + 1, pos - el_end - 1);
76                         attribute.condition     = select_equal;
77                         attribute.attribute     = _t("id");
78                         m_attrs.push_back(attribute);
79                         el_end = pos;
80                 } else if(txt[el_end] == _t('['))
81                 {
82                         css_attribute_selector attribute;
83
84                         tstring::size_type pos = txt.find_first_of(_t("]~=|$*^"), el_end + 1);
85                         tstring attr = txt.substr(el_end + 1, pos - el_end - 1);
86                         trim(attr);
87                         litehtml::lcase(attr);
88                         if(pos != tstring::npos)
89                         {
90                                 if(txt[pos] == _t(']'))
91                                 {
92                                         attribute.condition = select_exists;
93                                 } else if(txt[pos] == _t('='))
94                                 {
95                                         attribute.condition = select_equal;
96                                         pos++;
97                                 } else if(txt.substr(pos, 2) == _t("~="))
98                                 {
99                                         attribute.condition = select_contain_str;
100                                         pos += 2;
101                                 } else if(txt.substr(pos, 2) == _t("|="))
102                                 {
103                                         attribute.condition = select_start_str;
104                                         pos += 2;
105                                 } else if(txt.substr(pos, 2) == _t("^="))
106                                 {
107                                         attribute.condition = select_start_str;
108                                         pos += 2;
109                                 } else if(txt.substr(pos, 2) == _t("$="))
110                                 {
111                                         attribute.condition = select_end_str;
112                                         pos += 2;
113                                 } else if(txt.substr(pos, 2) == _t("*="))
114                                 {
115                                         attribute.condition = select_contain_str;
116                                         pos += 2;
117                                 } else
118                                 {
119                                         attribute.condition = select_exists;
120                                         pos += 1;
121                                 }
122                                 pos = txt.find_first_not_of(_t(" \t"), pos);
123                                 if(pos != tstring::npos)
124                                 {
125                                         if(txt[pos] == _t('"'))
126                                         {
127                                                 tstring::size_type pos2 = txt.find_first_of(_t("\""), pos + 1);
128                                                 attribute.val = txt.substr(pos + 1, pos2 == tstring::npos ? pos2 : (pos2 - pos - 1));
129                                                 pos = pos2 == tstring::npos ? pos2 : (pos2 + 1);
130                                         } else if(txt[pos] == _t(']'))
131                                         {
132                                                 pos ++;
133                                         } else
134                                         {
135                                                 tstring::size_type pos2 = txt.find_first_of(_t("]"), pos + 1);
136                                                 attribute.val = txt.substr(pos, pos2 == tstring::npos ? pos2 : (pos2 - pos));
137                                                 trim(attribute.val);
138                                                 pos = pos2 == tstring::npos ? pos2 : (pos2 + 1);
139                                         }
140                                 }
141                         } else
142                         {
143                                 attribute.condition = select_exists;
144                         }
145                         attribute.attribute     = attr;
146                         m_attrs.push_back(attribute);
147                         el_end = pos;
148                 } else
149                 {
150                         el_end++;
151                 }
152                 el_end = txt.find_first_of(_t(".#[:"), el_end);
153         }
154 }
155
156
157 bool litehtml::css_selector::parse( const tstring& text )
158 {
159         if(text.empty())
160         {
161                 return false;
162         }
163         string_vector tokens;
164         split_string(text, tokens, _t(""), _t(" \t>+~"), _t("(["));
165
166         if(tokens.empty())
167         {
168                 return false;
169         }
170
171         tstring left;
172         tstring right = tokens.back();
173         tchar_t combinator = 0;
174
175         tokens.pop_back();
176         while(!tokens.empty() && (tokens.back() == _t(" ") || tokens.back() == _t("\t") || tokens.back() == _t("+") || tokens.back() == _t("~") || tokens.back() == _t(">")))
177         {
178                 if(combinator == _t(' ') || combinator == 0)
179                 {
180                         combinator = tokens.back()[0];
181                 }
182                 tokens.pop_back();
183         }
184
185         for(string_vector::const_iterator i = tokens.begin(); i != tokens.end(); i++)
186         {
187                 left += *i;
188         }
189
190         trim(left);
191         trim(right);
192
193         if(right.empty())
194         {
195                 return false;
196         }
197
198         m_right.parse(right);
199
200         switch(combinator)
201         {
202         case _t('>'):
203                 m_combinator    = combinator_child;
204                 break;
205         case _t('+'):
206                 m_combinator    = combinator_adjacent_sibling;
207                 break;
208         case _t('~'):
209                 m_combinator    = combinator_general_sibling;
210                 break;
211         default:
212                 m_combinator    = combinator_descendant;
213                 break;
214         }
215
216         m_left = 0;
217
218         if(!left.empty())
219         {
220                 m_left = std::make_shared<css_selector>(media_query_list::ptr(0));
221                 if(!m_left->parse(left))
222                 {
223                         return false;
224                 }
225         }
226
227         return true;
228 }
229
230 void litehtml::css_selector::calc_specificity()
231 {
232         if(!m_right.m_tag.empty() && m_right.m_tag != _t("*"))
233         {
234                 m_specificity.d = 1;
235         }
236         for(css_attribute_selector::vector::iterator i = m_right.m_attrs.begin(); i != m_right.m_attrs.end(); i++)
237         {
238                 if(i->attribute == _t("id"))
239                 {
240                         m_specificity.b++;
241                 } else
242                 {
243                         if(i->attribute == _t("class"))
244                         {
245                                 m_specificity.c += (int) i->class_val.size();
246                         } else
247                         {
248                                 m_specificity.c++;
249                         }
250                 }       
251         }
252         if(m_left)
253         {
254                 m_left->calc_specificity();
255                 m_specificity += m_left->m_specificity;
256         }
257 }
258
259 void litehtml::css_selector::add_media_to_doc( document* doc ) const
260 {
261         if(m_media_query && doc)
262         {
263                 doc->add_media_list(m_media_query);
264         }
265 }
266