blob: 14d2eb6b6290429616ff7273bbebf23f898321ea [file] [log] [blame]
/*
* Copyright 2010 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.safehtml.shared;
import com.google.gwt.core.client.GWT;
import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.streamhtmlparser.HtmlParser;
import com.google.gwt.thirdparty.streamhtmlparser.HtmlParserFactory;
import com.google.gwt.thirdparty.streamhtmlparser.ParseException;
/**
* SafeHtml utilities whose implementation differs between hosted and web mode.
*
* <p>
* This class has a super-source peer that provides the web-mode implementation.
*/
public class SafeHtmlHostedModeUtils {
public static final String FORCE_CHECK_COMPLETE_HTML =
"com.google.gwt.safehtml.ForceCheckCompleteHtml";
private static boolean forceCheckCompleteHtml;
static {
setForceCheckCompleteHtmlFromProperty();
}
/**
* Checks if the provided HTML string is complete (ends in "inner HTML"
* context).
*
* <p>
* This method parses the provided string as HTML and determines the HTML
* context at the end of the string. If the context is not "inner HTML text",
* a {@link IllegalArgumentException} or {@link AssertionError} is thrown.
*
* <p>
* For example, this check will pass for the following strings:
*
* <pre>{@code
* <foo>blah
* baz<em>foo</em> <x a="b">hello
* }</pre>
*
* <p>
* The check will fail for the following strings:
*
* <pre>{@code
* baz<em>foo</em> <x
* baz<em>foo</em> <x a="b
* baz<em>foo</em> <x a="b"
* }</pre>
*
* <p>
* Note that the parser is lenient and this check will pass for HTML that is
* not well-formed, or contains invalid tags, as long as the parser can
* determine the HTML context at the end of the string.
*
* <p>
* This check is intended to assert a convention-of-use constraint of {@link
* com.google.gwt.safehtml.shared.SafeHtmlBuilder#appendHtmlConstant(String)}.
* Since the check is somewhat expensive, it is intended to run only in the
* context of unit-tests or test environments, and not in production
* environments. Hence this check will only execute under the following
* conditions, and will be short-circuited otherwise:
*
* <ul>
* <li>In client-side code in hosted mode,</li>
* <li>In server-side code if assertions are enabled,</li>
* <li>In server-side code if the property {@code
* com.google.gwt.safehtml.ForceCheckCompleteHtml} is set.</li>
* <li>In server-side code if {@link #setForceCheckCompleteHtml(boolean)} has
* been called with a {@code true} argument.</li>
* </ul>
*
* @param html the HTML to check
*/
public static void maybeCheckCompleteHtml(String html) {
if (GWT.isClient() || forceCheckCompleteHtml) {
Preconditions.checkArgument(isCompleteHtml(html),
"String is not complete HTML (ends in non-inner-HTML context): %s",
html);
} else {
assert isCompleteHtml(html) :
"String is not complete HTML (ends in non-inner-HTML context): "
+ html;
}
}
/**
* Sets a global flag that controls whether or not
* {@link #maybeCheckCompleteHtml(String)} should perform its check in a
* server-side environment.
*/
public static void setForceCheckCompleteHtml(boolean check) {
forceCheckCompleteHtml = check;
}
@VisibleForTesting
public static void setForceCheckCompleteHtmlFromProperty() {
forceCheckCompleteHtml =
System.getProperty(FORCE_CHECK_COMPLETE_HTML) != null;
}
private static boolean isCompleteHtml(String html) {
HtmlParser htmlParser = HtmlParserFactory.createParser();
try {
htmlParser.parse(html);
} catch (ParseException e) {
return false;
}
return htmlParser.getState() == HtmlParser.STATE_TEXT;
}
}