about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-12-15 16:44:53 +0100
committerbptato <nincsnevem662@gmail.com>2024-12-15 16:44:53 +0100
commit8a586beaa26fa7ad4dbc4573e655927f8f32e158 (patch)
treecad39d5751c769bbdaf5b0bc5403322c63ebefc2 /src
parent2dc6b651e60604e1fe139ede00ff9c172535a939 (diff)
downloadchawan-8a586beaa26fa7ad4dbc4573e655927f8f32e158.tar.gz
dom, css: fix case-insensitive class/id/attr matching
Uses an additional lower-case map for O(1) case-insensitive comparisons.
Diffstat (limited to 'src')
-rw-r--r--src/css/cascade.nim16
-rw-r--r--src/css/match.nim8
-rw-r--r--src/css/selectorparser.nim11
-rw-r--r--src/css/sheet.nim2
-rw-r--r--src/html/catom.nim28
-rw-r--r--src/html/dom.nim16
6 files changed, 53 insertions, 28 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim
index e5652781..c2ac7f9a 100644
--- a/src/css/cascade.nim
+++ b/src/css/cascade.nim
@@ -88,20 +88,16 @@ func calcRules(styledNode: StyledNode; sheet: CSSStylesheet): RuleList =
   let element = Element(styledNode.node)
   var rules: seq[CSSRuleDef] = @[]
   sheet.tagTable.withValue(element.localName, v):
-    for rule in v[]:
-      rules.add(rule)
+    rules.add(v[])
   if element.id != CAtomNull:
-    sheet.idTable.withValue(element.id, v):
-      for rule in v[]:
-        rules.add(rule)
+    sheet.idTable.withValue(sheet.factory.toLowerAscii(element.id), v):
+      rules.add(v[])
   for class in element.classList.toks:
-    sheet.classTable.withValue(class, v):
-      for rule in v[]:
-        rules.add(rule)
+    sheet.classTable.withValue(sheet.factory.toLowerAscii(class), v):
+      rules.add(v[])
   for attr in element.attrs:
     sheet.attrTable.withValue(attr.qualifiedName, v):
-      for rule in v[]:
-        rules.add(rule)
+      rules.add(v[])
   for rule in sheet.generalList:
     rules.add(rule)
   rules.sort(ruleDefCmp, order = Ascending)
diff --git a/src/css/match.nim b/src/css/match.nim
index 05564600..9cdf23c4 100644
--- a/src/css/match.nim
+++ b/src/css/match.nim
@@ -156,9 +156,13 @@ func selectorMatches(element: Element; sel: Selector;
   of stType:
     return element.localName == sel.tag
   of stClass:
-    return sel.class in element.classList
+    let factory = element.document.factory
+    for it in element.classList.toks:
+      if sel.class == factory.toLowerAscii(it):
+        return true
+    return false
   of stId:
-    return sel.id == element.id
+    return sel.id == element.document.factory.toLowerAscii(element.id)
   of stAttr:
     return element.attrSelectorMatches(sel)
   of stPseudoClass:
diff --git a/src/css/selectorparser.nim b/src/css/selectorparser.nim
index b4db5a84..33302068 100644
--- a/src/css/selectorparser.nim
+++ b/src/css/selectorparser.nim
@@ -398,7 +398,7 @@ proc parseAttributeSelector(state: var SelectorParser;
   if not state2.has():
     return Selector(
       t: stAttr,
-      attr: state.factory.toAtom(attr.value),
+      attr: state.factory.toAtomLower(attr.value),
       rel: SelectorRelation(t: rtExists)
     )
   let delim = get_tok state2.consume()
@@ -441,7 +441,7 @@ proc parseClassSelector(state: var SelectorParser): Selector =
   if not state.has(): fail
   let tok = get_tok state.consume()
   if tok.t != cttIdent: fail
-  let class = state.factory.toAtom(tok.value)
+  let class = state.factory.toAtomLower(tok.value)
   result = Selector(t: stClass, class: class)
   when defined(debug):
     result.classs = tok.value
@@ -455,18 +455,17 @@ proc parseCompoundSelector(state: var SelectorParser): CompoundSelector =
       case tok.t
       of cttIdent:
         inc state.at
-        let s = tok.value.toLowerAscii()
-        let tag = state.factory.toAtom(s)
+        let tag = state.factory.toAtomLower(tok.value)
         let sel = Selector(t: stType, tag: tag)
         when defined(debug):
-          sel.tags = s
+          sel.tags = tok.value
         result.add(sel)
       of cttColon:
         inc state.at
         result.add(state.parsePseudoSelector())
       of cttHash:
         inc state.at
-        let id = state.factory.toAtom(tok.value)
+        let id = state.factory.toAtomLower(tok.value)
         result.add(Selector(t: stId, id: id))
         when defined(debug):
           result[^1].ids = tok.value
diff --git a/src/css/sheet.nim b/src/css/sheet.nim
index 248033af..0793e903 100644
--- a/src/css/sheet.nim
+++ b/src/css/sheet.nim
@@ -35,7 +35,7 @@ type
     generalList*: seq[CSSRuleDef]
     importList*: seq[URL]
     len: int
-    factory: CAtomFactory
+    factory*: CAtomFactory
 
 type SelectorHashes = object
   tag: CAtom
diff --git a/src/html/catom.nim b/src/html/catom.nim
index 9b3041b7..8d1696be 100644
--- a/src/html/catom.nim
+++ b/src/html/catom.nim
@@ -148,6 +148,7 @@ type
   CAtomFactoryObj = object
     strMap: array[CAtomFactoryStrMapLength, seq[CAtom]]
     atomMap: seq[string]
+    lowerMap: seq[CAtom]
 
   #TODO could be a ptr probably
   CAtomFactory* = ref CAtomFactoryObj
@@ -160,7 +161,8 @@ func hash*(atom: CAtom): Hash {.borrow.}
 
 func `$`*(a: CAtom): string {.borrow.}
 
-func toAtom(factory: var CAtomFactoryObj; s: string): CAtom =
+func toAtom(factory: var CAtomFactoryObj; s: string;
+    isInit: static bool = false): CAtom =
   let h = s.hash()
   let i = h and (factory.strMap.len - 1)
   for atom in factory.strMap[i]:
@@ -170,6 +172,12 @@ func toAtom(factory: var CAtomFactoryObj; s: string): CAtom =
   # Not found
   let atom = CAtom(factory.atomMap.len)
   factory.atomMap.add(s)
+  when not isInit:
+    let lower = if AsciiUpperAlpha notin s:
+      atom
+    else:
+      factory.toAtom(s.toLowerAscii())
+    factory.lowerMap.add(lower)
   factory.strMap[i].add(atom)
   return atom
 
@@ -177,9 +185,16 @@ const factoryInit = (func(): CAtomFactoryInit =
   var init = CAtomFactoryInit()
   # Null atom
   init.obj.atomMap.add("")
+  init.obj.lowerMap.add(CAtom(0))
   # StaticAtom includes TagType too.
   for sa in StaticAtom(1) .. StaticAtom.high:
-    discard init.obj.toAtom($sa)
+    discard init.obj.toAtom($sa, isInit = true)
+  for sa in StaticAtom(1) .. StaticAtom.high:
+    let atom = init.obj.toAtom(($sa).toLowerAscii(), isInit = true)
+    init.obj.lowerMap.add(atom)
+  # fill slots of newly added lower mappings
+  while init.obj.lowerMap.len < init.obj.atomMap.len:
+    init.obj.lowerMap.add(CAtom(init.obj.lowerMap.len))
   return init
 )()
 
@@ -188,6 +203,12 @@ proc newCAtomFactory*(): CAtomFactory =
   factory[] = factoryInit.obj
   return factory
 
+func toLowerAscii*(factory: CAtomFactory; a: CAtom): CAtom =
+  return factory.lowerMap[int32(a)]
+
+func equalsIgnoreCase*(factory: CAtomFactory; a, b: CAtom): bool =
+  return factory.lowerMap[int32(a)] == factory.lowerMap[int32(b)]
+
 func toAtom*(factory: CAtomFactory; s: string): CAtom =
   return factory[].toAtom(s)
 
@@ -199,6 +220,9 @@ func toAtom*(factory: CAtomFactory; attrType: StaticAtom): CAtom =
   assert attrType != atUnknown
   return CAtom(attrType)
 
+func toAtomLower*(factory: CAtomFactory; s: string): CAtom =
+  return factory.lowerMap[int32(factory.toAtom(s))]
+
 func toStr*(factory: CAtomFactory; atom: CAtom): string =
   return factory.atomMap[int(atom)]
 
diff --git a/src/html/dom.nim b/src/html/dom.nim
index ec01acc2..41f37744 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -1043,6 +1043,9 @@ proc toStr*(window: Window; atom: CAtom): string =
 proc toAtom*(document: Document; s: string): CAtom =
   return document.factory.toAtom(s)
 
+proc toAtomLower*(document: Document; s: string): CAtom =
+  return document.factory.toAtomLower(s)
+
 proc toAtom*(document: Document; at: StaticAtom): CAtom =
   return document.factory.toAtom(at)
 
@@ -1927,7 +1930,7 @@ proc getAttr(map: NamedNodeMap; dataIdx: int): Attr =
 
 func normalizeAttrQName(element: Element; qualifiedName: string): CAtom =
   if element.namespace == Namespace.HTML and not element.document.isxml:
-    return element.document.toAtom(qualifiedName.toLowerAscii())
+    return element.document.toAtomLower(qualifiedName)
   return element.document.toAtom(qualifiedName)
 
 func hasAttributes(element: Element): bool {.jsfunc.} =
@@ -2300,7 +2303,7 @@ func getElementsByTagName0(root: Node; tagName: string): HTMLCollection =
       childonly = false
     )
   let localName = root.document.toAtom(tagName)
-  let localNameLower = root.document.toAtom(tagName.toLowerAscii())
+  let localNameLower = root.document.factory.toLowerAscii(localName)
   return newCollection[HTMLCollection](
     root,
     func(node: Node): bool =
@@ -2328,7 +2331,7 @@ func getElementsByClassName0(node: Node; classNames: string): HTMLCollection =
   let isquirks = document.mode == QUIRKS
   if isquirks:
     for class in classNames.split(AsciiWhitespace):
-      classAtoms.add(document.toAtom(class.toLowerAscii()))
+      classAtoms.add(document.toAtomLower(class))
   else:
     for class in classNames.split(AsciiWhitespace):
       classAtoms.add(document.toAtom(class))
@@ -2339,8 +2342,7 @@ func getElementsByClassName0(node: Node; classNames: string): HTMLCollection =
         if isquirks:
           var cl = newSeq[CAtom]()
           for tok in element.classList.toks:
-            let s = document.toStr(tok)
-            cl.add(document.toAtom(s.toLowerAscii()))
+            cl.add(document.factory.toLowerAscii(tok))
           for class in classAtoms:
             if class notin cl:
               return false
@@ -3700,7 +3702,7 @@ proc setAttribute(element: Element; qualifiedName, value: string):
   ?validateAttributeName(qualifiedName)
   let qualifiedName = if element.namespace == Namespace.HTML and
       not element.document.isxml:
-    element.document.toAtom(qualifiedName.toLowerAscii())
+    element.document.toAtomLower(qualifiedName)
   else:
     element.document.toAtom(qualifiedName)
   element.attr(qualifiedName, value)
@@ -4489,7 +4491,7 @@ proc createElement(document: Document; localName: string):
     return errDOMException("Invalid character in element name",
       "InvalidCharacterError")
   let localName = if not document.isxml:
-    document.toAtom(localName.toLowerAscii())
+    document.toAtomLower(localName)
   else:
     document.toAtom(localName)
   let namespace = if not document.isxml: