diff options
author | bptato <nincsnevem662@gmail.com> | 2024-08-11 14:05:15 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-08-11 14:05:15 +0200 |
commit | a0c6f80f76d60bc2bb36942d4162b90e6b5aa2a2 (patch) | |
tree | dbf93dc3b168c4a9e135f0fa4e1300892645ba68 | |
parent | d0910f85adfec4a2cddfe2297be821841f4dd671 (diff) | |
download | chawan-a0c6f80f76d60bc2bb36942d4162b90e6b5aa2a2.tar.gz |
dom: remove generic JS bindings
Dispatch manually with fromJS instead.
-rw-r--r-- | src/config/mimetypes.nim | 4 | ||||
-rw-r--r-- | src/html/dom.nim | 149 | ||||
-rw-r--r-- | src/html/formdata.nim | 31 | ||||
-rw-r--r-- | src/local/client.nim | 2 | ||||
-rw-r--r-- | src/local/container.nim | 38 | ||||
-rw-r--r-- | src/local/pager.nim | 17 | ||||
-rw-r--r-- | src/server/buffer.nim | 11 | ||||
-rw-r--r-- | src/types/url.nim | 36 | ||||
-rw-r--r-- | src/utils/mimeguess.nim | 3 |
9 files changed, 156 insertions, 135 deletions
diff --git a/src/config/mimetypes.nim b/src/config/mimetypes.nim index 9beb42c7..d9e21aaf 100644 --- a/src/config/mimetypes.nim +++ b/src/config/mimetypes.nim @@ -6,8 +6,8 @@ import utils/twtstr # extension -> type type MimeTypes* = distinct Table[string, string] -template `[]`*(mimeTypes: MimeTypes; k: string): string = - Table[string, string](mimeTypes)[k] +template getOrDefault*(mimeTypes: MimeTypes; k, fallback: string): string = + Table[string, string](mimeTypes).getOrDefault(k, fallback) template contains*(mimeTypes: MimeTypes; k: string): bool = k in Table[string, string](mimeTypes) diff --git a/src/html/dom.nim b/src/html/dom.nim index 20f21264..208f8168 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -1302,40 +1302,41 @@ proc update(tokenList: DOMTokenList) = return tokenList.element.attr(tokenList.localName, $tokenList) -func validateDOMToken(tok: string): Err[DOMException] = - if tok == "": +func validateDOMToken(ctx: JSContext; document: Document; tok: JSValue): + DOMResult[CAtom] = + var res: string + ?ctx.fromJS(tok, res) + if res == "": return errDOMException("Got an empty string", "SyntaxError") - if AsciiWhitespace in tok: + if AsciiWhitespace in res: return errDOMException("Got a string containing whitespace", "InvalidCharacterError") - return ok() + return ok(document.toAtom(res)) -proc add(tokenList: DOMTokenList; tokens: varargs[string]): Err[DOMException] - {.jsfunc.} = - for tok in tokens: - ?validateDOMToken(tok) +proc add(ctx: JSContext; tokenList: DOMTokenList; tokens: varargs[JSValue]): + Err[DOMException] {.jsfunc.} = + var toks: seq[CAtom] = @[] for tok in tokens: - let tok = tokenList.element.document.toAtom(tok) - tokenList.toks.add(tok) + toks.add(?ctx.validateDOMToken(tokenList.element.document, tok)) + tokenList.toks.add(toks) tokenList.update() return ok() -proc remove(tokenList: DOMTokenList; tokens: varargs[string]): +proc remove(ctx: JSContext; tokenList: DOMTokenList; tokens: varargs[JSValue]): Err[DOMException] {.jsfunc.} = + var toks: seq[CAtom] = @[] for tok in tokens: - ?validateDOMToken(tok) - for tok in tokens: - let tok = tokenList.element.document.toAtom(tok) + toks.add(?ctx.validateDOMToken(tokenList.element.document, tok)) + for tok in toks: let i = tokenList.toks.find(tok) if i != -1: tokenList.toks.delete(i) tokenList.update() return ok() -proc toggle(tokenList: DOMTokenList; token: string; force = none(bool)): - DOMResult[bool] {.jsfunc.} = - ?validateDOMToken(token) - let token = tokenList.element.document.toAtom(token) +proc toggle(ctx: JSContext; tokenList: DOMTokenList; token: JSValue; + force = none(bool)): DOMResult[bool] {.jsfunc.} = + let token = ?ctx.validateDOMToken(tokenList.element.document, token) let i = tokenList.toks.find(token) if i != -1: if not force.get(false): @@ -1349,15 +1350,13 @@ proc toggle(tokenList: DOMTokenList; token: string; force = none(bool)): return ok(true) return ok(false) -proc replace(tokenList: DOMTokenList; token, newToken: string): +proc replace(ctx: JSContext; tokenList: DOMTokenList; token, newToken: JSValue): DOMResult[bool] {.jsfunc.} = - ?validateDOMToken(token) - ?validateDOMToken(newToken) - let token = tokenList.element.document.toAtom(token) + let token = ?ctx.validateDOMToken(tokenList.element.document, token) + let newToken = ?ctx.validateDOMToken(tokenList.element.document, newToken) 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) @@ -1520,6 +1519,7 @@ func item(collection: HTMLAllCollection; i: uint32): Element {.jsfunc.} = let i = int(i) if i < collection.len: return Element(collection.snapshot[i]) + return nil func getter(collection: HTMLAllCollection; i: uint32): Option[Element] {.jsgetprop.} = @@ -1780,40 +1780,43 @@ func getAttributeNS(element: Element; namespace, localName: string): return some(element.attrs[i].value) return none(string) -proc getNamedItem(map: NamedNodeMap; qualifiedName: string): Option[Attr] +proc getNamedItem(map: NamedNodeMap; qualifiedName: string): Attr {.jsfunc.} = let i = map.element.findAttr(qualifiedName) if i != -1: - return some(map.getAttr(i)) - return none(Attr) + return map.getAttr(i) + return nil proc getNamedItemNS(map: NamedNodeMap; namespace, localName: string): - Option[Attr] {.jsfunc.} = + Attr {.jsfunc.} = let i = map.element.findAttrNS(namespace, localName) if i != -1: - return some(map.getAttr(i)) - return none(Attr) + return map.getAttr(i) + return nil func length(map: NamedNodeMap): uint32 {.jsfget.} = return uint32(map.element.attrs.len) -proc item(map: NamedNodeMap; i: uint32): Option[Attr] {.jsfunc.} = +proc item(map: NamedNodeMap; i: uint32): Attr {.jsfunc.} = if int(i) < map.element.attrs.len: - return some(map.getAttr(int(i))) - return none(Attr) - -func hasprop[T: uint32|string](map: NamedNodeMap; i: T): bool {.jshasprop.} = - when T is uint32: - return int(i) < map.element.attrs.len - else: - return map.getNamedItem(i).isSome + return map.getAttr(int(i)) + return nil -func getter[T: uint32|string](map: NamedNodeMap; i: T): Option[Attr] +func getter(ctx: JSContext; map: NamedNodeMap; atom: JSAtom): Opt[Attr] {.jsgetprop.} = - when T is uint32: - return map.item(i) - else: - return map.getNamedItem(i) + var u: uint32 + if ctx.fromJS(atom, u).isSome: + return ok(map.item(u)) + var s: string + ?ctx.fromJS(atom, s) + return ok(map.getNamedItem(s)) + +func hasprop(ctx: JSContext; map: NamedNodeMap; atom: JSAtom): cint + {.jshasprop.} = + var x = ctx.getter(map, atom) + if x.isNone: + return -1 + return cint(x.get != nil) func names(ctx: JSContext; map: NamedNodeMap): JSPropertyEnumList {.jspropnames.} = @@ -1912,8 +1915,8 @@ template toOA*(writeBuffer: DocumentWriteBuffer): openArray[char] = proc CDB_parseDocumentWriteChunk(wrapper: pointer) {.importc.} # https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps -proc write(document: Document; text: varargs[string]): Err[DOMException] - {.jsfunc.} = +proc write(ctx: JSContext; document: Document; args: varargs[JSValue]): + Err[DOMException] {.jsfunc.} = if document.isxml: return errDOMException("document.write not supported in XML documents", "InvalidStateError") @@ -1927,8 +1930,12 @@ proc write(document: Document; text: varargs[string]): Err[DOMException] if document.writeBuffers.len == 0: return ok() #TODO (probably covered by open above) let buffer = document.writeBuffers[^1] - for s in text: - buffer.data &= s + var text = "" + for arg in args: + var s: string + ?ctx.fromJS(arg, s) + text &= s + buffer.data &= text if document.parserBlockingScript == nil: CDB_parseDocumentWriteChunk(document.parser) return ok() @@ -2917,17 +2924,20 @@ func IDLAttributeToCSSProperty(s: string; dashPrefix = false): string = else: result &= c -proc getter[T: uint32|string](this: CSSStyleDeclaration; u: T): - Option[string] {.jsgetprop.} = - when T is uint32: - return this.item(u) - else: - if u.isSupportedProperty(): - return some(this.getPropertyValue(u)) - let u = IDLAttributeToCSSProperty(u) - if u.isSupportedProperty(): - return some(this.getPropertyValue(u)) - return none(string) +proc getter(ctx: JSContext; this: CSSStyleDeclaration; atom: JSAtom): + JSValue {.jsgetprop.} = + var u: uint32 + if ctx.fromJS(atom, u).isSome: + return ctx.toJS(this.item(u)) + var s: string + if ctx.fromJS(atom, s).isNone: + return JS_EXCEPTION + if s.isSupportedProperty(): + return ctx.toJS(this.getPropertyValue(s)) + s = IDLAttributeToCSSProperty(s) + if s.isSupportedProperty(): + return ctx.toJS(this.getPropertyValue(s)) + return JS_NULL #TODO eh? proc setValue(this: CSSStyleDeclaration; i: int; cvals: seq[CSSComponentValue]): Err[void] = @@ -2938,23 +2948,28 @@ proc setValue(this: CSSStyleDeclaration; i: int; cvals: seq[CSSComponentValue]): this.decls[i].value = cvals return ok() -proc setter[T: uint32|string](this: CSSStyleDeclaration; u: T; - value: string) {.jssetprop.} = +proc setter(ctx: JSContext; this: CSSStyleDeclaration; atom: JSAtom; + value: string): Opt[void] {.jssetprop.} = let cvals = parseComponentValues(value) - when u is uint32: + var u: uint32 + if ctx.fromJS(atom, u).isSome: if this.setValue(int(u), cvals).isNone: - return + return ok() else: - if (let i = this.find(u); i != -1): + var s: string + ?ctx.fromJS(atom, s) + if (let i = this.find(s); i != -1): if this.setValue(i, cvals).isNone: - return + # not err! this does not throw. + return ok() else: var dummy: seq[CSSComputedEntry] - let val0 = parseComputedValues(dummy, u, cvals) + let val0 = parseComputedValues(dummy, s, cvals) if val0.isNone: - return - this.decls.add(CSSDeclaration(name: u, value: cvals)) + return ok() + this.decls.add(CSSDeclaration(name: s, value: cvals)) this.element.attr(satStyle, $this.decls) + ok() proc style*(element: Element): CSSStyleDeclaration {.jsfget.} = if element.cachedStyle == nil: diff --git a/src/html/formdata.nim b/src/html/formdata.nim index 687c5600..b5a21b1e 100644 --- a/src/html/formdata.nim +++ b/src/html/formdata.nim @@ -5,6 +5,7 @@ import html/enums import io/dynstream import js/base64 import js/domexception +import monoucha/fromjs import monoucha/javascript import monoucha/tojs import types/blob @@ -26,7 +27,10 @@ proc generateBoundary(): string = proc newFormData0*(): FormData = return FormData(boundary: generateBoundary()) -proc newFormData*(form: HTMLFormElement = nil; submitter: HTMLElement = nil): +proc newFormData0*(entries: seq[FormDataEntry]): FormData = + return FormData(boundary: generateBoundary(), entries: entries) + +proc newFormData(form: HTMLFormElement = nil; submitter: HTMLElement = nil): DOMResult[FormData] {.jsctor.} = let this = newFormData0() if form != nil: @@ -43,27 +47,28 @@ proc newFormData*(form: HTMLFormElement = nil; submitter: HTMLElement = nil): #TODO filename should not be allowed for string entries # in other words, this should be an overloaded function, not just an or type -proc append*[T: string|Blob](this: FormData; name: string; value: T; - filename = none(string)) {.jsfunc.} = - when T is Blob: +proc append*(ctx: JSContext; this: FormData; name: string; val: JSValue; + filename = none(string)): Opt[void] {.jsfunc.} = + var blob: Blob + if ctx.fromJS(val, blob).isSome: let filename = if filename.isSome: filename.get - elif value of WebFile: - WebFile(value).name + elif blob of WebFile: + WebFile(blob).name else: "blob" this.entries.add(FormDataEntry( name: name, isstr: false, - value: value, + value: blob, filename: filename )) - else: # string - this.entries.add(FormDataEntry( - name: name, - isstr: true, - svalue: value - )) + ok() + else: + var s: string + ?ctx.fromJS(val, s) + this.entries.add(FormDataEntry(name: name, isstr: true, svalue: s)) + ok() proc delete(this: FormData; name: string) {.jsfunc.} = for i in countdown(this.entries.high, 0): diff --git a/src/local/client.nim b/src/local/client.nim index d3fd9ebe..3d3e1bb0 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -821,7 +821,7 @@ proc newClient*(config: Config; forkserver: ForkServer; loaderPid: int; factory: newCAtomFactory(), loader: loader ) - jsrt.setInterruptHandler(interruptHandler, cast[pointer](client)) + JS_SetInterruptHandler(jsrt, interruptHandler, cast[pointer](client)) let global = JS_GetGlobalObject(jsctx) jsctx.setGlobal(client) jsctx.definePropertyE(global, "cmd", config.cmd.jsObj) diff --git a/src/local/container.nim b/src/local/container.nim index de4a78ff..a01d5e3b 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -1340,26 +1340,26 @@ proc updateCursor(container: Container) = container.setCursorY(container.lastVisibleLine) container.alert("Last line is #" & $container.numLines) -proc gotoLine*[T: string|int](container: Container; s: T) = - when s is string: - if s == "": - container.redraw = true - elif s[0] == '^': - container.cursorFirstLine() - elif s[0] == '$': - container.cursorLastLine() - else: - let i = parseUInt32(s, allowSign = true) - if i.isSome and i.get > 0: - container.markPos0() - container.setCursorY(int(i.get - 1)) - container.markPos() - else: - container.alert("First line is #1") # :) +proc gotoLine*(container: Container; s: string) = + if s == "": + container.redraw = true + elif s[0] == '^': + container.cursorFirstLine() + elif s[0] == '$': + container.cursorLastLine() else: - container.markPos0() - container.setCursorY(s - 1) - container.markPos() + let i = parseUInt32(s, allowSign = true) + if i.isSome and i.get > 0: + container.markPos0() + container.setCursorY(int(i.get - 1)) + container.markPos() + else: + container.alert("First line is #1") # :) + +proc gotoLine*(container: Container; n: int) = + container.markPos0() + container.setCursorY(n - 1) + container.markPos() proc pushCursorPos*(container: Container) = if container.select != nil: diff --git a/src/local/pager.nim b/src/local/pager.nim index 592db0c6..354b1dc5 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -312,12 +312,17 @@ proc isearchBackward(pager: Pager) {.jsfunc.} = pager.container.markPos0() pager.setLineEdit(lmISearchB) -proc gotoLine[T: string|int](pager: Pager; s: T = "") {.jsfunc.} = - when s is string: - if s == "": - pager.setLineEdit(lmGotoLine) - return - pager.container.gotoLine(s) +proc gotoLine(ctx: JSContext; pager: Pager; val = JS_UNDEFINED): Opt[void] + {.jsfunc.} = + var n: int + if ctx.fromJS(val, n).isSome: + pager.container.gotoLine(n) + elif JS_IsUndefined(val): + pager.setLineEdit(lmGotoLine) + else: + var s: string + ?ctx.fromJS(val, s) + pager.container.gotoLine(s) proc dumpAlerts*(pager: Pager) = for msg in pager.alerts: diff --git a/src/server/buffer.nim b/src/server/buffer.nim index 7abac9e1..f92d8174 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -1194,14 +1194,9 @@ proc cancel*(buffer: Buffer) {.proxy.} = #https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart/form-data-encoding-algorithm proc serializeMultipart(entries: seq[FormDataEntry]): FormData = - let formData = newFormData0() - for entry in entries: - let name = makeCRLF(entry.name) - if entry.isstr: - let value = makeCRLF(entry.svalue) - formData.append(name, value) - else: - formData.append(name, entry.value, some(entry.filename)) + let formData = newFormData0(entries) + for entry in formData.entries.mitems: + entry.name = makeCRLF(entry.name) return formData proc serializePlainTextFormData(kvs: seq[(string, string)]): string = diff --git a/src/types/url.nim b/src/types/url.nim index 7ecc9531..deeea08b 100644 --- a/src/types/url.nim +++ b/src/types/url.nim @@ -6,9 +6,11 @@ import std/tables import std/unicode import lib/punycode +import monoucha/fromjs import monoucha/javascript import monoucha/jserror import monoucha/libunicode +import monoucha/quickjs import types/blob import types/opt import utils/luwrap @@ -1103,27 +1105,27 @@ proc serializeFormURLEncoded*(kvs: seq[(string, string)]; spaceAsPlus = true): result &= '=' result.percentEncode(value, ApplicationXWWWFormUrlEncodedSet, spaceAsPlus) -proc newURLSearchParams[ - T: seq[(string, string)]| - Table[string, string]| - string - ](init: T = ""): URLSearchParams {.jsctor.} = - result = URLSearchParams() - when T is seq[(string, string)]: - result.list = init - elif T is Table[string, string]: - for k, v in init: - result.list.add((k, v)) - elif T is string: - let init = if init.len > 0 and init[0] == '?': - init.substr(1) +proc newURLSearchParams(ctx: JSContext; init: varargs[JSValue]): + Opt[URLSearchParams] {.jsctor.} = + let params = URLSearchParams() + if init.len > 0: + let val = init[0] + if ctx.fromJS(val, params.list).isSome: + discard + elif (var t: Table[string, string]; ctx.fromJS(val, t).isSome): + for k, v in t: + params.list.add((k, v)) else: - init - result.list = parseFromURLEncoded(init) + var res: string + ?ctx.fromJS(val, res) + if res.len > 0 and res[0] == '?': + res.delete(0..0) + params.list = parseFromURLEncoded(res) + return ok(params) proc searchParams(url: URL): URLSearchParams {.jsfget.} = if url.searchParamsInternal == nil: - let params = newURLSearchParams(url.query.get("")) + let params = URLSearchParams(list: parseFromURLEncoded(url.query.get(""))) params.url = url url.searchParamsInternal = params return url.searchParamsInternal diff --git a/src/utils/mimeguess.nim b/src/utils/mimeguess.nim index f13eaf89..d6224303 100644 --- a/src/utils/mimeguess.nim +++ b/src/utils/mimeguess.nim @@ -19,8 +19,7 @@ func guessContentType*(mimeTypes: MimeTypes; path: string; break if n > 0: let ext = path.substr(n + 1) - if ext in mimeTypes: - return mimeTypes[ext] + return mimeTypes.getOrDefault(ext, fallback) return fallback const JavaScriptTypes = [ |