Ensure consistent JSP tag attribute processing

Closes gh-36797
This commit is contained in:
Sébastien Deleuze
2026-05-03 11:55:37 +02:00
committed by Brian Clozel
parent a1826b725c
commit e8f10244e3
4 changed files with 42 additions and 7 deletions
@@ -24,7 +24,6 @@ import jakarta.servlet.jsp.tagext.DynamicAttributes;
import org.jspecify.annotations.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -40,6 +39,7 @@ import org.springframework.util.StringUtils;
* @author Rob Harrop
* @author Jeremy Grelle
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 2.0
*/
@SuppressWarnings("serial")
@@ -429,8 +429,7 @@ public abstract class AbstractHtmlElementTag extends AbstractDataBoundFormElemen
*/
protected void writeOptionalAttributes(TagWriter tagWriter) throws JspException {
tagWriter.writeOptionalAttributeValue(CLASS_ATTRIBUTE, resolveCssClass());
tagWriter.writeOptionalAttributeValue(STYLE_ATTRIBUTE,
ObjectUtils.getDisplayString(evaluate("cssStyle", getCssStyle())));
writeOptionalAttribute(tagWriter, STYLE_ATTRIBUTE, getCssStyle());
writeOptionalAttribute(tagWriter, LANG_ATTRIBUTE, getLang());
writeOptionalAttribute(tagWriter, TITLE_ATTRIBUTE, getTitle());
writeOptionalAttribute(tagWriter, DIR_ATTRIBUTE, getDir());
@@ -459,10 +458,10 @@ public abstract class AbstractHtmlElementTag extends AbstractDataBoundFormElemen
*/
protected String resolveCssClass() throws JspException {
if (getBindStatus().isError() && StringUtils.hasText(getCssErrorClass())) {
return ObjectUtils.getDisplayString(evaluate("cssErrorClass", getCssErrorClass()));
return getDisplayString(evaluate("cssErrorClass", getCssErrorClass()));
}
else {
return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()));
return getDisplayString(evaluate("cssClass", getCssClass()));
}
}
@@ -32,7 +32,6 @@ import org.springframework.core.Conventions;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
import org.springframework.web.util.HtmlUtils;
@@ -239,6 +238,7 @@ import org.springframework.web.util.UriUtils;
* @author Juergen Hoeller
* @author Scott Andrews
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 2.0
*/
@SuppressWarnings("serial")
@@ -719,7 +719,7 @@ public class FormTag extends AbstractHtmlElementTag {
*/
@Override
protected String resolveCssClass() throws JspException {
return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()));
return getDisplayString(evaluate("cssClass", getCssClass()));
}
/**
@@ -129,6 +129,20 @@ class FormTagTests extends AbstractHtmlElementTagTests {
assertContainsAttribute(output, dynamicAttribute2, dynamicAttribute2);
}
@Test
void writeFormWithHtmlEscaping() throws Exception {
this.tag.setCssClass("\"class\"");
this.tag.setCssStyle("\"style\"");
this.tag.doStartTag();
this.tag.doEndTag();
this.tag.doFinally();
String output = getOutput();
assertContainsAttribute(output, "class", ""class"");
assertContainsAttribute(output, "style", ""style"");
}
@Test
void withActionFromRequest() throws Exception {
String commandName = "myCommand";
@@ -314,6 +314,28 @@ class InputTagTests extends AbstractFormTagTests {
assertContainsAttribute(output, "class", "bad");
}
@Test
void withErrorsAndHtmlEscaping() throws Exception {
this.tag.setPath("name");
this.tag.setCssClass("\"good\"");
this.tag.setCssErrorClass("\"bad\"");
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(this.rob, COMMAND_NAME);
errors.rejectValue("name", "some.code", "Default Message");
errors.rejectValue("name", "too.short", "Too Short");
exposeBindingResult(errors);
assertThat(this.tag.doStartTag()).isEqualTo(Tag.SKIP_BODY);
String output = getOutput();
assertTagOpened(output);
assertTagClosed(output);
assertContainsAttribute(output, "type", getType());
assertValueAttribute(output, "Rob");
assertContainsAttribute(output, "class", ""bad"");
}
@Test
void disabledFalse() throws Exception {
this.tag.setPath("name");