Adding convenience methods to SafeStylesUtils and SafeStylesBuilder for style properties supported by Style. For most properies, the name is defined in SafeStylesUtils, and the value comes from an enum in Style or a primitive type, so we know that the combination of the name and value produces a string that satisfies the constraints of SafeStyles.
For properties that take open ended strings, such as "background-image" and "color", the method is prefixed with "Trusted" and JavaDoc'd, putting the burden on the user to ensure that the value is a trusted value. For example, SafeStylesUtils#forTrustedColor() and SafeStylesBuilder#appendTrustedColor(). It would be very difficult to guarentee that a string is safe. Unlike HTML where you can escape the brackets, style attribute XSS vulnerabilities are subtle, especially in older versions of IE where the "expression()" CSS value can execute arbitrary javascript. So, instead of trying to provide a sanitizing method, its up to the user to ensure the string is safe. Also added the methods fromTrustedNameAndValue(), which are escape hatches for create a SafeStyles from any trusted name and value pair.
The generic methods in SafeStyles are prefixes with "from", as in fromTrustedNameAndValue/fromTrustedString. The property specific methods are prefixed with "for", as in forPaddingTop/forZIndex. There isn't some underlying reason for this, it just sounded better to me.
Review at http://gwt-code-reviews.appspot.com/1454808
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10356 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/config/gwt23_24userApi.conf b/tools/api-checker/config/gwt23_24userApi.conf
index 7f371e7..3771fca 100644
--- a/tools/api-checker/config/gwt23_24userApi.conf
+++ b/tools/api-checker/config/gwt23_24userApi.conf
@@ -100,6 +100,7 @@
:user/src/com/google/gwt/rpc/client/impl/ClientWriterFactory.java\
:user/src/com/google/gwt/rpc/client/impl/EscapeUtil.java\
:user/src/com/google/gwt/rpc/client/impl/RpcCallbackAdapter.java\
+:user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java\
:user/src/com/google/gwt/safehtml/shared/SafeHtmlHostedModeUtils.java\
:user/src/com/google/gwt/safehtml/shared/SafeUriHostedModeUtils.java\
:user/src/com/google/gwt/user/client/rpc/core/**\
diff --git a/user/src/com/google/gwt/safecss/SafeCss.gwt.xml b/user/src/com/google/gwt/safecss/SafeCss.gwt.xml
index 4060626..5cb1f4e 100644
--- a/user/src/com/google/gwt/safecss/SafeCss.gwt.xml
+++ b/user/src/com/google/gwt/safecss/SafeCss.gwt.xml
@@ -17,5 +17,23 @@
<!-- SafeCss - facilities for avoiding XSS attacks -->
<!-- -->
<module>
+ <inherits name="com.google.gwt.core.Core"/>
+ <inherits name="com.google.gwt.user.UserAgent"/>
+
+ <replace-with class="com.google.gwt.safecss.shared.SafeStylesUtils.ImplIE6To8">
+ <when-type-is class="com.google.gwt.safecss.shared.SafeStylesUtils.Impl" />
+ <any>
+ <when-property-is name="user.agent" value="ie6" />
+ <when-property-is name="user.agent" value="ie8" />
+ </any>
+ </replace-with>
+
+ <!-- Override IE9 fallback. -->
+ <replace-with class="com.google.gwt.safecss.shared.SafeStylesUtils.Impl">
+ <when-type-is class="com.google.gwt.safecss.shared.SafeStylesUtils.Impl" />
+ <when-property-is name="user.agent" value="ie9" />
+ </replace-with>
+
<source path="shared"/>
+ <super-source path="super"/>
</module>
diff --git a/user/src/com/google/gwt/safecss/shared/SafeStylesBuilder.java b/user/src/com/google/gwt/safecss/shared/SafeStylesBuilder.java
index a1d8c02..12cf97b 100644
--- a/user/src/com/google/gwt/safecss/shared/SafeStylesBuilder.java
+++ b/user/src/com/google/gwt/safecss/shared/SafeStylesBuilder.java
@@ -15,6 +15,22 @@
*/
package com.google.gwt.safecss.shared;
+import com.google.gwt.dom.client.Style.BorderStyle;
+import com.google.gwt.dom.client.Style.Cursor;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.dom.client.Style.Float;
+import com.google.gwt.dom.client.Style.FontStyle;
+import com.google.gwt.dom.client.Style.FontWeight;
+import com.google.gwt.dom.client.Style.ListStyleType;
+import com.google.gwt.dom.client.Style.Overflow;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.TableLayout;
+import com.google.gwt.dom.client.Style.TextDecoration;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.dom.client.Style.VerticalAlign;
+import com.google.gwt.dom.client.Style.Visibility;
+import com.google.gwt.safehtml.shared.SafeUri;
+
/**
* A builder that facilitates the building up of XSS-safe CSS attribute strings
* from {@link SafeStyles}. It is used essentially like a {@link StringBuilder},
@@ -81,11 +97,13 @@
* In addition, the empty string is safe for use in a CSS attribute.
*
* <p>
- * The following example values do <em>not</em> comply with this type's contract:
+ * The following example values do <em>not</em> comply with this type's
+ * contract:
* <ul>
* <li><code>background: red</code> (missing a trailing semi-colon)</li>
* <li><code>background:</code> (missing a value and a trailing semi-colon)</li>
- * <li><code>1em</code> (missing an attribute name, which provides context for the value)</li>
+ * <li><code>1em</code> (missing an attribute name, which provides context for
+ * the value)</li>
* </ul>
*
* @param styles the input String
@@ -98,6 +116,239 @@
}
/**
+ * Append the background-image CSS property.
+ *
+ * @param uri the URI of the background image
+ * @see #trustedBackgroundImage(String)
+ */
+ public SafeStylesBuilder backgroundImage(SafeUri uri) {
+ return append(SafeStylesUtils.forBackgroundImage(uri));
+ }
+
+ /**
+ * Append the border-style CSS property.
+ */
+ public SafeStylesBuilder borderStyle(BorderStyle value) {
+ return append(SafeStylesUtils.forBorderStyle(value));
+ }
+
+ /**
+ * Append the border-width css property.
+ */
+ public SafeStylesBuilder borderWidth(double value, Unit unit) {
+ return append(SafeStylesUtils.forBorderWidth(value, unit));
+ }
+
+ /**
+ * Append the bottom css property.
+ */
+ public SafeStylesBuilder bottom(double value, Unit unit) {
+ return append(SafeStylesUtils.forBottom(value, unit));
+ }
+
+ /**
+ * Append the cursor CSS property.
+ */
+ public SafeStylesBuilder cursor(Cursor value) {
+ return append(SafeStylesUtils.forCursor(value));
+ }
+
+ /**
+ * Append the display CSS property.
+ */
+ public SafeStylesBuilder display(Display value) {
+ return append(SafeStylesUtils.forDisplay(value));
+ }
+
+ /**
+ * Append the float css property.
+ *
+ * <p>
+ * Note: This method has the suffix "prop" to avoid Java compilation errors.
+ * The term "float" is a reserved word in Java representing the primitive
+ * float.
+ * </p>
+ */
+ public SafeStylesBuilder floatprop(Float value) {
+ return append(SafeStylesUtils.forFloat(value));
+ }
+
+ /**
+ * Append the font-size css property.
+ */
+ public SafeStylesBuilder fontSize(double value, Unit unit) {
+ return append(SafeStylesUtils.forFontSize(value, unit));
+ }
+
+ /**
+ * Append the font-style CSS property.
+ */
+ public SafeStylesBuilder fontStyle(FontStyle value) {
+ return append(SafeStylesUtils.forFontStyle(value));
+ }
+
+ /**
+ * Append the font-weight CSS property.
+ */
+ public SafeStylesBuilder fontWeight(FontWeight value) {
+ return append(SafeStylesUtils.forFontWeight(value));
+ }
+
+ /**
+ * Append the height css property.
+ */
+ public SafeStylesBuilder height(double value, Unit unit) {
+ return append(SafeStylesUtils.forHeight(value, unit));
+ }
+
+ /**
+ * Append the left css property.
+ */
+ public SafeStylesBuilder left(double value, Unit unit) {
+ return append(SafeStylesUtils.forLeft(value, unit));
+ }
+
+ /**
+ * Append the list-style-type CSS property.
+ */
+ public SafeStylesBuilder listStyleType(ListStyleType value) {
+ return append(SafeStylesUtils.forListStyleType(value));
+ }
+
+ /**
+ * Append the margin css property.
+ */
+ public SafeStylesBuilder margin(double value, Unit unit) {
+ return append(SafeStylesUtils.forMargin(value, unit));
+ }
+
+ /**
+ * Append the margin-bottom css property.
+ */
+ public SafeStylesBuilder marginBottom(double value, Unit unit) {
+ return append(SafeStylesUtils.forMarginBottom(value, unit));
+ }
+
+ /**
+ * Append the margin-left css property.
+ */
+ public SafeStylesBuilder marginLeft(double value, Unit unit) {
+ return append(SafeStylesUtils.forMarginLeft(value, unit));
+ }
+
+ /**
+ * Append the margin-right css property.
+ */
+ public SafeStylesBuilder marginRight(double value, Unit unit) {
+ return append(SafeStylesUtils.forMarginRight(value, unit));
+ }
+
+ /**
+ * Append the margin-top css property.
+ */
+ public SafeStylesBuilder marginTop(double value, Unit unit) {
+ return append(SafeStylesUtils.forMarginTop(value, unit));
+ }
+
+ /**
+ * Append the opacity css property.
+ */
+ public SafeStylesBuilder opacity(double value) {
+ return append(SafeStylesUtils.forOpacity(value));
+ }
+
+ /**
+ * Append the overflow CSS property.
+ */
+ public SafeStylesBuilder overflow(Overflow value) {
+ return append(SafeStylesUtils.forOverflow(value));
+ }
+
+ /**
+ * Append the overflow-x CSS property.
+ */
+ public SafeStylesBuilder overflowX(Overflow value) {
+ return append(SafeStylesUtils.forOverflowX(value));
+ }
+
+ /**
+ * Append the overflow-y CSS property.
+ */
+ public SafeStylesBuilder overflowY(Overflow value) {
+ return append(SafeStylesUtils.forOverflowY(value));
+ }
+
+ /**
+ * Append the padding css property.
+ */
+ public SafeStylesBuilder padding(double value, Unit unit) {
+ return append(SafeStylesUtils.forPadding(value, unit));
+ }
+
+ /**
+ * Append the padding-bottom css property.
+ */
+ public SafeStylesBuilder paddingBottom(double value, Unit unit) {
+ return append(SafeStylesUtils.forPaddingBottom(value, unit));
+ }
+
+ /**
+ * Append the padding-left css property.
+ */
+ public SafeStylesBuilder paddingLeft(double value, Unit unit) {
+ return append(SafeStylesUtils.forPaddingLeft(value, unit));
+ }
+
+ /**
+ * Append the padding-right css property.
+ */
+ public SafeStylesBuilder paddingRight(double value, Unit unit) {
+ return append(SafeStylesUtils.forPaddingRight(value, unit));
+ }
+
+ /**
+ * Append the padding-top css property.
+ */
+ public SafeStylesBuilder paddingTop(double value, Unit unit) {
+ return append(SafeStylesUtils.forPaddingTop(value, unit));
+ }
+
+ /**
+ * Append the position CSS property.
+ */
+ public SafeStylesBuilder position(Position value) {
+ return append(SafeStylesUtils.forPosition(value));
+ }
+
+ /**
+ * Append the right css property.
+ */
+ public SafeStylesBuilder right(double value, Unit unit) {
+ return append(SafeStylesUtils.forRight(value, unit));
+ }
+
+ /**
+ * Append the table-layout CSS property.
+ */
+ public SafeStylesBuilder tableLayout(TableLayout value) {
+ return append(SafeStylesUtils.forTableLayout(value));
+ }
+
+ /**
+ * Append the text-decoration CSS property.
+ */
+ public SafeStylesBuilder textDecoration(TextDecoration value) {
+ return append(SafeStylesUtils.forTextDecoration(value));
+ }
+
+ /**
+ * Append the top css property.
+ */
+ public SafeStylesBuilder top(double value, Unit unit) {
+ return append(SafeStylesUtils.forTop(value, unit));
+ }
+
+ /**
* Returns the safe CSS properties accumulated in the builder as a
* {@link SafeStyles}.
*
@@ -106,4 +357,192 @@
public SafeStyles toSafeStyles() {
return new SafeStylesString(sb.toString());
}
+
+ /**
+ * <p>
+ * Append the trusted background color, i.e., without escaping the value. No
+ * checks are performed. The calling code should be carefully reviewed to
+ * ensure the argument will satisfy the {@link SafeStyles} contract when they
+ * are composed into the form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public SafeStylesBuilder trustedBackgroundColor(String value) {
+ return append(SafeStylesUtils.forTrustedBackgroundColor(value));
+ }
+
+ /**
+ * <p>
+ * Append the trusted background image, i.e., without escaping the value. No
+ * checks are performed. The calling code should be carefully reviewed to
+ * ensure the argument will satisfy the {@link SafeStyles} contract when they
+ * are composed into the form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ * @see #backgroundImage(SafeUri)
+ */
+ public SafeStylesBuilder trustedBackgroundImage(String value) {
+ return append(SafeStylesUtils.forTrustedBackgroundImage(value));
+ }
+
+ /**
+ * <p>
+ * Append the trusted border color, i.e., without escaping the value. No
+ * checks are performed. The calling code should be carefully reviewed to
+ * ensure the argument will satisfy the {@link SafeStyles} contract when they
+ * are composed into the form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public SafeStylesBuilder trustedBorderColor(String value) {
+ return append(SafeStylesUtils.forTrustedBorderColor(value));
+ }
+
+ /**
+ * <p>
+ * Append the trusted font color, i.e., without escaping the value. No checks
+ * are performed. The calling code should be carefully reviewed to ensure the
+ * argument will satisfy the {@link SafeStyles} contract when they are
+ * composed into the form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public SafeStylesBuilder trustedColor(String value) {
+ return append(SafeStylesUtils.forTrustedColor(value));
+ }
+
+ /**
+ * <p>
+ * Append a {@link SafeStyles} constructed from a trusted name and a trusted
+ * value, i.e., without escaping the name and value. No checks are performed.
+ * The calling code should be carefully reviewed to ensure the argument will
+ * satisfy the {@link SafeStyles} contract when they are composed into the
+ * form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * <p>
+ * The name should be in hyphenated format, not camelCase format.
+ * </p>
+ *
+ * @param name the property name
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public SafeStylesBuilder trustedNameAndValue(String name, double value, Unit unit) {
+ return append(SafeStylesUtils.fromTrustedNameAndValue(name, value, unit));
+ }
+
+ /**
+ * <p>
+ * Append a {@link SafeStyles} constructed from a trusted name and a trusted
+ * value, i.e., without escaping the name and value. No checks are performed.
+ * The calling code should be carefully reviewed to ensure the argument will
+ * satisfy the {@link SafeStyles} contract when they are composed into the
+ * form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * <p>
+ * The name should be in hyphenated format, not camelCase format.
+ * </p>
+ *
+ * @param name the property name
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public SafeStylesBuilder trustedNameAndValue(String name, String value) {
+ return append(SafeStylesUtils.fromTrustedNameAndValue(name, value));
+ }
+
+ /**
+ * Append the vertical-align CSS property.
+ */
+ public SafeStylesBuilder verticalAlign(double value, Unit unit) {
+ return append(SafeStylesUtils.forVerticalAlign(value, unit));
+ }
+
+ /**
+ * Append the vertical-align CSS property.
+ */
+ public SafeStylesBuilder verticalAlign(VerticalAlign value) {
+ return append(SafeStylesUtils.forVerticalAlign(value));
+ }
+
+ /**
+ * Append the visibility CSS property.
+ */
+ public SafeStylesBuilder visibility(Visibility value) {
+ return append(SafeStylesUtils.forVisibility(value));
+ }
+
+ /**
+ * Append the width css property.
+ */
+ public SafeStylesBuilder width(double value, Unit unit) {
+ return append(SafeStylesUtils.forWidth(value, unit));
+ }
+
+ /**
+ * Append the z-index css property.
+ */
+ public SafeStylesBuilder zIndex(int value) {
+ return append(SafeStylesUtils.forZIndex(value));
+ }
}
diff --git a/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java b/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
new file mode 100644
index 0000000..105532c
--- /dev/null
+++ b/user/src/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
+
+import java.util.HashMap;
+import java.util.Stack;
+
+/**
+ * SafeStyles utilities whose implementation differs between Development and
+ * Production Mode.
+ *
+ * <p>
+ * This class has a super-source peer that provides the Production Mode
+ * implementation.
+ *
+ * <p>
+ * Do not use this class - it is used for implementation only, and its methods
+ * may change in the future.
+ */
+public class SafeStylesHostedModeUtils {
+
+ /**
+ * Name of system property that if set, enables checks in server-side code
+ * (even if assertions are disabled).
+ */
+ public static final String FORCE_CHECK_VALID_STYLES =
+ "com.google.gwt.safecss.ForceCheckValidStyles";
+
+ private static boolean forceCheck;
+
+ static {
+ setForceCheckValidStyleFromProperty();
+ }
+
+ /**
+ * Check if the specified style property name is valid.
+ *
+ * <p>
+ * NOTE: This method does <em>NOT</em> guarantee the safety of a style name.
+ * It looks for common errors, but does not check for every possible error. It
+ * is intended to help validate a string that the user has already asserted is
+ * safe.
+ * </p>
+ *
+ * @param name the name to check
+ * @return null if valid, an error string if not
+ * @see <a
+ * href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS
+ * 2.1 identifiers</a>
+ */
+ // VisibleForTesting.
+ public static String isValidStyleName(String name) {
+ // Empty name.
+ if (name == null || name.isEmpty()) {
+ return "Style property names cannot be null or empty";
+ }
+
+ // Starts with a digit, or a hyphen followed by a digit.
+ char firstReal = name.charAt(0);
+ if (firstReal == '-' && name.length() > 0) {
+ firstReal = name.charAt(1);
+ }
+ if (firstReal >= '0' && firstReal <= '9') {
+ return "Style property names cannot start with a digit or a hyphen followed by a digit: "
+ + name;
+ }
+
+ // Starts with double hyphen.
+ if (name.startsWith("--")) {
+ return "Style property names cannot start with a double hyphen: " + name;
+ }
+
+ /*
+ * Technically an escaped colon/semi-colon (eg. "property\;name") is valid,
+ * but in practice nobody uses it and its safer to exclude it.
+ */
+ if (name.indexOf(';') >= 0) {
+ return "Style property names cannot contain a semi-colon: " + name;
+ } else if (name.indexOf(':') >= 0) {
+ return "Style property names cannot contain a colon: " + name;
+ }
+
+ return null;
+ }
+
+ /**
+ * Check if the specified style property value is valid.
+ *
+ * <p>
+ * NOTE: This method does <em>NOT</em> guarantee the safety of a style value.
+ * It looks for common errors, but does not check for every possible error. It
+ * is intended to help validate a string that the user has already asserted is
+ * safe.
+ * </p>
+ *
+ * @param value the value to check
+ * @return null if valid, an error string if not
+ * @see <a href="http://www.w3.org/TR/CSS21/syndata.html#declaration">CSS 2.1
+ * declarations and properties</a>
+ */
+ // VisibleForTesting.
+ public static String isValidStyleValue(String value) {
+ // Empty value.
+ if (value == null || value.length() == 0) {
+ return "Style property values cannot be null or empty";
+ }
+
+ /*
+ * A value can contain any token, but parenthesis, brackets, braces, and
+ * single/double quotes must be paired. Semi-colons are only allowed in
+ * strings, such as in a url.
+ */
+
+ // Create a map of pairable 'open' characters to 'close' characters.
+ final HashMap<Character, Character> pairs = new HashMap<Character, Character>();
+ pairs.put('(', ')');
+ pairs.put('[', ']');
+ pairs.put('{', '}');
+
+ // Iterate over the characters in the value.
+ final Stack<Character> pairsStack = new Stack<Character>();
+ final Stack<Integer> pairsPos = new Stack<Integer>();
+ Character inQuote = null; // The current quote character.
+ int inQuotePos = -1;
+ boolean inUrl = false;
+ boolean ignoreNext = false;
+ for (int i = 0; i < value.length(); i++) {
+ // This character was escaped.
+ if (ignoreNext) {
+ ignoreNext = false;
+ continue;
+ }
+
+ char ch = value.charAt(i);
+ if (ch == '\\') {
+ // Ignore the character immediately after an escape character.
+ ignoreNext = true;
+ continue;
+ }
+
+ if (inUrl) {
+ // Check for closing parenthesis.
+ if (ch == ')') {
+ // Close the URL.
+ inUrl = false;
+ } else if (ch == '(') {
+ // Parenthesis must be escaped within a url.
+ return "Unescaped parentheses within a url at index " + i + ": " + value;
+ }
+
+ /*
+ * Almost all tokens are valid within a URL.
+ *
+ *
+ * TODO(jlabanca): Check for unescaped quotes and whitespace in URLs.
+ * Quotes and whitespace (other than the optional ones that wrap the
+ * URL) should be escaped.
+ */
+ continue;
+ }
+
+ if (inQuote != null) {
+ // Check for a matching end quote.
+ if (ch == inQuote) {
+ inQuote = null;
+ }
+
+ // Else - still in quote, all tokens valid.
+ continue;
+ }
+
+ if (ch == '"' || ch == '\'') {
+ // Found an open quote.
+ inQuote = ch;
+ inQuotePos = i;
+ } else if ((ch == 'u' || ch == 'U') && value.length() >= i + 3
+ && value.substring(i, i + 4).equalsIgnoreCase("url(")) {
+ // Starting a URL.
+ inUrl = true;
+ i = i + 3; // Advance to the URL.
+ } else if (pairs.containsKey(ch)) {
+ // Opened a pairable.
+ pairsStack.push(ch);
+ pairsPos.push(i);
+ } else if (pairs.values().contains(ch)) {
+ // Closed a pairable.
+ if (pairsStack.isEmpty() || pairs.get(pairsStack.pop()) != ch) {
+ // Unmatched close token.
+ return "Style property value contains unpaired '" + ch + "' at index " + i + ": " + value;
+ }
+ pairsPos.pop();
+ } else if (ch == ';') {
+ // Contains an unescaped semi-colon.
+ return "Style property values cannot contain a semi-colon (except within quotes): " + value;
+ } else if (ch == ':') {
+ // Contains an unescaped colon.
+ return "Style property values cannot contain a colon (except within quotes): " + value;
+ }
+ }
+
+ // Unmatched open quote.
+ if (inQuote != null) {
+ return "Style property value contains unpaired open quote at index " + inQuotePos + ": "
+ + value;
+ }
+
+ // Unterminated url.
+ if (inUrl) {
+ return "Style property value contains an unterminated url: " + value;
+ }
+
+ // Unmatched open pairable.
+ if (!pairsStack.isEmpty()) {
+ char openToken = pairsStack.pop();
+ int index = pairsPos.pop();
+ return "Style property value contains unpaired '" + openToken + "' at index " + index + ": "
+ + value;
+ }
+
+ // Ends in an escape character,
+ if (ignoreNext) {
+ return "Style property values cannot end in an escape character: " + value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if the provided string is a valid style property name.
+ *
+ * @param name the style name
+ * @see <a
+ * href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS
+ * 2.1 identifiers</a>
+ */
+ public static void maybeCheckValidStyleName(String name) {
+ if (GWT.isClient() || forceCheck) {
+ String errorText = isValidStyleName(name);
+ Preconditions.checkArgument(errorText == null, errorText);
+ } else {
+ assert isValidStyleName(name) == null : isValidStyleName(name);
+ }
+ }
+
+ /**
+ * Checks if the provided string is a valid style property value.
+ *
+ * @param value the style value
+ * @see <a href="http://www.w3.org/TR/CSS21/syndata.html#declaration">CSS 2.1
+ * declarations and properties</a>
+ */
+ public static void maybeCheckValidStyleValue(String value) {
+ if (GWT.isClient() || forceCheck) {
+ String errorText = isValidStyleValue(value);
+ Preconditions.checkArgument(errorText == null, errorText);
+ } else {
+ assert isValidStyleValue(value) == null : isValidStyleValue(value);
+ }
+ }
+
+ /**
+ * Sets a global flag that controls whether or not
+ * {@link #maybeCheckValidStyleName(String)} and
+ * {@link #maybeCheckValidStyleValue(String)} should perform their checks in a
+ * server-side environment.
+ *
+ * @param check if true, perform server-side checks.
+ */
+ public static void setForceCheckValidStyle(boolean check) {
+ forceCheck = check;
+ }
+
+ /**
+ * Sets a global flag that controls whether or not
+ * {@link #maybeCheckValidStyleName(String)} and
+ * {@link #maybeCheckValidStyleValue(String)} should perform their checks in a
+ * server-side environment from the value of the
+ * {@value #FORCE_CHECK_VALID_STYLES} property.
+ */
+ static void setForceCheckValidStyleFromProperty() {
+ forceCheck = System.getProperty(FORCE_CHECK_VALID_STYLES) != null;
+ }
+}
diff --git a/user/src/com/google/gwt/safecss/shared/SafeStylesUtils.java b/user/src/com/google/gwt/safecss/shared/SafeStylesUtils.java
index 34de5d6..8c402ca 100644
--- a/user/src/com/google/gwt/safecss/shared/SafeStylesUtils.java
+++ b/user/src/com/google/gwt/safecss/shared/SafeStylesUtils.java
@@ -15,14 +15,498 @@
*/
package com.google.gwt.safecss.shared;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.BorderStyle;
+import com.google.gwt.dom.client.Style.Cursor;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.dom.client.Style.Float;
+import com.google.gwt.dom.client.Style.FontStyle;
+import com.google.gwt.dom.client.Style.FontWeight;
+import com.google.gwt.dom.client.Style.ListStyleType;
+import com.google.gwt.dom.client.Style.Overflow;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.TableLayout;
+import com.google.gwt.dom.client.Style.TextDecoration;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.dom.client.Style.VerticalAlign;
+import com.google.gwt.dom.client.Style.Visibility;
+import com.google.gwt.safehtml.shared.SafeUri;
+
/**
* Utility class containing static methods for creating {@link SafeStyles}.
*/
public final class SafeStylesUtils {
- /*
- * TODO(jlabanca): add context specific utility methods to create SafeStyles
- * (ex. #forHeight(double height, Unit unit).
+
+ /**
+ * Standard implementation of this class.
*/
+ static class Impl {
+ public SafeStyles forOpacity(double value) {
+ return new SafeStylesString("opacity: " + value + ";");
+ }
+ }
+
+ /**
+ * Server implementation of this class.
+ *
+ * <p>
+ * The server doesn't necessarily know the user agent of the client, so we
+ * combine the results of all other implementations.
+ * </p>
+ */
+ static class ImplServer extends Impl {
+
+ private ImplIE6To8 implIE = new ImplIE6To8();
+
+ @Override
+ public SafeStyles forOpacity(double value) {
+ SafeStylesBuilder sb = new SafeStylesBuilder();
+ sb.append(super.forOpacity(value));
+ sb.append(implIE.forOpacity(value));
+ return sb.toSafeStyles();
+ }
+ }
+
+ /**
+ * IE6-IE8 implementation of this class.
+ */
+ static class ImplIE6To8 extends Impl {
+ @Override
+ public SafeStyles forOpacity(double value) {
+ // IE6-IE8 uses an alpha filter instead of opacity.
+ return new SafeStylesString("filter: alpha(opacity=" + (value * 100) + ");");
+ }
+ }
+
+ private static Impl impl;
+
+ /**
+ * Sets the background-image CSS property.
+ *
+ * @param uri the URI of the background image
+ * @return a {@link SafeStyles} instance
+ * @see #forTrustedBackgroundImage(String)
+ */
+ public static SafeStyles forBackgroundImage(SafeUri uri) {
+ return fromTrustedNameAndValue("background-image", "url(\"" + uri.asString() + "\")");
+ }
+
+ /**
+ * Sets the border-style CSS property.
+ */
+ public static SafeStyles forBorderStyle(BorderStyle value) {
+ return fromTrustedNameAndValue("border-style", value.getCssName());
+ }
+
+ /**
+ * Set the border-width css property.
+ */
+ public static SafeStyles forBorderWidth(double value, Unit unit) {
+ return fromTrustedNameAndValue("border-width", value, unit);
+ }
+
+ /**
+ * Set the bottom css property.
+ */
+ public static SafeStyles forBottom(double value, Unit unit) {
+ return fromTrustedNameAndValue("bottom", value, unit);
+ }
+
+ /**
+ * Sets the cursor CSS property.
+ */
+ public static SafeStyles forCursor(Cursor value) {
+ return fromTrustedNameAndValue("cursor", value.getCssName());
+ }
+
+ /**
+ * Sets the display CSS property.
+ */
+ public static SafeStyles forDisplay(Display value) {
+ return fromTrustedNameAndValue("display", value.getCssName());
+ }
+
+ /**
+ * Set the float css property.
+ */
+ public static SafeStyles forFloat(Float value) {
+ return fromTrustedNameAndValue("float", value.getCssName());
+ }
+
+ /**
+ * Set the font-size css property.
+ */
+ public static SafeStyles forFontSize(double value, Unit unit) {
+ return fromTrustedNameAndValue("font-size", value, unit);
+ }
+
+ /**
+ * Sets the font-style CSS property.
+ */
+ public static SafeStyles forFontStyle(FontStyle value) {
+ return fromTrustedNameAndValue("font-style", value.getCssName());
+ }
+
+ /**
+ * Sets the font-weight CSS property.
+ */
+ public static SafeStyles forFontWeight(FontWeight value) {
+ return fromTrustedNameAndValue("font-weight", value.getCssName());
+ }
+
+ /**
+ * Set the height css property.
+ */
+ public static SafeStyles forHeight(double value, Unit unit) {
+ return fromTrustedNameAndValue("height", value, unit);
+ }
+
+ /**
+ * Set the left css property.
+ */
+ public static SafeStyles forLeft(double value, Unit unit) {
+ return fromTrustedNameAndValue("left", value, unit);
+ }
+
+ /**
+ * Sets the list-style-type CSS property.
+ */
+ public static SafeStyles forListStyleType(ListStyleType value) {
+ return fromTrustedNameAndValue("list-style-type", value.getCssName());
+ }
+
+ /**
+ * Set the margin css property.
+ */
+ public static SafeStyles forMargin(double value, Unit unit) {
+ return fromTrustedNameAndValue("margin", value, unit);
+ }
+
+ /**
+ * Set the margin-bottom css property.
+ */
+ public static SafeStyles forMarginBottom(double value, Unit unit) {
+ return fromTrustedNameAndValue("margin-bottom", value, unit);
+ }
+
+ /**
+ * Set the margin-left css property.
+ */
+ public static SafeStyles forMarginLeft(double value, Unit unit) {
+ return fromTrustedNameAndValue("margin-left", value, unit);
+ }
+
+ /**
+ * Set the margin-right css property.
+ */
+ public static SafeStyles forMarginRight(double value, Unit unit) {
+ return fromTrustedNameAndValue("margin-right", value, unit);
+ }
+
+ /**
+ * Set the margin-top css property.
+ */
+ public static SafeStyles forMarginTop(double value, Unit unit) {
+ return fromTrustedNameAndValue("margin-top", value, unit);
+ }
+
+ /**
+ * Set the opacity css property.
+ */
+ public static SafeStyles forOpacity(double value) {
+ return impl().forOpacity(value);
+ }
+
+ /**
+ * Sets the overflow CSS property.
+ */
+ public static SafeStyles forOverflow(Overflow value) {
+ return fromTrustedNameAndValue("overflow", value.getCssName());
+ }
+
+ /**
+ * Sets the overflow-x CSS property.
+ */
+ public static SafeStyles forOverflowX(Overflow value) {
+ return fromTrustedNameAndValue("overflow-x", value.getCssName());
+ }
+
+ /**
+ * Sets the overflow-y CSS property.
+ */
+ public static SafeStyles forOverflowY(Overflow value) {
+ return fromTrustedNameAndValue("overflow-y", value.getCssName());
+ }
+
+ /**
+ * Set the padding css property.
+ */
+ public static SafeStyles forPadding(double value, Unit unit) {
+ return fromTrustedNameAndValue("padding", value, unit);
+ }
+
+ /**
+ * Set the padding-bottom css property.
+ */
+ public static SafeStyles forPaddingBottom(double value, Unit unit) {
+ return fromTrustedNameAndValue("padding-bottom", value, unit);
+ }
+
+ /**
+ * Set the padding-left css property.
+ */
+ public static SafeStyles forPaddingLeft(double value, Unit unit) {
+ return fromTrustedNameAndValue("padding-left", value, unit);
+ }
+
+ /**
+ * Set the padding-right css property.
+ */
+ public static SafeStyles forPaddingRight(double value, Unit unit) {
+ return fromTrustedNameAndValue("padding-right", value, unit);
+ }
+
+ /**
+ * Set the padding-top css property.
+ */
+ public static SafeStyles forPaddingTop(double value, Unit unit) {
+ return fromTrustedNameAndValue("padding-top", value, unit);
+ }
+
+ /**
+ * Sets the position CSS property.
+ */
+ public static SafeStyles forPosition(Position value) {
+ return fromTrustedNameAndValue("position", value.getCssName());
+ }
+
+ /**
+ * Set the right css property.
+ */
+ public static SafeStyles forRight(double value, Unit unit) {
+ return fromTrustedNameAndValue("right", value, unit);
+ }
+
+ /**
+ * Set the table-layout CSS property.
+ */
+ public static SafeStyles forTableLayout(TableLayout value) {
+ return fromTrustedNameAndValue("table-layout", value.getCssName());
+ }
+
+ /**
+ * Sets the text-decoration CSS property.
+ */
+ public static SafeStyles forTextDecoration(TextDecoration value) {
+ return fromTrustedNameAndValue("text-decoration", value.getCssName());
+ }
+
+ /**
+ * Set the top css property.
+ */
+ public static SafeStyles forTop(double value, Unit unit) {
+ return fromTrustedNameAndValue("top", value, unit);
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted background color,
+ * i.e., without escaping the value. No checks are performed. The calling code
+ * should be carefully reviewed to ensure the argument will satisfy the
+ * {@link SafeStyles} contract when they are composed into the form:
+ * "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public static SafeStyles forTrustedBackgroundColor(String value) {
+ return fromTrustedNameAndValue("background-color", value);
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted background image,
+ * i.e., without escaping the value. No checks are performed. The calling code
+ * should be carefully reviewed to ensure the argument will satisfy the
+ * {@link SafeStyles} contract when they are composed into the form:
+ * "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ * @see #forBackgroundImage(SafeUri)
+ */
+ public static SafeStyles forTrustedBackgroundImage(String value) {
+ return fromTrustedNameAndValue("background-image", value);
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted border color, i.e.,
+ * without escaping the value. No checks are performed. The calling code
+ * should be carefully reviewed to ensure the argument will satisfy the
+ * {@link SafeStyles} contract when they are composed into the form:
+ * "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public static SafeStyles forTrustedBorderColor(String value) {
+ return fromTrustedNameAndValue("border-color", value);
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted font color, i.e.,
+ * without escaping the value. No checks are performed. The calling code
+ * should be carefully reviewed to ensure the argument will satisfy the
+ * {@link SafeStyles} contract when they are composed into the form:
+ * "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public static SafeStyles forTrustedColor(String value) {
+ return fromTrustedNameAndValue("color", value);
+ }
+
+ /**
+ * Sets the vertical-align CSS property.
+ */
+ public static SafeStyles forVerticalAlign(double value, Unit unit) {
+ return fromTrustedNameAndValue("vertical-align", value, unit);
+ }
+
+ /**
+ * Sets the vertical-align CSS property.
+ */
+ public static SafeStyles forVerticalAlign(VerticalAlign value) {
+ return fromTrustedNameAndValue("vertical-align", value.getCssName());
+ }
+
+ /**
+ * Sets the visibility CSS property.
+ */
+ public static SafeStyles forVisibility(Visibility value) {
+ return fromTrustedNameAndValue("visibility", value.getCssName());
+ }
+
+ /**
+ * Set the width css property.
+ */
+ public static SafeStyles forWidth(double value, Unit unit) {
+ return fromTrustedNameAndValue("width", value, unit);
+ }
+
+ /**
+ * Set the z-index css property.
+ */
+ public static SafeStyles forZIndex(int value) {
+ return new SafeStylesString("zIndex: " + value + ";");
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted name and a trusted
+ * value, i.e., without escaping the name and value. No checks are performed.
+ * The calling code should be carefully reviewed to ensure the argument will
+ * satisfy the {@link SafeStyles} contract when they are composed into the
+ * form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * <p>
+ * The name should be in hyphenated format, not camelCase format.
+ * </p>
+ *
+ * @param name the property name
+ * @param value the value
+ * @param unit the units of the value
+ * @return a {@link SafeStyles} instance
+ */
+ public static SafeStyles fromTrustedNameAndValue(String name, double value, Unit unit) {
+ SafeStylesHostedModeUtils.maybeCheckValidStyleName(name);
+ return new SafeStylesString(name + ":" + value + unit.getType() + ";");
+ }
+
+ /**
+ * <p>
+ * Returns a {@link SafeStyles} constructed from a trusted name and a trusted
+ * value, i.e., without escaping the name and value. No checks are performed.
+ * The calling code should be carefully reviewed to ensure the argument will
+ * satisfy the {@link SafeStyles} contract when they are composed into the
+ * form: "<name>:<value>;".
+ *
+ * <p>
+ * {@link SafeStyles} may never contain literal angle brackets. Otherwise, it
+ * could be unsafe to place a {@link SafeStyles} into a <style> tag
+ * (where it can't be HTML escaped). For example, if the {@link SafeStyles}
+ * containing "
+ * <code>font: 'foo <style><script>evil</script></code>'" is
+ * used in a style sheet in a <style> tag, this could then break out of
+ * the style context into HTML.
+ * </p>
+ *
+ * <p>
+ * The name should be in hyphenated format, not camelCase format.
+ * </p>
+ *
+ * @param name the property name
+ * @param value the property value
+ * @return a {@link SafeStyles} instance
+ */
+ public static SafeStyles fromTrustedNameAndValue(String name, String value) {
+ SafeStylesHostedModeUtils.maybeCheckValidStyleName(name);
+ SafeStylesHostedModeUtils.maybeCheckValidStyleValue(value);
+ return fromTrustedString(name + ":" + value + ";");
+ }
/**
* <p>
@@ -56,11 +540,13 @@
* In addition, the empty string is safe for use in a CSS attribute.
*
* <p>
- * The following example values do <em>not</em> comply with this type's contract:
+ * The following example values do <em>not</em> comply with this type's
+ * contract:
* <ul>
* <li><code>background: red</code> (missing a trailing semi-colon)</li>
* <li><code>background:</code> (missing a value and a trailing semi-colon)</li>
- * <li><code>1em</code> (missing an attribute name, which provides context for the value)</li>
+ * <li><code>1em</code> (missing an attribute name, which provides context for
+ * the value)</li>
* </ul>
*
* @param s the input String
@@ -93,6 +579,17 @@
+ "'. CSS should not contain brackets (< or >).";
}
+ private static Impl impl() {
+ if (impl == null) {
+ if (GWT.isClient()) {
+ impl = GWT.create(Impl.class);
+ } else {
+ impl = new ImplServer();
+ }
+ }
+ return impl;
+ }
+
// prevent instantiation
private SafeStylesUtils() {
}
diff --git a/user/src/com/google/gwt/safehtml/shared/SafeHtmlHostedModeUtils.java b/user/src/com/google/gwt/safehtml/shared/SafeHtmlHostedModeUtils.java
index 912e383..92d932f 100644
--- a/user/src/com/google/gwt/safehtml/shared/SafeHtmlHostedModeUtils.java
+++ b/user/src/com/google/gwt/safehtml/shared/SafeHtmlHostedModeUtils.java
@@ -32,7 +32,8 @@
public class SafeHtmlHostedModeUtils {
/**
- * If true, perform checks in server-side code.
+ * Name of system property that if set, enables checks in server-side code
+ * (even if assertions are disabled).
*/
public static final String FORCE_CHECK_COMPLETE_HTML =
"com.google.gwt.safehtml.ForceCheckCompleteHtml";
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
index afa0c7c..d1cd4b7 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
@@ -18,7 +18,9 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.safecss.shared.SafeStyles;
+import com.google.gwt.safecss.shared.SafeStylesBuilder;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
@@ -46,7 +48,7 @@
private static Template template;
public void adjust(Element img, SafeUri url, int left, int top, int width, int height) {
- String style = "url(" + url.asString() + ") no-repeat " + (-left + "px ") + (-top + "px");
+ String style = "url(\"" + url.asString() + "\") no-repeat " + (-left + "px ") + (-top + "px");
img.getStyle().setProperty("background", style);
img.getStyle().setPropertyPx("width", width);
img.getStyle().setPropertyPx("height", height);
@@ -63,12 +65,12 @@
}
public SafeHtml getSafeHtml(SafeUri url, int left, int top, int width, int height) {
- // TODO(t.broyer): use the context-specific SafeStyles API once it's been introduced.
- String style = "width: " + width + "px; height: " + height + "px; background: url("
- + url.asString() + ") " + "no-repeat " + (-left + "px ")
- + (-top + "px;");
+ SafeStylesBuilder builder = new SafeStylesBuilder();
+ builder.width(width, Unit.PX).height(height, Unit.PX).trustedNameAndValue("background",
+ "url(" + url.asString() + ") " + "no-repeat " + (-left + "px ") + (-top + "px"));
- return getTemplate().image(clearImage, SafeStylesUtils.fromTrustedString(style));
+ return getTemplate().image(clearImage,
+ SafeStylesUtils.fromTrustedString(builder.toSafeStyles().asString()));
}
private Template getTemplate() {
diff --git a/user/super/com/google/gwt/safecss/super/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java b/user/super/com/google/gwt/safecss/super/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
new file mode 100644
index 0000000..cc56540
--- /dev/null
+++ b/user/super/com/google/gwt/safecss/super/com/google/gwt/safecss/shared/SafeStylesHostedModeUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+import com.google.gwt.core.client.GwtScriptOnly;
+
+// This is the super-source peer of this class.
+@GwtScriptOnly
+public class SafeStylesHostedModeUtils {
+
+ // Unused in super-source; only defined to avoid compiler warnings
+ public static final String FORCE_CHECK_VALID_STYLES = null;
+
+ // Unused in super-source; only defined to avoid compiler warnings
+ public static String isValidStyleName(String name) {
+ return null;
+ }
+
+ // Unused in super-source; only defined to avoid compiler warnings
+ public static String isValidStyleValue(String value) {
+ return null;
+ }
+
+ public static void maybeCheckValidStyleName(String html) {
+ // This check is a noop in web mode.
+ }
+
+ public static void maybeCheckValidStyleValue(String html) {
+ // This check is a noop in web mode.
+ }
+
+ // Unused in super-source; only defined to avoid compiler warnings
+ public static void setForceCheckValidStyle(boolean check) {
+ }
+
+ static void setForceCheckCompleteHtmlFromProperty() {
+ }
+}
diff --git a/user/test/com/google/gwt/safecss/SafeCssGwtSuite.java b/user/test/com/google/gwt/safecss/SafeCssGwtSuite.java
index b92c6df..0c4a352 100644
--- a/user/test/com/google/gwt/safecss/SafeCssGwtSuite.java
+++ b/user/test/com/google/gwt/safecss/SafeCssGwtSuite.java
@@ -17,7 +17,9 @@
import com.google.gwt.junit.tools.GWTTestSuite;
import com.google.gwt.safecss.shared.GwtSafeStylesBuilderTest;
+import com.google.gwt.safecss.shared.GwtSafeStylesHostedModeUtilsTest;
import com.google.gwt.safecss.shared.GwtSafeStylesStringTest;
+import com.google.gwt.safecss.shared.GwtSafeStylesUtilsTest;
import junit.framework.Test;
@@ -29,7 +31,9 @@
GWTTestSuite suite = new GWTTestSuite("Test suite for safe css GWTTestCases");
suite.addTestSuite(GwtSafeStylesBuilderTest.class);
+ suite.addTestSuite(GwtSafeStylesHostedModeUtilsTest.class);
suite.addTestSuite(GwtSafeStylesStringTest.class);
+ suite.addTestSuite(GwtSafeStylesUtilsTest.class);
return suite;
}
diff --git a/user/test/com/google/gwt/safecss/SafeCssJreSuite.java b/user/test/com/google/gwt/safecss/SafeCssJreSuite.java
index c53d204..5c2db82 100644
--- a/user/test/com/google/gwt/safecss/SafeCssJreSuite.java
+++ b/user/test/com/google/gwt/safecss/SafeCssJreSuite.java
@@ -17,7 +17,9 @@
import com.google.gwt.junit.tools.GWTTestSuite;
import com.google.gwt.safecss.shared.SafeStylesBuilderTest;
+import com.google.gwt.safecss.shared.SafeStylesHostedModeUtilsTest;
import com.google.gwt.safecss.shared.SafeStylesStringTest;
+import com.google.gwt.safecss.shared.SafeStylesUtilsTest;
import junit.framework.Test;
@@ -29,7 +31,9 @@
GWTTestSuite suite = new GWTTestSuite("Test suite for safe css tests that require the JRE");
suite.addTestSuite(SafeStylesBuilderTest.class);
+ suite.addTestSuite(SafeStylesHostedModeUtilsTest.class);
suite.addTestSuite(SafeStylesStringTest.class);
+ suite.addTestSuite(SafeStylesUtilsTest.class);
return suite;
}
diff --git a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesBuilderTest.java b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesBuilderTest.java
index 0d02a44..1d05fc8 100644
--- a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesBuilderTest.java
+++ b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesBuilderTest.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.safecss.shared;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
/**
@@ -50,19 +49,22 @@
}
public void testAppendTrustedStringWithInvalidCss() {
- if (GWT.isScript()) {
- // Assertions are disabled in compiled scripts.
+ if (!GwtSafeStylesUtilsTest.isAssertionEnabled()) {
return;
}
SafeStylesBuilder sb = new SafeStylesBuilder();
sb.appendTrustedString(CSS0);
+ boolean caught = false;
try {
sb.appendTrustedString(INVALID_CSS);
- fail("Expected AssertionError");
} catch (AssertionError e) {
// Expected.
+ caught = true;
+ }
+ if (!caught) {
+ fail("Expected AssertionError for invalid css: " + INVALID_CSS);
}
}
diff --git a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesHostedModeUtilsTest.java b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesHostedModeUtilsTest.java
new file mode 100644
index 0000000..2544d42
--- /dev/null
+++ b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesHostedModeUtilsTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * GWT Unit tests for {@link SafeStylesHostedModeUtils}.
+ */
+public class GwtSafeStylesHostedModeUtilsTest extends GWTTestCase {
+
+ private static final String ERROR_MESSAGE_MISMATCH =
+ "Expected error message does not match actual error message";
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.safecss.SafeCss";
+ }
+
+ public void testIsValidStyleName() {
+ if (GWT.isProdMode()) {
+ // isValidStyleName always returns true in prod mode.
+ return;
+ }
+
+ // Valid names.
+ for (String s : GwtSafeStylesUtilsTest.VALID_STYLE_NAMES) {
+ String error = SafeStylesHostedModeUtils.isValidStyleValue(s);
+ assertNull("'" + s + "' incorrectly reported as an invalid style name: " + error, error);
+ }
+
+ // Invalid names.
+ for (String s : GwtSafeStylesUtilsTest.INVALID_STYLE_NAMES) {
+ assertNotNull("'" + s + "' incorrectly reported as an valid style name",
+ SafeStylesHostedModeUtils.isValidStyleName(s));
+ }
+ }
+
+ public void testIsValidStyleValue() {
+ if (GWT.isProdMode()) {
+ // isValidStyleValue always returns true in prod mode.
+ return;
+ }
+
+ // Valid values.
+ for (String s : GwtSafeStylesUtilsTest.VALID_STYLE_VALUES) {
+ String error = SafeStylesHostedModeUtils.isValidStyleValue(s);
+ assertNull("'" + s + "' incorrectly reported as an invalid style value: " + error, error);
+ }
+
+ // Invalid values.
+ for (String s : GwtSafeStylesUtilsTest.INVALID_STYLE_VALUES) {
+ assertNotNull("'" + s + "' incorrectly reported as an valid style value",
+ SafeStylesHostedModeUtils.isValidStyleValue(s));
+ }
+ }
+
+ public void testMaybeCheckValidStyleName() {
+ if (GWT.isProdMode()) {
+ /*
+ * SafeStylesHostedModeUtils.maybeCheckValidStyleName is a no-op in prod
+ * mode.
+ */
+ SafeStylesHostedModeUtils.maybeCheckValidStyleName(GwtSafeStylesUtilsTest.INVALID_STYLE_NAME);
+ } else {
+ // Check a valid name.
+ SafeStylesHostedModeUtils.maybeCheckValidStyleName("name");
+
+ // Check an invalid name.
+ String expectedError =
+ SafeStylesHostedModeUtils.isValidStyleName(GwtSafeStylesUtilsTest.INVALID_STYLE_NAME);
+ assertNotNull(expectedError);
+ boolean caught = false;
+ try {
+ SafeStylesHostedModeUtils
+ .maybeCheckValidStyleName(GwtSafeStylesUtilsTest.INVALID_STYLE_NAME);
+ } catch (IllegalArgumentException e) {
+ /*
+ * Expected - maybeCheckValidStyleName() use either
+ * Preconditions.checkArgument() (which throws an
+ * IllegalArgumentException), or an assert (which throws an
+ * AssertionError).
+ */
+ assertEquals(ERROR_MESSAGE_MISMATCH, expectedError, e.getMessage());
+ caught = true;
+ } catch (AssertionError e) {
+ // Expected - see comment above.
+ assertEquals(ERROR_MESSAGE_MISMATCH, expectedError, e.getMessage());
+ caught = true;
+ }
+
+ if (!caught) {
+ fail("Expected an exception for invalid style name.");
+ }
+ }
+ }
+
+ public void testMaybeCheckValidStyleValue() {
+ if (GWT.isProdMode()) {
+ /*
+ * SafeStylesHostedModeUtils.maybeCheckValidStyleValue is a no-op in prod
+ * mode.
+ */
+ SafeStylesHostedModeUtils
+ .maybeCheckValidStyleValue(GwtSafeStylesUtilsTest.INVALID_STYLE_VALUE);
+ } else {
+ // Check a valid value.
+ SafeStylesHostedModeUtils.maybeCheckValidStyleValue("value");
+
+ String expectedError =
+ SafeStylesHostedModeUtils.isValidStyleValue(GwtSafeStylesUtilsTest.INVALID_STYLE_VALUE);
+ assertNotNull(expectedError);
+ boolean caught = false;
+ try {
+ SafeStylesHostedModeUtils
+ .maybeCheckValidStyleValue(GwtSafeStylesUtilsTest.INVALID_STYLE_VALUE);
+ } catch (IllegalArgumentException e) {
+ /*
+ * Expected - maybeCheckValidStyleValue() use either
+ * Preconditions.checkArgument() (which throws an
+ * IllegalArgumentException), or an assert (which throws an
+ * AssertionError).
+ */
+ assertEquals(ERROR_MESSAGE_MISMATCH, expectedError, e.getMessage());
+ caught = true;
+ } catch (AssertionError e) {
+ // Expected - see comment above.
+ assertEquals(ERROR_MESSAGE_MISMATCH, expectedError, e.getMessage());
+ caught = true;
+ }
+
+ if (!caught) {
+ fail("Expected an exception for invalid style value.");
+ }
+ }
+ }
+}
diff --git a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesStringTest.java b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesStringTest.java
index 4019050..bee4aca 100644
--- a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesStringTest.java
+++ b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesStringTest.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.safecss.shared;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
/**
@@ -32,45 +31,24 @@
* Test that {@link SafeStyles} throws an assertion error if the string
* contains a bracket.
*/
- public void testBrackets() {
- if (GWT.isScript()) {
- // Assertions are disabled in compiled scripts.
+ public void testCloseBracket() {
+ if (!GwtSafeStylesUtilsTest.isAssertionEnabled()) {
return;
}
+ String invalid = "contains:close>;";
+ boolean caught = false;
try {
- new SafeStylesString("name:value<;");
- fail("Expected AssertionError");
+ new SafeStylesString(invalid);
} catch (AssertionError e) {
// Expected.
+ caught = true;
}
- try {
- new SafeStylesString("name:value>;");
- fail("Expected AssertionError");
- } catch (AssertionError e) {
- // Expected.
+ if (!caught) {
+ fail("Expected AssertionError for: " + invalid);
}
}
- /**
- * Test that {@link SafeStyles} throws an assertion error if the string
- * contains a double quote.
- */
- public void testQuotes() {
- if (GWT.isScript()) {
- // Assertions are disabled in compiled scripts.
- return;
- }
-
- // Verify that a string containing single quotes does not cause an
- // exception.
- new SafeStylesString("name:'value';");
-
- // Verify that a string containing double quotes does not cause an
- // exception.
- new SafeStylesString("name:\"value\";");
- }
-
public void testEquals() {
SafeStylesString safe1 = new SafeStylesString("string:same;");
SafeStylesString safe2 = new SafeStylesString("string:same;");
@@ -93,8 +71,7 @@
* missing a semi-colon.
*/
public void testMissingSemiColon() {
- if (GWT.isScript()) {
- // Assertions are disabled in compiled scripts.
+ if (!GwtSafeStylesUtilsTest.isAssertionEnabled()) {
return;
}
@@ -102,20 +79,64 @@
new SafeStylesString(""); // no error expected.
new SafeStylesString(" "); // no error expected.
+ String invalid = "missing:semicolon";
+ boolean caught = false;
try {
- new SafeStylesString("string:same"); // missing a semi-colon.
- fail("Expected AssertionError");
+ new SafeStylesString(invalid);
} catch (AssertionError e) {
// Expected.
+ caught = true;
+ }
+ if (!caught) {
+ fail("Expected AssertionError for: " + invalid);
}
}
public void testNull() {
try {
new SafeStylesString(null);
- fail("Expected IllegalArgumentException");
+ fail("Expected NullPointerException");
} catch (NullPointerException e) {
// Expected.
}
}
+
+ /**
+ * Test that {@link SafeStyles} throws an assertion error if the string
+ * contains a bracket.
+ */
+ public void testOpenBracket() {
+ if (!GwtSafeStylesUtilsTest.isAssertionEnabled()) {
+ return;
+ }
+
+ String invalid = "contains:open<;";
+ boolean caught = false;
+ try {
+ new SafeStylesString(invalid);
+ } catch (AssertionError e) {
+ // Expected.
+ caught = true;
+ }
+ if (!caught) {
+ fail("Expected AssertionError for: " + invalid);
+ }
+ }
+
+ /**
+ * Test that {@link SafeStyles} allows quotes.
+ */
+ public void testQuotes() {
+ if (!GwtSafeStylesUtilsTest.isAssertionEnabled()) {
+ return;
+ }
+
+ // Verify that a string containing single quotes does not cause an
+ // exception.
+ new SafeStylesString("name:'value';");
+
+ // Verify that a string containing double quotes does not cause an
+ // exception.
+ new SafeStylesString("name:\"value\";");
+ }
}
diff --git a/user/test/com/google/gwt/safecss/shared/GwtSafeStylesUtilsTest.java b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesUtilsTest.java
new file mode 100644
index 0000000..fa3b591
--- /dev/null
+++ b/user/test/com/google/gwt/safecss/shared/GwtSafeStylesUtilsTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.safehtml.shared.UriUtils;
+
+/**
+ * GWT Unit tests for {@link SafeStylesUtils}.
+ */
+public class GwtSafeStylesUtilsTest extends GWTTestCase {
+
+ static final String INVALID_STYLE_NAME = "contains;semicolon";
+
+ static final String[] INVALID_STYLE_NAMES = {
+ null, "", ";startsWithSemicolon", "endsWithSemicolon;", "contains;semicolon",
+ "--starts-with-double-hyphen", "0starts-with-digit", "-0starts-with-hyphen-digit",
+ "contains:colon"};
+
+ static final String INVALID_STYLE_VALUE = "contains;semicolon";
+
+ static final String[] INVALID_STYLE_VALUES = {
+ null, "", "unmatched}close-bracket", "unmatched{open-bracket", "mismatched[bracket)",
+ ";startsWithSemicolon", "endsWithSemicolon;", "contains;semicolon",
+ "almost-escaped\\\\;semi-colon", "almost-escaped\\\\:colon", "unmatched'singlequote",
+ "unmatched\"doublequote", "url(http://withUnmatched(Paren)", "url(http://unterminated",
+ "end-in-escape-character\\"};
+
+ static final String[] VALID_STYLE_NAMES = {
+ "simple", "one-hyphen", "has-two-hyphens", "-starts-with-hyphen", "_startsWithUnderscore",
+ "endsWithUnderscore_", "contains_underscore", "contains--double-hyphen", "contains-1-digit"};
+
+ static final String[] VALID_STYLE_VALUES = {
+ "simple", "one-hyphen", "has-two-hyphens", "-starts-with-hyphen", "[braces]",
+ "curly{brackets}", "paren(thes)es", "(nested[pair]ables)",
+ "(sibling)parenthesis{and[braces]}", "semicolon-in'single;quote'",
+ "semicolon-in\"double;quote\"", "unmatched'in quote}'", "unmatched\"in [double quote\"",
+ "escaped\\;semi-colon", "escaped\\:colon", "url(http://localhost)",
+ "url('http://withSingleQuotes')", "url(\"http://withDoubleQuotes\")",
+ "url(http://withSemiColon;)", "url(http://withUnmatchedBracket{[)",
+ "url(http://withUnmatchedCloseBracket}])", "end-in-escaped-backslash\\\\"};
+
+ private static Boolean isAssertionEnabled;
+
+ /**
+ * Check if assertions are enabled.
+ *
+ * @return true if enabled, false if not
+ */
+ static boolean isAssertionEnabled() {
+ if (isAssertionEnabled == null) {
+ try {
+ assert false;
+ isAssertionEnabled = false;
+ } catch (AssertionError e) {
+ isAssertionEnabled = true;
+ }
+ }
+ return isAssertionEnabled;
+ }
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.safecss.SafeCss";
+ }
+
+ public void testForBackgroundImage() {
+ SafeUri uri = UriUtils.fromSafeConstant("http://localhost");
+ assertEquals("background-image:url(\"http://localhost\");", SafeStylesUtils.forBackgroundImage(
+ uri).asString());
+ }
+
+ public void testForDisplay() {
+ assertEquals("display:none;", SafeStylesUtils.forDisplay(Display.NONE).asString());
+ }
+
+ public void testForZIndex() {
+ assertEquals("zIndex: 5;", SafeStylesUtils.forZIndex(5).asString());
+ }
+
+ public void testFromTrustedNameAndValue() {
+ assertEquals("name:value;", SafeStylesUtils.fromTrustedNameAndValue("name", "value").asString());
+ assertEquals("name-top:value;", SafeStylesUtils.fromTrustedNameAndValue("name-top", "value")
+ .asString());
+ assertEquals("-name-top:value;", SafeStylesUtils.fromTrustedNameAndValue("-name-top", "value")
+ .asString());
+ assertEquals("_name_:value;", SafeStylesUtils.fromTrustedNameAndValue("_name_", "value")
+ .asString());
+
+ assertEquals("name:1px solid red;", SafeStylesUtils.fromTrustedNameAndValue("name",
+ "1px solid red").asString());
+ assertEquals("name:url('test.png');", SafeStylesUtils.fromTrustedNameAndValue("name",
+ "url('test.png')").asString());
+ assertEquals("name:url(\"test.png\");", SafeStylesUtils.fromTrustedNameAndValue("name",
+ "url(\"test.png\")").asString());
+ }
+
+ public void testFromTrustedNameAndValueInvalidName() {
+ if (GWT.isProdMode()) {
+ // fromTrustedNameAndValue only catches errors in dev mode.
+ return;
+ }
+
+ for (String s : INVALID_STYLE_NAMES) {
+ boolean caught = false;
+ try {
+ SafeStylesUtils.fromTrustedNameAndValue(s, "value");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ caught = true;
+ } catch (AssertionError e) {
+ // Expected.
+ caught = true;
+ }
+ if (!caught) {
+ fail("Expected an exception for invalid style name: '" + s + "'");
+ }
+ }
+ }
+
+ public void testFromTrustedNameAndValueInvalidValue() {
+ if (GWT.isProdMode()) {
+ // fromTrustedNameAndValue only catches errors in dev mode.
+ return;
+ }
+
+ boolean caught = false;
+ for (String s : INVALID_STYLE_VALUES) {
+ try {
+ SafeStylesUtils.fromTrustedNameAndValue("name", s);
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ caught = true;
+ } catch (AssertionError e) {
+ // Expected.
+ caught = true;
+ }
+ if (!caught) {
+ fail("Expected an exception for invalid style value: '" + s + "'");
+ }
+ }
+ }
+
+ public void testFromTrustedNameAndValueValidName() {
+ if (GWT.isProdMode()) {
+ // fromTrustedNameAndValue only catches errors in dev mode.
+ return;
+ }
+
+ for (String s : VALID_STYLE_NAMES) {
+ try {
+ SafeStyles styles = SafeStylesUtils.fromTrustedNameAndValue(s, "value");
+ assertEquals(s + ":value;", styles.asString());
+ } catch (Exception e) {
+ fail("Unexpected exception thrown for valid style name: '" + s + "'.\n" + e.getMessage());
+ }
+ }
+ }
+
+ public void testFromTrustedNameAndValueValidValue() {
+ if (GWT.isProdMode()) {
+ // fromTrustedNameAndValue only catches errors in dev mode.
+ return;
+ }
+
+ for (String s : VALID_STYLE_VALUES) {
+ try {
+ SafeStyles styles = SafeStylesUtils.fromTrustedNameAndValue("name", s);
+ assertEquals("name" + ":" + s + ";", styles.asString());
+ } catch (Exception e) {
+ fail("Unexpected exception thrown for valid style value: '" + s + "'.\n" + e.getMessage());
+ }
+ }
+ }
+
+ public void testFromTrustedString() {
+ assertEquals("name:value;", SafeStylesUtils.fromTrustedString("name:value;").asString());
+ }
+}
diff --git a/user/test/com/google/gwt/safecss/shared/SafeStylesHostedModeUtilsTest.java b/user/test/com/google/gwt/safecss/shared/SafeStylesHostedModeUtilsTest.java
new file mode 100644
index 0000000..9a75a36
--- /dev/null
+++ b/user/test/com/google/gwt/safecss/shared/SafeStylesHostedModeUtilsTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+/**
+ * JUnit tests for {@link SafeStylesHostedModeUtils}.
+ */
+public class SafeStylesHostedModeUtilsTest extends GwtSafeStylesHostedModeUtilsTest {
+
+ // This forces a GWTTestCase to run as a vanilla JUnit TestCase.
+ @Override
+ public String getModuleName() {
+ return null;
+ }
+
+ @Override
+ protected void gwtSetUp() throws Exception {
+ super.gwtSetUp();
+ // Since we can't assume assertions are enabled, force
+ // SafeStylesHostedModeUtils to perform its check when running in JRE.
+ SafeStylesHostedModeUtils.setForceCheckValidStyle(true);
+ }
+}
diff --git a/user/test/com/google/gwt/safecss/shared/SafeStylesStringTest.java b/user/test/com/google/gwt/safecss/shared/SafeStylesStringTest.java
index 8e1009f..710d4f7 100644
--- a/user/test/com/google/gwt/safecss/shared/SafeStylesStringTest.java
+++ b/user/test/com/google/gwt/safecss/shared/SafeStylesStringTest.java
@@ -16,7 +16,7 @@
package com.google.gwt.safecss.shared;
/**
- * GWT Unit tests for {@link SafeStylesString}.
+ * Unit tests for {@link SafeStylesString}.
*/
public class SafeStylesStringTest extends GwtSafeStylesStringTest {
diff --git a/user/test/com/google/gwt/safecss/shared/SafeStylesUtilsTest.java b/user/test/com/google/gwt/safecss/shared/SafeStylesUtilsTest.java
new file mode 100644
index 0000000..82c637e
--- /dev/null
+++ b/user/test/com/google/gwt/safecss/shared/SafeStylesUtilsTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.safecss.shared;
+
+/**
+ * Unit tests for {@link SafeStylesUtils}.
+ */
+public class SafeStylesUtilsTest extends GwtSafeStylesUtilsTest {
+
+ // This forces a GWTTestCase to run as a vanilla JUnit TestCase.
+ @Override
+ public String getModuleName() {
+ return null;
+ }
+}