about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config/config.nim6
-rw-r--r--src/display/pager.nim2
-rw-r--r--src/html/dom.nim5
-rw-r--r--src/io/about.nim6
-rw-r--r--src/io/file.nim6
-rw-r--r--src/io/http.nim4
-rw-r--r--src/io/loader.nim2
-rw-r--r--src/io/request.nim131
-rw-r--r--src/ips/forkserver.nim4
-rw-r--r--src/js/javascript.nim27
10 files changed, 143 insertions, 50 deletions
diff --git a/src/config/config.nim b/src/config/config.nim
index 45c177ad..d2650a2e 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -109,7 +109,7 @@ type
     userstyle*: string
     filter*: URLFilter
     cookiejar*: CookieJar
-    headers*: HeaderList
+    headers*: Headers
     refererfrom*: bool
     referrerpolicy*: ReferrerPolicy
     scripting*: bool
@@ -125,7 +125,7 @@ const DefaultHeaders* = {
   "Accept-Language": "en;q=1.0",
   "Pragma": "no-cache",
   "Cache-Control": "no-cache",
-}.toTable().newHeaderList()[]
+}.toTable().newHeaders()[]
 
 func getForkServerConfig*(config: Config): ForkServerConfig =
   return ForkServerConfig(
@@ -134,7 +134,7 @@ func getForkServerConfig*(config: Config): ForkServerConfig =
   )
 
 proc getBufferConfig*(config: Config, location: URL, cookiejar: CookieJar = nil,
-      headers: HeaderList = nil, refererfrom = false, scripting = false,
+      headers: Headers = nil, refererfrom = false, scripting = false,
       charsets = config.encoding.document_charset): BufferConfig =
   result = BufferConfig(
     userstyle: config.css.stylesheet,
diff --git a/src/display/pager.nim b/src/display/pager.nim
index 1767f09b..e79df4c7 100644
--- a/src/display/pager.nim
+++ b/src/display/pager.nim
@@ -545,7 +545,7 @@ proc applySiteconf(pager: Pager, request: Request): BufferConfig =
   let host = request.url.host
   var refererfrom: bool
   var cookiejar: CookieJar
-  var headers: HeaderList
+  var headers: Headers
   var scripting: bool
   var charsets = pager.config.encoding.document_charset
   for sc in pager.siteconf:
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 7dbe39f1..fef66e2d 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -128,7 +128,7 @@ type
     localName: string
 
   Node* = ref object of EventTarget
-    nodeType* {.jsget.}: NodeType
+    nodeType*: NodeType
     childList*: seq[Node]
     parentNode* {.jsget.}: Node
     parentElement* {.jsget.}: Element
@@ -565,6 +565,9 @@ func newCollection[T: Collection](root: Node, match: proc(node: Node): bool {.no
   inc root.document.colln
   result.populateCollection()
 
+func nodeType(node: Node): uint16 {.jsfget.} =
+  return uint16(node.nodeType)
+
 func isElement(node: Node): bool =
   return node.nodeType == ELEMENT_NODE
 
diff --git a/src/io/about.nim b/src/io/about.nim
index a3ecb1fe..b852c556 100644
--- a/src/io/about.nim
+++ b/src/io/about.nim
@@ -6,7 +6,7 @@ import ips/serialize
 import types/url
 
 const chawan = staticRead"res/chawan.html"
-const Headers = {
+const HeaderTable = {
   "Content-Type": "text/html"
 }.toTable()
 
@@ -14,12 +14,12 @@ proc loadAbout*(request: Request, ostream: Stream) =
   if request.url.pathname == "blank":
     ostream.swrite(0)
     ostream.swrite(200) # ok
-    let headers = newHeaderList(Headers)
+    let headers = newHeaders(HeaderTable)
     ostream.swrite(headers)
   elif request.url.pathname == "chawan":
     ostream.swrite(0)
     ostream.swrite(200) # ok
-    let headers = newHeaderList(Headers)
+    let headers = newHeaders(HeaderTable)
     ostream.swrite(headers)
     ostream.write(chawan)
   else:
diff --git a/src/io/file.nim b/src/io/file.nim
index 092810e2..24dea51c 100644
--- a/src/io/file.nim
+++ b/src/io/file.nim
@@ -10,7 +10,7 @@ import types/url
 proc loadDir(url: URL, path: string, ostream: Stream) =
   ostream.swrite(0)
   ostream.swrite(200) # ok
-  ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable()))
+  ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable()))
   ostream.write("""
 <HTML>
 <HEAD>
@@ -48,7 +48,7 @@ proc loadDir(url: URL, path: string, ostream: Stream) =
 proc loadSymlink(path: string, ostream: Stream) =
   ostream.swrite(0)
   ostream.swrite(200) # ok
-  ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable()))
+  ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable()))
   let sl = expandSymlink(path)
   ostream.write("""
 <HTML>
@@ -79,7 +79,7 @@ proc loadFile*(url: URL, ostream: Stream) =
   else:
     ostream.swrite(0)
     ostream.swrite(200) # ok
-    ostream.swrite(newHeaderList())
+    ostream.swrite(newHeaders())
     while not istream.atEnd:
       const bufferSize = 4096
       var buffer {.noinit.}: array[bufferSize, char]
diff --git a/src/io/http.nim b/src/io/http.nim
index 2eefb9ce..984cae7c 100644
--- a/src/io/http.nim
+++ b/src/io/http.nim
@@ -13,7 +13,7 @@ type
   HandleDataObj = object
     curl*: CURL
     statusline: bool
-    headers: HeaderList
+    headers: Headers
     request: Request
     ostream*: Stream
     mime: curl_mime
@@ -21,7 +21,7 @@ type
 
 func newHandleData(curl: CURL, request: Request, ostream: Stream): HandleData =
   let handleData = HandleData(
-    headers: newHeaderList(),
+    headers: newHeaders(),
     curl: curl,
     ostream: ostream,
     request: request
diff --git a/src/io/loader.nim b/src/io/loader.nim
index 746c760d..1102b4b5 100644
--- a/src/io/loader.nim
+++ b/src/io/loader.nim
@@ -67,7 +67,7 @@ type
     handleList: seq[HandleData]
 
   LoaderConfig* = object
-    defaultheaders*: HeaderList
+    defaultheaders*: Headers
     filter*: URLFilter
     cookiejar*: CookieJar
     referrerpolicy*: ReferrerPolicy
diff --git a/src/io/request.nim b/src/io/request.nim
index f6fe5a73..531a2fc3 100644
--- a/src/io/request.nim
+++ b/src/io/request.nim
@@ -1,5 +1,6 @@
 import options
 import streams
+import strutils
 import tables
 
 import bindings/quickjs
@@ -20,32 +21,57 @@ type
     HTTP_TRACE = "TRACE"
 
   RequestMode* = enum
-    NO_CORS, SAME_ORIGIN, CORS, NAVIGATE, WEBSOCKET
+    NO_CORS = "no-cors"
+    SAME_ORIGIN = "same-origin"
+    CORS = "cors"
+    NAVIGATE = "navigate"
+    WEBSOCKET = "websocket"
 
   RequestDestination* = enum
-    NO_DESTINATION, AUDIO, AUDIOWORKLET, DOCUMENT, EMBED, FONT, FRAME, IFRAME,
-    IMAGE, MANIFEST, OBJECT, PAINTWORKLET, REPORT, SCRIPT, SERVICEWORKER,
-    SHAREDWORKER, STYLE, TRACK, WORKER, XSLT
+    NO_DESTINATION = ""
+    AUDIO = "audio"
+    AUDIOWORKLET = "audioworklet"
+    DOCUMENT = "document"
+    EMBED = "embed"
+    FONT = "font"
+    FRAME = "frame"
+    IFRAME = "iframe"
+    IMAGE = "image"
+    MANIFEST = "manifest"
+    OBJECT = "object"
+    PAINTWORKLET = "paintworklet"
+    REPORT = "report"
+    SCRIPT = "script"
+    SERVICEWORKER = "serviceworker"
+    SHAREDWORKER = "sharedworker"
+    STYLE = "style"
+    TRACK = "track"
+    WORKER = "worker"
+    XSLT = "xslt"
 
   CredentialsMode* = enum
-    SAME_ORIGIN, OMIT, INCLUDE
+    SAME_ORIGIN = "same-origin"
+    OMIT = "omit"
+    INCLUDE = "include"
 
   CORSAttribute* = enum
-    NO_CORS, ANONYMOUS, USE_CREDENTIALS
+    NO_CORS = "no-cors"
+    ANONYMOUS = "anonymous"
+    USE_CREDENTIALS = "use-credentials"
 
 type
   Request* = ref RequestObj
   RequestObj* = object
     httpmethod*: HttpMethod
     url*: Url
-    headers*: HeaderList
+    headers* {.jsget.}: Headers
     body*: Option[string]
     multipart*: Option[MimeData]
     referer*: URL
-    mode*: RequestMode
-    destination*: RequestDestination
-    credentialsMode*: CredentialsMode
-    proxy*: URL #TODO: this should definitely be a different API.
+    mode* {.jsget.}: RequestMode
+    destination* {.jsget.}: RequestDestination
+    credentialsMode* {.jsget.}: CredentialsMode
+    proxy*: URL #TODO do something with this
 
   Response* = ref object
     body*: Stream
@@ -53,7 +79,7 @@ type
     res* {.jsget.}: int
     contenttype* {.jsget.}: string
     status* {.jsget.}: int
-    headers* {.jsget.}: HeaderList
+    headers* {.jsget.}: Headers
     redirect*: Request
     url*: URL #TODO should be urllist?
     unregisterFun*: proc()
@@ -63,7 +89,7 @@ type
     buf: string
     isend: bool
 
-  HeaderList* = ref object
+  Headers* = ref object
     table* {.jsget.}: Table[string, seq[string]]
 
 # Originally from the stdlib
@@ -86,7 +112,14 @@ proc Request_url(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.}
   let request = cast[Request](op)
   return toJS(ctx, $request.url)
 
-iterator pairs*(headers: HeaderList): (string, string) =
+proc Request_referrer(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.} =
+  let op = getOpaque0(this)
+  if unlikely(not ctx.isInstanceOf(this, "Request") or op == nil):
+    return JS_ThrowTypeError(ctx, "Value is not an instance of %s", "Request")
+  let request = cast[Request](op)
+  return toJS(ctx, $request.referer)
+
+iterator pairs*(headers: Headers): (string, string) =
   for k, vs in headers.table:
     for v in vs:
       yield (k, v)
@@ -147,10 +180,30 @@ proc newReadableStream*(isource: Stream): ReadableStream =
   else:
     result.isource.readStr(len, result.buf)
 
-func newHeaderList*(): HeaderList =
+proc fill(headers: Headers, ctx: JSContext, val: JSValue) =
+  if isSequence(ctx, val):
+    let x = fromJS[seq[(string, string)]](ctx, val)
+    if x.isSome:
+      for (k, v) in x.get:
+        if k in headers.table:
+          headers.table[k].add(v)
+        else:
+          headers.table[k] = @[v]
+  else:
+    let x = fromJS[Table[string, string]](ctx, val)
+    if x.isSome:
+      for k, v in x.get:
+        if k in headers.table:
+          headers.table[k].add(v)
+        else:
+          headers.table[k] = @[v]
+
+func newHeaders*(obj = none(JSObject)): Headers {.jsctor.} =
   new(result)
+  if obj.isSome:
+    result.fill(obj.get.ctx, obj.get.val)
 
-func newHeaderList*(table: Table[string, string]): HeaderList =
+func newHeaders*(table: Table[string, string]): Headers =
   new(result)
   for k, v in table:
     let k = k.toHeaderCase()
@@ -159,7 +212,7 @@ func newHeaderList*(table: Table[string, string]): HeaderList =
     else:
       result.table[k] = @[v]
 
-func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(),
+func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaders(),
                  body = none(string), # multipart = none(MimeData),
                  mode = RequestMode.NO_CORS,
                  credentialsMode = CredentialsMode.SAME_ORIGIN,
@@ -179,8 +232,8 @@ func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(),
 
 func newRequest*(url: URL, httpmethod = HTTP_GET, headers: seq[(string, string)] = @[],
                  body = none(string), # multipart = none(MimeData), TODO TODO TODO multipart
-                 mode = RequestMode.NO_CORS, proxy: URL = nil): Request {.jsctor.} =
-  let hl = newHeaderList()
+                 mode = RequestMode.NO_CORS, proxy: URL = nil): Request =
+  let hl = newHeaders()
   for pair in headers:
     let (k, v) = pair
     hl.table[k] = @[v]
@@ -198,20 +251,47 @@ func createPotentialCORSRequest*(url: URL, destination: RequestDestination, cors
   else: CredentialsMode.INCLUDE
   return newRequest(url, destination = destination, mode = mode, credentialsMode = credentialsMode)
 
+#TODO resource as Request
+#TODO also, I'm not sure what to do with init. For now I've re-introduced
+# JSObject to make this work, but I really wish we had a better solution.
+func newRequest*(resource: string, init: JSObject): Request {.jserr, jsctor.} =
+  let x = parseURL(resource)
+  if x.isNone:
+    JS_ERR JS_TypeError, resource & " is not a valid URL"
+  if x.get.username != "" or x.get.password != "":
+    JS_ERR JS_TypeError, resource & " is not a valid URL"
+  let url = x.get
+  let ctx = init.ctx
+  let fallbackMode = some(RequestMode.CORS) #TODO none if resource is request
+  #TODO fallback mode, origin, window, request mode, ...
+  let httpMethod = fromJS[HttpMethod](ctx,
+    JS_GetPropertyStr(ctx, init.val, "method")).get(HTTP_GET)
+  let body = fromJS[string](ctx, JS_GetPropertyStr(ctx, init.val, "body"))
+  let jheaders = JS_GetPropertyStr(ctx, init.val, "headers")
+  let hl = newHeaders()
+  hl.fill(ctx, jheaders)
+  let credentials = fromJS[CredentialsMode](ctx, JS_GetPropertyStr(ctx, init.val, "credentials"))
+    .get(CredentialsMode.SAME_ORIGIN)
+  let mode = fromJS[RequestMode](ctx, JS_GetPropertyStr(ctx, init.val, "mode"))
+    .get(fallbackMode.get(RequestMode.NO_CORS))
+  #TODO find a standard compatible way to implement this
+  let proxyUrl = fromJS[URL](ctx, JS_GetPropertyStr(ctx, init.val, "proxyUrl"))
+  return newRequest(url, httpMethod, hl, body, mode, credentials, proxy = proxyUrl.get(nil))
+
 proc `[]=`*(multipart: var MimeData, k, v: string) =
   multipart.content.add(MimePart(name: k, content: v))
 
-proc add*(headers: var HeaderList, k, v: string) =
+proc add*(headers: var Headers, k, v: string) =
   let k = k.toHeaderCase()
   if k notin headers.table:
     headers.table[k] = @[v]
   else:
     headers.table[k].add(v)
 
-proc `[]=`*(headers: var HeaderList, k, v: string) =
+proc `[]=`*(headers: var Headers, k, v: string) =
   headers.table[k.toHeaderCase()] = @[v]
 
-func getOrDefault*(headers: HeaderList, k: string, default = ""): string =
+func getOrDefault*(headers: Headers, k: string, default = ""): string =
   let k = k.toHeaderCase()
   if k in headers.table:
     headers.table[k][0]
@@ -252,6 +332,9 @@ func credentialsMode*(attribute: CORSAttribute): CredentialsMode =
     return INCLUDE
 
 proc addRequestModule*(ctx: JSContext) =
-  ctx.registerType(Request, extra_getset = [TabGetSet(name: "url", get: Request_url)])
+  ctx.registerType(Request, extra_getset = [
+    TabGetSet(name: "url", get: Request_url),
+    TabGetSet(name: "referrer", get: Request_referrer)
+  ])
   ctx.registerType(Response, extra_funcs = [TabFunc(name: "json", fun: Response_json)])
-  ctx.registerType(HeaderList)
+  ctx.registerType(Headers)
diff --git a/src/ips/forkserver.nim b/src/ips/forkserver.nim
index e7ddf1c0..c6acefc6 100644
--- a/src/ips/forkserver.nim
+++ b/src/ips/forkserver.nim
@@ -32,7 +32,9 @@ type
     ostream: Stream
     children: seq[(Pid, Pid)]
 
-proc newFileLoader*(forkserver: ForkServer, defaultHeaders: HeaderList = nil, filter = newURLFilter(default = true), cookiejar: CookieJar = nil): FileLoader =
+proc newFileLoader*(forkserver: ForkServer, defaultHeaders: Headers = nil,
+    filter = newURLFilter(default = true),
+    cookiejar: CookieJar = nil): FileLoader =
   new(result)
   forkserver.ostream.swrite(FORK_LOADER)
   var defaultHeaders = defaultHeaders
diff --git a/src/js/javascript.nim b/src/js/javascript.nim
index 263fa8aa..438858ef 100644
--- a/src/js/javascript.nim
+++ b/src/js/javascript.nim
@@ -80,6 +80,10 @@ type
 
   JSFunctionList* = openArray[JSCFunctionListEntry]
 
+  JSObject* = object
+    ctx*: JSContext
+    val*: JSValue
+
 func getOpaque*(ctx: JSContext): JSContextOpaque =
   return cast[JSContextOpaque](JS_GetContextOpaque(ctx))
 
@@ -652,6 +656,8 @@ proc fromJS*[T](ctx: JSContext, val: JSValue): Option[T] =
       return none(T)
   elif T is JSValue:
     return some(val)
+  elif T is JSObject:
+    return some(JSObject(ctx: ctx, val: val))
   elif T is object:
     doAssert false, "Dictionary case has not been implemented yet!"
     #TODO TODO TODO implement dictionary case
@@ -751,7 +757,7 @@ proc toJS*(ctx: JSContext, obj: ref object): JSValue =
   return jsObj
 
 proc toJS(ctx: JSContext, e: enum): JSValue =
-  return toJS(ctx, int(e))
+  return toJS(ctx, $e)
 
 proc toJS(ctx: JSContext, j: JSValue): JSValue =
   return j
@@ -996,6 +1002,14 @@ proc addUnionParamBranch(gen: var JSFuncGenerator, query, newBranch: NimNode, fa
     gen.jsFunCallLists[i] = oldBranch
   gen.newBranchList.add(newBranch)
 
+func isSequence*(ctx: JSContext, o: JSValue): bool =
+  if not JS_IsObject(o):
+    return false
+  let prop = JS_GetProperty(ctx, o, ctx.getOpaque().sym_iterator)
+  # prop can't be exception (throws_ref_error is 0 and tag is object)
+  result = not JS_IsUndefined(prop)
+  JS_FreeValue(ctx, prop)
+
 proc addUnionParam0(gen: var JSFuncGenerator, tt: NimNode, s: NimNode, val: NimNode, fallback: NimNode = nil) =
   # Union types.
   #TODO quite a few types are still missing.
@@ -1028,16 +1042,7 @@ proc addUnionParam0(gen: var JSFuncGenerator, tt: NimNode, s: NimNode, val: NimN
   # Sequence:
   if seqg.issome:
     let query = quote do:
-      (
-        let o = `val`
-        JS_IsObject(o) and (
-          let prop = JS_GetProperty(ctx, o, ctx.getOpaque().sym_iterator)
-          # prop can't be exception (throws_ref_error is 0 and tag is object)
-          let ret = not JS_IsUndefined(prop)
-          JS_FreeValue(ctx, prop)
-          ret
-        )
-      )
+      isSequence(ctx, `val`)
     let a = seqg.get[1]
     gen.addUnionParamBranch(query, quote do:
       let `s` = fromJS_or_die(seq[`a`], ctx, `val`, `ev`, `dl`),