mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-05 11:24:08 +00:00
Rstgen/xml tree fixes (#7823)
* Don't prefix lang names with "lang" in rstgen. * Implements ability to render xmltree w/o \n. Fixes <> for `data-*` attrs. * Various rstgen fixes. * Fixes security vulnerabilities due to not escaping some code. * Adds <video> support and improve `.. image:: ` directive. * Adds comment as requested.
This commit is contained in:
committed by
Andreas Rumpf
parent
17b8bb8b47
commit
85b7d8fcc4
@@ -208,6 +208,7 @@ proc nextSplitPoint*(s: string, start: int): int =
|
||||
dec(result) # last valid index
|
||||
|
||||
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
|
||||
## Escapes the HTML.
|
||||
result = ""
|
||||
if splitAfter >= 0:
|
||||
var partLen = 0
|
||||
@@ -769,43 +770,45 @@ proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
|
||||
result.add(tmp)
|
||||
|
||||
proc renderImage(d: PDoc, n: PRstNode, result: var string) =
|
||||
template valid(s): bool =
|
||||
s.len > 0 and allCharsInSet(s, {'.','/',':','%','_','\\','\128'..'\xFF'} +
|
||||
Digits + Letters + WhiteSpace)
|
||||
let
|
||||
arg = getArgument(n)
|
||||
isObject = arg.toLowerAscii().endsWith(".svg")
|
||||
var
|
||||
options = ""
|
||||
content = ""
|
||||
var s = getFieldValue(n, "scale")
|
||||
if s.valid: dispA(d.target, options, if isObject: "" else: " scale=\"$1\"",
|
||||
" scale=$1", [strip(s)])
|
||||
|
||||
s = getFieldValue(n, "height")
|
||||
if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
|
||||
var s = esc(d.target, getFieldValue(n, "scale").strip())
|
||||
if s.len > 0:
|
||||
dispA(d.target, options, " scale=\"$1\"", " scale=$1", [s])
|
||||
|
||||
s = getFieldValue(n, "width")
|
||||
if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
|
||||
s = esc(d.target, getFieldValue(n, "height").strip())
|
||||
if s.len > 0:
|
||||
dispA(d.target, options, " height=\"$1\"", " height=$1", [s])
|
||||
|
||||
s = getFieldValue(n, "alt")
|
||||
if s.valid:
|
||||
# <object> displays its content if it cannot render the image
|
||||
if isObject: dispA(d.target, content, "$1", "", [strip(s)])
|
||||
else: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
|
||||
s = esc(d.target, getFieldValue(n, "width").strip())
|
||||
if s.len > 0:
|
||||
dispA(d.target, options, " width=\"$1\"", " width=$1", [s])
|
||||
|
||||
s = getFieldValue(n, "align")
|
||||
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
|
||||
s = esc(d.target, getFieldValue(n, "alt").strip())
|
||||
if s.len > 0:
|
||||
dispA(d.target, options, " alt=\"$1\"", "", [s])
|
||||
|
||||
s = esc(d.target, getFieldValue(n, "align").strip())
|
||||
if s.len > 0:
|
||||
dispA(d.target, options, " align=\"$1\"", "", [s])
|
||||
|
||||
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
|
||||
|
||||
if arg.valid:
|
||||
let htmlOut = if isObject:
|
||||
"<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"
|
||||
else:
|
||||
"<img src=\"$1\"$2 />"
|
||||
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
|
||||
[arg, options])
|
||||
var htmlOut = ""
|
||||
if arg.endsWith(".mp4") or arg.endsWith(".ogg") or
|
||||
arg.endsWith(".webm"):
|
||||
htmlOut = """
|
||||
<video src="$1"$2 autoPlay='true' loop='true' muted='true'>
|
||||
Sorry, your browser doesn't support embedded videos
|
||||
</video>
|
||||
"""
|
||||
else:
|
||||
htmlOut = "<img src=\"$1\"$2/>"
|
||||
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
|
||||
[esc(d.target, arg), options])
|
||||
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
|
||||
|
||||
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
|
||||
@@ -881,7 +884,8 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
|
||||
inc d.listingCounter
|
||||
let id = $d.listingCounter
|
||||
if not params.numberLines:
|
||||
result = (d.config.getOrDefault"doc.listing_start" % [id, $params.lang],
|
||||
result = (d.config.getOrDefault"doc.listing_start" %
|
||||
[id, sourceLanguageToStr[params.lang]],
|
||||
d.config.getOrDefault"doc.listing_end" % id)
|
||||
return
|
||||
|
||||
@@ -894,7 +898,8 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
|
||||
line.inc
|
||||
codeLines.dec
|
||||
result.beginTable.add("</pre></td><td>" & (
|
||||
d.config.getOrDefault"doc.listing_start" % [id, $params.lang]))
|
||||
d.config.getOrDefault"doc.listing_start" %
|
||||
[id, sourceLanguageToStr[params.lang]]))
|
||||
result.endTable = (d.config.getOrDefault"doc.listing_end" % id) &
|
||||
"</td></tr></tbody></table>" & (
|
||||
d.config.getOrDefault"doc.listing_button" % id)
|
||||
@@ -944,7 +949,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
|
||||
var tmp = ""
|
||||
renderRstToOut(d, n.sons[2], tmp)
|
||||
var arg = strip(getArgument(n))
|
||||
var arg = esc(d.target, strip(getArgument(n)))
|
||||
if arg == "":
|
||||
dispA(d.target, result, "<div>$1</div>", "$1", [tmp])
|
||||
else:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## A simple XML tree. More efficient and simpler than the DOM.
|
||||
|
||||
import macros, strtabs
|
||||
import macros, strtabs, strutils
|
||||
|
||||
type
|
||||
XmlNode* = ref XmlNodeObj ## an XML tree consists of ``XmlNode``'s.
|
||||
@@ -217,8 +217,9 @@ proc escape*(s: string): string =
|
||||
result = newStringOfCap(s.len)
|
||||
addEscaped(result, s)
|
||||
|
||||
proc addIndent(result: var string, indent: int) =
|
||||
result.add("\n")
|
||||
proc addIndent(result: var string, indent: int, addNewLines: bool) =
|
||||
if addNewLines:
|
||||
result.add("\n")
|
||||
for i in 1..indent: result.add(' ')
|
||||
|
||||
proc noWhitespace(n: XmlNode): bool =
|
||||
@@ -227,7 +228,8 @@ proc noWhitespace(n: XmlNode): bool =
|
||||
for i in 0..n.len-1:
|
||||
if n[i].kind in {xnText, xnEntity}: return true
|
||||
|
||||
proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2) =
|
||||
proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
|
||||
addNewLines=true) =
|
||||
## adds the textual representation of `n` to `result`.
|
||||
|
||||
proc addEscapedAttr(result: var string, s: string) =
|
||||
@@ -260,14 +262,15 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2) =
|
||||
# for mixed leaves, we cannot output whitespace for readability,
|
||||
# because this would be wrong. For example: ``a<b>b</b>`` is
|
||||
# different from ``a <b>b</b>``.
|
||||
for i in 0..n.len-1: result.add(n[i], indent+indWidth, indWidth)
|
||||
for i in 0..n.len-1:
|
||||
result.add(n[i], indent+indWidth, indWidth, addNewLines)
|
||||
else:
|
||||
for i in 0..n.len-1:
|
||||
result.addIndent(indent+indWidth)
|
||||
result.add(n[i], indent+indWidth, indWidth)
|
||||
result.addIndent(indent)
|
||||
result.addIndent(indent+indWidth, addNewLines)
|
||||
result.add(n[i], indent+indWidth, indWidth, addNewLines)
|
||||
result.addIndent(indent, addNewLines)
|
||||
else:
|
||||
result.add(n[0], indent+indWidth, indWidth)
|
||||
result.add(n[0], indent+indWidth, indWidth, addNewLines)
|
||||
result.add("</")
|
||||
result.add(n.fTag)
|
||||
result.add(">")
|
||||
@@ -316,7 +319,10 @@ proc xmlConstructor(a: NimNode): NimNode {.compileTime.} =
|
||||
var elements = newNimNode(nnkBracket, a)
|
||||
for i in 1..a.len-1:
|
||||
if a[i].kind == nnkExprEqExpr:
|
||||
attrs.add(toStrLit(a[i][0]))
|
||||
# In order to support attributes like `data-lang` we have to
|
||||
# replace whitespace because `toStrLit` gives `data - lang`.
|
||||
let attrName = toStrLit(a[i][0]).strVal.replace(" ", "")
|
||||
attrs.add(newStrLitNode(attrName))
|
||||
attrs.add(a[i][1])
|
||||
#echo repr(attrs)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user