about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/html/dom.nim2
-rw-r--r--src/html/enums.nim1
-rw-r--r--src/server/buffer.nim42
-rw-r--r--src/xhr/formdata.nim26
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)