diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/html/dom.nim | 2 | ||||
-rw-r--r-- | src/html/enums.nim | 1 | ||||
-rw-r--r-- | src/server/buffer.nim | 42 | ||||
-rw-r--r-- | src/xhr/formdata.nim | 26 |
4 files changed, 45 insertions, 26 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim index 70f907c5..cb0c41d8 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -286,7 +286,7 @@ type smethod*: string enctype*: string novalidate*: bool - constructingentrylist*: bool + constructingEntryList*: bool controls*: seq[FormAssociatedElement] relList {.jsget.}: DOMTokenList diff --git a/src/html/enums.nim b/src/html/enums.nim index 6d1a0771..4b4faa63 100644 --- a/src/html/enums.nim +++ b/src/html/enums.nim @@ -32,6 +32,7 @@ type AttrType* = enum atUnknown = "" + atAcceptCharset = "accept-charset" atAction = "action" atAlign = "align" atAlt = "alt" diff --git a/src/server/buffer.nim b/src/server/buffer.nim index d0a50284..ef7afc44 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -1,3 +1,5 @@ +from std/strutils import split + import std/macros import std/nativesockets import std/net @@ -1176,11 +1178,29 @@ proc serializePlainTextFormData(kvs: seq[(string, string)]): string = result &= value result &= "\r\n" +func getOutputEncoding(charset: Charset): Charset = + if charset in {CHARSET_REPLACEMENT, CHARSET_UTF_16_BE, CHARSET_UTF_16_LE}: + return CHARSET_UTF_8 + return charset + +func pickCharset(form: HTMLFormElement): Charset = + if form.attrb(atAcceptCharset): + let input = form.attr(atAcceptCharset) + for label in input.split(AsciiWhitespace): + let charset = label.getCharset() + if charset != CHARSET_UNKNOWN: + return charset.getOutputEncoding() + return CHARSET_UTF_8 + return form.document.charset.getOutputEncoding() + # https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = - if form.constructingentrylist: - return - let entrylist = form.constructEntryList(submitter).get(@[]) + if form.constructingEntryList: + return none(Request) + #TODO submit() + let charset = form.pickCharset() + discard charset #TODO pass to constructEntryList + let entrylist = form.constructEntryList(submitter) let subAction = submitter.action() let action = if subAction != "": @@ -1188,6 +1208,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = else: $form.document.url + #TODO encoding-parse let url = submitter.document.parseURL(action) if url.isNone: return none(Request) @@ -1213,6 +1234,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = template mutateActionUrl() = let kvlist = entrylist.toNameValuePairs() + #TODO with charset let query = serializeApplicationXWWWFormUrlEncoded(kvlist) parsedaction.query = some(query) return some(newRequest(parsedaction, httpmethod)) @@ -1223,13 +1245,16 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = var multipart: Opt[FormData] case enctype of FORM_ENCODING_TYPE_URLENCODED: + #TODO with charset let kvlist = entrylist.toNameValuePairs() body.ok(serializeApplicationXWWWFormUrlEncoded(kvlist)) mimeType = $enctype of FORM_ENCODING_TYPE_MULTIPART: + #TODO with charset multipart.ok(serializeMultipartFormData(entrylist)) mimetype = $enctype of FORM_ENCODING_TYPE_TEXT_PLAIN: + #TODO with charset let kvlist = entrylist.toNameValuePairs() body.ok(serializePlainTextFormData(kvlist)) mimetype = $enctype @@ -1242,6 +1267,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = template mailWithHeaders() = let kvlist = entrylist.toNameValuePairs() + #TODO with charset let headers = serializeApplicationXWWWFormUrlEncoded(kvlist, spaceAsPlus = false) parsedaction.query = some(headers) @@ -1253,6 +1279,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = let text = serializePlainTextFormData(kvlist) percentEncode(text, PathPercentEncodeSet) else: + #TODO with charset serializeApplicationXWWWFormUrlEncoded(kvlist) if parsedaction.query.isNone: parsedaction.query = some("") @@ -1262,14 +1289,6 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = return some(newRequest(parsedaction, httpmethod)) case scheme - of "http", "https", "gopher", "gophers", "cgi-bin": - # Note: only http & https are defined by the standard. - # We implement gopher, gophers & cgi-bin as HTTP-like protocols. - if formmethod == FORM_METHOD_GET: - mutateActionUrl - else: - assert formmethod == FORM_METHOD_POST - submitAsEntityBody of "ftp", "javascript": getActionUrl of "data": @@ -1285,6 +1304,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] = assert formmethod == FORM_METHOD_POST mailAsBody else: + # Note: only http & https are defined by the standard. # Assume an HTTP-like protocol. if formmethod == FORM_METHOD_GET: mutateActionUrl diff --git a/src/xhr/formdata.nim b/src/xhr/formdata.nim index 87fa8dac..544aafd9 100644 --- a/src/xhr/formdata.nim +++ b/src/xhr/formdata.nim @@ -13,7 +13,7 @@ import utils/twtstr import chame/tags proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil, - encoding: string = ""): Option[seq[FormDataEntry]] + encoding = "UTF-8"): seq[FormDataEntry] proc generateBoundary(): string = @@ -37,7 +37,8 @@ proc newFormData*(form: HTMLFormElement = nil, if FormAssociatedElement(submitter).form != form: return errDOMException("Submitter's form owner is not form", "InvalidStateError") - this.entries = constructEntryList(form, submitter).get(@[]) + if not form.constructingEntryList: + this.entries = constructEntryList(form, submitter) return ok(this) #TODO filename should not be allowed for string entries @@ -103,11 +104,12 @@ func toNameValuePairs*(list: seq[FormDataEntry]): result.add((entry.name, entry.name)) # https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set +# Warning: we skip the first "constructing entry list" check; the caller must +# do it. proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil, - encoding: string = ""): Option[seq[FormDataEntry]] = - if form.constructingentrylist: - return - form.constructingentrylist = true + encoding = "UTF-8"): seq[FormDataEntry] = + assert not form.constructingEntryList + form.constructingEntryList = true var entrylist: seq[FormDataEntry] = @[] for field in form.controls: if field.findAncestor({TAG_DATALIST}) != nil or @@ -132,7 +134,7 @@ proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil, if field of HTMLSelectElement: let field = HTMLSelectElement(field) for option in field.options: - if option.selected or option.isDisabled: + if option.selected and not option.isDisabled: entrylist.add((name, option.value)) elif field of HTMLInputElement: let field = HTMLInputElement(field) @@ -149,11 +151,7 @@ proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil, discard of INPUT_HIDDEN: if name.equalsIgnoreCase("_charset_"): - let charset = if encoding != "": - encoding - else: - "UTF-8" - entrylist.add((name, charset)) + entrylist.add((name, encoding)) else: entrylist.add((name, field.value)) else: @@ -172,8 +170,8 @@ proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil, if dirname != "": let dir = "ltr" #TODO bidi entrylist.add((dirname, dir)) - form.constructingentrylist = false - return some(entrylist) + form.constructingEntryList = false + return entrylist proc addFormDataModule*(ctx: JSContext) = ctx.registerType(FormData) |