summaryrefslogtreecommitdiff
path: root/libs/litehtml/src/css_selector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/litehtml/src/css_selector.cpp')
-rw-r--r--libs/litehtml/src/css_selector.cpp323
1 files changed, 323 insertions, 0 deletions
diff --git a/libs/litehtml/src/css_selector.cpp b/libs/litehtml/src/css_selector.cpp
new file mode 100644
index 0000000000..af46892397
--- /dev/null
+++ b/libs/litehtml/src/css_selector.cpp
@@ -0,0 +1,323 @@
+#include "html.h"
+#include "css_selector.h"
+#include "document.h"
+
+void litehtml::css_element_selector::parse_nth_child_params(const string& param, int& num, int& off)
+{
+ if (param == "odd")
+ {
+ num = 2;
+ off = 1;
+ }
+ else if (param == "even")
+ {
+ num = 2;
+ off = 0;
+ }
+ else
+ {
+ string_vector tokens;
+ split_string(param, tokens, " n", "n");
+
+ string s_num;
+ string s_off;
+
+ string s_int;
+ for (const auto& token : tokens)
+ {
+ if (token == "n")
+ {
+ s_num = s_int;
+ s_int.clear();
+ }
+ else
+ {
+ s_int += token;
+ }
+ }
+ s_off = s_int;
+
+ num = atoi(s_num.c_str());
+ off = atoi(s_off.c_str());
+ }
+}
+
+void litehtml::css_element_selector::parse( const string& txt )
+{
+ string::size_type el_end = txt.find_first_of(".#[:");
+ string tag = txt.substr(0, el_end);
+ litehtml::lcase(tag);
+ if (tag == "") tag = "*";
+ m_tag = _id(tag);
+
+ m_attrs.clear();
+ while(el_end != string::npos)
+ {
+ if(txt[el_end] == '.')
+ {
+ css_attribute_selector attribute;
+
+ attribute.type = select_class;
+ string::size_type pos = txt.find_first_of(".#[:", el_end + 1);
+ string name = txt.substr(el_end + 1, pos - el_end - 1);
+ litehtml::lcase(name);
+ attribute.name = _id(name);
+ m_attrs.push_back(attribute);
+ el_end = pos;
+ } else if(txt[el_end] == '#')
+ {
+ css_attribute_selector attribute;
+
+ attribute.type = select_id;
+ string::size_type pos = txt.find_first_of(".#[:", el_end + 1);
+ string name = txt.substr(el_end + 1, pos - el_end - 1);
+ litehtml::lcase(name);
+ attribute.name = _id(name);
+ m_attrs.push_back(attribute);
+ el_end = pos;
+ } else if(txt[el_end] == ':')
+ {
+ css_attribute_selector attribute;
+
+ if(txt[el_end + 1] == ':')
+ {
+ attribute.type = select_pseudo_element;
+ string::size_type pos = txt.find_first_of(".#[:", el_end + 2);
+ string name = txt.substr(el_end + 2, pos - el_end - 2);
+ litehtml::lcase(name);
+ attribute.name = _id(name);
+ m_attrs.push_back(attribute);
+ el_end = pos;
+ } else
+ {
+ string::size_type pos = txt.find_first_of(".#[:(", el_end + 1);
+ string name = txt.substr(el_end + 1, pos - el_end - 1);
+ lcase(name);
+ attribute.name = _id(name);
+ if(attribute.name == _after_ || attribute.name == _before_)
+ {
+ attribute.type = select_pseudo_element;
+ } else
+ {
+ attribute.type = select_pseudo_class;
+ }
+
+ string val;
+ if(pos != string::npos && txt.at(pos) == '(')
+ {
+ auto end = find_close_bracket(txt, pos);
+ val = txt.substr(pos + 1, end - pos - 1);
+ if (end != string::npos) pos = end + 1;
+ }
+
+ switch (attribute.name)
+ {
+ case _nth_child_:
+ case _nth_of_type_:
+ case _nth_last_child_:
+ case _nth_last_of_type_:
+ lcase(val);
+ parse_nth_child_params(val, attribute.a, attribute.b);
+ break;
+ case _not_:
+ attribute.sel = std::make_shared<css_element_selector>();
+ attribute.sel->parse(val);
+ break;
+ case _lang_:
+ trim(val);
+ lcase(val);
+ attribute.val = val;
+ break;
+ default:
+ break;
+ }
+
+ m_attrs.push_back(attribute);
+ el_end = pos;
+ }
+ } else if(txt[el_end] == '[')
+ {
+ css_attribute_selector attribute;
+
+ string::size_type pos = txt.find_first_of("]~=|$*^", el_end + 1);
+ string attr = txt.substr(el_end + 1, pos - el_end - 1);
+ trim(attr);
+ litehtml::lcase(attr);
+ if(pos != string::npos)
+ {
+ if(txt[pos] == ']')
+ {
+ attribute.type = select_exists;
+ } else if(txt[pos] == '=')
+ {
+ attribute.type = select_equal;
+ pos++;
+ } else if(txt.substr(pos, 2) == "~=")
+ {
+ attribute.type = select_contain_str;
+ pos += 2;
+ } else if(txt.substr(pos, 2) == "|=")
+ {
+ attribute.type = select_start_str;
+ pos += 2;
+ } else if(txt.substr(pos, 2) == "^=")
+ {
+ attribute.type = select_start_str;
+ pos += 2;
+ } else if(txt.substr(pos, 2) == "$=")
+ {
+ attribute.type = select_end_str;
+ pos += 2;
+ } else if(txt.substr(pos, 2) == "*=")
+ {
+ attribute.type = select_contain_str;
+ pos += 2;
+ } else
+ {
+ attribute.type = select_exists;
+ pos += 1;
+ }
+ pos = txt.find_first_not_of(" \t", pos);
+ if(pos != string::npos)
+ {
+ if(txt[pos] == '"')
+ {
+ string::size_type pos2 = txt.find_first_of('\"', pos + 1);
+ attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1));
+ pos = pos2 == string::npos ? pos2 : (pos2 + 1);
+ } else if(txt[pos] == '\'')
+ {
+ string::size_type pos2 = txt.find_first_of('\'', pos + 1);
+ attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1));
+ pos = pos2 == string::npos ? pos2 : (pos2 + 1);
+ } else if(txt[pos] == ']')
+ {
+ pos ++;
+ } else
+ {
+ string::size_type pos2 = txt.find_first_of(']', pos + 1);
+ attribute.val = txt.substr(pos, pos2 == string::npos ? pos2 : (pos2 - pos));
+ trim(attribute.val);
+ pos = pos2 == string::npos ? pos2 : (pos2 + 1);
+ }
+ }
+ } else
+ {
+ attribute.type = select_exists;
+ }
+ attribute.name = _id(attr);
+ m_attrs.push_back(attribute);
+ el_end = pos;
+ } else
+ {
+ el_end++;
+ }
+ el_end = txt.find_first_of(".#[:", el_end);
+ }
+}
+
+
+bool litehtml::css_selector::parse( const string& text )
+{
+ if(text.empty())
+ {
+ return false;
+ }
+ string_vector tokens;
+ split_string(text, tokens, "", " \t>+~", "([");
+
+ if(tokens.empty())
+ {
+ return false;
+ }
+
+ string left;
+ string right = tokens.back();
+ char combinator = 0;
+
+ tokens.pop_back();
+ while(!tokens.empty() && (tokens.back() == " " || tokens.back() == "\t" || tokens.back() == "+" || tokens.back() == "~" || tokens.back() == ">"))
+ {
+ if(combinator == ' ' || combinator == 0)
+ {
+ combinator = tokens.back()[0];
+ }
+ tokens.pop_back();
+ }
+
+ for(const auto & token : tokens)
+ {
+ left += token;
+ }
+
+ trim(left);
+ trim(right);
+
+ if(right.empty())
+ {
+ return false;
+ }
+
+ m_right.parse(right);
+
+ switch(combinator)
+ {
+ case '>':
+ m_combinator = combinator_child;
+ break;
+ case '+':
+ m_combinator = combinator_adjacent_sibling;
+ break;
+ case '~':
+ m_combinator = combinator_general_sibling;
+ break;
+ default:
+ m_combinator = combinator_descendant;
+ break;
+ }
+
+ m_left = nullptr;
+
+ if(!left.empty())
+ {
+ m_left = std::make_shared<css_selector>();
+ if(!m_left->parse(left))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void litehtml::css_selector::calc_specificity()
+{
+ if(m_right.m_tag != star_id)
+ {
+ m_specificity.d = 1;
+ }
+ for(const auto& attr : m_right.m_attrs)
+ {
+ if(attr.type == select_id)
+ {
+ m_specificity.b++;
+ } else
+ {
+ m_specificity.c++;
+ }
+ }
+ if(m_left)
+ {
+ m_left->calc_specificity();
+ m_specificity += m_left->m_specificity;
+ }
+}
+
+void litehtml::css_selector::add_media_to_doc( document* doc ) const
+{
+ if(m_media_query && doc)
+ {
+ doc->add_media_list(m_media_query);
+ }
+}
+