LEFT | RIGHT |
1 // Copyright (C) 2009 Google Inc. | 1 // Copyright (C) 2009 Google Inc. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 17 matching lines...) Expand all Loading... |
28 import com.google.caja.plugin.CssRuleRewriter; | 28 import com.google.caja.plugin.CssRuleRewriter; |
29 | 29 |
30 import java.util.ArrayList; | 30 import java.util.ArrayList; |
31 import java.util.List; | 31 import java.util.List; |
32 | 32 |
33 import org.w3c.dom.Document; | 33 import org.w3c.dom.Document; |
34 import org.w3c.dom.DocumentFragment; | 34 import org.w3c.dom.DocumentFragment; |
35 import org.w3c.dom.Element; | 35 import org.w3c.dom.Element; |
36 import org.w3c.dom.Node; | 36 import org.w3c.dom.Node; |
37 | 37 |
| 38 /** |
| 39 * Attaches CSS to either the static HTML or the uncajoled JS as appropriate. |
| 40 * <p> |
| 41 * Depends on <code>emitCss___</code> as defined in DOMita. |
| 42 * |
| 43 * @author mikesamuel@gmail.com |
| 44 */ |
38 final class SafeCssMaker { | 45 final class SafeCssMaker { |
39 private final Node safeHtml; | 46 private final Node safeHtml; |
40 private final Block safeJs; | 47 private final Block safeJs; |
41 private final List<CssTree.StyleSheet> validatedStylesheets; | 48 private final List<CssTree.StyleSheet> validatedStylesheets; |
42 | 49 |
43 SafeCssMaker(Node safeHtml, Block safeJs, | 50 SafeCssMaker(Node safeHtml, Block safeJs, |
44 List<CssTree.StyleSheet> validatedStylesheets) { | 51 List<CssTree.StyleSheet> validatedStylesheets) { |
45 this.safeHtml = safeHtml; | 52 this.safeHtml = safeHtml; |
46 this.safeJs = safeJs; | 53 this.safeJs = safeJs; |
47 this.validatedStylesheets = validatedStylesheets; | 54 this.validatedStylesheets = validatedStylesheets; |
48 } | 55 } |
49 | 56 |
50 void make() { | 57 void make() { |
51 if (validatedStylesheets.isEmpty()) { return; } | 58 if (validatedStylesheets.isEmpty()) { return; } |
| 59 |
| 60 // Accumulates dynamic CSS that will be added to the JS. |
52 List<Expression> cssParts = new ArrayList<Expression>(); | 61 List<Expression> cssParts = new ArrayList<Expression>(); |
| 62 // Accumulate static CSS that can be embedded in the DOM. |
53 StringBuilder css = new StringBuilder(); | 63 StringBuilder css = new StringBuilder(); |
54 FilePosition staticPos = null, dynamicPos = null; | 64 FilePosition staticPos = null, dynamicPos = null; |
55 for (CssTree.StyleSheet ss : validatedStylesheets) { | 65 for (CssTree.StyleSheet ss : validatedStylesheets) { |
56 ArrayConstructor ac = CssRuleRewriter.cssToJs(ss); | 66 ArrayConstructor ac = CssRuleRewriter.cssToJs(ss); |
57 List<? extends Expression> children = ac.children(); | 67 List<? extends Expression> children = ac.children(); |
58 if (children.isEmpty()) { continue; } | 68 if (children.isEmpty()) { continue; } |
59 FilePosition acPos = ac.getFilePosition(); | 69 FilePosition acPos = ac.getFilePosition(); |
60 Expression child0 = children.get(0); | 70 Expression child0 = children.get(0); |
| 71 // The CssRuleRewriter gets to distinguish between static and dynamic. |
| 72 // If the output is a single string, then joining it on the idClass would |
| 73 // not add any information, so we can put it in the static HTML. |
61 if (children.size() == 1 && child0 instanceof StringLiteral) { | 74 if (children.size() == 1 && child0 instanceof StringLiteral) { |
62 css.append('\n').append(((StringLiteral) child0).getUnquotedValue()); | 75 css.append('\n').append(((StringLiteral) child0).getUnquotedValue()); |
63 staticPos = staticPos == null | 76 staticPos = staticPos == null |
64 ? acPos : FilePosition.span(staticPos, acPos); | 77 ? acPos : FilePosition.span(staticPos, acPos); |
65 } else { | 78 } else { |
66 // Don't just push all onto the list since that would create an | 79 // Don't just push all onto the list since that would create an |
67 // extra, spurious separator after they're joined. | 80 // extra, spurious separator after they're joined. |
68 // To avoid the spurious separator, we concatenate the last item | 81 // To avoid the spurious separator, we concatenate the last item |
69 // already on cssParts with child0. | 82 // already on cssParts with child0. |
70 int n = cssParts.size(); | 83 int n = cssParts.size(); |
71 if (n == 0) { | 84 if (n == 0) { |
72 cssParts.addAll(children); | 85 cssParts.addAll(children); |
73 } else { | 86 } else { |
74 cssParts.set( | 87 cssParts.set( |
75 n - 1, | 88 n - 1, |
76 QuasiUtil.concat( | 89 QuasiUtil.concat( |
77 cssParts.get(n - 1), | 90 cssParts.get(n - 1), |
78 StringLiteral.valueOf( | 91 StringLiteral.valueOf( |
79 FilePosition.startOf(child0.getFilePosition()), "\n"), | 92 FilePosition.startOf(child0.getFilePosition()), "\n"), |
80 child0)); | 93 child0)); |
81 cssParts.addAll(children.subList(1, children.size())); | 94 cssParts.addAll(children.subList(1, children.size())); |
82 } | 95 } |
83 dynamicPos = dynamicPos == null | 96 dynamicPos = dynamicPos == null |
84 ? acPos : FilePosition.span(dynamicPos, acPos); | 97 ? acPos : FilePosition.span(dynamicPos, acPos); |
85 } | 98 } |
86 } | 99 } |
87 | 100 |
| 101 // Emit any dynamic CSS. |
88 if (!cssParts.isEmpty()) { | 102 if (!cssParts.isEmpty()) { |
89 Statement firstChild = safeJs.children().isEmpty() | 103 Statement firstChild = safeJs.children().isEmpty() |
90 ? null : safeJs.children().get(0); | 104 ? null : safeJs.children().get(0); |
91 // The CSS rule | 105 // The CSS rule |
92 // p { color: purple } | 106 // p { color: purple } |
93 // is converted to the JavaScript | 107 // is converted to the JavaScript |
94 // IMPORTS___.emitCss___( | 108 // IMPORTS___.emitCss___( |
95 // ['.', ' p { color: purple }'] | 109 // ['.', ' p { color: purple }'] |
96 // .join(IMPORTS___.getIdClass___())); | 110 // .join(IMPORTS___.getIdClass___())); |
97 // | 111 // |
98 // If IMPORTS___.getIdClass() returns "g123___", then the resulting | 112 // If IMPORTS___.getIdClass() returns "g123___", then the resulting |
99 // .g123___ p { color: purple } | 113 // .g123___ p { color: purple } |
100 // will only make purple paragraphs that are under a node with class | 114 // will only make purple paragraphs that are under a node with class |
101 // g123__. | 115 // g123__. |
102 safeJs.insertBefore(new ExpressionStmt( | 116 safeJs.insertBefore(new ExpressionStmt( |
103 dynamicPos, | 117 dynamicPos, |
104 (Expression) QuasiBuilder.substV( | 118 (Expression) QuasiBuilder.substV( |
105 ReservedNames.IMPORTS | 119 ReservedNames.IMPORTS |
106 + ".emitCss___(@cssParts./*@synthetic*/join(" | 120 + ".emitCss___(@cssParts./*@synthetic*/join(" |
107 + ReservedNames.IMPORTS + ".getIdClass___()))", | 121 + ReservedNames.IMPORTS + ".getIdClass___()))", |
108 "cssParts", new ArrayConstructor(dynamicPos, cssParts))), | 122 "cssParts", new ArrayConstructor(dynamicPos, cssParts))), |
109 firstChild); | 123 firstChild); |
110 } | 124 } |
111 | 125 |
| 126 // Emit any static CSS. |
112 Node safeHtml = this.safeHtml; | 127 Node safeHtml = this.safeHtml; |
113 if (css.length() != 0) { | 128 if (css.length() != 0) { |
114 Document doc = safeHtml.getOwnerDocument(); | 129 Document doc = safeHtml.getOwnerDocument(); |
115 Element style = doc.createElement("style"); | 130 Element style = doc.createElement("style"); |
116 style.setAttribute("type", "text/css"); | 131 style.setAttribute("type", "text/css"); |
117 style.appendChild(doc.createTextNode(css.toString())); | 132 style.appendChild(doc.createTextNode(css.toString())); |
118 Nodes.setFilePositionFor(style, dynamicPos); | 133 Nodes.setFilePositionFor(style, dynamicPos); |
119 if (!(safeHtml instanceof DocumentFragment)) { | 134 if (!(safeHtml instanceof DocumentFragment)) { |
120 DocumentFragment f = doc.createDocumentFragment(); | 135 DocumentFragment f = doc.createDocumentFragment(); |
121 f.appendChild(safeHtml); | 136 f.appendChild(safeHtml); |
122 safeHtml = f; | 137 safeHtml = f; |
123 } | 138 } |
124 safeHtml.insertBefore(style, safeHtml.getFirstChild()); | 139 safeHtml.insertBefore(style, safeHtml.getFirstChild()); |
125 } | 140 } |
126 } | 141 } |
127 } | 142 } |
LEFT | RIGHT |