diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.nim | 63 | ||||
-rw-r--r-- | src/html/dom.nim | 11 | ||||
-rw-r--r-- | src/html/parser.nim | 5 | ||||
-rw-r--r-- | src/io/buffer.nim | 8 | ||||
-rw-r--r-- | src/layout/box.nim | 13 | ||||
-rw-r--r-- | src/layout/engine.nim | 57 | ||||
-rw-r--r-- | src/render/renderdocument.nim | 11 | ||||
-rw-r--r-- | src/types/mime.nim | 6 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 8 |
9 files changed, 109 insertions, 73 deletions
diff --git a/src/client.nim b/src/client.nim index 2de2a639..3c95c420 100644 --- a/src/client.nim +++ b/src/client.nim @@ -52,7 +52,7 @@ proc getPage(client: Client, url: Url): tuple[s: Stream, contenttype: string] = let resp = client.http.get(url.serialize(true)) let ct = resp.contentType() if ct != "": - result.contenttype = ct + result.contenttype = ct.until(';') else: result.contenttype = guessContentType(url.path.serialize()) result.s = resp.bodyStream @@ -112,6 +112,33 @@ proc readPipe(client: Client) = client.buffer.drawBuffer() var g_client: Client +proc gotoUrl(client: Client, url: Url, prevurl = none(Url), force = false, newbuf = true) = + setControlCHook(proc() {.noconv.} = + raise newException(InterruptError, "Interrupted")) + if force or prevurl.issome or not prevurl.get.equals(url, true): + try: + let page = client.getPage(url) + if page.s != nil: + if newbuf: + client.addBuffer() + g_client = client + setControlCHook(proc() {.noconv.} = + if g_client.buffer.prev != nil or g_client.buffer.next != nil: + g_client.discardBuffer() + interruptError()) + client.buffer.istream = page.s + client.buffer.contenttype = page.contenttype + client.buffer.streamclosed = false + else: + loadError("Couldn't load " & $url) + except IOError, OSError: + loadError("Couldn't load " & $url) + elif client.buffer != nil and prevurl.isnone or not prevurl.get.equals(url): + if not client.buffer.hasAnchor(url.anchor): + loadError("Couldn't find anchor " & url.anchor) + client.buffer.location = url + client.setupBuffer() + proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false, newbuf = true) = var oldurl = prevurl if oldurl.isnone and client.buffer != nil: @@ -119,36 +146,7 @@ proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false, ne let newurl = parseUrl(url, oldurl) if newurl.isnone: loadError("Invalid URL " & url) - if newurl.issome: - setControlCHook(proc() {.noconv.} = - raise newException(InterruptError, "Interrupted")) - let url = newurl.get - let prevurl = oldurl - if force or prevurl.issome or not prevurl.get.equals(url, true): - try: - let page = client.getPage(url) - if page.s != nil: - if newbuf: - client.addBuffer() - g_client = client - setControlCHook(proc() {.noconv.} = - if g_client.buffer.prev != nil or g_client.buffer.next != nil: - g_client.discardBuffer() - interruptError()) - client.buffer.istream = page.s - client.buffer.contenttype = page.contenttype - client.buffer.streamclosed = false - else: - loadError("Couldn't load " & $url) - except IOError, OSError: - loadError("Couldn't load " & $url) - elif client.buffer != nil and prevurl.isnone or not prevurl.get.equals(url): - if not client.buffer.hasAnchor(url.anchor): - loadError("Couldn't find anchor " & url.anchor) - client.buffer.setLocation(url) - client.setupBuffer() - else: - loadError("Couldn't parse URL " & url) + client.gotoUrl(newurl.get, oldurl, force, newbuf) proc loadUrl(client: Client, url: string) = let firstparse = parseUrl(url) @@ -163,7 +161,7 @@ proc loadUrl(client: Client, url: string) = proc reloadPage(client: Client) = let pbuffer = client.buffer - client.gotoUrl("", none(Url), true, false) + client.gotoUrl(pbuffer.location, none(Url), true, false) client.buffer.setCursorXY(pbuffer.cursorx, pbuffer.cursory) client.buffer.setFromXY(pbuffer.fromx, pbuffer.fromy) client.buffer.contenttype = pbuffer.contenttype @@ -193,6 +191,7 @@ proc toggleSource*(client: Client) = client.buffer.sourcepair.sourcepair = client.buffer client.buffer.source = client.buffer.prev.source client.buffer.streamclosed = true + client.buffer.location = client.buffer.sourcepair.location let prevtype = client.buffer.prev.contenttype if prevtype == "text/html": client.buffer.contenttype = "text/plain" diff --git a/src/html/dom.nim b/src/html/dom.nim index f6d2878b..0105b72f 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -22,6 +22,7 @@ type parentNode*: Node parentElement*: Element ownerDocument*: Document + uid*: int # Unique id Attr* = ref AttrObj AttrObj = object of NodeObj @@ -266,7 +267,7 @@ func newComment*(): Comment = new(result) result.nodeType = COMMENT_NODE -func newHtmlElement*(tagType: TagType): HTMLElement = +func newHtmlElement*(document: Document, tagType: TagType): HTMLElement = case tagType of TAG_INPUT: result = new(HTMLInputElement) @@ -300,12 +301,14 @@ func newHtmlElement*(tagType: TagType): HTMLElement = result.nodeType = ELEMENT_NODE result.tagType = tagType result.css = rootProperties() + result.uid = document.all_elements.len + document.all_elements.add(result) func newDocument*(): Document = new(result) - result.root = newHtmlElement(TAG_HTML) - result.head = newHtmlElement(TAG_HEAD) - result.body = newHtmlElement(TAG_BODY) + result.root = result.newHtmlElement(TAG_HTML) + result.head = result.newHtmlElement(TAG_HEAD) + result.body = result.newHtmlElement(TAG_BODY) result.nodeType = DOCUMENT_NODE func newAttr*(parent: Element, key, value: string): Attr = diff --git a/src/html/parser.nim b/src/html/parser.nim index 0d2ccd3e..7488534a 100644 --- a/src/html/parser.nim +++ b/src/html/parser.nim @@ -23,6 +23,7 @@ type elementNode: Element textNode: Text commentNode: Comment + document: Document func inputSize*(str: string): int = if str.len == 0: @@ -189,7 +190,6 @@ proc insertNode(parent, node: Node) = let element = (Element(node)) if element.ownerDocument != nil: - node.ownerDocument.all_elements.add(Element(node)) element.ownerDocument.type_elements[element.tagType].add(element) if element.id != "": if not (element.id in element.ownerDocument.id_elements): @@ -351,7 +351,7 @@ proc processDocumentTag(state: var HTMLParseState, tag: DOMParsedTag) = return if tag.open: - processDocumentStartElement(state, newHtmlElement(tag.tagid), tag) + processDocumentStartElement(state, state.document.newHtmlElement(tag.tagid), tag) else: processDocumentEndElement(state, tag) @@ -455,6 +455,7 @@ proc parseHtml*(inputStream: Stream): Document = insertNode(document.root, document.body) var state = HTMLParseState() + state.document = document state.elementNode = document.root var till_when = false diff --git a/src/io/buffer.nim b/src/io/buffer.nim index 48bd64d3..f219fe2c 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -34,6 +34,7 @@ type fromy*: int attrs*: TermAttributes document*: Document + viewport*: Viewport redraw*: bool reshape*: bool nostatus*: bool @@ -656,9 +657,6 @@ proc gotoAnchor*(buffer: Buffer) = return inc i -proc setLocation*(buffer: Buffer, location: Url) = - buffer.location = location - proc gotoLocation*(buffer: Buffer, s: string) = discard parseUrl(s, buffer.location.some, buffer.location, true) @@ -735,7 +733,9 @@ proc load*(buffer: Buffer) = proc render*(buffer: Buffer) = case buffer.contenttype of "text/html": - buffer.lines = renderDocument(buffer.document, buffer.attrs, buffer.userstyle) + if buffer.viewport == nil: + buffer.viewport = Viewport(term: buffer.attrs) + buffer.lines = renderDocument(buffer.document, buffer.attrs, buffer.userstyle, buffer.viewport) else: discard buffer.updateCursor() diff --git a/src/layout/box.nim b/src/layout/box.nim index 6502ec80..67cb9be9 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -5,6 +5,12 @@ import html/dom import io/term type + Viewport* = ref object + term*: TermAttributes + nodes*: seq[Node] + root*: BlockBox + map*: seq[CSSBox] + CSSBox* = ref object of RootObj t*: CSSDisplay children*: seq[CSSBox] @@ -12,10 +18,6 @@ type specified*: CSSSpecifiedValues node*: Node - Viewport* = ref object of CSSBox - term*: TermAttributes - nodes*: seq[Node] - InlineAtom* = ref object of RootObj relx*: int width*: int @@ -75,8 +77,11 @@ type text*: seq[string] ictx*: InlineContext newline*: bool + BlockBox* = ref object of CSSBox bctx*: BlockContext + InlineBlockBox* = ref object of BlockBox ictx*: InlineContext + ListItemBox* = ref object of BlockBox diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 04c2ff26..f45181dc 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -108,11 +108,12 @@ proc processWhitespace(state: var InlineState, c: char) = else: state.ictx.whitespace = true -proc renderText*(ictx: InlineContext, str: string, maxwidth: int, specified: CSSSpecifiedValues) = +proc renderText*(ictx: InlineContext, str: string, maxwidth: int, specified: CSSSpecifiedValues, nodes: seq[Node]) = var state: InlineState state.specified = specified state.ictx = ictx state.maxwidth = maxwidth + state.nodes = nodes state.newWord() #if str.strip().len > 0: @@ -237,13 +238,16 @@ proc alignInlineBlock(bctx: BlockContext, box: InlineBlockBox, parentcss: CSSSpe box.ictx.whitespace = false proc alignInline(bctx: BlockContext, box: InlineBox) = + if box.node != nil: + bctx.viewport.nodes.add(box.node) + let box = InlineBox(box) assert box.ictx != nil if box.newline: box.ictx.flushLine() for text in box.text: assert box.children.len == 0 - box.ictx.renderText(text, bctx.compwidth, box.specified) + box.ictx.renderText(text, bctx.compwidth, box.specified, bctx.viewport.nodes) for child in box.children: case child.t @@ -257,6 +261,8 @@ proc alignInline(bctx: BlockContext, box: InlineBox) = bctx.alignInlineBlock(child, box.specified) else: assert false, "child.t is " & $child.t + if box.node != nil: + discard bctx.viewport.nodes.pop() proc alignInlines(bctx: BlockContext, inlines: seq[CSSBox]) = let ictx = bctx.newInlineContext() @@ -311,12 +317,16 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox]) = flush_group() proc alignBlock(box: BlockBox) = + if box.node != nil: + box.bctx.viewport.nodes.add(box.node) if box.inlinelayout: # Box only contains inline boxes. box.bctx.alignInlines(box.children) else: box.bctx.alignBlocks(box.children) box.bctx.arrangeBlocks() + if box.node != nil: + discard box.bctx.viewport.nodes.pop() proc getBox(specified: CSSSpecifiedValues): CSSBox = case specified{"display"} @@ -358,10 +368,20 @@ proc getPseudoBox(specified: CSSSpecifiedValues): CSSBox = box.children.add(content) return box -proc generateBox(elem: Element, box = getBox(elem.css)): CSSBox = +proc generateBox(elem: Element, viewport: Viewport, first = false): CSSBox = + if viewport.map[elem.uid] != nil: + return viewport.map[elem.uid] + + let box = if not first: + getBox(elem.css) + else: + getBlockBox(elem.css) + if box == nil: return nil + box.node = elem + var ibox: InlineBox template add_ibox() = if ibox != nil: @@ -386,6 +406,7 @@ proc generateBox(elem: Element, box = getBox(elem.css)): CSSBox = let before = elem.pseudo[PSEUDO_BEFORE] if before != nil: let bbox = getPseudoBox(before) + bbox.node = elem if bbox != nil: add_box(bbox) @@ -398,7 +419,7 @@ proc generateBox(elem: Element, box = getBox(elem.css)): CSSBox = ibox = box.getTextBox() ibox.newline = true - let cbox = generateBox(elem) + let cbox = elem.generateBox(viewport) if cbox != nil: add_ibox() add_box(cbox) @@ -411,7 +432,7 @@ proc generateBox(elem: Element, box = getBox(elem.css)): CSSBox = not text.data.onlyWhitespace(): if ibox == nil: ibox = box.getTextBox() - ibox.text.add(Text(child).data) + ibox.text.add(text.data) else: discard add_ibox() @@ -421,19 +442,19 @@ proc generateBox(elem: Element, box = getBox(elem.css)): CSSBox = if abox != nil: add_box(abox) - return box + viewport.map[elem.uid] = box -proc generateBoxes(document: Document): BlockBox = - let box = document.root.generateBox(getBlockBox(document.root.css)) - assert box != nil - assert box.t == DISPLAY_BLOCK + return box - return BlockBox(box) +proc renderLayout*(viewport: var Viewport, document: Document) = + if viewport.root == nil or document.all_elements.len != viewport.map.len: + viewport.map = newSeq[CSSBox](document.all_elements.len) + viewport.root = BlockBox(document.root.generateBox(viewport, true)) + else: + for uid in 0..viewport.map.high: + if not document.all_elements[uid].rendered: + viewport.map[uid] = nil + viewport.root = BlockBox(document.root.generateBox(viewport)) -proc renderLayout*(document: Document, term: TermAttributes): BlockBox = - #eprint document.root - let viewport = Viewport(term: term) - let root = document.generateBoxes() - root.bctx = viewport.newBlockContext() - alignBlock(root) - return root + viewport.root.bctx = viewport.newBlockContext() + alignBlock(viewport.root) diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim index 4b74bde1..9d5e0dcd 100644 --- a/src/render/renderdocument.nim +++ b/src/render/renderdocument.nim @@ -152,13 +152,10 @@ proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockContext, x, y: int) = const css = staticRead"res/ua.css" let uastyle = css.parseStylesheet() -proc renderDocument*(document: Document, attrs: TermAttributes, userstyle: CSSStylesheet): FlexibleGrid = +proc renderDocument*(document: Document, attrs: TermAttributes, userstyle: CSSStylesheet, layout: var Viewport): FlexibleGrid = document.applyStylesheets(uastyle, userstyle) - let rootbox = document.renderLayout(attrs) - var stack: seq[BlockContext] - if rootbox.bctx == nil: #TODO - result.addLine() - return - result.renderBlockContext(rootbox.bctx, 0, 0) + layout.renderLayout(document) + result.setLen(0) + result.renderBlockContext(layout.root.bctx, 0, 0) if result.len == 0: result.addLine() diff --git a/src/types/mime.nim b/src/types/mime.nim index 4dfe87e9..5c48c896 100644 --- a/src/types/mime.nim +++ b/src/types/mime.nim @@ -6,6 +6,8 @@ const DefaultGuess = [ ("xhtml", "application/xhtml+xml"), ("xhtm", "application/xhtml+xml"), ("xht", "application/xhtml+xml"), + ("txt", "text/plain"), + ("", "text/plain") ].toTable() proc guessContentType*(path: string): string = @@ -13,7 +15,7 @@ proc guessContentType*(path: string): string = var n = 0 while i > 0: if path[i] == '/': - return "text/plain" + return DefaultGuess[""] if path[i] == '.': n = i break @@ -22,4 +24,4 @@ proc guessContentType*(path: string): string = let ext = path.substr(n + 1) if ext in DefaultGuess: return DefaultGuess[ext] - return "text/plain" + return DefaultGuess[""] diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim index cb236b53..3050d50e 100644 --- a/src/utils/twtstr.nim +++ b/src/utils/twtstr.nim @@ -217,6 +217,14 @@ func skipBlanks*(buf: string, at: int): int = while result < buf.len and buf[result].isWhitespace(): inc result +func until*(s: string, c: char): string = + var i = 0 + while i < s.len: + if s[i] == c: + break + result.add(s[i]) + inc i + func number_additive*(i: int, range: HSlice[int, int], symbols: openarray[(int, string)]): string = if i notin range: return $i |