#include "html.h" #include "style.h" namespace litehtml { std::map style::m_valid_values = { { _display_, style_display_strings }, { _visibility_, visibility_strings }, { _position_, element_position_strings }, { _float_, element_float_strings }, { _clear_, element_clear_strings }, { _overflow_, overflow_strings }, { _box_sizing_, box_sizing_strings }, { _text_align_, text_align_strings }, { _vertical_align_, vertical_align_strings }, { _text_transform_, text_transform_strings }, { _white_space_, white_space_strings }, { _font_style_, font_style_strings }, { _font_variant_, font_variant_strings }, { _font_weight_, font_weight_strings }, { _list_style_type_, list_style_type_strings }, { _list_style_position_, list_style_position_strings }, { _border_left_style_, border_style_strings }, { _border_right_style_, border_style_strings }, { _border_top_style_, border_style_strings }, { _border_bottom_style_, border_style_strings }, { _border_collapse_, border_collapse_strings }, // these 4 properties are comma-separated lists of keywords, see parse_keyword_comma_list { _background_attachment_, background_attachment_strings }, { _background_repeat_, background_repeat_strings }, { _background_clip_, background_box_strings }, { _background_origin_, background_box_strings }, { _flex_direction_, flex_direction_strings }, { _flex_wrap_, flex_wrap_strings }, { _justify_content_, flex_justify_content_strings }, { _align_items_, flex_align_items_strings }, { _align_content_, flex_align_content_strings }, { _align_self_, flex_align_items_strings }, { _caption_side_, caption_side_strings }, }; void style::parse(const string& txt, const string& baseurl, document_container* container) { std::vector properties; split_string(txt, properties, ";", "", "\"'"); for(const auto & property : properties) { parse_property(property, baseurl, container); } } void style::parse_property(const string& txt, const string& baseurl, document_container* container) { string::size_type pos = txt.find_first_of(':'); if(pos != string::npos) { string name = txt.substr(0, pos); string val = txt.substr(pos + 1); trim(name); lcase(name); trim(val); if(!name.empty() && !val.empty()) { string_vector vals; split_string(val, vals, "!"); if(vals.size() == 1) { add_property(_id(name), val, baseurl, false, container); } else if(vals.size() > 1) { trim(vals[0]); lcase(vals[1]); add_property(_id(name), vals[0], baseurl, vals[1] == "important", container); } } } } void style::inherit_property(string_id name, bool important) { switch (name) { case _font_: add_parsed_property(_font_style_, property_value(inherit(), important)); add_parsed_property(_font_variant_, property_value(inherit(), important)); add_parsed_property(_font_weight_, property_value(inherit(), important)); add_parsed_property(_font_size_, property_value(inherit(), important)); add_parsed_property(_line_height_, property_value(inherit(), important)); break; case _background_: add_parsed_property(_background_color_, property_value(inherit(), important)); add_parsed_property(_background_position_x_, property_value(inherit(), important)); add_parsed_property(_background_position_y_, property_value(inherit(), important)); add_parsed_property(_background_repeat_, property_value(inherit(), important)); add_parsed_property(_background_attachment_, property_value(inherit(), important)); add_parsed_property(_background_image_, property_value(inherit(), important)); add_parsed_property(_background_image_baseurl_, property_value(inherit(), important)); add_parsed_property(_background_size_, property_value(inherit(), important)); add_parsed_property(_background_origin_, property_value(inherit(), important)); add_parsed_property(_background_clip_, property_value(inherit(), important)); break; default: add_parsed_property(name, property_value(inherit(), important)); } } void style::add_property(string_id name, const string& val, const string& baseurl, bool important, document_container* container) { if (val.find("var(") != string::npos) return add_parsed_property(name, property_value(val, important, true)); if (val == "inherit") return inherit_property(name, important); string url; css_length len[4], length; switch (name) { // keyword-only properties case _display_: case _visibility_: case _position_: case _float_: case _clear_: case _box_sizing_: case _overflow_: case _text_align_: case _vertical_align_: case _text_transform_: case _white_space_: case _font_style_: case _font_variant_: case _font_weight_: case _list_style_type_: case _list_style_position_: case _border_top_style_: case _border_bottom_style_: case _border_left_style_: case _border_right_style_: case _border_collapse_: case _flex_direction_: case _flex_wrap_: case _justify_content_: case _align_content_: case _caption_side_: { int idx = value_index(val, m_valid_values[name]); if (idx >= 0) { add_parsed_property(name, property_value(idx, important)); } break; } case _align_items_: case _align_self_: parse_align_self(name, val, important); break; // case _text_indent_: case _padding_left_: case _padding_right_: case _padding_top_: case _padding_bottom_: length.fromString(val); add_parsed_property(name, property_value(length, important)); break; // | auto case _left_: case _right_: case _top_: case _bottom_: case _z_index_: // | auto case _width_: case _height_: case _min_width_: case _min_height_: case _margin_left_: case _margin_right_: case _margin_top_: case _margin_bottom_: length.fromString(val, "auto", -1); add_parsed_property(name, property_value(length, important)); break; // | none case _max_width_: case _max_height_: length.fromString(val, "none", -1); add_parsed_property(name, property_value(length, important)); break; case _line_height_: length.fromString(val, "normal", -1); add_parsed_property(name, property_value(length, important)); break; case _font_size_: length.fromString(val, font_size_strings, -1); add_parsed_property(name, property_value(length, important)); break; // Parse background shorthand properties case _background_: parse_background(val, baseurl, important, container); break; case _background_image_: parse_background_image(val, container, baseurl, important); break; case _background_attachment_: case _background_repeat_: case _background_clip_: case _background_origin_: parse_keyword_comma_list(name, val, important); break; case _background_position_: parse_background_position(val, important); break; case _background_size_: parse_background_size(val, important); break; // Parse border spacing properties case _border_spacing_: parse_two_lengths(val, len); add_parsed_property(__litehtml_border_spacing_x_, property_value(len[0], important)); add_parsed_property(__litehtml_border_spacing_y_, property_value(len[1], important)); break; // Parse borders shorthand properties case _border_: { string_vector tokens; split_string(val, tokens, " ", "", "("); for (const auto& token : tokens) { int idx = value_index(token, border_style_strings); if (idx >= 0) { property_value style(idx, important); add_parsed_property(_border_left_style_, style); add_parsed_property(_border_right_style_, style); add_parsed_property(_border_top_style_, style); add_parsed_property(_border_bottom_style_, style); } else if (t_isdigit(token[0]) || token[0] == '.' || value_in_list(token, border_width_strings)) { property_value width(parse_border_width(token), important); add_parsed_property(_border_left_width_, width); add_parsed_property(_border_right_width_, width); add_parsed_property(_border_top_width_, width); add_parsed_property(_border_bottom_width_, width); } else if (web_color::is_color(token, container)) { web_color _color = web_color::from_string(token, container); property_value color(_color, important); add_parsed_property(_border_left_color_, color); add_parsed_property(_border_right_color_, color); add_parsed_property(_border_top_color_, color); add_parsed_property(_border_bottom_color_, color); } } break; } case _border_left_: case _border_right_: case _border_top_: case _border_bottom_: { string_vector tokens; split_string(val, tokens, " ", "", "("); for (const auto& token : tokens) { int idx = value_index(token, border_style_strings); if (idx >= 0) { add_parsed_property(_id(_s(name) + "-style"), property_value(idx, important)); } else if (t_isdigit(token[0]) || token[0] == '.' || value_in_list(token, border_width_strings)) { property_value width(parse_border_width(token), important); add_parsed_property(_id(_s(name) + "-width"), width); } else if (web_color::is_color(token, container)) { web_color color = web_color::from_string(token, container); add_parsed_property(_id(_s(name) + "-color"), property_value(color, important)); } } break; } // Parse border-width/style/color shorthand properties case _border_width_: case _border_style_: case _border_color_: { string prop = name == _border_width_ ? "-width" : name == _border_style_ ? "-style" : "-color"; string_vector tokens; split_string(val, tokens, " "); if (tokens.size() == 4) { add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); add_property(_id("border-left" + prop), tokens[3], baseurl, important, container); } else if (tokens.size() == 3) { add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); add_property(_id("border-bottom" + prop), tokens[2], baseurl, important, container); } else if (tokens.size() == 2) { add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); add_property(_id("border-right" + prop), tokens[1], baseurl, important, container); add_property(_id("border-left" + prop), tokens[1], baseurl, important, container); } else if (tokens.size() == 1) { add_property(_id("border-top" + prop), tokens[0], baseurl, important, container); add_property(_id("border-bottom" + prop), tokens[0], baseurl, important, container); add_property(_id("border-right" + prop), tokens[0], baseurl, important, container); add_property(_id("border-left" + prop), tokens[0], baseurl, important, container); } break; } case _border_top_width_: case _border_bottom_width_: case _border_left_width_: case _border_right_width_: length = parse_border_width(val); add_parsed_property(name, property_value(length, important)); break; case _color_: case _background_color_: case _border_top_color_: case _border_bottom_color_: case _border_left_color_: case _border_right_color_: if (web_color::is_color(val, container)) { web_color color = web_color::from_string(val, container); add_parsed_property(name, property_value(color, important)); } break; // Parse border radius shorthand properties case _border_bottom_left_radius_: case _border_bottom_right_radius_: case _border_top_right_radius_: case _border_top_left_radius_: parse_two_lengths(val, len); add_parsed_property(_id(_s(name) + "-x"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-y"), property_value(len[1], important)); break; // Parse border-radius shorthand properties case _border_radius_: { string_vector tokens; split_string(val, tokens, "/"); if (tokens.size() == 1) { add_property(_border_radius_x_, tokens[0], baseurl, important, container); add_property(_border_radius_y_, tokens[0], baseurl, important, container); } else if (tokens.size() >= 2) { add_property(_border_radius_x_, tokens[0], baseurl, important, container); add_property(_border_radius_y_, tokens[1], baseurl, important, container); } break; } case _border_radius_x_: case _border_radius_y_: { string_id top_left, top_right, bottom_right, bottom_left; if (name == _border_radius_x_) { top_left = _border_top_left_radius_x_; top_right = _border_top_right_radius_x_; bottom_right = _border_bottom_right_radius_x_; bottom_left = _border_bottom_left_radius_x_; } else { top_left = _border_top_left_radius_y_; top_right = _border_top_right_radius_y_; bottom_right = _border_bottom_right_radius_y_; bottom_left = _border_bottom_left_radius_y_; } switch (parse_four_lengths(val, len)) { case 1: add_parsed_property(top_left, property_value(len[0], important)); add_parsed_property(top_right, property_value(len[0], important)); add_parsed_property(bottom_right, property_value(len[0], important)); add_parsed_property(bottom_left, property_value(len[0], important)); break; case 2: add_parsed_property(top_left, property_value(len[0], important)); add_parsed_property(top_right, property_value(len[1], important)); add_parsed_property(bottom_right, property_value(len[0], important)); add_parsed_property(bottom_left, property_value(len[1], important)); break; case 3: add_parsed_property(top_left, property_value(len[0], important)); add_parsed_property(top_right, property_value(len[1], important)); add_parsed_property(bottom_right, property_value(len[2], important)); add_parsed_property(bottom_left, property_value(len[1], important)); break; case 4: add_parsed_property(top_left, property_value(len[0], important)); add_parsed_property(top_right, property_value(len[1], important)); add_parsed_property(bottom_right, property_value(len[2], important)); add_parsed_property(bottom_left, property_value(len[3], important)); break; } break; } // Parse list-style shorthand properties case _list_style_: { add_parsed_property(_list_style_type_, property_value(list_style_type_disc, important)); add_parsed_property(_list_style_position_, property_value(list_style_position_outside, important)); add_parsed_property(_list_style_image_, property_value("", important)); add_parsed_property(_list_style_image_baseurl_, property_value("", important)); string_vector tokens; split_string(val, tokens, " ", "", "("); for (const auto& token : tokens) { int idx = value_index(token, list_style_type_strings); if (idx >= 0) { add_parsed_property(_list_style_type_, property_value(idx, important)); } else { idx = value_index(token, list_style_position_strings); if (idx >= 0) { add_parsed_property(_list_style_position_, property_value(idx, important)); } else if (!strncmp(token.c_str(), "url", 3)) { css::parse_css_url(token, url); add_parsed_property(_list_style_image_, property_value(url, important)); add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); } } } break; } case _list_style_image_: css::parse_css_url(val, url); add_parsed_property(_list_style_image_, property_value(url, important)); add_parsed_property(_list_style_image_baseurl_, property_value(baseurl, important)); break; // Parse margin and padding shorthand properties case _margin_: case _padding_: { switch (parse_four_lengths(val, len)) { case 4: add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); add_parsed_property(_id(_s(name) + "-left"), property_value(len[3], important)); break; case 3: add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[2], important)); break; case 2: add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-right"), property_value(len[1], important)); add_parsed_property(_id(_s(name) + "-left"), property_value(len[1], important)); break; case 1: add_parsed_property(_id(_s(name) + "-top"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-bottom"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-right"), property_value(len[0], important)); add_parsed_property(_id(_s(name) + "-left"), property_value(len[0], important)); break; } break; } // Parse font shorthand properties case _font_: parse_font(val, important); break; // Parse flex-flow shorthand properties case _flex_flow_: { string_vector tokens; split_string(val, tokens, " "); for (const auto& tok : tokens) { int idx; if ((idx = value_index(tok, flex_direction_strings)) >= 0) { add_parsed_property(_flex_direction_, property_value(idx, important)); } else if ((idx = value_index(tok, flex_wrap_strings)) >= 0) { add_parsed_property(_flex_wrap_, property_value(idx, important)); } } break; } // Parse flex shorthand properties case _flex_: parse_flex(val, important); break; case _flex_grow_: case _flex_shrink_: add_parsed_property(name, property_value(t_strtof(val), important)); break; case _flex_basis_: length.fromString(val, flex_basis_strings, -1); add_parsed_property(_flex_basis_, property_value(length, important)); break; case _order_: // { char* end; int int_val = (int) strtol(val.c_str(), &end, 10); if(end[0] == '\0') { add_parsed_property(name, property_value(int_val, important)); } } break; case _counter_increment_: case _counter_reset_: { string_vector tokens; split_string(val, tokens, " "); add_parsed_property(name, property_value(tokens, important)); break; } default: add_parsed_property(name, property_value(val, important)); } } css_length style::parse_border_width(const string& str) { css_length len; if (t_isdigit(str[0]) || str[0] == '.') { len.fromString(str); } else { int idx = value_index(str, border_width_strings); if (idx >= 0) { len.set_value(border_width_values[idx], css_units_px); } } return len; } void style::parse_two_lengths(const string& str, css_length len[2]) { string_vector tokens; split_string(str, tokens, " "); if (tokens.size() == 1) { css_length length; length.fromString(tokens[0]); len[0] = len[1] = length; } else if (tokens.size() == 2) { len[0].fromString(tokens[0]); len[1].fromString(tokens[1]); } } int style::parse_four_lengths(const string& str, css_length len[4]) { string_vector tokens; split_string(str, tokens, " "); if (tokens.size() == 0 || tokens.size() > 4) { return 0; } for (size_t i = 0; i < tokens.size(); i++) { len[i].fromString(tokens[i]); } return (int)tokens.size(); } void style::parse_background(const string& val, const string& baseurl, bool important, document_container* container) { string_vector tokens; split_string(val, tokens, ",", "", "("); if (tokens.empty()) return; web_color color; std::vector images; int_vector repeats, origins, clips, attachments; length_vector x_positions, y_positions; size_vector sizes; background_gradient grad; for (const auto& token : tokens) { background bg; if (!parse_one_background(token, container, bg)) return; color = bg.m_color; images.push_back(bg.m_image[0]); repeats.push_back(bg.m_repeat[0]); origins.push_back(bg.m_origin[0]); clips.push_back(bg.m_clip[0]); attachments.push_back(bg.m_attachment[0]); x_positions.push_back(bg.m_position_x[0]); y_positions.push_back(bg.m_position_y[0]); sizes.push_back(bg.m_size[0]); } add_parsed_property(_background_color_, property_value(color, important)); add_parsed_property(_background_image_, property_value(images, important)); add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); add_parsed_property(_background_repeat_, property_value(repeats, important)); add_parsed_property(_background_origin_, property_value(origins, important)); add_parsed_property(_background_clip_, property_value(clips, important)); add_parsed_property(_background_attachment_, property_value(attachments, important)); add_parsed_property(_background_position_x_, property_value(x_positions, important)); add_parsed_property(_background_position_y_, property_value(y_positions, important)); add_parsed_property(_background_size_, property_value(sizes, important)); } bool style::parse_one_background(const string& val, document_container* container, background& bg) { bg.m_color = web_color::transparent; bg.m_image = { background_image() }; bg.m_repeat = { background_repeat_repeat }; bg.m_origin = { background_box_padding }; bg.m_clip = { background_box_border }; bg.m_attachment = { background_attachment_scroll }; bg.m_position_x = { css_length(0, css_units_percentage) }; bg.m_position_y = { css_length(0, css_units_percentage) }; bg.m_size = { css_size(css_length::predef_value(background_size_auto), css_length::predef_value(background_size_auto)) }; if(val == "none") { return true; } string_vector tokens; split_string(val, tokens, " \t\n\r", "", "("); bool color_found = false; bool image_found = false; bool origin_found = false; bool clip_found = false; bool repeat_found = false; bool attachment_found = false; string position; for(const auto& token : tokens) { int idx; if(token.substr(0, 3) == "url") { if (image_found) return false; string url; css::parse_css_url(token, url); background_image img; img.type = background_image::bg_image_type_url; img.url = url; bg.m_image = { img }; image_found = true; } else if( (idx = value_index(token, background_repeat_strings)) >= 0 ) { if (repeat_found) return false; bg.m_repeat = { idx }; repeat_found = true; } else if( (idx = value_index(token, background_attachment_strings)) >= 0 ) { if (attachment_found) return false; bg.m_attachment = { idx }; attachment_found = true; } else if( (idx = value_index(token, background_box_strings)) >= 0 ) { if(!origin_found) { bg.m_origin = { idx }; origin_found = true; } else { if (clip_found) return false; bg.m_clip = { idx }; clip_found = true; } } else if( value_in_list(token, background_position_strings) || token.find('/') != string::npos || t_isdigit(token[0]) || token[0] == '+' || token[0] == '-' || token[0] == '.' ) { position += " " + token; } else if (web_color::is_color(token, container)) { if (color_found) return false; bg.m_color = web_color::from_string(token, container); color_found = true; } else if ( token.substr(0, 15) == "linear-gradient" || token.substr(0, 25) == "repeating-linear-gradient" || token.substr(0, 15) == "radial-gradient" || token.substr(0, 25) == "repeating-radial-gradient" || token.substr(0, 14) == "conic-gradient" || token.substr(0, 24) == "repeating-conic-gradient") { if (image_found) return false; background_image img; img.type = background_image::bg_image_type_gradient; css::parse_gradient(token, container, img.gradient); if(img.is_empty()) return false; bg.m_image = { img }; image_found = true; } else { return false; } } if (!position.empty()) { tokens.clear(); split_string(position, tokens, "/"); if (tokens.size() > 2) return false; if (tokens.size() == 2 && !parse_one_background_size(tokens[1], bg.m_size[0])) return false; if (!tokens.empty() && !parse_one_background_position(tokens[0], bg.m_position_x[0], bg.m_position_y[0])) return false; } return true; } void style::parse_background_image(const string& val, document_container* container, const string& baseurl, bool important) { string_vector tokens; split_string(val, tokens, ",", "", "("); if (tokens.empty()) return; std::vector images; for (auto& token : tokens) { trim(token); if(token.substr(0, 3) == "url") { string url; css::parse_css_url(token, url); background_image img; img.type = background_image::bg_image_type_url; img.url = url; images.emplace_back(img); } else if (token.substr(0, 15) == "linear-gradient" || token.substr(0, 25) == "repeating-linear-gradient" || token.substr(0, 15) == "radial-gradient" || token.substr(0, 25) == "repeating-radial-gradient" || token.substr(0, 14) == "conic-gradient" || token.substr(0, 24) == "repeating-conic-gradient") { background_image img; img.type = background_image::bg_image_type_gradient; css::parse_gradient(token, container, img.gradient); if(!img.is_empty()) { images.emplace_back(img); } } } if(!images.empty()) { add_parsed_property(_background_image_, property_value(images, important)); add_parsed_property(_background_image_baseurl_, property_value(baseurl, important)); } } void style::parse_keyword_comma_list(string_id name, const string& val, bool important) { string_vector tokens; split_string(val, tokens, ","); if (tokens.empty()) return; int_vector vec; for (auto& token : tokens) { trim(token); int idx = value_index(token, m_valid_values[name]); if (idx == -1) return; vec.push_back(idx); } add_parsed_property(name, property_value(vec, important)); } void style::parse_background_position(const string& val, bool important) { string_vector tokens; split_string(val, tokens, ","); if (tokens.empty()) return; length_vector x_positions, y_positions; for (const auto& token : tokens) { css_length x, y; if(!parse_one_background_position(token, x, y)) return; x_positions.push_back(x); y_positions.push_back(y); } add_parsed_property(_background_position_x_, property_value(x_positions, important)); add_parsed_property(_background_position_y_, property_value(y_positions, important)); } bool style::parse_one_background_position(const string& val, css_length& x, css_length& y) { string_vector pos; split_string(val, pos, split_delims_spaces); if (pos.empty() || pos.size() > 2) { return false; } if (pos.size() == 1) { if (value_in_list(pos[0], "left;right;center")) { x.fromString(pos[0], "left;right;center"); y.set_value(50, css_units_percentage); } else if (value_in_list(pos[0], "top;bottom;center")) { y.fromString(pos[0], "top;bottom;center"); x.set_value(50, css_units_percentage); } else { x.fromString(pos[0], "left;right;center"); y.set_value(50, css_units_percentage); } } else if (pos.size() == 2) { if (value_in_list(pos[0], "left;right")) { x.fromString(pos[0], "left;right;center"); y.fromString(pos[1], "top;bottom;center"); } else if (value_in_list(pos[0], "top;bottom")) { x.fromString(pos[1], "left;right;center"); y.fromString(pos[0], "top;bottom;center"); } else if (value_in_list(pos[1], "left;right")) { x.fromString(pos[1], "left;right;center"); y.fromString(pos[0], "top;bottom;center"); } else if (value_in_list(pos[1], "top;bottom")) { x.fromString(pos[0], "left;right;center"); y.fromString(pos[1], "top;bottom;center"); } else { x.fromString(pos[0], "left;right;center"); y.fromString(pos[1], "top;bottom;center"); } } if (x.is_predefined()) { switch (x.predef()) { case 0: x.set_value(0, css_units_percentage); break; case 1: x.set_value(100, css_units_percentage); break; case 2: x.set_value(50, css_units_percentage); break; } } if (y.is_predefined()) { switch (y.predef()) { case 0: y.set_value(0, css_units_percentage); break; case 1: y.set_value(100, css_units_percentage); break; case 2: y.set_value(50, css_units_percentage); break; } } return true; } void style::parse_background_size(const string& val, bool important) { string_vector tokens; split_string(val, tokens, ","); if (tokens.empty()) return; size_vector sizes; for (const auto& token : tokens) { css_size size; if (!parse_one_background_size(token, size)) return; sizes.push_back(size); } add_parsed_property(_background_size_, property_value(sizes, important)); } bool style::parse_one_background_size(const string& val, css_size& size) { string_vector res; split_string(val, res, split_delims_spaces); if (res.empty()) { return false; } size.width.fromString(res[0], background_size_strings); if (res.size() > 1) { size.height.fromString(res[1], background_size_strings); } else { size.height.predef(background_size_auto); } return true; } void style::parse_font(const string& val, bool important) { add_parsed_property(_font_style_, property_value(font_style_normal, important)); add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); add_parsed_property(_font_size_, property_value(font_size_medium, important)); add_parsed_property(_line_height_, property_value(line_height_normal, important)); string_vector tokens; split_string(val, tokens, " ", "", "\""); int idx; bool is_family = false; string font_family; for(const auto& token : tokens) { if(is_family) { font_family += token; continue; } if((idx = value_index(token, font_style_strings)) >= 0) { if(idx == 0) { add_parsed_property(_font_style_, property_value(font_style_normal, important)); add_parsed_property(_font_variant_, property_value(font_variant_normal, important)); add_parsed_property(_font_weight_, property_value(font_weight_normal, important)); } else { add_parsed_property(_font_style_, property_value(idx, important)); } } else if((idx = value_index(token, font_weight_strings)) >= 0) { add_parsed_property(_font_weight_, property_value(idx, important)); } else if((idx = value_index(token, font_variant_strings)) >= 0) { add_parsed_property(_font_variant_, property_value(idx, important)); } else if(t_isdigit(token[0]) || token[0] == '.' || value_in_list(token, font_size_strings) || token.find('/') != string::npos) { string_vector szlh; split_string(token, szlh, "/"); if(!szlh.empty()) { auto size = css_length::from_string(szlh[0], font_size_strings, -1); add_parsed_property(_font_size_, property_value(size, important)); if (szlh.size() == 2) { auto height = css_length::from_string(szlh[1], "normal", -1); add_parsed_property(_line_height_, property_value(height, important)); } } } else { is_family = true; font_family += token; } } add_parsed_property(_font_family_, property_value(font_family, important)); } void style::parse_flex(const string& val, bool important) { css_length _auto = css_length::predef_value(flex_basis_auto); if (val == "initial") { // 0 1 auto add_parsed_property(_flex_grow_, property_value(0.f, important)); add_parsed_property(_flex_shrink_, property_value(1.f, important)); add_parsed_property(_flex_basis_, property_value(_auto, important)); } else if (val == "auto") { // 1 1 auto add_parsed_property(_flex_grow_, property_value(1.f, important)); add_parsed_property(_flex_shrink_, property_value(1.f, important)); add_parsed_property(_flex_basis_, property_value(_auto, important)); } else if (val == "none") { // 0 0 auto add_parsed_property(_flex_grow_, property_value(0.f, important)); add_parsed_property(_flex_shrink_, property_value(0.f, important)); add_parsed_property(_flex_basis_, property_value(_auto, important)); } else { string_vector tokens; split_string(val, tokens, " "); if (tokens.size() == 3) { float grow = t_strtof(tokens[0]); float shrink = t_strtof(tokens[1]); auto basis = css_length::from_string(tokens[2], flex_basis_strings, -1); if(!basis.is_predefined() && basis.units() == css_units_none && basis.val() == 0) { basis.set_value(basis.val(), css_units_px); } add_parsed_property(_flex_grow_, property_value(grow, important)); add_parsed_property(_flex_shrink_, property_value(shrink, important)); add_parsed_property(_flex_basis_, property_value(basis, important)); } else if (tokens.size() == 2) { float grow = t_strtof(tokens[0]); add_parsed_property(_flex_grow_, property_value(grow, important)); if (litehtml::is_number(tokens[1])) { float shrink = t_strtof(tokens[1]); add_parsed_property(_flex_shrink_, property_value(shrink, important)); add_parsed_property(_flex_basis_, property_value(css_length(0), important)); } else { auto basis = css_length::from_string(tokens[1], flex_basis_strings, -1); add_parsed_property(_flex_basis_, property_value(basis, important)); } } else if (tokens.size() == 1) { if (is_number(tokens[0])) { float grow = t_strtof(tokens[0]); add_parsed_property(_flex_grow_, property_value(grow, important)); add_parsed_property(_flex_shrink_, property_value(1.f, important)); add_parsed_property(_flex_basis_, property_value(css_length(0), important)); } else { auto basis = css_length::from_string(tokens[0], flex_basis_strings, -1); add_parsed_property(_flex_grow_, property_value(1.f, important)); add_parsed_property(_flex_shrink_, property_value(1.f, important)); add_parsed_property(_flex_basis_, property_value(basis, important)); } } } } void style::parse_align_self(string_id name, const string& val, bool important) { string_vector tokens; split_string(val, tokens, " "); if(tokens.size() == 1) { int idx = value_index(val, m_valid_values[name]); if (idx >= 0) { add_parsed_property(name, property_value(idx, important)); } } else { int val1 = 0; int val2 = -1; for(auto &token : tokens) { if(token == "first") { val1 |= flex_align_items_first; } else if(token == "last") { val1 |= flex_align_items_last; } else if(token == "safe") { val1 |= flex_align_items_safe; } else if(token == "unsafe") { val1 |= flex_align_items_unsafe; } else { int idx = value_index(token, m_valid_values[name]); if(idx >= 0) { val2 = idx; } } } if(val2 >= 0) { add_parsed_property(name, property_value(val1 | val2, important)); } } } void style::add_parsed_property( string_id name, const property_value& propval ) { auto prop = m_properties.find(name); if (prop != m_properties.end()) { if (!prop->second.m_important || (propval.m_important && prop->second.m_important)) { prop->second = propval; } } else { m_properties[name] = propval; } } void style::remove_property( string_id name, bool important ) { auto prop = m_properties.find(name); if(prop != m_properties.end()) { if( !prop->second.m_important || (important && prop->second.m_important) ) { m_properties.erase(prop); } } } void style::combine(const style& src) { for (const auto& property : src.m_properties) { add_parsed_property(property.first, property.second); } } const property_value& style::get_property(string_id name) const { auto it = m_properties.find(name); if (it != m_properties.end()) { return it->second; } static property_value _invalid(invalid(), false); return _invalid; } void style::subst_vars_(string& str, const html_tag* el) { while (true) { auto start = str.find("var("); if (start == string::npos) break; if (start > 0 && isalnum(str[start - 1])) break; auto end = str.find(')', start + 4); if (end == string::npos) break; auto name = str.substr(start + 4, end - start - 4); trim(name); string val = el->get_custom_property(_id(name), ""); str.replace(start, end - start + 1, val); } } void style::subst_vars(const html_tag* el) { for (auto& prop : m_properties) { if (prop.second.m_has_var) { string str = prop.second.get(); subst_vars_(str, el); // re-adding the same property // if it is a custom property it will be readded as a string // if it is a standard css property it will be parsed and properly added as typed property add_property(prop.first, str, "", prop.second.m_important, el->get_document()->container()); } } } } // namespace litehtml