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