Update litehtml_viewer to build on Windows
[claws.git] / src / plugins / litehtml_viewer / litehtml / stylesheet.cpp
1 #include "html.h"
2 #include "stylesheet.h"
3 #include <algorithm>
4 #include "document.h"
5
6
7 void litehtml::css::parse_stylesheet(const tchar_t* str, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media)
8 {
9         tstring text = str;
10
11         // remove comments
12         tstring::size_type c_start = text.find(_t("/*"));
13         while(c_start != tstring::npos)
14         {
15                 tstring::size_type c_end = text.find(_t("*/"), c_start + 2);
16                 text.erase(c_start, c_end - c_start + 2);
17                 c_start = text.find(_t("/*"));
18         }
19
20         tstring::size_type pos = text.find_first_not_of(_t(" \n\r\t"));
21         while(pos != tstring::npos)
22         {
23                 while(pos != tstring::npos && text[pos] == _t('@'))
24                 {
25                         tstring::size_type sPos = pos;
26                         pos = text.find_first_of(_t("{;"), pos);
27                         if(pos != tstring::npos && text[pos] == _t('{'))
28                         {
29                                 pos = find_close_bracket(text, pos, _t('{'), _t('}'));
30                         }
31                         if(pos != tstring::npos)
32                         {
33                                 parse_atrule(text.substr(sPos, pos - sPos + 1), baseurl, doc, media);
34                         } else
35                         {
36                                 parse_atrule(text.substr(sPos), baseurl, doc, media);
37                         }
38
39                         if(pos != tstring::npos)
40                         {
41                                 pos = text.find_first_not_of(_t(" \n\r\t"), pos + 1);
42                         }
43                 }
44
45                 if(pos == tstring::npos)
46                 {
47                         break;
48                 }
49
50                 tstring::size_type style_start = text.find(_t("{"), pos);
51                 tstring::size_type style_end    = text.find(_t("}"), pos);
52                 if(style_start != tstring::npos && style_end != tstring::npos)
53                 {
54                         style::ptr st = std::make_shared<style>();
55                         st->add(text.substr(style_start + 1, style_end - style_start - 1).c_str(), baseurl);
56
57                         parse_selectors(text.substr(pos, style_start - pos), st, media);
58
59                         if(media && doc)
60                         {
61                                 doc->add_media_list(media);
62                         }
63
64                         pos = style_end + 1;
65                 } else
66                 {
67                         pos = tstring::npos;
68                 }
69
70                 if(pos != tstring::npos)
71                 {
72                         pos = text.find_first_not_of(_t(" \n\r\t"), pos);
73                 }
74         }
75 }
76
77 void litehtml::css::parse_css_url( const tstring& str, tstring& url )
78 {
79         url = _t("");
80         size_t pos1 = str.find(_t('('));
81         size_t pos2 = str.find(_t(')'));
82         if(pos1 != tstring::npos && pos2 != tstring::npos)
83         {
84                 url = str.substr(pos1 + 1, pos2 - pos1 - 1);
85                 if(url.length())
86                 {
87                         if(url[0] == _t('\'') || url[0] == _t('"'))
88                         {
89                                 url.erase(0, 1);
90                         }
91                 }
92                 if(url.length())
93                 {
94                         if(url[url.length() - 1] == _t('\'') || url[url.length() - 1] == _t('"'))
95                         {
96                                 url.erase(url.length() - 1, 1);
97                         }
98                 }
99         }
100 }
101
102 bool litehtml::css::parse_selectors( const tstring& txt, const litehtml::style::ptr& styles, const media_query_list::ptr& media )
103 {
104         tstring selector = txt;
105         trim(selector);
106         string_vector tokens;
107         split_string(selector, tokens, _t(","));
108
109         bool added_something = false;
110
111         for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++)
112         {
113                 css_selector::ptr selector = std::make_shared<css_selector>(media);
114                 selector->m_style = styles;
115                 trim(*tok);
116                 if(selector->parse(*tok))
117                 {
118                         selector->calc_specificity();
119                         add_selector(selector);
120                         added_something = true;
121                 }
122         }
123
124         return added_something;
125 }
126
127 void litehtml::css::sort_selectors()
128 {
129         std::sort(m_selectors.begin(), m_selectors.end(),
130                  [](const css_selector::ptr& v1, const css_selector::ptr& v2)
131                  {
132                          return (*v1) < (*v2);
133                  }
134         );
135 }
136
137 void litehtml::css::parse_atrule(const tstring& text, const tchar_t* baseurl, const std::shared_ptr<document>& doc, const media_query_list::ptr& media)
138 {
139         if(text.substr(0, 7) == _t("@import"))
140         {
141                 int sPos = 7;
142                 tstring iStr;
143                 iStr = text.substr(sPos);
144                 if(iStr[iStr.length() - 1] == _t(';'))
145                 {
146                         iStr.erase(iStr.length() - 1);
147                 }
148                 trim(iStr);
149                 string_vector tokens;
150                 split_string(iStr, tokens, _t(" "), _t(""), _t("(\""));
151                 if(!tokens.empty())
152                 {
153                         tstring url;
154                         parse_css_url(tokens.front(), url);
155                         if(url.empty())
156                         {
157                                 url = tokens.front();
158                         }
159                         tokens.erase(tokens.begin());
160                         if(doc)
161                         {
162                                 document_container* doc_cont = doc->container();
163                                 if(doc_cont)
164                                 {
165                                         tstring css_text;
166                                         tstring css_baseurl;
167                                         if(baseurl)
168                                         {
169                                                 css_baseurl = baseurl;
170                                         }
171                                         doc_cont->import_css(css_text, url, css_baseurl);
172                                         if(!css_text.empty())
173                                         {
174                                                 media_query_list::ptr new_media = media;
175                                                 if(!tokens.empty())
176                                                 {
177                                                         tstring media_str;
178                                                         for(string_vector::iterator iter = tokens.begin(); iter != tokens.end(); iter++)
179                                                         {
180                                                                 if(iter != tokens.begin())
181                                                                 {
182                                                                         media_str += _t(" ");
183                                                                 }
184                                                                 media_str += (*iter);
185                                                         }
186                                                         new_media = media_query_list::create_from_string(media_str, doc);
187                                                         if(!new_media)
188                                                         {
189                                                                 new_media = media;
190                                                         }
191                                                 }
192                                                 parse_stylesheet(css_text.c_str(), css_baseurl.c_str(), doc, new_media);
193                                         }
194                                 }
195                         }
196                 }
197         } else if(text.substr(0, 6) == _t("@media"))
198         {
199                 tstring::size_type b1 = text.find_first_of(_t('{'));
200                 tstring::size_type b2 = text.find_last_of(_t('}'));
201                 if(b1 != tstring::npos)
202                 {
203                         tstring media_type = text.substr(6, b1 - 6);
204                         trim(media_type);
205                         media_query_list::ptr new_media = media_query_list::create_from_string(media_type, doc);
206
207                         tstring media_style;
208                         if(b2 != tstring::npos)
209                         {
210                                 media_style = text.substr(b1 + 1, b2 - b1 - 1);
211                         } else
212                         {
213                                 media_style = text.substr(b1 + 1);
214                         }
215
216                         parse_stylesheet(media_style.c_str(), baseurl, doc, new_media);
217                 }
218         }
219 }