about summary refs log tree commit diff stats
path: root/src/css
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-11-23 13:01:23 +0100
committerbptato <nincsnevem662@gmail.com>2021-11-23 13:01:23 +0100
commit30f3b5deb3be13683e73175f241600c028910eaa (patch)
tree8524e4451cbf7ef4f8850f9b74f9df8bfd839ac0 /src/css
parentff1b68086d699510dcdbea6051460926556bd401 (diff)
downloadchawan-30f3b5deb3be13683e73175f241600c028910eaa.tar.gz
Support CSS child combinators
Diffstat (limited to 'src/css')
-rw-r--r--src/css/selector.nim49
-rw-r--r--src/css/style.nim33
2 files changed, 56 insertions, 26 deletions
diff --git a/src/css/selector.nim b/src/css/selector.nim
index 97c7d695..8489f240 100644
--- a/src/css/selector.nim
+++ b/src/css/selector.nim
@@ -12,13 +12,13 @@ type
 
   QueryMode* = enum
     QUERY_TYPE, QUERY_CLASS, QUERY_ATTR, QUERY_DELIM, QUERY_VALUE,
-    QUERY_PSEUDO, QUERY_PSELEM, QUERY_COMBINATOR
+    QUERY_PSEUDO, QUERY_PSELEM, QUERY_DESC_COMBINATOR, QUERY_CHILD_COMBINATOR
 
   PseudoElem* = enum
     PSEUDO_NONE, PSEUDO_BEFORE, PSEUDO_AFTER
 
   CombinatorType* = enum
-    DESCENDANT_COMBINATOR
+    DESCENDANT_COMBINATOR, CHILD_COMBINATOR
 
   SelectorParser = object
     selectors: seq[SelectorList]
@@ -86,10 +86,8 @@ func getSpecificity(sel: Selector): int =
   of UNIVERSAL_SELECTOR:
     discard
   of COMBINATOR_SELECTOR:
-    case sel.ct
-    of DESCENDANT_COMBINATOR:
-      for child in sel.csels:
-        result += getSpecificity(child)
+    for child in sel.csels:
+      result += getSpecificity(child)
 
 func getSpecificity*(sels: SelectorList): int =
   for sel in sels.sels:
@@ -135,17 +133,31 @@ proc addSelectorList(state: var SelectorParser) =
     state.combinator = nil
   state.selectors.add(SelectorList())
 
+proc parseSelectorCombinator(state: var SelectorParser, ct: CombinatorType, csstoken: CSSToken) =
+  if csstoken.tokenType in {CSS_IDENT_TOKEN, CSS_HASH_TOKEN,
+                            CSS_COLON_TOKEN}:
+    if state.combinator != nil and state.combinator.ct != ct:
+      let nc = Selector(t: COMBINATOR_SELECTOR, ct: ct)
+      nc.csels.add(SelectorList())
+      nc.csels[^1].add(state.combinator)
+      state.combinator = nc
+
+    if state.combinator == nil:
+      state.combinator = Selector(t: COMBINATOR_SELECTOR, ct: ct)
+
+    state.combinator.csels.add(state.selectors[^1])
+    if state.combinator.csels[^1].len > 0:
+      state.combinator.csels.add(SelectorList())
+    state.selectors[^1] = SelectorList()
+    state.query = QUERY_TYPE
+
 proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) =
-  if state.query == QUERY_COMBINATOR:
-    if csstoken.tokenType in {CSS_IDENT_TOKEN, CSS_HASH_TOKEN,
-                              CSS_COLON_TOKEN}:
-      if state.combinator == nil:
-        state.combinator = Selector(t: COMBINATOR_SELECTOR, ct: DESCENDANT_COMBINATOR)
-      state.combinator.csels.add(state.selectors[^1])
-      if state.combinator.csels[^1].len > 0:
-        state.combinator.csels.add(SelectorList())
-      state.selectors[^1] = SelectorList()
-      state.query = QUERY_TYPE
+  if state.query == QUERY_DESC_COMBINATOR:
+    state.parseSelectorCombinator(DESCENDANT_COMBINATOR, csstoken)
+  elif state.query == QUERY_CHILD_COMBINATOR:
+    if csstoken.tokenType == CSS_WHITESPACE_TOKEN:
+      return
+    state.parseSelectorCombinator(CHILD_COMBINATOR, csstoken)
 
   case csstoken.tokenType
   of CSS_IDENT_TOKEN:
@@ -163,6 +175,9 @@ proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) =
   of CSS_DELIM_TOKEN:
     if csstoken.rvalue == Rune('.'):
       state.query = QUERY_CLASS
+    elif csstoken.rvalue == Rune('>'):
+      if state.selectors[^1].len > 0 or state.combinator != nil:
+        state.query = QUERY_CHILD_COMBINATOR
   of CSS_HASH_TOKEN:
     state.addSelector(Selector(t: ID_SELECTOR, id: $csstoken.value))
   of CSS_COMMA_TOKEN:
@@ -170,7 +185,7 @@ proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) =
       state.addSelectorList()
   of CSS_WHITESPACE_TOKEN:
     if state.selectors[^1].len > 0 or state.combinator != nil:
-      state.query = QUERY_COMBINATOR
+      state.query = QUERY_DESC_COMBINATOR
   of CSS_COLON_TOKEN:
     if state.query == QUERY_PSEUDO:
       state.query = QUERY_PSELEM
diff --git a/src/css/style.nim b/src/css/style.nim
index 897c1652..d42b8e25 100644
--- a/src/css/style.nim
+++ b/src/css/style.nim
@@ -71,26 +71,42 @@ func selectorMatches(elem: Element, sel: Selector): SelectResult =
   of FUNC_SELECTOR:
     return selectres(false)
   of COMBINATOR_SELECTOR:
+    #combinator without at least two members makes no sense
+    assert sel.csels.len > 1
     case sel.ct
     of DESCENDANT_COMBINATOR:
-      #combinator without at least two members makes no sense
-      assert sel.csels.len > 1
-      if elem.selectorsMatch(sel.csels[^1]).success:
+      let match = elem.selectorsMatch(sel.csels[^1])
+      if match.success:
         var i = sel.csels.len - 2
         var e = elem.parentElement
-        var pseudo = PSEUDO_NONE
         while e != nil and e != elem.ownerDocument.root and i >= 0:
           let res = e.selectorsMatch(sel.csels[i])
 
           if res.pseudo != PSEUDO_NONE:
-            if pseudo != PSEUDO_NONE:
-              return selectres(false)
-            pseudo = res.pseudo
+            return selectres(false)
 
           if res.success:
             dec i
           e = e.parentElement
-        return selectres(i == -1, pseudo)
+        return selectres(i == -1, match.pseudo)
+      else:
+        return selectres(false)
+    of CHILD_COMBINATOR:
+      let match = elem.selectorsMatch(sel.csels[^1])
+      if match.success:
+        var i = sel.csels.len - 2
+        var e = elem.parentElement
+        while e != nil and e != elem.ownerDocument.root and i >= 0:
+          let res = e.selectorsMatch(sel.csels[i])
+
+          if res.pseudo != PSEUDO_NONE:
+            return selectres(false)
+
+          if not res.success:
+            return selectres(false)
+          dec i
+          e = e.parentElement
+        return selectres(i == -1, match.pseudo)
       else:
         return selectres(false)
 
@@ -129,7 +145,6 @@ func selectElems(document: Document, sel: Selector): seq[Element] =
       return document.all_elements.filter((elem) => selectorsMatch(elem, sel.fsels).psuccess)
     return newSeq[Element]()
   of COMBINATOR_SELECTOR:
-    #TODO
     return document.all_elements.filter((elem) => selectorMatches(elem, sel))
 
 func selectElems(document: Document, selectors: SelectorList): seq[Element] =