html.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. // Package html implements renderer that outputs HTMLs.
  2. package html
  3. import (
  4. "bytes"
  5. "fmt"
  6. "strconv"
  7. "unicode/utf8"
  8. "github.com/yuin/goldmark/ast"
  9. "github.com/yuin/goldmark/renderer"
  10. "github.com/yuin/goldmark/util"
  11. )
  12. // A Config struct has configurations for the HTML based renderers.
  13. type Config struct {
  14. Writer Writer
  15. HardWraps bool
  16. EastAsianLineBreaks bool
  17. XHTML bool
  18. Unsafe bool
  19. }
  20. // NewConfig returns a new Config with defaults.
  21. func NewConfig() Config {
  22. return Config{
  23. Writer: DefaultWriter,
  24. HardWraps: false,
  25. EastAsianLineBreaks: false,
  26. XHTML: false,
  27. Unsafe: false,
  28. }
  29. }
  30. // SetOption implements renderer.NodeRenderer.SetOption.
  31. func (c *Config) SetOption(name renderer.OptionName, value interface{}) {
  32. switch name {
  33. case optHardWraps:
  34. c.HardWraps = value.(bool)
  35. case optEastAsianLineBreaks:
  36. c.EastAsianLineBreaks = value.(bool)
  37. case optXHTML:
  38. c.XHTML = value.(bool)
  39. case optUnsafe:
  40. c.Unsafe = value.(bool)
  41. case optTextWriter:
  42. c.Writer = value.(Writer)
  43. }
  44. }
  45. // An Option interface sets options for HTML based renderers.
  46. type Option interface {
  47. SetHTMLOption(*Config)
  48. }
  49. // TextWriter is an option name used in WithWriter.
  50. const optTextWriter renderer.OptionName = "Writer"
  51. type withWriter struct {
  52. value Writer
  53. }
  54. func (o *withWriter) SetConfig(c *renderer.Config) {
  55. c.Options[optTextWriter] = o.value
  56. }
  57. func (o *withWriter) SetHTMLOption(c *Config) {
  58. c.Writer = o.value
  59. }
  60. // WithWriter is a functional option that allow you to set the given writer to
  61. // the renderer.
  62. func WithWriter(writer Writer) interface {
  63. renderer.Option
  64. Option
  65. } {
  66. return &withWriter{writer}
  67. }
  68. // HardWraps is an option name used in WithHardWraps.
  69. const optHardWraps renderer.OptionName = "HardWraps"
  70. type withHardWraps struct {
  71. }
  72. func (o *withHardWraps) SetConfig(c *renderer.Config) {
  73. c.Options[optHardWraps] = true
  74. }
  75. func (o *withHardWraps) SetHTMLOption(c *Config) {
  76. c.HardWraps = true
  77. }
  78. // WithHardWraps is a functional option that indicates whether softline breaks
  79. // should be rendered as '<br>'.
  80. func WithHardWraps() interface {
  81. renderer.Option
  82. Option
  83. } {
  84. return &withHardWraps{}
  85. }
  86. // EastAsianLineBreaks is an option name used in WithEastAsianLineBreaks.
  87. const optEastAsianLineBreaks renderer.OptionName = "EastAsianLineBreaks"
  88. type withEastAsianLineBreaks struct {
  89. }
  90. func (o *withEastAsianLineBreaks) SetConfig(c *renderer.Config) {
  91. c.Options[optEastAsianLineBreaks] = true
  92. }
  93. func (o *withEastAsianLineBreaks) SetHTMLOption(c *Config) {
  94. c.EastAsianLineBreaks = true
  95. }
  96. // WithEastAsianLineBreaks is a functional option that indicates whether softline breaks
  97. // between east asian wide characters should be ignored.
  98. func WithEastAsianLineBreaks() interface {
  99. renderer.Option
  100. Option
  101. } {
  102. return &withEastAsianLineBreaks{}
  103. }
  104. // XHTML is an option name used in WithXHTML.
  105. const optXHTML renderer.OptionName = "XHTML"
  106. type withXHTML struct {
  107. }
  108. func (o *withXHTML) SetConfig(c *renderer.Config) {
  109. c.Options[optXHTML] = true
  110. }
  111. func (o *withXHTML) SetHTMLOption(c *Config) {
  112. c.XHTML = true
  113. }
  114. // WithXHTML is a functional option indicates that nodes should be rendered in
  115. // xhtml instead of HTML5.
  116. func WithXHTML() interface {
  117. Option
  118. renderer.Option
  119. } {
  120. return &withXHTML{}
  121. }
  122. // Unsafe is an option name used in WithUnsafe.
  123. const optUnsafe renderer.OptionName = "Unsafe"
  124. type withUnsafe struct {
  125. }
  126. func (o *withUnsafe) SetConfig(c *renderer.Config) {
  127. c.Options[optUnsafe] = true
  128. }
  129. func (o *withUnsafe) SetHTMLOption(c *Config) {
  130. c.Unsafe = true
  131. }
  132. // WithUnsafe is a functional option that renders dangerous contents
  133. // (raw htmls and potentially dangerous links) as it is.
  134. func WithUnsafe() interface {
  135. renderer.Option
  136. Option
  137. } {
  138. return &withUnsafe{}
  139. }
  140. // A Renderer struct is an implementation of renderer.NodeRenderer that renders
  141. // nodes as (X)HTML.
  142. type Renderer struct {
  143. Config
  144. }
  145. // NewRenderer returns a new Renderer with given options.
  146. func NewRenderer(opts ...Option) renderer.NodeRenderer {
  147. r := &Renderer{
  148. Config: NewConfig(),
  149. }
  150. for _, opt := range opts {
  151. opt.SetHTMLOption(&r.Config)
  152. }
  153. return r
  154. }
  155. // RegisterFuncs implements NodeRenderer.RegisterFuncs .
  156. func (r *Renderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
  157. // blocks
  158. reg.Register(ast.KindDocument, r.renderDocument)
  159. reg.Register(ast.KindHeading, r.renderHeading)
  160. reg.Register(ast.KindBlockquote, r.renderBlockquote)
  161. reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
  162. reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
  163. reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock)
  164. reg.Register(ast.KindList, r.renderList)
  165. reg.Register(ast.KindListItem, r.renderListItem)
  166. reg.Register(ast.KindParagraph, r.renderParagraph)
  167. reg.Register(ast.KindTextBlock, r.renderTextBlock)
  168. reg.Register(ast.KindThematicBreak, r.renderThematicBreak)
  169. // inlines
  170. reg.Register(ast.KindAutoLink, r.renderAutoLink)
  171. reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
  172. reg.Register(ast.KindEmphasis, r.renderEmphasis)
  173. reg.Register(ast.KindImage, r.renderImage)
  174. reg.Register(ast.KindLink, r.renderLink)
  175. reg.Register(ast.KindRawHTML, r.renderRawHTML)
  176. reg.Register(ast.KindText, r.renderText)
  177. reg.Register(ast.KindString, r.renderString)
  178. }
  179. func (r *Renderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
  180. l := n.Lines().Len()
  181. for i := 0; i < l; i++ {
  182. line := n.Lines().At(i)
  183. r.Writer.RawWrite(w, line.Value(source))
  184. }
  185. }
  186. // GlobalAttributeFilter defines attribute names which any elements can have.
  187. var GlobalAttributeFilter = util.NewBytesFilter(
  188. []byte("accesskey"),
  189. []byte("autocapitalize"),
  190. []byte("autofocus"),
  191. []byte("class"),
  192. []byte("contenteditable"),
  193. []byte("dir"),
  194. []byte("draggable"),
  195. []byte("enterkeyhint"),
  196. []byte("hidden"),
  197. []byte("id"),
  198. []byte("inert"),
  199. []byte("inputmode"),
  200. []byte("is"),
  201. []byte("itemid"),
  202. []byte("itemprop"),
  203. []byte("itemref"),
  204. []byte("itemscope"),
  205. []byte("itemtype"),
  206. []byte("lang"),
  207. []byte("part"),
  208. []byte("role"),
  209. []byte("slot"),
  210. []byte("spellcheck"),
  211. []byte("style"),
  212. []byte("tabindex"),
  213. []byte("title"),
  214. []byte("translate"),
  215. )
  216. func (r *Renderer) renderDocument(
  217. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  218. // nothing to do
  219. return ast.WalkContinue, nil
  220. }
  221. // HeadingAttributeFilter defines attribute names which heading elements can have.
  222. var HeadingAttributeFilter = GlobalAttributeFilter
  223. func (r *Renderer) renderHeading(
  224. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  225. n := node.(*ast.Heading)
  226. if entering {
  227. _, _ = w.WriteString("<h")
  228. _ = w.WriteByte("0123456"[n.Level])
  229. if n.Attributes() != nil {
  230. RenderAttributes(w, node, HeadingAttributeFilter)
  231. }
  232. _ = w.WriteByte('>')
  233. } else {
  234. _, _ = w.WriteString("</h")
  235. _ = w.WriteByte("0123456"[n.Level])
  236. _, _ = w.WriteString(">\n")
  237. }
  238. return ast.WalkContinue, nil
  239. }
  240. // BlockquoteAttributeFilter defines attribute names which blockquote elements can have.
  241. var BlockquoteAttributeFilter = GlobalAttributeFilter.Extend(
  242. []byte("cite"),
  243. )
  244. func (r *Renderer) renderBlockquote(
  245. w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  246. if entering {
  247. if n.Attributes() != nil {
  248. _, _ = w.WriteString("<blockquote")
  249. RenderAttributes(w, n, BlockquoteAttributeFilter)
  250. _ = w.WriteByte('>')
  251. } else {
  252. _, _ = w.WriteString("<blockquote>\n")
  253. }
  254. } else {
  255. _, _ = w.WriteString("</blockquote>\n")
  256. }
  257. return ast.WalkContinue, nil
  258. }
  259. func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  260. if entering {
  261. _, _ = w.WriteString("<pre><code>")
  262. r.writeLines(w, source, n)
  263. } else {
  264. _, _ = w.WriteString("</code></pre>\n")
  265. }
  266. return ast.WalkContinue, nil
  267. }
  268. func (r *Renderer) renderFencedCodeBlock(
  269. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  270. n := node.(*ast.FencedCodeBlock)
  271. if entering {
  272. _, _ = w.WriteString("<pre><code")
  273. language := n.Language(source)
  274. if language != nil {
  275. _, _ = w.WriteString(" class=\"language-")
  276. r.Writer.Write(w, language)
  277. _, _ = w.WriteString("\"")
  278. }
  279. _ = w.WriteByte('>')
  280. r.writeLines(w, source, n)
  281. } else {
  282. _, _ = w.WriteString("</code></pre>\n")
  283. }
  284. return ast.WalkContinue, nil
  285. }
  286. func (r *Renderer) renderHTMLBlock(
  287. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  288. n := node.(*ast.HTMLBlock)
  289. if entering {
  290. if r.Unsafe {
  291. l := n.Lines().Len()
  292. for i := 0; i < l; i++ {
  293. line := n.Lines().At(i)
  294. r.Writer.SecureWrite(w, line.Value(source))
  295. }
  296. } else {
  297. _, _ = w.WriteString("<!-- raw HTML omitted -->\n")
  298. }
  299. } else {
  300. if n.HasClosure() {
  301. if r.Unsafe {
  302. closure := n.ClosureLine
  303. r.Writer.SecureWrite(w, closure.Value(source))
  304. } else {
  305. _, _ = w.WriteString("<!-- raw HTML omitted -->\n")
  306. }
  307. }
  308. }
  309. return ast.WalkContinue, nil
  310. }
  311. // ListAttributeFilter defines attribute names which list elements can have.
  312. var ListAttributeFilter = GlobalAttributeFilter.Extend(
  313. []byte("start"),
  314. []byte("reversed"),
  315. []byte("type"),
  316. )
  317. func (r *Renderer) renderList(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  318. n := node.(*ast.List)
  319. tag := "ul"
  320. if n.IsOrdered() {
  321. tag = "ol"
  322. }
  323. if entering {
  324. _ = w.WriteByte('<')
  325. _, _ = w.WriteString(tag)
  326. if n.IsOrdered() && n.Start != 1 {
  327. fmt.Fprintf(w, " start=\"%d\"", n.Start)
  328. }
  329. if n.Attributes() != nil {
  330. RenderAttributes(w, n, ListAttributeFilter)
  331. }
  332. _, _ = w.WriteString(">\n")
  333. } else {
  334. _, _ = w.WriteString("</")
  335. _, _ = w.WriteString(tag)
  336. _, _ = w.WriteString(">\n")
  337. }
  338. return ast.WalkContinue, nil
  339. }
  340. // ListItemAttributeFilter defines attribute names which list item elements can have.
  341. var ListItemAttributeFilter = GlobalAttributeFilter.Extend(
  342. []byte("value"),
  343. )
  344. func (r *Renderer) renderListItem(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  345. if entering {
  346. if n.Attributes() != nil {
  347. _, _ = w.WriteString("<li")
  348. RenderAttributes(w, n, ListItemAttributeFilter)
  349. _ = w.WriteByte('>')
  350. } else {
  351. _, _ = w.WriteString("<li>")
  352. }
  353. fc := n.FirstChild()
  354. if fc != nil {
  355. if _, ok := fc.(*ast.TextBlock); !ok {
  356. _ = w.WriteByte('\n')
  357. }
  358. }
  359. } else {
  360. _, _ = w.WriteString("</li>\n")
  361. }
  362. return ast.WalkContinue, nil
  363. }
  364. // ParagraphAttributeFilter defines attribute names which paragraph elements can have.
  365. var ParagraphAttributeFilter = GlobalAttributeFilter
  366. func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  367. if entering {
  368. if n.Attributes() != nil {
  369. _, _ = w.WriteString("<p")
  370. RenderAttributes(w, n, ParagraphAttributeFilter)
  371. _ = w.WriteByte('>')
  372. } else {
  373. _, _ = w.WriteString("<p>")
  374. }
  375. } else {
  376. _, _ = w.WriteString("</p>\n")
  377. }
  378. return ast.WalkContinue, nil
  379. }
  380. func (r *Renderer) renderTextBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  381. if !entering {
  382. if n.NextSibling() != nil && n.FirstChild() != nil {
  383. _ = w.WriteByte('\n')
  384. }
  385. }
  386. return ast.WalkContinue, nil
  387. }
  388. // ThematicAttributeFilter defines attribute names which hr elements can have.
  389. var ThematicAttributeFilter = GlobalAttributeFilter.Extend(
  390. []byte("align"), // [Deprecated]
  391. []byte("color"), // [Not Standardized]
  392. []byte("noshade"), // [Deprecated]
  393. []byte("size"), // [Deprecated]
  394. []byte("width"), // [Deprecated]
  395. )
  396. func (r *Renderer) renderThematicBreak(
  397. w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  398. if !entering {
  399. return ast.WalkContinue, nil
  400. }
  401. _, _ = w.WriteString("<hr")
  402. if n.Attributes() != nil {
  403. RenderAttributes(w, n, ThematicAttributeFilter)
  404. }
  405. if r.XHTML {
  406. _, _ = w.WriteString(" />\n")
  407. } else {
  408. _, _ = w.WriteString(">\n")
  409. }
  410. return ast.WalkContinue, nil
  411. }
  412. // LinkAttributeFilter defines attribute names which link elements can have.
  413. var LinkAttributeFilter = GlobalAttributeFilter.Extend(
  414. []byte("download"),
  415. // []byte("href"),
  416. []byte("hreflang"),
  417. []byte("media"),
  418. []byte("ping"),
  419. []byte("referrerpolicy"),
  420. []byte("rel"),
  421. []byte("shape"),
  422. []byte("target"),
  423. )
  424. func (r *Renderer) renderAutoLink(
  425. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  426. n := node.(*ast.AutoLink)
  427. if !entering {
  428. return ast.WalkContinue, nil
  429. }
  430. _, _ = w.WriteString(`<a href="`)
  431. url := n.URL(source)
  432. label := n.Label(source)
  433. if n.AutoLinkType == ast.AutoLinkEmail && !bytes.HasPrefix(bytes.ToLower(url), []byte("mailto:")) {
  434. _, _ = w.WriteString("mailto:")
  435. }
  436. _, _ = w.Write(util.EscapeHTML(util.URLEscape(url, false)))
  437. if n.Attributes() != nil {
  438. _ = w.WriteByte('"')
  439. RenderAttributes(w, n, LinkAttributeFilter)
  440. _ = w.WriteByte('>')
  441. } else {
  442. _, _ = w.WriteString(`">`)
  443. }
  444. _, _ = w.Write(util.EscapeHTML(label))
  445. _, _ = w.WriteString(`</a>`)
  446. return ast.WalkContinue, nil
  447. }
  448. // CodeAttributeFilter defines attribute names which code elements can have.
  449. var CodeAttributeFilter = GlobalAttributeFilter
  450. func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
  451. if entering {
  452. if n.Attributes() != nil {
  453. _, _ = w.WriteString("<code")
  454. RenderAttributes(w, n, CodeAttributeFilter)
  455. _ = w.WriteByte('>')
  456. } else {
  457. _, _ = w.WriteString("<code>")
  458. }
  459. for c := n.FirstChild(); c != nil; c = c.NextSibling() {
  460. segment := c.(*ast.Text).Segment
  461. value := segment.Value(source)
  462. if bytes.HasSuffix(value, []byte("\n")) {
  463. r.Writer.RawWrite(w, value[:len(value)-1])
  464. r.Writer.RawWrite(w, []byte(" "))
  465. } else {
  466. r.Writer.RawWrite(w, value)
  467. }
  468. }
  469. return ast.WalkSkipChildren, nil
  470. }
  471. _, _ = w.WriteString("</code>")
  472. return ast.WalkContinue, nil
  473. }
  474. // EmphasisAttributeFilter defines attribute names which emphasis elements can have.
  475. var EmphasisAttributeFilter = GlobalAttributeFilter
  476. func (r *Renderer) renderEmphasis(
  477. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  478. n := node.(*ast.Emphasis)
  479. tag := "em"
  480. if n.Level == 2 {
  481. tag = "strong"
  482. }
  483. if entering {
  484. _ = w.WriteByte('<')
  485. _, _ = w.WriteString(tag)
  486. if n.Attributes() != nil {
  487. RenderAttributes(w, n, EmphasisAttributeFilter)
  488. }
  489. _ = w.WriteByte('>')
  490. } else {
  491. _, _ = w.WriteString("</")
  492. _, _ = w.WriteString(tag)
  493. _ = w.WriteByte('>')
  494. }
  495. return ast.WalkContinue, nil
  496. }
  497. func (r *Renderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  498. n := node.(*ast.Link)
  499. if entering {
  500. _, _ = w.WriteString("<a href=\"")
  501. if r.Unsafe || !IsDangerousURL(n.Destination) {
  502. _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
  503. }
  504. _ = w.WriteByte('"')
  505. if n.Title != nil {
  506. _, _ = w.WriteString(` title="`)
  507. r.Writer.Write(w, n.Title)
  508. _ = w.WriteByte('"')
  509. }
  510. if n.Attributes() != nil {
  511. RenderAttributes(w, n, LinkAttributeFilter)
  512. }
  513. _ = w.WriteByte('>')
  514. } else {
  515. _, _ = w.WriteString("</a>")
  516. }
  517. return ast.WalkContinue, nil
  518. }
  519. // ImageAttributeFilter defines attribute names which image elements can have.
  520. var ImageAttributeFilter = GlobalAttributeFilter.Extend(
  521. []byte("align"),
  522. []byte("border"),
  523. []byte("crossorigin"),
  524. []byte("decoding"),
  525. []byte("height"),
  526. []byte("importance"),
  527. []byte("intrinsicsize"),
  528. []byte("ismap"),
  529. []byte("loading"),
  530. []byte("referrerpolicy"),
  531. []byte("sizes"),
  532. []byte("srcset"),
  533. []byte("usemap"),
  534. []byte("width"),
  535. )
  536. func (r *Renderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  537. if !entering {
  538. return ast.WalkContinue, nil
  539. }
  540. n := node.(*ast.Image)
  541. _, _ = w.WriteString("<img src=\"")
  542. if r.Unsafe || !IsDangerousURL(n.Destination) {
  543. _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
  544. }
  545. _, _ = w.WriteString(`" alt="`)
  546. _, _ = w.Write(nodeToHTMLText(n, source))
  547. _ = w.WriteByte('"')
  548. if n.Title != nil {
  549. _, _ = w.WriteString(` title="`)
  550. r.Writer.Write(w, n.Title)
  551. _ = w.WriteByte('"')
  552. }
  553. if n.Attributes() != nil {
  554. RenderAttributes(w, n, ImageAttributeFilter)
  555. }
  556. if r.XHTML {
  557. _, _ = w.WriteString(" />")
  558. } else {
  559. _, _ = w.WriteString(">")
  560. }
  561. return ast.WalkSkipChildren, nil
  562. }
  563. func (r *Renderer) renderRawHTML(
  564. w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  565. if !entering {
  566. return ast.WalkSkipChildren, nil
  567. }
  568. if r.Unsafe {
  569. n := node.(*ast.RawHTML)
  570. l := n.Segments.Len()
  571. for i := 0; i < l; i++ {
  572. segment := n.Segments.At(i)
  573. _, _ = w.Write(segment.Value(source))
  574. }
  575. return ast.WalkSkipChildren, nil
  576. }
  577. _, _ = w.WriteString("<!-- raw HTML omitted -->")
  578. return ast.WalkSkipChildren, nil
  579. }
  580. func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  581. if !entering {
  582. return ast.WalkContinue, nil
  583. }
  584. n := node.(*ast.Text)
  585. segment := n.Segment
  586. if n.IsRaw() {
  587. r.Writer.RawWrite(w, segment.Value(source))
  588. } else {
  589. value := segment.Value(source)
  590. r.Writer.Write(w, value)
  591. if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) {
  592. if r.XHTML {
  593. _, _ = w.WriteString("<br />\n")
  594. } else {
  595. _, _ = w.WriteString("<br>\n")
  596. }
  597. } else if n.SoftLineBreak() {
  598. if r.EastAsianLineBreaks && len(value) != 0 {
  599. sibling := node.NextSibling()
  600. if sibling != nil && sibling.Kind() == ast.KindText {
  601. if siblingText := sibling.(*ast.Text).Text(source); len(siblingText) != 0 {
  602. thisLastRune := util.ToRune(value, len(value)-1)
  603. siblingFirstRune, _ := utf8.DecodeRune(siblingText)
  604. if !(util.IsEastAsianWideRune(thisLastRune) &&
  605. util.IsEastAsianWideRune(siblingFirstRune)) {
  606. _ = w.WriteByte('\n')
  607. }
  608. }
  609. }
  610. } else {
  611. _ = w.WriteByte('\n')
  612. }
  613. }
  614. }
  615. return ast.WalkContinue, nil
  616. }
  617. func (r *Renderer) renderString(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
  618. if !entering {
  619. return ast.WalkContinue, nil
  620. }
  621. n := node.(*ast.String)
  622. if n.IsCode() {
  623. _, _ = w.Write(n.Value)
  624. } else {
  625. if n.IsRaw() {
  626. r.Writer.RawWrite(w, n.Value)
  627. } else {
  628. r.Writer.Write(w, n.Value)
  629. }
  630. }
  631. return ast.WalkContinue, nil
  632. }
  633. var dataPrefix = []byte("data-")
  634. // RenderAttributes renders given node's attributes.
  635. // You can specify attribute names to render by the filter.
  636. // If filter is nil, RenderAttributes renders all attributes.
  637. func RenderAttributes(w util.BufWriter, node ast.Node, filter util.BytesFilter) {
  638. for _, attr := range node.Attributes() {
  639. if filter != nil && !filter.Contains(attr.Name) {
  640. if !bytes.HasPrefix(attr.Name, dataPrefix) {
  641. continue
  642. }
  643. }
  644. _, _ = w.WriteString(" ")
  645. _, _ = w.Write(attr.Name)
  646. _, _ = w.WriteString(`="`)
  647. // TODO: convert numeric values to strings
  648. _, _ = w.Write(util.EscapeHTML(attr.Value.([]byte)))
  649. _ = w.WriteByte('"')
  650. }
  651. }
  652. // A Writer interface writes textual contents to a writer.
  653. type Writer interface {
  654. // Write writes the given source to writer with resolving references and unescaping
  655. // backslash escaped characters.
  656. Write(writer util.BufWriter, source []byte)
  657. // RawWrite writes the given source to writer without resolving references and
  658. // unescaping backslash escaped characters.
  659. RawWrite(writer util.BufWriter, source []byte)
  660. // SecureWrite writes the given source to writer with replacing insecure characters.
  661. SecureWrite(writer util.BufWriter, source []byte)
  662. }
  663. var replacementCharacter = []byte("\ufffd")
  664. // A WriterConfig struct has configurations for the HTML based writers.
  665. type WriterConfig struct {
  666. // EscapedSpace is an option that indicates that a '\' escaped half-space(0x20) should not be rendered.
  667. EscapedSpace bool
  668. }
  669. // A WriterOption interface sets options for HTML based writers.
  670. type WriterOption func(*WriterConfig)
  671. // WithEscapedSpace is a WriterOption indicates that a '\' escaped half-space(0x20) should not be rendered.
  672. func WithEscapedSpace() WriterOption {
  673. return func(c *WriterConfig) {
  674. c.EscapedSpace = true
  675. }
  676. }
  677. type defaultWriter struct {
  678. WriterConfig
  679. }
  680. // NewWriter returns a new Writer.
  681. func NewWriter(opts ...WriterOption) Writer {
  682. w := &defaultWriter{}
  683. for _, opt := range opts {
  684. opt(&w.WriterConfig)
  685. }
  686. return w
  687. }
  688. func escapeRune(writer util.BufWriter, r rune) {
  689. if r < 256 {
  690. v := util.EscapeHTMLByte(byte(r))
  691. if v != nil {
  692. _, _ = writer.Write(v)
  693. return
  694. }
  695. }
  696. _, _ = writer.WriteRune(util.ToValidRune(r))
  697. }
  698. func (d *defaultWriter) SecureWrite(writer util.BufWriter, source []byte) {
  699. n := 0
  700. l := len(source)
  701. for i := 0; i < l; i++ {
  702. if source[i] == '\u0000' {
  703. _, _ = writer.Write(source[i-n : i])
  704. n = 0
  705. _, _ = writer.Write(replacementCharacter)
  706. continue
  707. }
  708. n++
  709. }
  710. if n != 0 {
  711. _, _ = writer.Write(source[l-n:])
  712. }
  713. }
  714. func (d *defaultWriter) RawWrite(writer util.BufWriter, source []byte) {
  715. n := 0
  716. l := len(source)
  717. for i := 0; i < l; i++ {
  718. v := util.EscapeHTMLByte(source[i])
  719. if v != nil {
  720. _, _ = writer.Write(source[i-n : i])
  721. n = 0
  722. _, _ = writer.Write(v)
  723. continue
  724. }
  725. n++
  726. }
  727. if n != 0 {
  728. _, _ = writer.Write(source[l-n:])
  729. }
  730. }
  731. func (d *defaultWriter) Write(writer util.BufWriter, source []byte) {
  732. escaped := false
  733. var ok bool
  734. limit := len(source)
  735. n := 0
  736. for i := 0; i < limit; i++ {
  737. c := source[i]
  738. if escaped {
  739. if util.IsPunct(c) {
  740. d.RawWrite(writer, source[n:i-1])
  741. n = i
  742. escaped = false
  743. continue
  744. }
  745. if d.EscapedSpace && c == ' ' {
  746. d.RawWrite(writer, source[n:i-1])
  747. n = i + 1
  748. escaped = false
  749. continue
  750. }
  751. }
  752. if c == '\x00' {
  753. d.RawWrite(writer, source[n:i])
  754. d.RawWrite(writer, replacementCharacter)
  755. n = i + 1
  756. escaped = false
  757. continue
  758. }
  759. if c == '&' {
  760. pos := i
  761. next := i + 1
  762. if next < limit && source[next] == '#' {
  763. nnext := next + 1
  764. if nnext < limit {
  765. nc := source[nnext]
  766. // code point like #x22;
  767. if nnext < limit && nc == 'x' || nc == 'X' {
  768. start := nnext + 1
  769. i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsHexDecimal)
  770. if ok && i < limit && source[i] == ';' && i-start < 7 {
  771. v, _ := strconv.ParseUint(util.BytesToReadOnlyString(source[start:i]), 16, 32)
  772. d.RawWrite(writer, source[n:pos])
  773. n = i + 1
  774. escapeRune(writer, rune(v))
  775. continue
  776. }
  777. // code point like #1234;
  778. } else if nc >= '0' && nc <= '9' {
  779. start := nnext
  780. i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsNumeric)
  781. if ok && i < limit && i-start < 8 && source[i] == ';' {
  782. v, _ := strconv.ParseUint(util.BytesToReadOnlyString(source[start:i]), 10, 32)
  783. d.RawWrite(writer, source[n:pos])
  784. n = i + 1
  785. escapeRune(writer, rune(v))
  786. continue
  787. }
  788. }
  789. }
  790. } else {
  791. start := next
  792. i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsAlphaNumeric)
  793. // entity reference
  794. if ok && i < limit && source[i] == ';' {
  795. name := util.BytesToReadOnlyString(source[start:i])
  796. entity, ok := util.LookUpHTML5EntityByName(name)
  797. if ok {
  798. d.RawWrite(writer, source[n:pos])
  799. n = i + 1
  800. d.RawWrite(writer, entity.Characters)
  801. continue
  802. }
  803. }
  804. }
  805. i = next - 1
  806. }
  807. if c == '\\' {
  808. escaped = true
  809. continue
  810. }
  811. escaped = false
  812. }
  813. d.RawWrite(writer, source[n:])
  814. }
  815. // DefaultWriter is a default instance of the Writer.
  816. var DefaultWriter = NewWriter()
  817. var bDataImage = []byte("data:image/")
  818. var bPng = []byte("png;")
  819. var bGif = []byte("gif;")
  820. var bJpeg = []byte("jpeg;")
  821. var bWebp = []byte("webp;")
  822. var bSvg = []byte("svg+xml;")
  823. var bJs = []byte("javascript:")
  824. var bVb = []byte("vbscript:")
  825. var bFile = []byte("file:")
  826. var bData = []byte("data:")
  827. func hasPrefix(s, prefix []byte) bool {
  828. return len(s) >= len(prefix) && bytes.Equal(bytes.ToLower(s[0:len(prefix)]), bytes.ToLower(prefix))
  829. }
  830. // IsDangerousURL returns true if the given url seems a potentially dangerous url,
  831. // otherwise false.
  832. func IsDangerousURL(url []byte) bool {
  833. if hasPrefix(url, bDataImage) && len(url) >= 11 {
  834. v := url[11:]
  835. if hasPrefix(v, bPng) || hasPrefix(v, bGif) ||
  836. hasPrefix(v, bJpeg) || hasPrefix(v, bWebp) ||
  837. hasPrefix(v, bSvg) {
  838. return false
  839. }
  840. return true
  841. }
  842. return hasPrefix(url, bJs) || hasPrefix(url, bVb) ||
  843. hasPrefix(url, bFile) || hasPrefix(url, bData)
  844. }
  845. func nodeToHTMLText(n ast.Node, source []byte) []byte {
  846. var buf bytes.Buffer
  847. for c := n.FirstChild(); c != nil; c = c.NextSibling() {
  848. if s, ok := c.(*ast.String); ok && s.IsCode() {
  849. buf.Write(s.Text(source))
  850. } else if !c.HasChildren() {
  851. buf.Write(util.EscapeHTML(c.Text(source)))
  852. } else {
  853. buf.Write(nodeToHTMLText(c, source))
  854. }
  855. }
  856. return buf.Bytes()
  857. }