about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/client.nim31
-rw-r--r--src/css/cascade.nim13
-rw-r--r--src/css/mediaquery.nim2
-rw-r--r--src/html/dom.nim13
-rw-r--r--src/html/parser.nim3
-rw-r--r--src/io/buffer.nim17
-rw-r--r--src/io/loader.nim33
-rw-r--r--src/types/mime.nim1
-rw-r--r--src/types/url.nim5
9 files changed, 90 insertions, 28 deletions
diff --git a/src/client.nim b/src/client.nim
index 1a18bc56..2cce60a7 100644
--- a/src/client.nim
+++ b/src/client.nim
@@ -1,26 +1,25 @@
-import httpclient
 import streams
 import terminal
 import options
 import os
 
+import css/sheet
+import config/config
 import io/buffer
 import io/lineedit
-import config/config
-import utils/twtstr
-import css/sheet
-import types/mime
+import io/loader
 import types/url
+import utils/twtstr
 
 type
   Client* = ref object
-    http: HttpClient
     buffer: Buffer
     feednext: bool
     s: string
     iserror: bool
     errormessage: string
     userstyle: CSSStylesheet
+    loader: FileLoader
 
   ActionError = object of IOError
   LoadError = object of ActionError
@@ -28,7 +27,7 @@ type
 
 proc newClient*(): Client =
   new(result)
-  result.http = newHttpClient()
+  result.loader = newFileLoader()
 
 proc loadError(s: string) =
   raise newException(LoadError, s)
@@ -39,20 +38,6 @@ proc actionError(s: string) =
 proc interruptError() =
   raise newException(InterruptError, "Interrupted")
 
-proc getPage(client: Client, url: Url): tuple[s: Stream, contenttype: string] =
-  if url.scheme == "file":
-    let path = url.path.serialize_unicode()
-    result.contenttype = guessContentType(path)
-    result.s = newFileStream(path, fmRead)
-  elif url.scheme == "http" or url.scheme == "https":
-    let resp = client.http.get(url.serialize(true))
-    let ct = resp.contentType()
-    if ct != "":
-      result.contenttype = ct.until(';')
-    else:
-      result.contenttype = guessContentType(url.path.serialize())
-    result.s = resp.bodyStream
-
 proc addBuffer(client: Client) =
   if client.buffer == nil:
     client.buffer = newBuffer()
@@ -64,6 +49,7 @@ proc addBuffer(client: Client) =
     client.buffer.next.prev = client.buffer
     client.buffer.next.next = oldnext
     client.buffer = client.buffer.next
+  client.buffer.loader = client.loader
 
 proc prevBuffer(client: Client) =
   if client.buffer.prev != nil:
@@ -99,6 +85,7 @@ proc setupBuffer(client: Client) =
 
 proc readPipe(client: Client, ctype: string) =
   client.buffer = newBuffer()
+  client.buffer.loader = client.loader
   client.buffer.contenttype = if ctype != "": ctype else: "text/plain"
   client.buffer.ispipe = true
   client.buffer.istream = newFileStream(stdin)
@@ -115,7 +102,7 @@ proc gotoUrl(client: Client, url: Url, prevurl = none(Url), force = false, newbu
     raise newException(InterruptError, "Interrupted"))
   if force or prevurl.issome or not prevurl.get.equals(url, true):
     try:
-      let page = client.getPage(url)
+      let page = client.loader.getPage(url)
       if page.s != nil:
         if newbuf:
           client.addBuffer()
diff --git a/src/css/cascade.nim b/src/css/cascade.nim
index 223a3ec5..dc11628f 100644
--- a/src/css/cascade.nim
+++ b/src/css/cascade.nim
@@ -167,9 +167,20 @@ proc applyRules*(document: Document, ua, user: CSSStylesheet) =
   var rules_head: CSSStylesheet
 
   for child in document.head.children:
-    if child.tagType == TAG_STYLE:
+    case child.tagType
+    of TAG_STYLE:
       let style = HTMLStyleElement(child)
       rules_head.add(style.sheet)
+    of TAG_LINK:
+      let link = HTMLLinkElement(child)
+      if link.s != nil:
+        let content = link.s.readAll()
+        link.sheet = parseStylesheet(content)
+        link.s.close()
+        link.s = nil
+      rules_head.add(link.sheet)
+    else:
+      discard
 
   if rules_head.len > 0:
     embedded_rules.add(rules_head)
diff --git a/src/css/mediaquery.nim b/src/css/mediaquery.nim
index cea35c36..4e8c1e9f 100644
--- a/src/css/mediaquery.nim
+++ b/src/css/mediaquery.nim
@@ -186,7 +186,7 @@ proc parseMediaCondition(parser: var MediaQueryParser, non = false, noor = false
   var non = non
   if not non:
     let cval = parser.consume()
-    if cval of CSSToken:
+    if cval of CSSToken and CSSToken(cval).tokenType == CSS_IDENT_TOKEN:
       if $CSSToken(cval).value == "not":
         non = true
     else:
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 0105b72f..5537491c 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -1,11 +1,12 @@
-import uri
 import tables
 import options
+import streams
 import strutils
 
 import css/values
 import css/sheet
 import html/tags
+import types/url
 
 type
   EventTarget* = ref EventTargetObj
@@ -35,7 +36,7 @@ type
 
   Document* = ref DocumentObj
   DocumentObj = object of NodeObj
-    location*: Uri
+    location*: Url
     type_elements*: array[TagType, seq[Element]]
     id_elements*: Table[string, seq[Element]]
     class_elements*: Table[string, seq[Element]]
@@ -117,6 +118,12 @@ type
   HTMLStyleElement* = ref object of HTMLElement
     sheet*: CSSStylesheet
 
+  HTMLLinkElement* = ref object of HTMLElement
+    href*: string
+    rel*: string
+    sheet*: CSSStylesheet
+    s*: Stream
+
 iterator textNodes*(node: Node): Text {.inline.} =
   for node in node.childNodes:
     if node.nodeType == TEXT_NODE:
@@ -295,6 +302,8 @@ func newHtmlElement*(document: Document, tagType: TagType): HTMLElement =
     result = new(HTMLLIElement)
   of TAG_STYLE:
     result = new(HTMLStyleElement)
+  of TAG_LINK:
+    result = new(HTMLLinkElement)
   else:
     result = new(HTMLElement)
 
diff --git a/src/html/parser.nim b/src/html/parser.nim
index 7488534a..febe3bce 100644
--- a/src/html/parser.nim
+++ b/src/html/parser.nim
@@ -284,6 +284,9 @@ proc processDocumentStartElement(state: var HTMLParseState, element: Element, ta
     HTMLHeadingElement(element).rank = 5
   of TAG_H6:
     HTMLHeadingElement(element).rank = 6
+  of TAG_LINK:
+    HTMLLinkElement(element).href = element.attr("href")
+    HTMLLinkElement(element).rel = element.attr("rel")
   else: discard
 
   if not state.in_body and not (element.tagType in HeadTagTypes):
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index f219fe2c..a082cfe3 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -8,8 +8,9 @@ import css/sheet
 import html/dom
 import html/tags
 import html/parser
-import io/term
 import io/cell
+import io/loader
+import io/term
 import layout/box
 import render/renderdocument
 import render/rendertext
@@ -49,6 +50,7 @@ type
     prev*: Buffer
     next* {.cursor.}: Buffer
     userstyle*: CSSStylesheet
+    loader*: FileLoader
 
 proc newBuffer*(): Buffer =
   new(result)
@@ -711,6 +713,17 @@ proc updateHover(buffer: Buffer) =
         elem.refreshStyle()
   buffer.prevnodes = nodes
 
+proc loadResources(buffer: Buffer, document: Document) =
+  for elem in document.head.children:
+    if elem.tagType == TAG_LINK:
+      let elem = HTMLLinkElement(elem)
+      if elem.rel == "stylesheet":
+        let url = parseUrl(elem.href, document.location.some)
+        if url.issome:
+          let res = buffer.loader.getPage(url.get)
+          if res.s != nil and res.contenttype == "text/css":
+            elem.s = res.s
+
 proc load*(buffer: Buffer) =
   case buffer.contenttype
   of "text/html":
@@ -723,6 +736,8 @@ proc load*(buffer: Buffer) =
       buffer.istream.close()
       buffer.streamclosed = true
     buffer.document = parseHtml(newStringStream(buffer.source))
+    buffer.document.location = buffer.location
+    buffer.loadResources(buffer.document)
   else:
     if not buffer.streamclosed:
       buffer.source = buffer.istream.readAll()
diff --git a/src/io/loader.nim b/src/io/loader.nim
new file mode 100644
index 00000000..3ef5320e
--- /dev/null
+++ b/src/io/loader.nim
@@ -0,0 +1,33 @@
+import httpclient
+import options
+import streams
+
+import types/mime
+import types/url
+import utils/twtstr
+
+type
+  FileLoader* = ref object
+    http: HttpClient
+
+  LoadResult* = object
+    s*: Stream
+    contenttype*: string
+
+proc newFileLoader*(): FileLoader =
+  new(result)
+  result.http = newHttpClient()
+
+proc getPage*(loader: FileLoader, url: Url): LoadResult =
+  if url.scheme == "file":
+    let path = url.path.serialize_unicode()
+    result.contenttype = guessContentType(path)
+    result.s = newFileStream(path, fmRead)
+  elif url.scheme == "http" or url.scheme == "https":
+    let resp = loader.http.get(url.serialize(true))
+    let ct = resp.contentType()
+    if ct != "":
+      result.contenttype = ct.until(';')
+    else:
+      result.contenttype = guessContentType(url.path.serialize())
+    result.s = resp.bodyStream
diff --git a/src/types/mime.nim b/src/types/mime.nim
index 5c48c896..fb3f4b2c 100644
--- a/src/types/mime.nim
+++ b/src/types/mime.nim
@@ -7,6 +7,7 @@ const DefaultGuess = [
   ("xhtm", "application/xhtml+xml"),
   ("xht", "application/xhtml+xml"),
   ("txt", "text/plain"),
+  ("css", "text/css"),
   ("", "text/plain")
 ].toTable()
 
diff --git a/src/types/url.nim b/src/types/url.nim
index 2356fb73..00a5fd51 100644
--- a/src/types/url.nim
+++ b/src/types/url.nim
@@ -317,7 +317,10 @@ proc shorten_path(url: var Url) {.inline.} =
     discard url.path.ss.pop()
 
 proc append(path: var UrlPath, s: string) =
-  path.ss.add(s)
+  if path.opaque:
+    path.s &= s
+  else:
+    path.ss.add(s)
 
 template includes_credentials(url: Url): bool = url.username != "" or url.password != ""