about summary refs log tree commit diff stats
path: root/src/html/xmlhttprequest.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/html/xmlhttprequest.nim')
-rw-r--r--src/html/xmlhttprequest.nim118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/html/xmlhttprequest.nim b/src/html/xmlhttprequest.nim
new file mode 100644
index 00000000..e9905760
--- /dev/null
+++ b/src/html/xmlhttprequest.nim
@@ -0,0 +1,118 @@
+import std/options
+import std/strutils
+
+import bindings/quickjs
+import html/dom
+import html/event
+import js/domexception
+import js/fromjs
+import js/javascript
+import loader/headers
+import loader/request
+import loader/response
+import types/url
+
+type
+  XMLHttpRequestResponseType = enum
+    TYPE_UNKNOWN = ""
+    TYPE_ARRAYBUFFER = "arraybuffer"
+    TYPE_BLOB = "blob"
+    TYPE_DOCUMENT = "document"
+    TYPE_JSON = "json"
+    TYPE_TEXT = "text"
+
+  XMLHttpRequestState = enum
+    UNSENT = 0u16
+    OPENED = 1u16
+    HEADERS_RECEIVED = 2u16
+    LOADING = 3u16
+    DONE = 4u16
+
+  XMLHttpRequestFlag = enum
+    SEND_FLAG, UPLOAD_LISTENER_FLAG, SYNC_FLAG
+
+  XMLHttpRequestEventTarget = ref object of EventTarget
+    onloadstart {.jsgetset.}: EventHandler
+    onprogress {.jsgetset.}: EventHandler
+    onabort {.jsgetset.}: EventHandler
+    onerror {.jsgetset.}: EventHandler
+    onload {.jsgetset.}: EventHandler
+    ontimeout {.jsgetset.}: EventHandler
+    onloadend {.jsgetset.}: EventHandler
+
+  XMLHttpRequestUpload = ref object of XMLHttpRequestEventTarget
+
+  XMLHttpRequest = ref object of XMLHttpRequestEventTarget
+    onreadystatechange {.jsgetset.}: EventHandler
+    readyState: XMLHttpRequestState
+    upload {.jsget.}: XMLHttpRequestUpload
+    flags: set[XMLHttpRequestFlag]
+    requestMethod: HttpMethod
+    requestURL: URL
+    authorRequestHeaders: Headers
+    response: Response
+    responseType {.jsgetset.}: XMLHttpRequestResponseType
+
+jsDestructor(XMLHttpRequestEventTarget)
+jsDestructor(XMLHttpRequestUpload)
+jsDestructor(XMLHttpRequest)
+
+func newXMLHttpRequest(): XMLHttpRequest {.jsctor.} =
+  let upload = XMLHttpRequestUpload()
+  return XMLHttpRequest(
+    upload: upload,
+    authorRequestHeaders: newHeaders()
+  )
+
+func readyState(this: XMLHttpRequest): uint16 {.jsfget.} =
+  return uint16(this.readyState)
+
+proc parseMethod(s: string): DOMResult[HttpMethod] =
+  return case s.toLowerAscii()
+  of "get": ok(HTTP_GET)
+  of "delete": ok(HTTP_DELETE)
+  of "head": ok(HTTP_HEAD)
+  of "options": ok(HTTP_OPTIONS)
+  of "patch": ok(HTTP_PATCH)
+  of "post": ok(HTTP_POST)
+  of "put": ok(HTTP_PUT)
+  of "connect", "trace", "track":
+    errDOMException("Forbidden method", "SecurityError")
+  else:
+    errDOMException("Invalid method", "SyntaxError")
+
+proc open(ctx: JSContext, this: XMLHttpRequest, httpMethod, url: string):
+    Err[DOMException] {.jsfunc.} =
+  let httpMethod = ?parseMethod(httpMethod)
+  let global = JS_GetGlobalObject(ctx)
+  let window = fromJS[Window](ctx, global)
+  JS_FreeValue(ctx, global)
+  let x = if window.isSome:
+    parseURL(url, some(window.get.document.baseURL))
+  else:
+    parseURL(url)
+  if x.isNone:
+    return errDOMException("Invalid URL", "SyntaxError")
+  let parsedURL = x.get
+  #TODO async, username, password arguments
+  let async = true
+  #TODO if async is false... probably just throw.
+  #TODO terminate fetch controller
+  this.flags.excl(SEND_FLAG)
+  this.flags.excl(UPLOAD_LISTENER_FLAG)
+  if async:
+    this.flags.excl(SYNC_FLAG)
+  else:
+    this.flags.incl(SYNC_FLAG)
+  this.requestMethod = httpMethod
+  this.authorRequestHeaders = newHeaders()
+  this.response = makeNetworkError()
+  this.requestURL = parsedURL
+  return ok()
+
+proc addXMLHttpRequestModule*(ctx: JSContext) =
+  let eventTargetCID = ctx.getClass("EventTarget")
+  let xhretCID = ctx.registerType(XMLHttpRequestEventTarget, eventTargetCID)
+  ctx.registerType(XMLHttpRequestUpload, xhretCID)
+  let xhrCID = ctx.registerType(XMLHttpRequest, xhretCID)
+  ctx.defineConsts(xhrCID, XMLHttpRequestState, uint16)