[gxml: 1/25] Improve CSS Selectors and update tests.



commit 3ed8c0918497a0aa5dcc7f795831135f4ee2429b
Author: BZHDeveloper <inizan yannick gmail com>
Date:   Wed Oct 24 23:50:38 2018 +0200

    Improve CSS Selectors and update tests.

 gxml/CssSelectorParser.vala | 899 ++++++++++++++++++++++++++------------------
 test/CssSelectorTest.vala   | 121 +++---
 2 files changed, 584 insertions(+), 436 deletions(-)
---
diff --git a/gxml/CssSelectorParser.vala b/gxml/CssSelectorParser.vala
index 7938401..fab24d5 100644
--- a/gxml/CssSelectorParser.vala
+++ b/gxml/CssSelectorParser.vala
@@ -1,7 +1,7 @@
 /* -*- Mode: vala; indent-tabs-mode: tab; c-basic-offset: 0; tab-width: 2 -*- */
 /*
  *
- * Copyright (C) 2017  Yannick Inizan <inizan yannick gmail com>
+ * Copyright (C) 2018  Yannick Inizan <inizan yannick gmail com>
  * Copyright (C) 2017  Daniel Espinosa <esodan gmail com>
  *
  * This library is free software; you can redistribute it and/or
@@ -21,13 +21,15 @@
  *      Yannick Inizan <inizan yannick gmail com>
  *      Daniel Espinosa <esodan gmail com>
  */
-public errordomain GXml.CssSelectorError {
+
+public enum GXml.CssCombiner {
        NULL,
-       ATTRIBUTE,
-       INVALID,
-       LENGTH,
-       STRING,
-       TYPE
+       NONE,
+       INSIDE,
+       AND,
+       PARENT,
+       AFTER,
+       BEFORE
 }
 
 public enum GXml.CssSelectorType {
@@ -39,398 +41,573 @@ public enum GXml.CssSelectorType {
        ATTRIBUTE_EQUAL,
        ATTRIBUTE_CONTAINS,
        ATTRIBUTE_SUBSTRING,
-       ATTRIBUTE_START_WITH,
-       ATTRIBUTE_START_WITH_HYPHEN,
-       ATTRIBUTE_END_WITH,
+       ATTRIBUTE_STARTS_WITH,
+       ATTRIBUTE_STARTS_WITH_WORD,
+       ATTRIBUTE_ENDS_WITH,
+       PSEUDO_CLASS
+}
+
+public errordomain GXml.CssSelectorError {
+       NULL,
+       EOF,
+       NOT,
        PSEUDO,
-       AND,
-       INSIDE,
-       PARENT,
-       AFTER,
-       BEFORE
+       ATTRIBUTE,
+       IDENTIFIER,
+       COMBINER
 }
-public class GXml.CssSelectorData : GLib.Object {
-       public CssSelectorType selector_type { get; set; default = CssSelectorType.ALL; }
-       public string data { get; set; default = ""; }
-       public string value { get; set; default = ""; }
-       public CssSelectorData.with_values (CssSelectorType t, string data, string val) {
-               selector_type = t;
-               this.data = data;
-               value = val;
+
+internal class GXml.CssString : GLib.Object {
+       public CssString (string text) {
+               GLib.Object (text : text);
+       }
+       
+       int position;
+       
+       public unichar peek() {
+               int pos = this.position;
+               unichar u = 0;
+               if (!this.text.get_next_char (ref pos, out u))
+                       return 0;
+               return u;
+       }
+       
+       public unichar read() {
+               unichar u = 0;
+               if (!this.text.get_next_char (ref this.position, out u))
+                       return 0;
+               return u;
+       }
+       
+       public unichar read_r() {
+               unichar u = 0;
+               if (!this.text.get_prev_char (ref this.position, out u))
+                       return 0;
+               return u;
+       }
+       
+       public bool eof {
+               get {
+                       return peek() == 0;
+               }
        }
+       
+       public string text { get; construct; }
 }
 
-public class GXml.CssSelectorParser : GLib.Object {
-       Gee.ArrayList<CssSelectorData?> list;
+internal delegate bool GXml.CssStringFunc (GXml.CssString str);
 
-       public Gee.List<CssSelectorData?> selectors {
-               get {
-                       return list;
+public class GXml.CssSelector : GLib.Object {
+       public CssSelector (GXml.CssSelectorType t = GXml.CssSelectorType.ELEMENT, string name = "") {
+               GLib.Object (selector_type : t, name : name, value : "");
+       }
+       
+       public CssSelector.with_value (GXml.CssSelectorType t = GXml.CssSelectorType.ELEMENT, string name, 
string value) {
+               GLib.Object (selector_type : t, name : name, value : value);
+       }
+       
+       public GXml.CssSelectorType selector_type { get; set construct; }
+       
+       public string name { get; set construct; }
+       
+       public string value { get; set construct; }
+       
+       public GXml.CssCombiner combiner { get; set; }
+}
+
+public class GXml.CssElementSelector : GXml.CssSelector {
+       public CssElementSelector (string prefix = "", string local_name = "", bool prefixed = false) {
+               GLib.Object (selector_type : GXml.CssSelectorType.ELEMENT, name : prefix, value : local_name, 
prefixed : prefixed);
+       }
+       
+       public bool prefixed { get; set construct; }
+       
+       public string prefix {
+               owned get {
+                       return this.name;
+               }
+               set {
+                       this.name = value;
                }
        }
+       
+       public string local_name {
+               owned get {
+                       return this.value;
+               }
+               set {
+                       this.value = value;
+               }
+       }
+}
 
+public class GXml.CssNotSelector : GXml.CssSelector {
+       public CssNotSelector() {
+               GLib.Object (selector_type : GXml.CssSelectorType.PSEUDO_CLASS, name : "not");
+       }
+       
+       Gee.ArrayList<GXml.CssSelector> list;
+       
        construct {
-               list = new Gee.ArrayList<CssSelectorData?>();
+               this.list = new Gee.ArrayList<GXml.CssSelector>();
        }
-
-       static bool is_valid_identifier (string data) {
-               unichar u = 0;
-               int index = 0;
-               while (data.get_next_char (ref index, out u))
-                       if (u == '[' || u == ']' || u == '{' || u == '}' ||
-                               u == '$' || u == '&' || u == '#' || u == '|' ||
-                               u == '`' || u == '^' || u == '@' || u == '+' ||
-                               u == '~' || u == '*' || u == '%' || u == '!' ||
-                               u == '?' || u == '<' || u == '>' || u == '"' ||
-                               u == '\'')
-                                       return false;
-               return true;
+       
+       public Gee.List<GXml.CssSelector> selectors {
+               get {
+                       return this.list;
+               }
        }
+}
 
-       void parse_class (string css, ref int position) {
-               CssSelectorData idata = new CssSelectorData.with_values (CssSelectorType.INSIDE, "", "");
-               list.add (idata);
-               position++;
-               StringBuilder sb = new StringBuilder();
-               unichar u = 0;
-               while (css.get_next_char (ref position, out u) && (u.isalnum() || u == '-'))
-                       sb.append_unichar (u);
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.CLASS, sb.str, "");
-               list.add (data);
+public class GXml.CssSelectorParser : GLib.Object {
+       static bool is_valid_char (unichar u) {
+               unichar[] array = { 
+                       '=', '[', ']', '{', '}', 
+                       '$', '&', '#', '|', '`',
+                       '^', '@', '+', '~', '*',
+                       '%', '!', '?', '<', '>', 
+                       '.', '"', '\''
+               };
+               return !(u in array);
        }
-
-       void parse_id (string css, ref int position) {
-               CssSelectorData idata = new CssSelectorData.with_values (CssSelectorType.INSIDE, "", "");
-               list.add (idata);
-               position++;
-               StringBuilder sb = new StringBuilder();
-               unichar u = 0;
-               while (css.get_next_char (ref position, out u) && (u.isalnum() || u == '-'))
-                       sb.append_unichar (u);
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.ID, sb.str, "");
-               list.add (data);
+       
+       static string parse_identifier (GXml.CssString str, GXml.CssStringFunc func) {
+               var builder = new StringBuilder();
+               while (!str.eof && is_valid_char (str.peek()) && func (str))
+                       builder.append_unichar (str.read());
+               return builder.str;
        }
-
-       void parse_all (string css, ref int position) {
-               position++;
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.ALL, "*", "");
-               list.add (data);
+       
+       static GXml.CssSelector parse_element (GXml.CssString str) throws GLib.Error {
+               var builder = new StringBuilder();
+               var extra_builder = new StringBuilder();
+               while (!str.eof && (str.peek() == '*' || is_valid_char (str.peek())) && !str.peek().isspace())
+                       builder.append_unichar (str.read());
+               if (builder.str.contains ("*") && builder.len > 1)
+                       throw new GXml.CssSelectorError.IDENTIFIER ("Invalid identifier");
+               if (str.peek() == '|') {
+                       str.read();
+                       while (!str.eof && (str.peek() == '*' || is_valid_char (str.peek())) && 
!str.peek().isspace())
+                               extra_builder.append_unichar (str.read());
+                       if (extra_builder.len == 0)
+                               throw new GXml.CssSelectorError.IDENTIFIER ("string value is empty");
+                       if (extra_builder.str.contains ("*") && extra_builder.len > 1)
+                               throw new GXml.CssSelectorError.IDENTIFIER ("Invalid identifier");
+                       return new GXml.CssElementSelector (builder.str, extra_builder.str, true);
+               }
+               if (builder.len == 0)
+                       throw new GXml.CssSelectorError.IDENTIFIER ("string value is empty");
+               return new GXml.CssElementSelector ("", builder.str);
        }
-
-       void parse_element (string css, ref int position) {
-               StringBuilder sb = new StringBuilder();
-               unichar u = 0;
-               while (css.get_next_char (ref position, out u) && (u.isalnum() || u == '-' || u == '|' || u 
== '*'))
-                       sb.append_unichar (u);
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.ELEMENT, sb.str, "");
-               list.add (data);
+       
+       static GXml.CssNotSelector parse_not_selector (GXml.CssString str) throws GLib.Error {
+               if (str.read() != '(')
+                       throw new GXml.CssSelectorError.NOT ("Cannot find start of 'not selector' value");
+               var selector = new GXml.CssNotSelector();
+               while (str.peek().isspace())
+                       str.read();
+               parse_selectors (str, selector.selectors, ')');
+               if (str.read() != ')')
+                       throw new GXml.CssSelectorError.NOT ("Cannot find end of 'not selector' value");
+               return selector;
        }
-
-       void parse_attribute (string css, ref int position) throws GLib.Error {
-               CssSelectorData idata = new CssSelectorData.with_values (CssSelectorType.INSIDE, "", "");
-               list.add (idata);
-               position++;
-               StringBuilder sb = new StringBuilder();
-               unichar u = 0;
-               css.get_next_char (ref position, out u);
-               while (u.isspace())
-                       css.get_next_char (ref position, out u);
-               if (!u.isalnum())
-                       throw new CssSelectorError.ATTRIBUTE (_("Invalid attribute character"));
-               sb.append_unichar (u);
-               while (css.get_next_char (ref position, out u) && (u.isalnum() || u == '-'))
-                       sb.append_unichar (u);
-               while (u.isspace())
-                       css.get_next_char (ref position, out u);
-               if (u == ']') {
-                       CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.ATTRIBUTE, 
sb.str, "");
-                       list.add (data);
-                       return;
+       
+       static GXml.CssSelector parse_pseudo (GXml.CssString str) throws GLib.Error {
+               string[] pseudo_strv = { "checked", "enabled", "disabled", "root", "empty", "first-child", 
"last-child", "only-child", "first-of-type", "last-of-type", "only-of-type" };
+               string[] pseudo_value_strv = { "nth-child", "nth-last-child", "nth-of-type", 
"nth-last-of-type" };
+               
+               str.read();
+               var builder = new StringBuilder();
+               while (str.peek().isalpha() || str.peek() == '-')
+                       builder.append_unichar (str.read());
+               if (builder.str == "not")
+                       return parse_not_selector (str);
+               if (!(builder.str in pseudo_strv) && !(builder.str in pseudo_value_strv))
+                       throw new GXml.CssSelectorError.PSEUDO ("Invalid '%s' pseudo class".printf 
(builder.str));
+               if (builder.str in pseudo_strv)
+                       return new GXml.CssSelector (GXml.CssSelectorType.PSEUDO_CLASS, builder.str);
+               if (builder.str in pseudo_value_strv && str.read() != '(')
+                       throw new GXml.CssSelectorError.PSEUDO ("Invalid '%s' pseudo class : cannot find 
value".printf (builder.str));
+               var vbuilder = new StringBuilder();
+               while (str.peek().isalnum() || str.peek() == '-')
+                       vbuilder.append_unichar (str.read());
+               if (str.read() != ')')
+                       throw new GXml.CssSelectorError.PSEUDO ("Cannot find end of pseudo class value");
+               if (builder.str == "lang")
+                       return new GXml.CssSelector.with_value (GXml.CssSelectorType.PSEUDO_CLASS, "lang", 
vbuilder.str);
+               uint64 val = 0;
+               if (vbuilder.str != "odd" && vbuilder.str != "even" && (!uint64.try_parse (vbuilder.str, out 
val) || val == 0))
+                       throw new GXml.CssSelectorError.PSEUDO ("Pseudo class value isn't a valid number");
+               return new GXml.CssSelector.with_value (GXml.CssSelectorType.PSEUDO_CLASS, builder.str, 
vbuilder.str);
+       }
+       
+       static GXml.CssSelector parse_class (GXml.CssString str) throws GLib.Error {
+               str.read();
+               var builder = new StringBuilder();
+               if (!str.peek().isalpha())
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("current class doesn't start with letter");
+               while (!str.eof && is_valid_char (str.peek()) && !str.peek().isspace())
+                       builder.append_unichar (str.read());
+               return new GXml.CssSelector (GXml.CssSelectorType.CLASS, builder.str);
+       }
+       
+       static GXml.CssSelector parse_id (GXml.CssString str) throws GLib.Error {
+               str.read();
+               var builder = new StringBuilder();
+               if (!str.peek().isalpha())
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("current id doesn't start with letter");
+               while (!str.eof && is_valid_char (str.peek()) && !str.peek().isspace())
+                       builder.append_unichar (str.read());
+               return new GXml.CssSelector (GXml.CssSelectorType.ID, builder.str);
+       }
+       
+       static GXml.CssSelector parse_attribute (GXml.CssString str) throws GLib.Error {
+               str.read();
+               while (str.peek().isspace())
+                       str.read();
+               if (!str.peek().isalpha())
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Attribute key doesn't start with letter");
+               var key = parse_identifier (str, s => { return !s.peek().isspace(); });
+               if (key.length == 0)
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("No key found for current attribute 
selector");
+               while (str.peek().isspace())
+                       str.read();
+               if (str.peek() == ']') {
+                       str.read();
+                       return new GXml.CssSelector (GXml.CssSelectorType.ATTRIBUTE, key);
                }
-               if (u == '=') {
-                       StringBuilder sb1 = new StringBuilder();
-                       css.get_next_char (ref position, out u);
-                       while (u.isspace())
-                               css.get_next_char (ref position, out u);
-                       unichar s = 0;
-                       if (u == '"' || u == '\'') {
-                               s = u;
-                               css.get_next_char (ref position, out u);
-                       }
-                       if (s != 0 || is_valid_identifier (u.to_string()))
-                               sb1.append_unichar (u);
-                       while (css.get_next_char (ref position, out u) && (s != 0 ? u != s : u != ']'))
-                               sb1.append_unichar (u);
-                       if (s == 0 && !is_valid_identifier (sb1.str))
-                               throw new CssSelectorError.ATTRIBUTE (_("Invalid attribute selector value: 
%s").printf (sb1.str));
-                       if (s != 0) {
-                               if (u != s)
-                                       throw new CssSelectorError.STRING (_("Invalid end of attribute 
value"));
-                               css.get_next_char (ref position, out u);
+               var ct = GXml.CssSelectorType.CLASS;
+               if (str.peek() == '=')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_EQUAL;
+               else if (str.peek() == '|')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH_WORD;
+               else if (str.peek() == '^')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH;
+               else if (str.peek() == '$')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_ENDS_WITH;
+               else if (str.peek() == '~')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_CONTAINS;
+               else if (str.peek() == '*')
+                       ct = GXml.CssSelectorType.ATTRIBUTE_SUBSTRING;
+               if (ct == GXml.CssSelectorType.CLASS)
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Invalid attribute selector");
+               if (ct != GXml.CssSelectorType.ATTRIBUTE_EQUAL)
+                       str.read();
+               if (str.peek() != '=')
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Invalid attribute selector. '=' expected 
but '%s' was found".printf (str.peek().to_string()));
+               str.read();
+               while (str.peek().isspace())
+                       str.read();
+               unichar quote = 0;
+               if (str.peek() == '"' || str.peek() == '\'')
+                       quote = str.read();
+               var attr_value = parse_identifier (str, s => { return s.peek() != quote; });
+               if (quote > 0 && quote != str.read())
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Cannot find end of attribute value");
+               while (str.peek().isspace())
+                       str.read();
+               if (str.read() != ']')
+                       throw new GXml.CssSelectorError.ATTRIBUTE ("Cannot find end of attribute selector");
+               
+               // TODO : CSS Selectors level 4 : case sensivity.
+               
+               return new GXml.CssSelector.with_value (ct, key, attr_value);
+       }
+       
+       static void parse_selectors (GXml.CssString str, Gee.List<GXml.CssSelector> list, unichar stop_char = 
0) throws GLib.Error {
+               while (!str.eof && str.peek() != stop_char) {
+                       if (str.peek().isalpha() || str.peek() == '*' || str.peek() == '|')
+                               list.add (parse_element (str));
+                       else if (str.peek() == '.')
+                               list.add (parse_class (str));
+                       else if (str.peek() == '#')
+                               list.add (parse_id (str));
+                       else if (str.peek() == ':')
+                               list.add (parse_pseudo (str));
+                       else if (str.peek() == '[')
+                               list.add (parse_attribute (str));
+                       if (list[list.size - 1] is GXml.CssElementSelector) {
+                               var sel = list[list.size - 1] as GXml.CssElementSelector;
+                               if (sel.prefix == "" && !sel.prefixed && sel.local_name == "*")
+                                       list[list.size - 1] = new GXml.CssSelector (GXml.CssSelectorType.ALL);
                        }
-                       while (u.isspace())
-                               css.get_next_char (ref position, out u);
-                       if (u != ']')
-                               throw new CssSelectorError.ATTRIBUTE (_("Invalid end of attribute selector"));
-                       CssSelectorData data = new CssSelectorData.with_values 
(CssSelectorType.ATTRIBUTE_EQUAL, sb.str, sb1.str);
-                       if (s == 0)
-                               data.value = sb1.str.strip();
-                       list.add (data);
-                       return;
-               }
-               StringBuilder sb1 = new StringBuilder();
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.ATTRIBUTE, sb.str, 
"");
-               if (u == '~')
-                       data.selector_type = CssSelectorType.ATTRIBUTE_CONTAINS;
-               else if (u == '*')
-                       data.selector_type = CssSelectorType.ATTRIBUTE_SUBSTRING;
-               else if (u == '^')
-                       data.selector_type = CssSelectorType.ATTRIBUTE_START_WITH;
-               else if (u == '|')
-                       data.selector_type = CssSelectorType.ATTRIBUTE_START_WITH_HYPHEN;
-               else if (u == '$')
-                       data.selector_type = CssSelectorType.ATTRIBUTE_END_WITH;
-               else
-                       throw new CssSelectorError.ATTRIBUTE (_("Invalid attribute selector character"));
-               css.get_next_char (ref position, out u);
-               if (u != '=')
-                       throw new CssSelectorError.ATTRIBUTE (_("Invalid attribute selector character: can't 
find '=' character"));
-               css.get_next_char (ref position, out u);
-               while (u.isspace())
-                       css.get_next_char (ref position, out u);
-               unichar s = 0;
-               if (u == '"' || u == '\'') {
-                       s = u;
-                       css.get_next_char (ref position, out u);
-               }
-               if (s != 0 || is_valid_identifier (u.to_string()))
-                       sb1.append_unichar (u);
-               while (css.get_next_char (ref position, out u) && (s != 0 ? u != s : u != ']'))
-                       sb1.append_unichar (u);
-               if (s == 0 && !is_valid_identifier (sb1.str))
-                       throw new CssSelectorError.ATTRIBUTE (_("Invalid attribute selector value: 
%s").printf (sb1.str));
-               if (s != 0) {
-                       if (u != s)
-                               throw new CssSelectorError.STRING (_("Invalid end of attribute value"));
-                       css.get_next_char (ref position, out u);
+                       
+                       GXml.CssCombiner combiner = 0;
+                       if (str.peek().isspace())
+                               combiner = GXml.CssCombiner.INSIDE;
+                       while (str.peek().isspace())
+                               str.read();
+                       if (str.peek() == ',')
+                               combiner = GXml.CssCombiner.AND;
+                       else if (str.peek() == '>')
+                               combiner = GXml.CssCombiner.PARENT;
+                       else if (str.peek() == '+')
+                               combiner = GXml.CssCombiner.AFTER;
+                       else if (str.peek() == '~')
+                               combiner = GXml.CssCombiner.BEFORE;
+                       else if (combiner == 0)
+                               combiner = GXml.CssCombiner.NONE;
+                       if (combiner != GXml.CssCombiner.NONE && combiner != GXml.CssCombiner.INSIDE)
+                               str.read();
+                       list[list.size - 1].combiner = combiner;
+                       while (str.peek().isspace())
+                               str.read();
                }
-               while (u.isspace())
-                       css.get_next_char (ref position, out u);
-               if (u != ']')
-                       throw new CssSelectorError.ATTRIBUTE (_("Invalid end of attribute selector"));
-               if (s == 0)
-                       data.value = sb1.str.strip();
-               else
-                       data.value = sb1.str;
-               list.add (data);
+               if (list.size == 0)
+                       throw new GXml.CssSelectorError.NULL ("No selectors found");
+               if (list[list.size - 1].combiner == GXml.CssCombiner.NONE)
+                       list[list.size - 1].combiner = GXml.CssCombiner.NULL;
+               if (list[list.size - 1].combiner != GXml.CssCombiner.NULL)
+                       throw new GXml.CssSelectorError.COMBINER ("Last selector has combiner assigned 
(%s)".printf (list[list.size - 1].combiner.to_string()));
        }
 
-       void parse_pseudo (string css, ref int position) throws GLib.Error {
-               CssSelectorData idata = new CssSelectorData.with_values (CssSelectorType.INSIDE, "", "");
-               list.add (idata);
-               position++;
-               StringBuilder sb = new StringBuilder();
-               unichar u = 0;
-               while (css.get_next_char (ref position, out u) && (u.isalnum() || u == '-' || u == ':'))
-                       sb.append_unichar (u);
-               string[] valid_selectors = {
-                       "root",
-                       "checked",
-                       "disabled",
-                       "empty",
-                       "enable",
-                       "first-child",
-                       ":first-letter",
-                       ":first-line",
-                       "first-of-type",
-                       "last-child",
-                       "last-of-type"
-               };
-               if (!(sb.str in valid_selectors))
-                       throw new CssSelectorError.INVALID (_("Invalid pseudo class selector %s").printf 
(sb.str));
-               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.PSEUDO, sb.str, "");
-               list.add (data);
+       public void parse (string selectors) throws GLib.Error {
+               this.list.clear();
+               var str = new GXml.CssString (selectors);
+               parse_selectors (str, this.list);
        }
 
-       public void parse (string query) throws GLib.Error {
-               string css = query.strip();
-               if (css.length == 0)
-                       throw new CssSelectorError.LENGTH (_("Invalid string length."));
-               int position = 0;
-               unichar u = 0;
-               while (position < css.length) {
-                       u = css.get_char (position);
-                       if (u.isspace()) {
-                               css.get_next_char (ref position, out u);
-                               while (u.isspace())
-                                       css.get_next_char (ref position, out u);
-                               position--;
-                       }
-                       if (selectors.size > 0) {
-                         var ps = selectors.get (selectors.size -1);
-                         if (ps != null) {
-                                       if (ps.selector_type == CssSelectorType.ELEMENT) {
-                                               css.get_prev_char (ref position, out u);
-                                               if (u == '.') {
-                                                       parse_class (css, ref position);
-                                                       continue;
-                                               }
-                                               else if (u == '#') {
-                                                       parse_id (css, ref position);
-                                                       continue;
-                                               }
-                                               else if (u == '[') {
-                                                       parse_attribute (css, ref position);
-                                                       continue;
-                                               }
-                                               else if (u == ':') {
-                                                       parse_pseudo (css, ref position);
-                                                       continue;
-                                               }
-                                               else
-                                                       throw new CssSelectorError.TYPE (_("Invalid '%s' 
character.").printf (css.substring (position, 1)));
-                                       }
-                               }
-                       }
-                       if (u == '*' && !("|" in css))
-                               parse_all (css, ref position);
-                       else if (u == '.') {
-                               parse_class (css, ref position);
-                               continue;
-                       }
-                       else if (u.isalnum() || u == '*' || u == '|')
-                               parse_element (css, ref position);
-                       else if (u == ',') {
-                               position++;
-                               CssSelectorData data = new CssSelectorData.with_values (CssSelectorType.AND, 
",", "");
-                               list.add (data);
-                       }
-                       else if (u == '+') {
-                               position++;
-                               CssSelectorData data = new CssSelectorData.with_values 
(CssSelectorType.AFTER, "+", "");
-                               list.add (data);
-                       }
-                       else if (u == '~') {
-                               position++;
-                               CssSelectorData data = new CssSelectorData.with_values 
(CssSelectorType.BEFORE, "~", "");
-                               list.add (data);
-                       }
-                       else if (u == '>') {
-                               position++;
-                               CssSelectorData data = new CssSelectorData.with_values 
(CssSelectorType.PARENT, ">", "");
-                               list.add (data);
-                       }
+       Gee.ArrayList<GXml.CssSelector> list;
+       
+       construct {
+               this.list = new Gee.ArrayList<GXml.CssSelector>();
+       }
+       
+       public Gee.List<GXml.CssSelector> selectors {
+               get {
+                       return this.list;
                }
        }
-       public bool match (DomElement element) throws GLib.Error {
-               bool is_element = false;
-               for (int i = 0; i < selectors.size; i++) {
-                       CssSelectorData s = selectors.get (i);
-                       if (s.selector_type == CssSelectorType.ALL) return true;
-                       if (s.selector_type == CssSelectorType.INSIDE) continue;
-                       if (s.selector_type == CssSelectorType.ELEMENT) {
-                               string ns = null;
-                               string nn = s.data.down ();
-                               bool nsnull = false;
-                               if ("|" in s.data) {
-                                       var nss = s.data.split ("|");
-                                       if (nss.length == 2) {
-                                               ns = nss[0].down ();
-                                               nn = nss[1].down ();
-                                               if (ns == null) nsnull = true;
-                                       }
-                               }
-                               message (element.prefix+";"+element.local_name);
-                               if (element.local_name.down () != nn) return false;
-                               if (!nsnull && ns != null && ns != "*" && element.prefix != null && 
element.prefix.down () != ns) return false;
-                               message (s.data);
-                               message (ns+";"+nn);
-                               is_element = true;
-                               if ((i+1) >= selectors.size) return true;
-                               continue;
-                       }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE) {
-                               var p = element.get_attribute (s.data);
-                               if (p != null) return true;
-                       }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE_EQUAL) {
-                               var p = element.get_attribute (s.data);
-                               if (p == null) return false;
-                               if (p == s.value) return true;
-                       }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE_CONTAINS) {
-                               var p = element.get_attribute (s.data);
-                               if (p == null) return false;
-                               var tl = new GDomTokenList (element, s.data);
-                               if (tl.contains (s.value)) return true;
+       
+       static bool match_pseudo (GXml.DomElement element, GXml.CssSelector selector) throws GLib.Error {
+               if (selector.name == "root")
+                       return (element as GXml.GNode).get_internal_node() == 
(element.owner_document.document_element as GXml.GNode).get_internal_node();
+               if (selector.name == "empty")
+                       return element.children.length == 0;
+               if (selector.name == "checked") {
+                       if (element.local_name != "input")
+                               return false;
+                       return element.attributes.get_named_item ("checked") != null;
+               }
+               if (selector.name == "enabled" || selector.name == "disabled") {
+                       if (element.local_name != "input")
+                               return false;
+                       if (selector.name == "disabled")
+                               return element.attributes.get_named_item ("disabled") != null;
+                       return element.attributes.get_named_item ("disabled") == null;
+               }
+               if (selector.name == "first-child")
+                       return element.previous_element_sibling == null;
+               if (selector.name == "last-child")
+                       return element.next_element_sibling == null;
+               if (selector.name == "only-child")
+                       return element.previous_element_sibling == null && element.next_element_sibling == 
null;
+               if (selector.name == "first-of-type") {
+                       var e = element.previous_element_sibling;
+                       bool res = true;
+                       while (e != null) {
+                               if (e.local_name == element.local_name)
+                                       res = false;
+                               e = e.previous_element_sibling;
                        }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE_START_WITH) {
-                               var p = element.get_attribute (s.data);
-                               if (p == null) return false;
-                               if (p.has_prefix (s.value)) return true;
+                       return res;
+               }
+               if (selector.name == "last-of-type") {
+                       var e = element.next_element_sibling;
+                       bool res = true;
+                       while (e != null) {
+                               if (e.local_name == element.local_name)
+                                       res = false;
+                               e = e.next_element_sibling;
                        }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE_END_WITH) {
-                               var p = element.get_attribute (s.data);
-                               if (p == null) return false;
-                               if (p.has_suffix (s.value)) return true;
+                       return res;
+               }
+               if (selector.name == "only-of-type") {
+                       var e = element.previous_element_sibling;
+                       var f = element.next_element_sibling;
+                       bool eres = true;
+                       bool fres = true;
+                       while (e != null) {
+                               if (e.local_name == element.local_name)
+                                       eres = false;
+                               e = e.previous_element_sibling;
                        }
-                       if (is_element && s.selector_type == CssSelectorType.ATTRIBUTE_START_WITH_HYPHEN) {
-                               var p = element.get_attribute (s.data);
-                               if (p == null) return false;
-                               if (p.has_suffix (s.value+"-")) return true;
+                       while (f != null) {
+                               if (f.local_name == element.local_name)
+                                       fres = false;
+                               f = f.next_element_sibling;
                        }
-                       if (is_element && s.selector_type == CssSelectorType.PSEUDO) {
-                               if (s.data.down () == "root") {
-                                       if (element is GomElement)
-                                               if (element != element.owner_document.document_element) 
return false;
-                                       if (element is GElement)
-                                               if ((element as GNode).get_internal_node () 
-                                                                       != 
(element.owner_document.document_element as GNode).get_internal_node ()) return false;
-                                       if (element.node_name.down () == 
element.owner_document.document_element.node_name.down ()) return true;
-                               }
-                               if (s.data.down () == "checked") {
-                                       if (!(element.owner_document is DomHtmlDocument)) return false;
-                                       // FIXME: check for tags UI allowed to have this state you can use 
E[checked="true"] instead
-                               }
-                               if (s.data.down () == "enable") {
-                                       if (!(element.owner_document is DomHtmlDocument)) return false;
-                                       // FIXME: check for tags UI allowed to have this state you can use 
E[enable="true"] instead
-                               }
-                               if (s.data.down () == "disabled") {
-                                       if (!(element.owner_document is DomHtmlDocument)) return false;
-                                       // FIXME: check for tags UI allowed to have this state you can use 
E[disable="true"] instead
+                       return eres && fres;
+               }
+               if (selector.name == "nth-child") {
+                       if (element.parent_element == null)
+                               return false;
+                       if (selector.value == "even" || selector.value == "odd") {
+                               for (var i = (selector.value == "even" ? 1 : 0); i < 
element.parent_element.children.length; i += 2) {
+                                       var child = element.parent_element.children.item (i);
+                                       if (child == element)
+                                               return true;
+                                       if ((child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node())
+                                               return true;
                                }
-                               if (s.data.down () == "empty") {
-                                       if (!element.has_child_nodes ()) return true;
+                               return false;
+                       }
+                       var index = int.parse (selector.value) - 1;
+                       if (index >= element.parent_element.children.length)
+                               return false;
+                       var child = element.parent_element.children.item (index);
+                       if (child == element)
+                               return true;
+                       return (child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node();
+               }
+               if (selector.name == "nth-last-child") {
+                       if (element.parent_element == null)
+                               return false;
+                       if (selector.value == "even" || selector.value == "odd") {
+                               for (var i = (selector.value == "even" ? 1 : 0); i < 
element.parent_element.children.length; i += 2) {
+                                       var child = element.parent_element.children.item 
(element.parent_element.children.length - 1 - i);
+                                       if (child == element)
+                                               return true;
+                                       if ((child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node())
+                                               return true;
                                }
-                               if (s.data.down () == "first-child") {
-                                       if (element.parent_node == null) return false;
-                                       if (!(element.parent_node is DomElement)) return false;
-                                       if (element is GomElement)
-                                               if (element == (element.parent_node as 
DomParentNode).first_element_child) return true;
-                                       if (element is GElement)
-                                               if ((element as GNode).get_internal_node () 
-                                                                       == ((element.parent_node as 
DomParentNode).first_element_child as GNode).get_internal_node ()) return true;
+                               return false;
+                       }
+                       var index = int.parse (selector.value) - 1;
+                       if (index >= element.parent_element.children.length)
+                               return false;
+                       var child = element.parent_element.children.item 
(element.parent_element.children.length - 1 - index);
+                       if (child == element)
+                               return true;
+                       return (child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node();
+               }
+               if (selector.name == "nth-of-type") {
+                       if (element.parent_element == null)
+                               return false;
+                       var list = new Gee.ArrayList<GXml.DomElement>();
+                       foreach (var child in element.parent_element.children)
+                               if (child.local_name == element.local_name)
+                                       list.add (child);
+                       if (selector.value == "even" || selector.value == "odd") {
+                               for (var i = (selector.value == "even" ? 1 : 0); i < list.size; i += 2) {
+                                       var child = list[i];
+                                       if (child == element)
+                                               return true;
+                                       if ((child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node())
+                                               return true;
                                }
-                               if (s.data.down () == "last-child") {
-                                       if (element.parent_node == null) return false;
-                                       if (!(element.parent_node is DomElement)) return false;
-                                       if (element is GomElement)
-                                               if (element == (element.parent_node as 
DomParentNode).last_element_child) return true;
-                                       if (element is GElement)
-                                               if ((element as GNode).get_internal_node () 
-                                                                       == ((element.parent_node as 
DomParentNode).last_element_child as GNode).get_internal_node ()) return true;
+                               return false;
+                       }
+                       var index = int.parse (selector.value) - 1;
+                       if (index >= list.size)
+                               return false;
+                       if (list[index] == element)
+                               return true;
+                       return (list[index] as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node();
+               }
+               if (selector.name == "nth-last-of-type") {
+                       if (element.parent_element == null)
+                               return false;
+                       var list = new Gee.ArrayList<GXml.DomElement>();
+                       foreach (var child in element.parent_element.children)
+                               if (child.local_name == element.local_name)
+                                       list.add (child);
+                       if (selector.value == "even" || selector.value == "odd") {
+                               for (var i = (selector.value == "even" ? 1 : 0); i < list.size; i += 2) {
+                                       var child = list[list.size - 1 - i];
+                                       if (child == element)
+                                               return true;
+                                       if ((child as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node())
+                                               return true;
                                }
+                               return false;
                        }
-                       if (s.selector_type == CssSelectorType.CLASS) {
-                               var p = element.get_attribute ("class");
-                               if (p == null) return false;
-                               var lc = element.class_list;
-                               if (lc.contains (s.data)) return true;
-                               if (p=="warning") warning ("Not found");
+                       var index = int.parse (selector.value) - 1;
+                       if (index >= list.size)
+                               return false;
+                       if (list[list.size - 1 - index] == element)
+                               return true;
+                       return (list[list.size - 1 - index] as GXml.GNode).get_internal_node() == (element as 
GXml.GNode).get_internal_node();
+               }
+               return false;
+       }
+                       
+       static bool match_node (GXml.DomElement element, GXml.CssSelector selector) throws GLib.Error {
+               if (selector.selector_type == GXml.CssSelectorType.ALL)
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.CLASS && element.get_attribute ("class") 
!= null && element.class_list.contains (selector.name))
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ID && element.id == selector.name)
+                       return true;
+               if (selector is GXml.CssElementSelector) {
+                       var sel = selector as GXml.CssElementSelector;
+                       if (sel.local_name == "*" && (!sel.prefixed || sel.prefix == "*"))
+                               return true;
+                       if (sel.prefixed && sel.prefix == "") {
+                               if (element.prefix != null && element.prefix.length > 0)
+                                       return false;
+                               return sel.local_name == "*" || element.local_name == sel.local_name;
+                       }
+                       if (sel.prefix == "*")
+                               return sel.local_name == element.local_name;
+                       return sel.local_name == element.local_name && sel.prefix == element.prefix;
+               }
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE && 
element.attributes.get_named_item (selector.name) != null)
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_EQUAL && element.get_attribute 
(selector.name) == selector.value)
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_CONTAINS && 
element.get_attribute (selector.name) != null && (selector.value in element.get_attribute 
(selector.name).split (" ")))
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_SUBSTRING && 
element.get_attribute (selector.name) != null && element.get_attribute (selector.name).contains 
(selector.value))
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH_WORD && 
element.get_attribute (selector.name) != null && element.get_attribute (selector.name).split ("-")[0] == 
selector.value)
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_STARTS_WITH && 
element.get_attribute (selector.name) != null && element.get_attribute (selector.name).index_of 
(selector.value) == 0)
+                       return true;
+               if (selector.selector_type == GXml.CssSelectorType.ATTRIBUTE_ENDS_WITH && 
element.get_attribute (selector.name) != null && element.get_attribute (selector.name).last_index_of 
(selector.value) == element.get_attribute (selector.name).length - selector.value.length)
+                       return true;
+               if (selector is GXml.CssNotSelector)
+                       return !match_element (element, (selector as GXml.CssNotSelector).selectors);
+               if (selector.selector_type == GXml.CssSelectorType.PSEUDO_CLASS)
+                       return match_pseudo (element, selector);
+               return false;
+       }
+       
+       static bool match_element (GXml.DomElement element, Gee.Collection<GXml.CssSelector> selectors) 
throws GLib.Error {
+               var list = new Gee.ArrayList<GXml.CssSelector>();
+               list.add_all (selectors);
+               var selector = list.remove_at (list.size - 1);
+               if (list.size == 0)
+                       return match_node (element, selector);
+               if (list[list.size - 1].combiner == GXml.CssCombiner.AND)
+                       return match_node (element, selector) || match_element (element, list);
+               if (list[list.size - 1].combiner == GXml.CssCombiner.NONE)
+                       return match_node (element, selector) && match_element (element, list);
+               if (list[list.size - 1].combiner == GXml.CssCombiner.AFTER) {
+                       if (element.previous_element_sibling == null)
+                               return false;
+                       return match_node (element, selector) && match_element 
(element.previous_element_sibling, list);
+               }
+               if (list[list.size - 1].combiner == GXml.CssCombiner.BEFORE) {
+                       if (element.next_element_sibling == null)
+                               return false;
+                       return match_node (element, selector) && match_element (element.next_element_sibling, 
list);
+               }
+               if (list[list.size - 1].combiner == GXml.CssCombiner.PARENT)
+                       return match_node (element, selector) && match_element (element.parent_element, list);
+               if (list[list.size - 1].combiner == GXml.CssCombiner.INSIDE) {
+                       var parent = element.parent_element;
+                       bool res = match_node (element, selector);
+                       while (parent != null) {
+                               if (res && match_element (parent, list))
+                                       return true;
+                               parent = parent.parent_element;
                        }
                }
                return false;
        }
 
+       public bool match (GXml.DomElement element) throws GLib.Error {
+               return match_element (element, this.list);
+       }
 }
diff --git a/test/CssSelectorTest.vala b/test/CssSelectorTest.vala
index 909a558..8bb08e5 100644
--- a/test/CssSelectorTest.vala
+++ b/test/CssSelectorTest.vala
@@ -66,14 +66,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
                                assert (sa.selector_type == CssSelectorType.ATTRIBUTE);
                                var d = new GomDocument ();
@@ -95,14 +93,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name~=\"val\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
                                assert (sa.selector_type == CssSelectorType.ATTRIBUTE_CONTAINS);
                                var d = new GomDocument ();
@@ -132,16 +128,14 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name^=\"val\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_START_WITH);
+                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_STARTS_WITH);
                                var d = new GomDocument ();
                                var r = d.create_element ("root");
                                d.append_child (r);
@@ -165,20 +159,18 @@ class CssSelectorTest : GXmlTest {
                                warning ("ERROR: "+e.message);
                        }
                });
-               Test.add_func ("/gxml/css-selector/element/attribute/starts_with_hyphen", () => {
+               Test.add_func ("/gxml/css-selector/element/attribute/starts_with_word", () => {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name|=\"val\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_START_WITH_HYPHEN);
+                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_STARTS_WITH_WORD);
                                var d = new GomDocument ();
                                var r = d.create_element ("root");
                                d.append_child (r);
@@ -210,12 +202,10 @@ class CssSelectorTest : GXmlTest {
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_END_WITH);
+                               assert (sa.selector_type == CssSelectorType.ATTRIBUTE_ENDS_WITH);
                                var d = new GomDocument ();
                                var r = d.create_element ("root");
                                d.append_child (r);
@@ -243,14 +233,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name=va7u3_unqu§ted]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
                                assert (sa.selector_type == CssSelectorType.ATTRIBUTE_EQUAL);
                                var d = new GomDocument ();
@@ -275,14 +263,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child[prop-name=\"val\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
                                assert (sa.selector_type == CssSelectorType.ATTRIBUTE_EQUAL);
                                var d = new GomDocument ();
@@ -307,11 +293,8 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse (".warning");
-                               assert (cp.selectors.size == 2);
-                               var s = cp.selectors[0];
-                               assert (s != null);
-                               assert (s.selector_type == CssSelectorType.INSIDE);
-                               var si = cp.selectors[1];
+                               assert (cp.selectors.size == 1);
+                               var si = cp.selectors[0];
                                assert (si != null);
                                assert (si.selector_type == CssSelectorType.CLASS);
                                var d = new GomDocument ();
@@ -345,14 +328,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child.warning");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sc = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sc = cp.selectors[1];
                                assert (sc != null);
                                assert (sc.selector_type == CssSelectorType.CLASS);
                                var d = new GomDocument ();
@@ -390,12 +371,10 @@ class CssSelectorTest : GXmlTest {
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.PSEUDO);
+                               assert (sa.selector_type == CssSelectorType.PSEUDO_CLASS);
                                var d = new GomDocument ();
                                var r = d.create_element ("toplevel");
                                d.append_child (r);
@@ -442,14 +421,12 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("radio[enable=\"true\"]");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
                                assert (sa.selector_type == CssSelectorType.ATTRIBUTE_EQUAL);
                                var d = new GomDocument ();
@@ -471,16 +448,14 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("child:empty");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.PSEUDO);
+                               assert (sa.selector_type == CssSelectorType.PSEUDO_CLASS);
                                var d = new GomDocument ();
                                var r = d.create_element ("toplevel");
                                d.append_child (r);
@@ -538,16 +513,14 @@ class CssSelectorTest : GXmlTest {
                        try {
                                var cp = new CssSelectorParser ();
                                cp.parse ("second:first-child");
-                               assert (cp.selectors.size == 3);
+                               assert (cp.selectors.size == 2);
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.PSEUDO);
+                               assert (sa.selector_type == CssSelectorType.PSEUDO_CLASS);
                                var d = new GomDocument ();
                                var r = d.create_element ("toplevel");
                                d.append_child (r);
@@ -616,12 +589,10 @@ class CssSelectorTest : GXmlTest {
                                var s = cp.selectors[0];
                                assert (s != null);
                                assert (s.selector_type == CssSelectorType.ELEMENT);
-                               var si = cp.selectors[1];
-                               assert (si != null);
-                               assert (si.selector_type == CssSelectorType.INSIDE);
-                               var sa = cp.selectors[2];
+                               assert (s.combiner == CssSelectorType.NONE);
+                               var sa = cp.selectors[1];
                                assert (sa != null);
-                               assert (sa.selector_type == CssSelectorType.PSEUDO);
+                               assert (sa.selector_type == CssSelectorType.PSEUDO_CLASS);
                                var d = new GomDocument ();
                                var r = d.create_element ("toplevel");
                                d.append_child (r);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]