diff options
author | bptato <nincsnevem662@gmail.com> | 2024-02-08 01:32:40 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-02-08 01:44:32 +0100 |
commit | f666dbafec85ba6ed64db8123ceae28b80c4cb3b (patch) | |
tree | c8a26185197f2391551ae9f2570042d7e8e0fa10 /src | |
parent | 6faf5cff21f8c1d382ffa605b7aa56be30ff54db (diff) | |
download | chawan-f666dbafec85ba6ed64db8123ceae28b80c4cb3b.tar.gz |
dom: atomize id, name, DOMTokenList
Diffstat (limited to 'src')
-rw-r--r-- | src/css/selectorparser.nim | 40 | ||||
-rw-r--r-- | src/css/sheet.nim | 117 | ||||
-rw-r--r-- | src/html/dom.nim | 108 |
3 files changed, 162 insertions, 103 deletions
diff --git a/src/css/selectorparser.nim b/src/css/selectorparser.nim index 9e79a36d..eacb48d9 100644 --- a/src/css/selectorparser.nim +++ b/src/css/selectorparser.nim @@ -51,13 +51,17 @@ type when defined(debug): tags: string of ID_SELECTOR: - id*: string + id*: CAtom + when defined(debug): + ids: string + of CLASS_SELECTOR: + class*: CAtom + when defined(debug): + classs: string of ATTR_SELECTOR: attr*: string value*: string rel*: SelectorRelation - of CLASS_SELECTOR: - class*: string of UNIVERSAL_SELECTOR: #TODO namespaces? discard of PSEUDO_SELECTOR: @@ -112,9 +116,12 @@ func `$`*(sel: Selector): string = when defined(debug): return sel.tags else: - return "tagt " & $int(sel.tag) + return "ATOM" & $int(sel.tag) of ID_SELECTOR: - return '#' & sel.id + when defined(debug): + return "#" & sel.ids + else: + return "#ATOM" & $int(sel.id) of ATTR_SELECTOR: let rel = case sel.rel.t of RELATION_EXISTS: "" @@ -130,7 +137,10 @@ func `$`*(sel: Selector): string = of FLAG_S: " s" return '[' & sel.attr & rel & sel.value & flag & ']' of CLASS_SELECTOR: - return '.' & sel.class + when defined(debug): + return "." & sel.classs + else: + return ".ATOM" & $int(sel.id) of UNIVERSAL_SELECTOR: return "*" of PSEUDO_SELECTOR: @@ -397,7 +407,10 @@ proc parseClassSelector(state: var SelectorParser): Selector = if not state.has(): fail let tok = get_tok state.consume() if tok.tokenType != CSS_IDENT_TOKEN: fail - return Selector(t: CLASS_SELECTOR, class: tok.value) + let class = state.factory.toAtom(tok.value) + result = Selector(t: CLASS_SELECTOR, class: class) + when defined(debug): + result.classs = tok.value proc parseCompoundSelector(state: var SelectorParser): CompoundSelector = result = CompoundSelector() @@ -408,14 +421,21 @@ proc parseCompoundSelector(state: var SelectorParser): CompoundSelector = case tok.tokenType of CSS_IDENT_TOKEN: inc state.at - let tag = state.factory.toAtom(tok.value.toLowerAscii()) - result.add(Selector(t: TYPE_SELECTOR, tag: tag)) + let s = tok.value.toLowerAscii() + let tag = state.factory.toAtom(s) + let sel = Selector(t: TYPE_SELECTOR, tag: tag) + when defined(debug): + sel.tags = s + result.add(sel) of CSS_COLON_TOKEN: inc state.at result.add(state.parsePseudoSelector()) of CSS_HASH_TOKEN: inc state.at - result.add(Selector(t: ID_SELECTOR, id: tok.value)) + let id = state.factory.toAtom(tok.value) + result.add(Selector(t: ID_SELECTOR, id: id)) + when defined(debug): + result[^1].ids = tok.value of CSS_COMMA_TOKEN: break of CSS_DELIM_TOKEN: case tok.cvalue diff --git a/src/css/sheet.nim b/src/css/sheet.nim index 4990d991..14d9b717 100644 --- a/src/css/sheet.nim +++ b/src/css/sheet.nim @@ -25,25 +25,24 @@ type CSSStylesheet* = ref object mqList*: seq[CSSMediaQueryDef] - #TODO maybe just array[TagType] would be more efficient tagTable: Table[CAtom, seq[CSSRuleDef]] - idTable: Table[string, seq[CSSRuleDef]] - classTable: Table[string, seq[CSSRuleDef]] + idTable: Table[CAtom, seq[CSSRuleDef]] + classTable: Table[CAtom, seq[CSSRuleDef]] generalList: seq[CSSRuleDef] len: int factory: CAtomFactory type SelectorHashes = object tag: CAtom - id: string - class: string + id: CAtom + class: CAtom func newStylesheet*(cap: int, factory: CAtomFactory): CSSStylesheet = let bucketsize = cap div 2 return CSSStylesheet( tagTable: initTable[CAtom, seq[CSSRuleDef]](bucketsize), - idTable: initTable[string, seq[CSSRuleDef]](bucketsize), - classTable: initTable[string, seq[CSSRuleDef]](bucketsize), + idTable: initTable[CAtom, seq[CSSRuleDef]](bucketsize), + classTable: initTable[CAtom, seq[CSSRuleDef]](bucketsize), generalList: newSeqOfCap[CSSRuleDef](bucketsize), factory: factory ) @@ -72,62 +71,66 @@ proc getSelectorIds(hashes: var SelectorHashes, sel: Selector): bool = of ATTR_SELECTOR, PSELEM_SELECTOR, UNIVERSAL_SELECTOR: return false of PSEUDO_SELECTOR: - if sel.pseudo.t in {PSEUDO_IS, PSEUDO_WHERE}: - # Basically just hash whatever the selectors have in common: - #1. get the hashable values of selector 1 - #2. for every other selector x: - #3. get hashable values of selector x - #4. store hashable values of selector x that aren't stored yet - #5. for every hashable value of selector 1 that doesn't match selector x - #6. cancel hashable value - var cancel_tag = false - var cancel_id = false - var cancel_class = false - var i = 0 - if i < sel.pseudo.fsels.len: - hashes.getSelectorIds(sel.pseudo.fsels[i]) - inc i - - while i < sel.pseudo.fsels.len: - var nhashes: SelectorHashes - nhashes.getSelectorIds(sel.pseudo.fsels[i]) - if hashes.tag == CAtomNull: - hashes.tag = nhashes.tag - elif not cancel_tag and nhashes.tag != CAtomNull and nhashes.tag != hashes.tag: - cancel_tag = true - - if hashes.id == "": - hashes.id = nhashes.id - elif not cancel_id and nhashes.id != "" and nhashes.id != hashes.id: - cancel_id = true - - if hashes.class == "": - hashes.class = nhashes.class - elif not cancel_class and nhashes.class != "" and nhashes.class != hashes.class: - cancel_class = true - - inc i - - if cancel_tag: - hashes.tag = CAtomNull - if cancel_id: - hashes.id = "" - if cancel_class: - hashes.class = "" - - if hashes.tag != CAtomNull or hashes.id != "" or hashes.class != "": - return true + if sel.pseudo.t notin {PSEUDO_IS, PSEUDO_WHERE}: + return false + # Basically just hash whatever the selectors have in common: + #1. get the hashable values of selector 1 + #2. for every other selector x: + #3. get hashable values of selector x + #4. store hashable values of selector x that aren't stored yet + #5. for every hashable value of selector 1 that doesn't match selector x + #6. cancel hashable value + var cancelTag = false + var cancelId = false + var cancelClass = false + var i = 0 + if i < sel.pseudo.fsels.len: + hashes.getSelectorIds(sel.pseudo.fsels[i]) + inc i + + while i < sel.pseudo.fsels.len: + var nhashes: SelectorHashes + nhashes.getSelectorIds(sel.pseudo.fsels[i]) + if hashes.tag == CAtomNull: + hashes.tag = nhashes.tag + elif not cancelTag and nhashes.tag != CAtomNull and + nhashes.tag != hashes.tag: + cancelTag = true + + if hashes.id == CAtomNull: + hashes.id = nhashes.id + elif not cancelId and nhashes.id != CAtomNull and + nhashes.id != hashes.id: + cancelId = true + + if hashes.class == CAtomNull: + hashes.class = nhashes.class + elif not cancelClass and nhashes.class != CAtomNull and + nhashes.class != hashes.class: + cancelClass = true + + inc i + + if cancelTag: + hashes.tag = CAtomNull + if cancelId: + hashes.id = CAtomNull + if cancelClass: + hashes.class = CAtomNull + + return hashes.tag != CAtomNull or hashes.id != CAtomNull or + hashes.class != CAtomNull proc ruleDefCmp(a, b: CSSRuleDef): int = cmp(a.idx, b.idx) -iterator genRules*(sheet: CSSStylesheet, tag: CAtom, id: string, - classes: seq[string]): CSSRuleDef = +iterator genRules*(sheet: CSSStylesheet, tag, id: CAtom, classes: seq[CAtom]): + CSSRuleDef = var rules: seq[CSSRuleDef] sheet.tagTable.withValue(tag, v): for rule in v[]: rules.add(rule) - if id != "": + if id != CAtomNull: sheet.idTable.withValue(id, v): for rule in v[]: rules.add(rule) @@ -150,12 +153,12 @@ proc add(sheet: var CSSStylesheet, rule: CSSRuleDef) = p[].add(rule) do: sheet.tagTable[hashes.tag] = @[rule] - elif hashes.id != "": + elif hashes.id != CAtomNull: sheet.idTable.withValue(hashes.id, p): p[].add(rule) do: sheet.idTable[hashes.id] = @[rule] - elif hashes.class != "": + elif hashes.class != CAtomNull: sheet.classTable.withValue(hashes.class, p): p[].add(rule) do: diff --git a/src/html/dom.nim b/src/html/dom.nim index cb4d71ee..700d1d22 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -107,7 +107,7 @@ type HTMLAllCollection = ref object of Collection DOMTokenList = ref object - toks*: seq[string] + toks*: seq[CAtom] element: Element localName: CAtom @@ -212,7 +212,8 @@ type prefix*: string localName*: CAtom - id* {.jsget.}: string + id*: CAtom + name*: CAtom classList* {.jsget.}: DOMTokenList attrs: seq[AttrData] attributesInternal: NamedNodeMap @@ -277,7 +278,6 @@ type relList {.jsget.}: DOMTokenList HTMLFormElement* = ref object of HTMLElement - name*: string smethod*: string enctype*: string novalidate*: bool @@ -1134,16 +1134,29 @@ func length(tokenList: DOMTokenList): uint32 {.jsfget.} = func item(tokenList: DOMTokenList, i: int): Option[string] {.jsfunc.} = if i < tokenList.toks.len: - return some(tokenList.toks[i]) + return some(tokenList.element.document.toStr(tokenList.toks[i])) + return none(string) + +func contains*(tokenList: DOMTokenList, a: CAtom): bool = + return a in tokenList.toks -func contains*(tokenList: DOMTokenList, s: string): bool {.jsfunc.} = - return s in tokenList.toks +func jsContains(tokenList: DOMTokenList, s: string): bool + {.jsfunc: "contains".} = + return tokenList.element.document.toAtom(s) in tokenList + +func `$`(tokenList: DOMTokenList): string {.jsfunc.} = + var s = "" + for i, tok in tokenList.toks: + if i != 0: + s &= ' ' + s &= tokenList.element.document.toStr(tok) + return s proc update(tokenList: DOMTokenList) = if not tokenList.element.attrb(tokenList.localName) and tokenList.toks.len == 0: return - tokenList.element.attr(tokenList.localName, tokenList.toks.join(' ')) + tokenList.element.attr(tokenList.localName, $tokenList) func validateDOMToken(tok: string): Err[DOMException] = if tok == "": @@ -1151,12 +1164,14 @@ func validateDOMToken(tok: string): Err[DOMException] = if AsciiWhitespace in tok: return errDOMException("Got a string containing whitespace", "InvalidCharacterError") + return ok() proc add(tokenList: DOMTokenList, tokens: varargs[string]): Err[DOMException] {.jsfunc.} = for tok in tokens: ?validateDOMToken(tok) for tok in tokens: + let tok = tokenList.element.document.toAtom(tok) tokenList.toks.add(tok) tokenList.update() return ok() @@ -1166,6 +1181,7 @@ proc remove(tokenList: DOMTokenList, tokens: varargs[string]): for tok in tokens: ?validateDOMToken(tok) for tok in tokens: + let tok = tokenList.element.document.toAtom(tok) let i = tokenList.toks.find(tok) if i != -1: tokenList.toks.delete(i) @@ -1175,6 +1191,7 @@ proc remove(tokenList: DOMTokenList, tokens: varargs[string]): proc toggle(tokenList: DOMTokenList, token: string, force = none(bool)): DOMResult[bool] {.jsfunc.} = ?validateDOMToken(token) + let token = tokenList.element.document.toAtom(token) let i = tokenList.toks.find(token) if i != -1: if not force.get(false): @@ -1192,9 +1209,11 @@ proc replace(tokenList: DOMTokenList, token, newToken: string): DOMResult[bool] {.jsfunc.} = ?validateDOMToken(token) ?validateDOMToken(newToken) + let token = tokenList.element.document.toAtom(token) let i = tokenList.toks.find(token) if i == -1: return ok(false) + let newToken = tokenList.element.document.toAtom(newToken) tokenList.toks[i] = newToken tokenList.update() return ok(true) @@ -1217,9 +1236,6 @@ func supports(tokenList: DOMTokenList, token: string): return err(newTypeError("No supported tokens defined for attribute " & localName)) -func `$`(tokenList: DOMTokenList): string {.jsfunc.} = - return tokenList.toks.join(' ') - func value(tokenList: DOMTokenList): string {.jsfget.} = return $tokenList @@ -1314,12 +1330,15 @@ func hasprop(collection: HTMLCollection, u: uint32): bool {.jshasprop.} = func item(collection: HTMLCollection, u: uint32): Element {.jsfunc.} = if u < collection.length: return Element(collection.snapshot[int(u)]) + return nil func namedItem(collection: HTMLCollection, s: string): Element {.jsfunc.} = + let a = collection.root.document.toAtom(s) for it in collection.snapshot: let it = Element(it) - if it.id == s or it.namespace == Namespace.HTML and it.attr("name") == s: + if it.id == a or it.namespace == Namespace.HTML and it.name == a: return it + return nil func getter[T: uint32|string](collection: HTMLCollection, u: T): Option[Element] {.jsgetprop.} = @@ -1330,20 +1349,18 @@ func getter[T: uint32|string](collection: HTMLCollection, u: T): func names(ctx: JSContext, collection: HTMLCollection): JSPropertyEnumList {.jspropnames.} = - let aName = collection.root.document.toAtom("name") #TODO enumize let L = collection.length var list = newJSPropertyEnumList(ctx, L) - var ids: OrderedSet[string] + var ids: OrderedSet[CAtom] for u in 0 ..< L: list.add(u) let elem = collection.item(u) - if elem.id != "": + if elem.id != CAtomNull: ids.incl(elem.id) if elem.namespace == Namespace.HTML: - let name = elem.attr(aName) - ids.incl(name) + ids.incl(elem.name) for id in ids: - list.add(id) + list.add(collection.root.document.toStr(id)) return list # HTMLAllCollection @@ -1882,6 +1899,7 @@ func findAncestor*(node: Node, tagTypes: set[TagType]): Element = func getElementById(node: Node, id: string): Element {.jsfunc.} = if id.len == 0: return nil + let id = node.document.toAtom(id) for child in node.elements: if child.id == id: return child @@ -1917,28 +1935,35 @@ func getElementsByTagName(element: Element, tagName: string): HTMLCollection {.j return element.getElementsByTagName0(tagName) func getElementsByClassName0(node: Node, classNames: string): HTMLCollection = - var classes = classNames.split(AsciiWhitespace) - let isquirks = node.document.mode == QUIRKS + var classAtoms = newSeq[CAtom]() + let document = node.document + let isquirks = document.mode == QUIRKS if isquirks: - for class in classes.mitems: - for c in class.mitems: - c = c.toLowerAscii() + for class in classNames.split(AsciiWhitespace): + classAtoms.add(document.toAtom(class.toLowerAscii())) + else: + for class in classNames.split(AsciiWhitespace): + classAtoms.add(document.toAtom(class)) return newCollection[HTMLCollection](node, func(node: Node): bool = if node of Element: + let element = Element(node) if isquirks: - var cl = Element(node).classList - for tok in cl.toks.mitems: - for c in tok.mitems: - c = c.toLowerAscii() - for class in classes: + var cl = newSeq[CAtom]() + for tok in element.classList.toks: + let s = document.toStr(tok) + cl.add(document.toAtom(s.toLowerAscii())) + for class in classAtoms: if class notin cl: return false else: - for class in classes: - if class notin Element(node).classList: + for class in classAtoms: + if class notin element.classList.toks: return false - return true, true, false) + return true, + islive = true, + childonly = false + ) func getElementsByClassName(document: Document, classNames: string): HTMLCollection {.jsfunc.} = return document.getElementsByClassName0(classNames) @@ -2060,6 +2085,9 @@ func serializeFragment(node: Node): string = return s # Element attribute reflection (getters) +func jsId(element: Element): string {.jsfget: "id".} = + return element.document.toStr(element.id) + func innerHTML(element: Element): string {.jsfget.} = #TODO xml return element.serializeFragment() @@ -2211,11 +2239,13 @@ func formmethod*(element: Element): FormMethod = func findAnchor*(document: Document, id: string): Element = if id.len == 0: return nil + let id = document.toAtom(id) for child in document.elements: if child.id == id: return child - if child of HTMLAnchorElement and child.attr("name") == id: + if child of HTMLAnchorElement and child.name == id: return child + return nil # Forward declaration hack isDefaultPassive = func (eventTarget: EventTarget): bool = @@ -2672,10 +2702,17 @@ proc reflectAttrs(element: Element, name: CAtom, value: string) = if name == n: element.val.toks.setLen(0) for x in value.split(AsciiWhitespace): - if x != "" and x notin element.val: - element.val.toks.add(x) + if x != "": + let a = element.document.toAtom(x) + if a notin element.val: + element.val.toks.add(a) return - element.reflect_str "id", id + if name == "id": + element.id = element.document.toAtom(value) + return + if name == "name": + element.name = element.document.toAtom(value) + return element.reflect_domtoklist "class", classList #TODO internalNonce if name == "style": @@ -2880,8 +2917,7 @@ proc removeNamedItemNS(map: NamedNodeMap, namespace, localName: string): return ok(attr) return errDOMException("Item not found", "NotFoundError") -proc id(element: Element, id: string) {.jsfset.} = - element.id = id +proc jsId(element: Element, id: string) {.jsfset: "id".} = element.attr("id", id) # Pass an index to avoid searching for the node in parent's child list. |