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.nim1
-rw-r--r--src/html/catom.nim1
-rw-r--r--src/html/dom.nim6
-rw-r--r--src/io/serversocket.nim3
-rw-r--r--src/local/container.nim47
-rw-r--r--src/local/pager.nim76
-rw-r--r--src/server/buffer.nim96
7 files changed, 129 insertions, 101 deletions
diff --git a/src/config/config.nim b/src/config/config.nim
index 50173057..311d52e8 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -64,6 +64,7 @@ type
     proxy*: Option[URL]
     default_headers*: TableRef[string, string]
     insecure_ssl_no_verify*: Option[bool]
+    autofocus*: Option[bool]
 
   OmniRule* = ref object
     match*: Regex
diff --git a/src/html/catom.nim b/src/html/catom.nim
index 9180ecd6..7c61e23e 100644
--- a/src/html/catom.nim
+++ b/src/html/catom.nim
@@ -16,6 +16,7 @@ macro makeStaticAtom =
       satAlign = "align"
       satAlt = "alt"
       satAsync = "async"
+      satAutofocus = "autofocus"
       satBgcolor = "bgcolor"
       satBlocking = "blocking"
       satCharset = "charset"
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 6eb1079f..d09849c7 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -2336,6 +2336,12 @@ func findAnchor*(document: Document; id: string): Element =
       return child
   return nil
 
+func findAutoFocus*(document: Document): Element =
+  for child in document.elements:
+    if child.attrb(satAutofocus):
+      return child
+  return nil
+
 # Forward declaration hack
 isDefaultPassive = func (eventTarget: EventTarget): bool =
   if eventTarget of Window:
diff --git a/src/io/serversocket.nim b/src/io/serversocket.nim
index dff6de70..abb3491a 100644
--- a/src/io/serversocket.nim
+++ b/src/io/serversocket.nim
@@ -14,6 +14,9 @@ const SocketPathPrefix = "cha_sock_"
 proc getSocketName*(pid: int): string =
   SocketPathPrefix & $pid
 
+func getFd*(ssock: ServerSocket): SocketHandle =
+  return ssock.sock.getFd()
+
 proc getSocketPath*(socketDir: string; pid: int): string =
   socketDir / getSocketName(pid)
 
diff --git a/src/local/container.nim b/src/local/container.nim
index 90e00ebb..b64606a1 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -1,5 +1,4 @@
 import std/deques
-import std/net
 import std/options
 import std/os
 import std/posix
@@ -208,8 +207,8 @@ proc clone*(container: Container; newurl: URL; loader: FileLoader):
   let ssock = initServerSocket(loader.sockDir, loader.sockDirFd,
     loader.clientPid)
   SocketStream(container.iface.stream.source)
-    .sendFileHandle(FileHandle(ssock.sock.getFd()))
-  ssock.sock.close()
+    .sendFileHandle(FileHandle(ssock.getFd()))
+  ssock.close(unlink = false)
   return p.then(proc(pid: int): Container =
     if pid == -1:
       return nil
@@ -1371,6 +1370,23 @@ proc setLoadInfo(container: Container; msg: string) =
   container.loadinfo = msg
   container.triggerEvent(cetSetLoadInfo)
 
+proc onReadLine(container: Container; rl: ReadLineResult) =
+  case rl.t
+  of rltText:
+    container.triggerEvent(ContainerEvent(
+      t: cetReadLine,
+      prompt: rl.prompt,
+      value: rl.value,
+      password: rl.hide
+    ))
+  of rltArea:
+    container.triggerEvent(ContainerEvent(
+      t: cetReadArea,
+      tvalue: rl.value
+    ))
+  of rltFile:
+    container.triggerEvent(ContainerEvent(t: cetReadFile))
+
 #TODO this should be called with a timeout.
 proc onload(container: Container; res: int) =
   if container.loadState == lsCanceled:
@@ -1380,13 +1396,15 @@ proc onload(container: Container; res: int) =
     container.setLoadInfo("")
     container.triggerEvent(cetStatus)
     container.triggerEvent(cetLoaded)
-    if cfHasStart notin container.flags and container.url.anchor != "":
+    if cfHasStart notin container.flags and (container.url.anchor != "" or
+        container.config.autofocus):
       container.requestLines().then(proc(): Promise[GotoAnchorResult] =
         return container.iface.gotoAnchor()
       ).then(proc(res: GotoAnchorResult) =
-        if res.isSome:
-          let res = res.get
+        if res.found:
           container.setCursorXYCenter(res.x, res.y)
+          if res.focus != nil:
+            container.onReadLine(res.focus)
       )
     else:
       container.needslines = true
@@ -1522,22 +1540,7 @@ proc onclick(container: Container; res: ClickResult; save: bool) =
   if res.select.isSome and not save:
     container.displaySelect(res.select.get)
   if res.readline.isSome:
-    let rl = res.readline.get
-    case rl.t
-    of rltText:
-      container.triggerEvent(ContainerEvent(
-        t: cetReadLine,
-        prompt: rl.prompt,
-        value: rl.value,
-        password: rl.hide
-      ))
-    of rltArea:
-      container.triggerEvent(ContainerEvent(
-        t: cetReadArea,
-        tvalue: rl.value
-      ))
-    of rltFile:
-      container.triggerEvent(ContainerEvent(t: cetReadFile))
+    container.onReadLine(res.readline.get)
 
 proc click*(container: Container) {.jsfunc.} =
   if container.select.open:
diff --git a/src/local/pager.nim b/src/local/pager.nim
index 48856837..16cd20a0 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -952,16 +952,28 @@ proc windowChange*(pager: Pager) =
 proc applySiteconf(pager: Pager; url: var URL; charsetOverride: Charset;
     loaderConfig: var LoaderClientConfig): BufferConfig =
   let host = url.host
-  var referer_from = false
-  var cookieJar: CookieJar = nil
-  var headers = newHeaders(pager.config.network.default_headers)
-  var scripting = false
-  var images = false
-  var charsets = pager.config.encoding.document_charset
-  var userstyle = pager.config.css.stylesheet
-  var proxy = pager.config.network.proxy
   let ctx = pager.jsctx
-  var insecureSSLNoVerify = false
+  var res = BufferConfig(
+    userstyle: pager.config.css.stylesheet,
+    referer_from: false,
+    scripting: false,
+    charsets: pager.config.encoding.document_charset,
+    images: false,
+    isdump: pager.config.start.headless,
+    charsetOverride: charsetOverride,
+    protocol: pager.config.protocol
+  )
+  loaderConfig = LoaderClientConfig(
+    defaultHeaders: newHeaders(pager.config.network.default_headers),
+    cookiejar: nil,
+    proxy: pager.config.network.proxy,
+    filter: newURLFilter(
+      scheme = some(url.scheme),
+      allowschemes = @["data", "cache"],
+      default = true
+    ),
+    insecureSSLNoVerify: false
+  )
   for sc in pager.config.siteconf:
     if sc.url.isSome and not sc.url.get.match($url):
       continue
@@ -987,47 +999,29 @@ proc applySiteconf(pager: Pager; url: var URL; charsetOverride: Charset;
         if jarid notin pager.cookiejars:
           pager.cookiejars[jarid] = newCookieJar(url,
             sc.third_party_cookie)
-        cookieJar = pager.cookiejars[jarid]
+        loaderConfig.cookieJar = pager.cookiejars[jarid]
       else:
-        cookieJar = nil # override
+        loaderConfig.cookieJar = nil # override
     if sc.scripting.isSome:
-      scripting = sc.scripting.get
+      res.scripting = sc.scripting.get
     if sc.referer_from.isSome:
-      referer_from = sc.referer_from.get
+      res.referer_from = sc.referer_from.get
     if sc.document_charset.len > 0:
-      charsets = sc.document_charset
+      res.charsets = sc.document_charset
     if sc.images.isSome:
-      images = sc.images.get
+      res.images = sc.images.get
     if sc.stylesheet.isSome:
-      userstyle &= "\n"
-      userstyle &= sc.stylesheet.get
+      res.userstyle &= "\n"
+      res.userstyle &= sc.stylesheet.get
     if sc.proxy.isSome:
-      proxy = sc.proxy.get
+      loaderConfig.proxy = sc.proxy.get
     if sc.default_headers != nil:
-      headers = newHeaders(sc.default_headers[])
+      loaderConfig.defaultHeaders = newHeaders(sc.default_headers[])
     if sc.insecure_ssl_no_verify.isSome:
-      insecureSSLNoVerify = sc.insecure_ssl_no_verify.get
-  loaderConfig = LoaderClientConfig(
-    defaultHeaders: headers,
-    cookiejar: cookieJar,
-    proxy: proxy,
-    filter: newURLFilter(
-      scheme = some(url.scheme),
-      allowschemes = @["data", "cache"],
-      default = true
-    ),
-    insecureSSLNoVerify: insecureSSLNoVerify
-  )
-  return BufferConfig(
-    userstyle: userstyle,
-    referer_from: referer_from,
-    scripting: scripting,
-    charsets: charsets,
-    images: images,
-    isdump: pager.config.start.headless,
-    charsetOverride: charsetOverride,
-    protocol: pager.config.protocol
-  )
+      loaderConfig.insecureSSLNoVerify = sc.insecure_ssl_no_verify.get
+    if sc.autofocus.isSome:
+      res.autofocus = sc.autofocus.get
+  return res
 
 # Load request in a new buffer.
 proc gotoURL(pager: Pager; request: Request; prevurl = none(URL);
diff --git a/src/server/buffer.nim b/src/server/buffer.nim
index c2ed2fb7..8b31fb64 100644
--- a/src/server/buffer.nim
+++ b/src/server/buffer.nim
@@ -145,6 +145,7 @@ type
     charsets*: seq[Charset]
     charsetOverride*: Charset
     protocol*: Table[string, ProtocolConfig]
+    autofocus*: bool
 
 proc getFromOpaque[T](opaque: pointer; res: var T) =
   let opaque = cast[InterfaceOpaque](opaque)
@@ -156,7 +157,7 @@ proc getFromOpaque[T](opaque: pointer; res: var T) =
 proc newBufferInterface*(stream: SocketStream; registerFun: proc(fd: int)):
     BufferInterface =
   let opaque = InterfaceOpaque(stream: stream)
-  result = BufferInterface(
+  return BufferInterface(
     map: newPromiseMap(cast[pointer](opaque)),
     packetid: 1, # ids below 1 are invalid
     opaque: opaque,
@@ -653,21 +654,61 @@ proc findNextMatch*(buffer: Buffer; regex: Regex; cursorx, cursory: int;
       break
     inc y
 
-type GotoAnchorResult* = Opt[tuple[x, y: int]]
+type
+  ReadLineType* = enum
+    rltText, rltArea, rltFile
+
+  ReadLineResult* = ref object
+    t*: ReadLineType
+    prompt*: string
+    value*: string
+    hide*: bool
+
+  SelectResult* = object
+    multiple*: bool
+    options*: seq[string]
+    selected*: seq[int]
+
+  ClickResult* = object
+    open*: Request
+    readline*: Option[ReadLineResult]
+    repaint*: bool
+    select*: Option[SelectResult]
+
+proc click(buffer: Buffer; clickable: Element): ClickResult
+
+type GotoAnchorResult* = object
+  found*: bool
+  x*: int
+  y*: int
+  focus*: ReadLineResult
 
 proc gotoAnchor*(buffer: Buffer): GotoAnchorResult {.proxy.} =
   if buffer.document == nil:
-    return err()
-  let anchor = buffer.document.findAnchor(buffer.url.anchor)
+    return GotoAnchorResult(found: false)
+  var anchor = buffer.document.findAnchor(buffer.url.anchor)
+  var focus: ReadLineResult = nil
+  if buffer.config.autofocus:
+    let autofocus = buffer.document.findAutoFocus()
+    if autofocus != nil:
+      if anchor == nil:
+        anchor = autofocus # jump to autofocus instead
+      let res = buffer.click(autofocus)
+      focus = res.readline.get(nil)
   if anchor == nil:
-    return err()
+    return GotoAnchorResult(found: false)
   for y in 0 ..< buffer.lines.len:
     let line = buffer.lines[y]
     for i in 0 ..< line.formats.len:
       let format = line.formats[i]
       if format.node != nil and format.node.node in anchor:
-        return ok((format.pos, y))
-  return err()
+        return GotoAnchorResult(
+          found: true,
+          x: format.pos,
+          y: y,
+          focus: focus
+        )
+  return GotoAnchorResult(found: false)
 
 proc do_reshape(buffer: Buffer) =
   if buffer.document == nil:
@@ -1268,6 +1309,7 @@ proc serializeMultipartFormData(entries: seq[FormDataEntry]): FormData =
   return formData
 
 proc serializePlainTextFormData(kvs: seq[(string, string)]): string =
+  result = ""
   for it in kvs:
     let (name, value) = it
     result &= name
@@ -1422,6 +1464,7 @@ proc implicitSubmit(buffer: Buffer; input: HTMLInputElement): Request =
 proc readSuccess*(buffer: Buffer; s: string; hasFd: bool): ReadSuccessResult
     {.proxy.} =
   var fd: FileHandle = -1
+  var res = ReadSuccessResult()
   if hasFd:
     fd = buffer.pstream.recvFileHandle()
   if buffer.document.focus != nil:
@@ -1433,48 +1476,25 @@ proc readSuccess*(buffer: Buffer; s: string; hasFd: bool): ReadSuccessResult
         input.file = newWebFile(s, fd)
         input.invalid = true
         buffer.do_reshape()
-        result.repaint = true
-        result.open = buffer.implicitSubmit(input)
+        res.repaint = true
+        res.open = buffer.implicitSubmit(input)
       else:
         input.value = s
         input.invalid = true
         buffer.do_reshape()
-        result.repaint = true
-        result.open = buffer.implicitSubmit(input)
+        res.repaint = true
+        res.open = buffer.implicitSubmit(input)
     of TAG_TEXTAREA:
       let textarea = HTMLTextAreaElement(buffer.document.focus)
       textarea.value = s
       textarea.invalid = true
       buffer.do_reshape()
-      result.repaint = true
+      res.repaint = true
     else: discard
     let r = buffer.restoreFocus()
-    if not result.repaint:
-      result.repaint = r
-
-type
-  ReadLineType* = enum
-    rltText, rltArea, rltFile
-
-  ReadLineResult* = object
-    t*: ReadLineType
-    prompt*: string
-    value*: string
-    hide*: bool
-
-type
-  SelectResult* = object
-    multiple*: bool
-    options*: seq[string]
-    selected*: seq[int]
-
-  ClickResult* = object
-    open*: Request
-    readline*: Option[ReadLineResult]
-    repaint*: bool
-    select*: Option[SelectResult]
-
-proc click(buffer: Buffer; clickable: Element): ClickResult
+    if not res.repaint:
+      res.repaint = r
+  return res
 
 proc click(buffer: Buffer; label: HTMLLabelElement): ClickResult =
   let control = label.control