about summary refs log tree commit diff stats
path: root/src/css
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-09-01 01:05:04 +0200
committerbptato <nincsnevem662@gmail.com>2022-09-01 01:33:23 +0200
commit252174bacfffd5b3eb21056f678a225b368dda10 (patch)
treef8dca1dda2201386d2e9ecc0adfd524480fa9af0 /src/css
parent48c236c0dbd58edebf558ebb822e4ceaa56038f2 (diff)
downloadchawan-252174bacfffd5b3eb21056f678a225b368dda10.tar.gz
Fix combinator bugs + reimplement querySelectorAll
Diffstat (limited to 'src/css')
-rw-r--r--src/css/match.nim108
-rw-r--r--src/css/selectorparser.nim62
2 files changed, 51 insertions, 119 deletions
diff --git a/src/css/match.nim b/src/css/match.nim
index 1fcb9fda..a50f8396 100644
--- a/src/css/match.nim
+++ b/src/css/match.nim
@@ -1,4 +1,5 @@
 import options
+import streams
 import strutils
 import tables
 
@@ -100,7 +101,9 @@ func pseudoSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem:
 
 func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
   let selem = elem
-  #combinator without at least two members makes no sense
+  # combinator without at least two members makes no sense
+  # actually, combinators with more than two elements are a pretty bad idea
+  # too. TODO getting rid of them would simplify this function greatly
   assert sel.csels.len > 1
   if selem.selectorsMatch(sel.csels[^1], felem):
     var i = sel.csels.len - 2
@@ -132,13 +135,12 @@ func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, fe
           e = e.parentElement
     of NEXT_SIBLING_COMBINATOR:
       var found = false
-      when elem is StyledNode:
-        var parent = elem.parent
-      else:
-        var parent = elem.parentElement
+      let parent = when elem is StyledNode: elem.parent
+      else: elem.parentElement
+      if parent == nil: return false
       for child in parent.children_rev:
         when elem is StyledNode:
-          if child.t != STYLED_ELEMENT or child.node == nil: continue
+          if not child.isDomElement: continue
         if found:
           if not child.selectorsMatch(sel.csels[i], felem):
             return false
@@ -149,24 +151,24 @@ func combinatorSelectorMatches[T: Element|StyledNode](elem: T, sel: Selector, fe
           found = true
     of SUBSEQ_SIBLING_COMBINATOR:
       var found = false
-      when selem is StyledNode:
-        var parent = selem.parent
-      else:
-        var parent = elem.parentElement
+      let parent = when selem is StyledNode: selem.parent
+      else: elem.parentElement
+      if parent == nil: return false
       for child in parent.children_rev:
         when selem is StyledNode:
-          if child.t != STYLED_ELEMENT or child.node == nil: continue
-        if found:
-          if child.selectorsMatch(sel.csels[i], felem):
-            dec i
-          if i < 0:
-            return true
+          if not child.isDomElement: continue
         if child == selem:
           found = true
+          continue
+        if not found: continue
+        if child.selectorsMatch(sel.csels[i], felem):
+          dec i
+        if i < 0:
+          return true
     return i == -1
   return false
 
-func selectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T): bool =
+func selectorMatches[T: Element|StyledNode](elem: T, sel: Selector, felem: T = nil): bool =
   let selem = elem
   when elem is StyledNode:
     let elem = Element(selem.node)
@@ -201,63 +203,15 @@ func selectorsMatch*[T: Element|StyledNode](elem: T, selectors: ComplexSelector,
       return false
   return true
 
-#TODO idk, it's not like we have JS anyways
-#func selectElems[T: Element|StyledNode](element: T, sel: Selector, felem: T): seq[T] =
-#  case sel.t
-#  of TYPE_SELECTOR:
-#    return element.filterDescendants((elem) => elem.tagType == sel.tag)
-#  of ID_SELECTOR:
-#    return element.filterDescendants((elem) => elem.id == sel.id)
-#  of CLASS_SELECTOR:
-#    return element.filterDescendants((elem) => sel.class in elem.classList)
-#  of UNIVERSAL_SELECTOR:
-#    return element.all_descendants
-#  of ATTR_SELECTOR:
-#    return element.filterDescendants((elem) => attrSelectorMatches(elem, sel))
-#  of PSEUDO_SELECTOR:
-#    return element.filterDescendants((elem) => pseudoSelectorMatches(elem, sel, felem))
-#  of PSELEM_SELECTOR:
-#    return element.all_descendants
-#  of FUNC_SELECTOR:
-#    return element.filterDescendants((elem) => selectorMatches(elem, sel))
-#  of COMBINATOR_SELECTOR:
-#    return element.filterDescendants((elem) => selectorMatches(elem, sel))
-#
-#func selectElems(element: Element, selectors: SelectorList): seq[Element] =
-#  assert(selectors.len > 0)
-#  let sellist = optimizeSelectorList(selectors)
-#  result = element.selectElems(selectors[0], element)
-#  var i = 1
-#
-#  while i < sellist.len:
-#    result = result.filter((elem) => selectorMatches(elem, sellist[i], elem))
-#    inc i
-#
-#proc querySelectorAll*(document: Document, q: string): seq[Element] =
-#  let ss = newStringStream(q)
-#  let cvals = parseListOfComponentValues(ss)
-#  let selectors = parseSelectors(cvals)
-#
-#  if document.html != nil:
-#    for sel in selectors:
-#      result.add(document.html.selectElems(sel))
-#
-#proc querySelector*(document: Document, q: string): Element =
-#  let elems = document.querySelectorAll(q)
-#  if elems.len > 0:
-#    return elems[0]
-#  return nil
-#
-#proc querySelectorAll*(element: Element, q: string): seq[Element] =
-#  let ss = newStringStream(q)
-#  let cvals = parseListOfComponentValues(ss)
-#  let selectors = parseSelectors(cvals)
-#
-#  for sel in selectors:
-#    result.add(element.selectElems(sel))
-#
-#proc querySelector*(element: Element, q: string): Element =
-#  let elems = element.querySelectorAll(q)
-#  if elems.len > 0:
-#    return elems[0]
-#  return nil
+proc querySelectorAll*(document: Document, q: string): seq[Element] =
+  let selectors = parseSelectors(newStringStream(q))
+  for element in document.elements:
+    if element.selectorsMatch(selectors):
+      result.add(element)
+
+proc querySelector*(document: Document, q: string): Element =
+  let selectors = parseSelectors(newStringStream(q))
+  for element in document.elements:
+    if element.selectorsMatch(selectors):
+      return element
+  return nil
diff --git a/src/css/selectorparser.nim b/src/css/selectorparser.nim
index b7197d95..f8abc7f7 100644
--- a/src/css/selectorparser.nim
+++ b/src/css/selectorparser.nim
@@ -1,4 +1,5 @@
 import options
+import streams
 import strutils
 import unicode
 
@@ -172,33 +173,6 @@ func pseudo*(sels: ComplexSelector): PseudoElem =
     return sels[^1].elem
   return PSEUDO_NONE
 
-func optimizeComplexSelector*(selectors: ComplexSelector): ComplexSelector =
-  #pass 1: check for invalid sequences
-  var i = 1
-  while i < selectors.len:
-    let sel = selectors[i]
-    if sel.t == TYPE_SELECTOR or sel.t == UNIVERSAL_SELECTOR:
-      return
-    inc i
-
-  #pass 2: move selectors in combination
-  if selectors.len > 1:
-    var i = 0
-    var slow: ComplexSelector
-    if selectors[0].t == UNIVERSAL_SELECTOR:
-      inc i
-
-    while i < selectors.len:
-      if selectors[i].t in {ATTR_SELECTOR, PSEUDO_SELECTOR, PSELEM_SELECTOR}:
-        slow.add(selectors[i])
-      else:
-        result.add(selectors[i])
-      inc i
-
-    result.add(slow)
-  else:
-    result.add(selectors[0])
-
 proc addSelector(state: var SelectorParser, sel: Selector) =
   if state.combinator != nil:
     state.combinator.csels[^1].add(sel)
@@ -223,21 +197,22 @@ func getComplexSelectors(state: var SelectorParser): seq[ComplexSelector] =
     result[^1].add(state.combinator)
 
 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(@[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(newSeq[Selector]())
-    state.selectors[^1].setLen(0)
-    state.query = QUERY_TYPE
+  if csstoken.tokenType notin {CSS_IDENT_TOKEN, CSS_HASH_TOKEN, CSS_COLON_TOKEN} and
+     (csstoken.tokenType != CSS_DELIM_TOKEN or csstoken.rvalue != Rune('.')):
+    return
+  if state.combinator != nil and state.combinator.ct != ct:
+    let nc = Selector(t: COMBINATOR_SELECTOR, ct: ct)
+    nc.csels.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(newSeq[Selector]())
+  state.selectors[^1].setLen(0)
+  state.query = QUERY_TYPE
 
 proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) =
   case state.query
@@ -440,3 +415,6 @@ func parseSelectors*(cvals: seq[CSSComponentValue]): seq[ComplexSelector] = {.ca
     state.selectors[^1].add(state.combinator)
     state.combinator = nil
   return state.selectors
+
+proc parseSelectors*(stream: Stream): seq[ComplexSelector] =
+  return parseSelectors(parseListOfComponentValues(stream))