From e8f10244e3859e8bb5b6be3823c9bbea2c130573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Sun, 3 May 2026 11:55:37 +0200 Subject: [PATCH] Ensure consistent JSP tag attribute processing Closes gh-36797 --- .../tags/form/AbstractHtmlElementTag.java | 9 ++++---- .../web/servlet/tags/form/FormTag.java | 4 ++-- .../web/servlet/tags/form/FormTagTests.java | 14 ++++++++++++ .../web/servlet/tags/form/InputTagTests.java | 22 +++++++++++++++++++ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTag.java index f40ec6cd327..d0771ae9a66 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/AbstractHtmlElementTag.java @@ -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())); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index 8129620af08..2ebd7ea7b49 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -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())); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index 48db96648e4..93885e2a834 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -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"; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/InputTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/InputTagTests.java index a8e35ccf9ae..effa54aeb4e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/InputTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/InputTagTests.java @@ -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");