Chromium Code Reviews| Index: utils/template/codegen.dart |
| diff --git a/utils/template/codegen.dart b/utils/template/codegen.dart |
| index 648ec3ba87dcc44fbcb7fcf053c296586063b4fd..a77262a6a5c46f8cffaf0c1a183a3e774ff25635 100644 |
| --- a/utils/template/codegen.dart |
| +++ b/utils/template/codegen.dart |
| @@ -58,7 +58,7 @@ class CGBlock { |
| } |
| } |
| - CGStatement get last() => _stmts.last(); |
| + CGStatement get last() => _stmts.length > 0 ? _stmts.last() : null; |
| /** |
| * Returns mixed list of elements marked with the var attribute. If the |
| @@ -168,7 +168,7 @@ class CGStatement { |
| bool get isClosed() => _closed; |
| void close() { |
| - if (_elem is TemplateElement) { |
| + if (_elem is TemplateElement && _elem.scoped) { |
| add("</${_elem.tagName}>"); |
| } |
| _closed = true; |
| @@ -272,11 +272,6 @@ class Codegen { |
| buff.add("// Generated Dart class from HTML template.\n"); |
| buff.add("// DO NOT EDIT.\n\n"); |
| - buff.add("String safeHTML(String html) {\n"); |
| - buff.add(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\n"); |
| - buff.add(" return html;\n"); |
| - buff.add("}\n\n"); |
| - |
| String addStylesheetFuncName = "add_${filename}_templatesStyles"; |
| for (final template in templates) { |
| @@ -386,14 +381,19 @@ class Codegen { |
| world.error("Error at ${content}"); |
| } |
| - var root = content.html.children[0]; |
| + var root = content.html.children.length > 0 ? content.html.children[0] : |
| + content.html; |
| bool firstTime = true; |
| for (var child in root.children) { |
| if (child is TemplateText) { |
| if (!firstTime) { |
| ecg.closeStatement(); |
| } |
| - CGStatement stmt = ecg.pushStatement(child, "_fragment"); |
| + |
| + String textNodeValue = child.value.trim(); |
| + if (textNodeValue.length > 0) { |
| + CGStatement stmt = ecg.pushStatement(child, "_fragment"); |
| + } |
| } |
| ecg.emitConstructHtml(child, "", "_fragment"); |
| firstTime = false; |
| @@ -433,6 +433,8 @@ class Codegen { |
| buff.add(" }\n\n"); // End constructor |
| + buff.add(emitGetters(content.getters)); |
| + |
| buff.add(" Element get root() => _fragment;\n"); |
| // Emit all CSS class selectors: |
| @@ -467,10 +469,33 @@ class Codegen { |
| buff.add("\"\";\n"); |
| } |
| + buff.add(" String safeHTML(String html) {\n"); |
|
Jacob
2012/04/05 18:06:01
use multiline strings to make this cleaner
|
| + buff.add(" // TODO(terry): Escaping for XSS vulnerabilities TBD.\n"); |
| + buff.add(" return html;\n"); |
| + buff.add(" }\n"); |
| + |
| buff.add("}\n"); // End class |
| return buff.toString(); |
| } |
| + |
| + // TODO(terry): Need to generate function to inject any TemplateExpressions |
| + // to call SafeHTML wrapper. |
| + static String emitGetters(List<TemplateGetter> getters) { |
| + StringBuffer buff = new StringBuffer(); |
| + for (final TemplateGetter getter in getters) { |
| + buff.add(' String ${getter.getterSignatureAsString()} {\n'); |
| + buff.add(' return \'\'\''); |
| + var docFrag = getter.docFrag.children[0]; |
| + for (final child in docFrag.children) { |
| + buff.add(child.toString().trim()); |
| + } |
| + buff.add('\'\'\';\n'); |
| + buff.add(' }\n\n'); |
| + } |
| + |
| + return buff.toString(); |
| + } |
| } |
| class ElemCG { |
| @@ -600,7 +625,10 @@ Nested #each or #with must have a localName; |
| return lastBlock.push(elem, parentName, true); |
| } |
| - bool get isClosedStatement() => lastBlock.last.isClosed; |
| + bool get isClosedStatement() { |
| + return (lastBlock != null && lastBlock.last != null) ? |
| + lastBlock.last.isClosed : false; |
| + } |
| void closeStatement() { |
| if (lastBlock != null && lastBlock.last != null && |
| @@ -615,6 +643,12 @@ Nested #each or #with must have a localName; |
| } |
| } |
| + String get lastParentName() { |
| + if (lastBlock != null && lastBlock.last != null) { |
| + return lastBlock.last.parentName; |
| + } |
| + } |
| + |
| CGBlock get lastBlock() => _cgBlocks.length > 0 ? _cgBlocks.last() : null; |
| void add(String str) { |
| @@ -654,16 +688,14 @@ Nested #each or #with must have a localName; |
| String prevParent = lastVariableName; |
| for (var childElem in elem.children) { |
| if (childElem is TemplateElement) { |
| + closeStatement(); |
| if (childElem.hasVar) { |
| - closeStatement(); |
| emitConstructHtml(childElem, scopeName, prevParent, |
| childElem.varName); |
| - closeStatement(); |
| } else { |
| - closeStatement(); |
| emitConstructHtml(childElem, scopeName, prevParent); |
| - closeStatement(); |
| } |
| + closeStatement(); |
| } else { |
| emitElement(childElem, scopeName, parentVarOrIdx); |
| } |
| @@ -672,7 +704,30 @@ Nested #each or #with must have a localName; |
| // Close this tag. |
| closeStatement(); |
| } else if (elem is TemplateText) { |
| - add("${elem.value}"); |
| + String outputValue = elem.value.trim(); |
| + if (outputValue.length > 0) { |
| + bool emitTextNode = false; |
| + if (isClosedStatement) { |
| + String prevParent = lastParentName; |
| + CGStatement stmt = pushStatement(elem, prevParent); |
| + emitTextNode = true; |
| + } |
| + |
| + // TODO(terry): Need to interpolate following: |
| + // {sp} → space |
| + // {nil} → empty string |
| + // {\r} → carriage return |
| + // {\n} → new line (line feed) |
| + // {\t} → tab |
| + // {lb} → left brace |
| + // {rb} → right brace |
| + |
| + add("${outputValue}"); // remove leading/trailing whitespace. |
| + |
| + if (emitTextNode) { |
| + closeStatement(); |
| + } |
| + } |
| } else if (elem is TemplateExpression) { |
| emitExpressions(elem, scopeName); |
| } else if (elem is TemplateEachCommand) { |