@@ -5,6 +5,7 @@ package play.twirl.api
55
66import play .twirl .api .utils .StringEscapeUtils
77import scala .collection .immutable
8+ import java .lang .{StringBuilder => jsStringBuilder }
89
910object MimeTypes {
1011 val TEXT = " text/plain"
@@ -36,28 +37,38 @@ class Html private[api] (elements: immutable.Seq[Html], text: String, escape: Bo
3637 * of Strings, if it doesn't, performance actually goes down (measured 10%), due to the fact that the JVM can't
3738 * optimise the invocation of buildString as well because there are two different possible implementations.
3839 */
39- override protected def buildString (builder : StringBuilder ): Unit = {
40- if (elements.nonEmpty ) {
40+ override protected def buildString (sb : jsStringBuilder ): Unit = {
41+ if (! elements.isEmpty ) {
4142 elements.foreach { e =>
42- e.buildString(builder )
43+ e.buildString(sb )
4344 }
4445 } else if (escape) {
4546 // Using our own algorithm here because commons lang escaping wasn't designed for protecting against XSS, and there
4647 // don't seem to be any other good generic escaping tools out there.
48+ val len = text.length
49+ var copyIdx = 0
4750 var i = 0
48- while (i < text.length ) {
51+ while (i < len ) {
4952 text.charAt(i) match {
50- case '<' => builder.append(" <" )
51- case '>' => builder.append(" >" )
52- case '"' => builder.append(" "" )
53- case '\' ' => builder.append(" '" )
54- case '&' => builder.append(" &" )
55- case c => builder += c
53+ case '<' | '>' | '"' | '\' ' | '&' => {
54+ sb.append(text, copyIdx, i)
55+ text.charAt(i) match {
56+ case '<' => sb.append(" <" )
57+ case '>' => sb.append(" >" )
58+ case '"' => sb.append(" "" )
59+ case '\' ' => sb.append(" '" )
60+ case '&' => sb.append(" &" )
61+ }
62+ copyIdx = i + 1
63+ }
64+ case _ => ()
5665 }
5766 i += 1
5867 }
68+ if (copyIdx == 0 ) sb.append(text)
69+ else sb.append(text, copyIdx, len)
5970 } else {
60- builder .append(text)
71+ sb .append(text)
6172 }
6273 }
6374
0 commit comments