#include "html.h" #include "stylesheet.h" #include #include "document.h" #include "gradient.h" #ifndef M_PI # define M_PI 3.14159265358979323846 #endif void litehtml::css::parse_stylesheet(const char* str, const char* baseurl, const std::shared_ptr& doc, const media_query_list::ptr& media) { string text = str; // remove comments string::size_type c_start = text.find("/*"); while(c_start != string::npos) { string::size_type c_end = text.find("*/", c_start + 2); if(c_end == string::npos) { text.erase(c_start); break; } text.erase(c_start, c_end - c_start + 2); c_start = text.find("/*"); } string::size_type pos = text.find_first_not_of(" \n\r\t"); while(pos != string::npos) { while(pos != string::npos && text[pos] == '@') { string::size_type sPos = pos; pos = text.find_first_of("{;", pos); if(pos != string::npos && text[pos] == '{') { pos = find_close_bracket(text, pos, '{', '}'); } if(pos != string::npos) { parse_atrule(text.substr(sPos, pos - sPos + 1), baseurl, doc, media); } else { parse_atrule(text.substr(sPos), baseurl, doc, media); } if(pos != string::npos) { pos = text.find_first_not_of(" \n\r\t", pos + 1); } } if(pos == string::npos) { break; } string::size_type style_start = text.find('{', pos); string::size_type style_end = text.find('}', pos); if(style_start != string::npos && style_end != string::npos) { auto str_style = text.substr(style_start + 1, style_end - style_start - 1); style::ptr style = std::make_shared(); style->add(str_style, baseurl ? baseurl : "", doc->container()); parse_selectors(text.substr(pos, style_start - pos), style, media); if(media && doc) { doc->add_media_list(media); } pos = style_end + 1; } else { pos = string::npos; } if(pos != string::npos) { pos = text.find_first_not_of(" \n\r\t", pos); } } } void litehtml::css::parse_gradient(const string &token, document_container *container, background_gradient& grad) { size_t pos1 = token.find('('); size_t pos2 = token.find_last_of(')'); std::string grad_str; if(pos1 != std::string::npos) { auto gradient_type_str = token.substr(0, pos1); trim(gradient_type_str); background_gradient::gradient_type gradient_type = (background_gradient::gradient_type) (value_index( gradient_type_str, "linear-gradient;repeating-linear-gradient;radial-gradient;repeating-radial-gradient;conic-gradient;repeating-conic-gradient", -2) + 1); if(pos2 != std::string::npos) { grad_str = token.substr(pos1 + 1, pos2 - pos1 - 1); } else { grad_str = token.substr(pos1); } if(gradient_type == background_gradient::linear_gradient || gradient_type == background_gradient::repeating_linear_gradient) { parse_linear_gradient(grad_str, container, grad); } else if(gradient_type == background_gradient::radial_gradient || gradient_type == background_gradient::repeating_radial_gradient) { parse_radial_gradient(grad_str, container, grad); } else if(gradient_type == background_gradient::conic_gradient || gradient_type == background_gradient::repeating_conic_gradient) { parse_conic_gradient(grad_str, container, grad); } if(grad.m_colors.size() >= 2) { grad.m_type = gradient_type; } } } void litehtml::css::parse_css_url( const string& str, string& url ) { url = ""; size_t pos1 = str.find('('); size_t pos2 = str.find(')'); if(pos1 != string::npos && pos2 != string::npos) { url = str.substr(pos1 + 1, pos2 - pos1 - 1); if(url.length()) { if(url[0] == '\'' || url[0] == '"') { url.erase(0, 1); } } if(url.length()) { if(url[url.length() - 1] == '\'' || url[url.length() - 1] == '"') { url.erase(url.length() - 1, 1); } } } } bool litehtml::css::parse_selectors( const string& txt, const style::ptr& styles, const media_query_list::ptr& media ) { string selector = txt; trim(selector); string_vector tokens; split_string(selector, tokens, ","); bool added_something = false; for(auto & token : tokens) { css_selector::ptr new_selector = std::make_shared(media); new_selector->m_style = styles; trim(token); if(new_selector->parse(token)) { new_selector->calc_specificity(); add_selector(new_selector); added_something = true; } } return added_something; } void litehtml::css::sort_selectors() { std::sort(m_selectors.begin(), m_selectors.end(), [](const css_selector::ptr& v1, const css_selector::ptr& v2) { return (*v1) < (*v2); } ); } void litehtml::css::parse_atrule(const string& text, const char* baseurl, const std::shared_ptr& doc, const media_query_list::ptr& media) { if(text.substr(0, 7) == "@import") { int sPos = 7; string iStr; iStr = text.substr(sPos); if(iStr[iStr.length() - 1] == ';') { iStr.erase(iStr.length() - 1); } trim(iStr); string_vector tokens; split_string(iStr, tokens, " ", "", "(\""); if(!tokens.empty()) { string url; parse_css_url(tokens.front(), url); if(url.empty()) { url = tokens.front(); trim(url, "\""); } tokens.erase(tokens.begin()); if(doc) { document_container* doc_cont = doc->container(); if(doc_cont) { string css_text; string css_baseurl; if(baseurl) { css_baseurl = baseurl; } doc_cont->import_css(css_text, url, css_baseurl); if(!css_text.empty()) { media_query_list::ptr new_media = media; if(!tokens.empty()) { string media_str; for(auto iter = tokens.begin(); iter != tokens.end(); iter++) { if(iter != tokens.begin()) { media_str += " "; } media_str += (*iter); } new_media = media_query_list::create_from_string(media_str, doc); if(!new_media) { new_media = media; } } parse_stylesheet(css_text.c_str(), css_baseurl.c_str(), doc, new_media); } } } } } else if(text.substr(0, 6) == "@media") { string::size_type b1 = text.find_first_of('{'); string::size_type b2 = text.find_last_of('}'); if(b1 != string::npos) { string media_type = text.substr(6, b1 - 6); trim(media_type); media_query_list::ptr new_media = media_query_list::create_from_string(media_type, doc); string media_style; if(b2 != string::npos) { media_style = text.substr(b1 + 1, b2 - b1 - 1); } else { media_style = text.substr(b1 + 1); } parse_stylesheet(media_style.c_str(), baseurl, doc, new_media); } } }