Skip to content

Commit 1ae407c

Browse files
committed
Backportof CSP-related changes
1 parent ac0a34e commit 1ae407c

16 files changed

Lines changed: 236 additions & 158 deletions

File tree

impl/src/main/java/com/sun/faces/facelets/tag/ui/UIDebug.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@
2222
import java.util.List;
2323
import java.util.Map;
2424

25-
import com.sun.faces.facelets.util.DevTools;
26-
import com.sun.faces.facelets.util.FastWriter;
27-
import com.sun.faces.renderkit.RenderKitUtils;
28-
import com.sun.faces.renderkit.html_basic.ScriptRenderer;
29-
3025
import jakarta.faces.component.UIComponentBase;
3126
import jakarta.faces.context.FacesContext;
3227
import jakarta.faces.context.ResponseWriter;
3328
import jakarta.servlet.http.HttpServletResponse;
3429

30+
import com.sun.faces.facelets.util.DevTools;
31+
import com.sun.faces.facelets.util.FastWriter;
32+
import com.sun.faces.renderkit.RenderKitUtils;
33+
3534
/**
3635
* @author Jacob Hookom
3736
*/
@@ -103,14 +102,9 @@ public void encodeBegin(FacesContext facesContext) throws IOException {
103102
ResponseWriter writer = facesContext.getResponseWriter();
104103
writer.startElement("span", this);
105104
writer.writeAttribute("id", getClientId(facesContext), "id");
106-
writer.startElement("script", this);
107-
108-
if (!RenderKitUtils.isOutputHtml5Doctype(facesContext)) {
109-
writer.writeAttribute("type", ScriptRenderer.DEFAULT_CONTENT_TYPE, "type");
110-
}
105+
106+
RenderKitUtils.renderScript(facesContext, this, null, sb.toString());
111107

112-
writer.writeText(sb.toString(), this, null);
113-
writer.endElement("script");
114108
writer.endElement("span");
115109
}
116110
}

impl/src/main/java/com/sun/faces/renderkit/RenderKitUtils.java

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.sun.faces.config.WebConfiguration;
4242
import com.sun.faces.el.ELUtils;
4343
import com.sun.faces.facelets.util.DevTools;
44+
import com.sun.faces.renderkit.html_basic.ScriptRenderer;
4445
import com.sun.faces.util.FacesLogger;
4546
import com.sun.faces.util.RequestStateManager;
4647
import com.sun.faces.util.Util;
@@ -143,6 +144,8 @@ public class RenderKitUtils {
143144
*/
144145
private static final String ATTRIBUTES_THAT_ARE_SET_KEY = UIComponentBase.class.getName() + ".attributesThatAreSet";
145146

147+
private static final String BEHAVIOR_EVENT_ATTRIBUTE_PREFIX = "on";
148+
146149
/**
147150
* UIViewRoot attribute key of a boolean value which remembers whether the view will be rendered with a HTML5 doctype.
148151
*/
@@ -347,10 +350,10 @@ public static void renderPassThruAttributes(FacesContext context, ResponseWriter
347350
}
348351
}
349352

350-
// Renders the onchange handler for input components. Handles
353+
// Renders the onchange event listener for input components. Handles
351354
// chaining together the user-provided onchange handler with
352355
// any Behavior scripts.
353-
public static void renderOnchange(FacesContext context, UIComponent component, boolean incExec) throws IOException {
356+
public static void renderOnchangeEventListener(FacesContext context, UIComponent component, boolean incExec) throws IOException {
354357

355358
final String handlerName = "onchange";
356359
final Object userHandler = component.getAttributes().get(handlerName);
@@ -369,11 +372,11 @@ public static void renderOnchange(FacesContext context, UIComponent component, b
369372
params = new LinkedList<>();
370373
params.add(new ClientBehaviorContext.Parameter("incExec", true));
371374
}
372-
renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec);
375+
renderHandler(context, component, null, params, handlerName, userHandler, behaviorEventName, "change", null, false, incExec, true);
373376
}
374377

375-
// Renders onclick handler for SelectRaidio and SelectCheckbox
376-
public static void renderSelectOnclick(FacesContext context, UIComponent component, boolean incExec) throws IOException {
378+
// Renders onclick event listener for SelectRadio and SelectCheckbox
379+
public static void renderSelectOnclickEventListener(FacesContext context, UIComponent component, String clientId, boolean incExec) throws IOException {
377380

378381
final String handlerName = "onclick";
379382
final Object userHandler = component.getAttributes().get(handlerName);
@@ -392,14 +395,16 @@ public static void renderSelectOnclick(FacesContext context, UIComponent compone
392395
params = new LinkedList<>();
393396
params.add(new ClientBehaviorContext.Parameter("incExec", true));
394397
}
395-
renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, null, false, incExec);
398+
renderHandler(context, component, clientId, params, handlerName, userHandler, behaviorEventName, "click", null, false, incExec, true);
396399
}
397400

398-
// Renders the onclick handler for command buttons. Handles
401+
// Renders the onclick event listener for command buttons. Handles
399402
// chaining together the user-provided onclick handler, any
400403
// Behavior scripts, plus the default button submit script.
401-
public static void renderOnclick(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String submitTarget,
402-
boolean needsSubmit) throws IOException {
404+
public static void renderOnclickEventListener(FacesContext context, UIComponent component,
405+
Collection<ClientBehaviorContext.Parameter> params,
406+
String submitTarget,
407+
boolean needsSubmit) throws IOException {
403408

404409
final String handlerName = "onclick";
405410
final Object userHandler = component.getAttributes().get(handlerName);
@@ -418,18 +423,17 @@ public static void renderOnclick(FacesContext context, UIComponent component, Co
418423
}
419424
}
420425

421-
renderHandler(context, component, params, handlerName, userHandler, behaviorEventName, submitTarget, needsSubmit, false);
426+
renderHandler(context, component, null, params, handlerName, userHandler, behaviorEventName, "click", submitTarget, needsSubmit, false, true);
422427
}
423428

424-
// Renders the script function for command scripts.
429+
// Renders the script element with the function for command scripts.
425430
public static void renderFunction(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String submitTarget)
426431
throws IOException {
427432

428433
ClientBehaviorContext behaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, component, "action", submitTarget, params);
429434
AjaxBehavior behavior = (AjaxBehavior) context.getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
430435
mapAttributes(component, behavior, "execute", "render", "onerror", "onevent", "resetValues");
431-
432-
context.getResponseWriter().append(behavior.getScript(behaviorContext));
436+
renderScript(context, component, null, behavior.getScript(behaviorContext));
433437
}
434438

435439
private static void mapAttributes(UIComponent component, AjaxBehavior behavior, String... attributeNames) {
@@ -623,7 +627,19 @@ private static void renderPassThruAttributesOptimized(FacesContext context, Resp
623627
Attribute attr = knownAttributes[index];
624628

625629
if (isBehaviorEventAttribute(attr, behaviorEventName)) {
626-
renderHandler(context, component, null, name, value, behaviorEventName, null, false, false);
630+
renderHandler(context, component, null, null, name, value, behaviorEventName, behaviorEventName, null, false, false, false);
631+
632+
renderedBehavior = true;
633+
} else {
634+
writer.writeAttribute(prefixAttribute(name, isXhtml), value, name);
635+
}
636+
}
637+
}
638+
else if (isBehaviorEventAttribute(name)) {
639+
Object value = attrMap.get(name);
640+
if (value != null && shouldRenderAttribute(value)) {
641+
if (name.substring(2).equals(behaviorEventName)) {
642+
renderHandler(context, component, null, null, name, value, behaviorEventName, behaviorEventName, null, false, false, false);
627643

628644
renderedBehavior = true;
629645
} else {
@@ -644,8 +660,8 @@ private static void renderPassThruAttributesOptimized(FacesContext context, Resp
644660
Attribute attr = knownAttributes[i];
645661
String[] events = attr.getEvents();
646662
if (events != null && events.length > 0 && behaviorEventName.equals(events[0])) {
647-
648-
renderHandler(context, component, null, attr.getName(), null, behaviorEventName, null, false, false);
663+
renderHandler(context, component, null, null, attr.getName(), null, behaviorEventName, behaviorEventName, null, false, false, false);
664+
return;
649665
}
650666
}
651667

@@ -683,12 +699,34 @@ private static void renderPassThruAttributesUnoptimized(FacesContext context, Re
683699

684700
// If we've got a behavior for this attribute,
685701
// we may need to chain scripts together, so use
686-
// renderHandler().
687-
renderHandler(context, component, null, attrName, value, events[0], null, false, false);
702+
// renderEventListener().
703+
renderHandler(context, component, null, null, attrName, value, events[0], events[0], null, false, false, false);
688704
}
689705
}
690706
}
691707

708+
private static void renderPassthruAttribute(FacesContext context, ResponseWriter writer, UIComponent component,
709+
Map<String, List<ClientBehavior>> behaviors, boolean isXhtml, Map<String, Object> attrMap, String attrName,
710+
String eventName) throws IOException {
711+
boolean hasBehavior = eventName != null && behaviors.containsKey(eventName);
712+
713+
Object value = attrMap.get(attrName);
714+
715+
if (value != null && shouldRenderAttribute(value) && !hasBehavior) {
716+
writer.writeAttribute(prefixAttribute(attrName, isXhtml), value, attrName);
717+
} else if (hasBehavior) {
718+
719+
// If we've got a behavior for this attribute,
720+
// we may need to chain scripts together, so use
721+
// renderEventListener().
722+
renderHandler(context, component, null, null, attrName, value, eventName, eventName, null, false, false, false);
723+
}
724+
}
725+
726+
public static boolean isBehaviorEventAttribute(String name) {
727+
return name.startsWith(BEHAVIOR_EVENT_ATTRIBUTE_PREFIX) && name.length() > 2;
728+
}
729+
692730
/**
693731
* <p>
694732
* Determines if an attribute should be rendered based on the specified #attributeVal.
@@ -1492,7 +1530,7 @@ private static String getSubmitHandler(FacesContext context, UIComponent compone
14921530
builder.append("')");
14931531

14941532
if (preventDefault) {
1495-
builder.append(";return false");
1533+
builder.append(";event.preventDefault()");
14961534
}
14971535

14981536
return builder.toString();
@@ -1530,10 +1568,10 @@ private static String getChainedHandler(FacesContext context, UIComponent compon
15301568
builder.append(")");
15311569

15321570
// If we're submitting (either via a behavior, or by rendering
1533-
// a submit script), we need to return false to prevent the
1534-
// default button/link action.
1571+
// a submit script), we need to prevent the
1572+
// default button/link action event.
15351573
if (submitting && ("action".equals(behaviorEventName) || "click".equals(behaviorEventName))) {
1536-
builder.append(";return false");
1574+
builder.append(";event.preventDefault()");
15371575
}
15381576

15391577
return builder.toString();
@@ -1554,7 +1592,7 @@ private static String getSingleBehaviorHandler(FacesContext context, UIComponent
15541592
script = getSubmitHandler(context, component, params, submitTarget, preventDefault);
15551593
}
15561594
} else if (preventDefault) {
1557-
script = script + ";return false";
1595+
script = script + ";event.preventDefault()";
15581596
}
15591597

15601598
return script;
@@ -1583,15 +1621,16 @@ private static boolean isSubmitting(ClientBehavior behavior) {
15831621
* @param handlerValue the user-specified value for the handler attribute
15841622
* @param behaviorEventName the name of the behavior event that corresponds to this handler (eg. "action" or
15851623
* "mouseover").
1624+
* @param domEventName the name of the DOM event that corresponds to this handler (eg. "click" or
1625+
* "change").
15861626
* @param needsSubmit indicates whether the mojarra.cljs() "submit" script is required by the component. Most
15871627
* components do not need this, either because they submit themselves (eg. commandButton), or because they do not
15881628
* perform submits (eg. non-command components). This flag is mainly here for the commandLink case, where we need to
15891629
* render the submit script to make the link submit.
15901630
*/
1591-
private static void renderHandler(FacesContext context, UIComponent component, Collection<ClientBehaviorContext.Parameter> params, String handlerName,
1592-
Object handlerValue, String behaviorEventName, String submitTarget, boolean needsSubmit, boolean includeExec) throws IOException {
1631+
private static void renderHandler(FacesContext context, UIComponent component, String clientId, Collection<ClientBehaviorContext.Parameter> params, String handlerName,
1632+
Object handlerValue, String behaviorEventName, String domEventName, String submitTarget, boolean needsSubmit, boolean includeExec, boolean asEventListener) throws IOException {
15931633

1594-
ResponseWriter writer = context.getResponseWriter();
15951634
String userHandler = getNonEmptyUserHandler(handlerValue);
15961635
List<ClientBehavior> behaviors = getClientBehaviors(component, behaviorEventName);
15971636

@@ -1625,7 +1664,46 @@ private static void renderHandler(FacesContext context, UIComponent component, C
16251664
assert false;
16261665
}
16271666

1628-
writer.writeAttribute(handlerName, handler, null);
1667+
if (handler != null) {
1668+
if (asEventListener) {
1669+
addEventListener(context, component, clientId, domEventName, handler);
1670+
}
1671+
else {
1672+
context.getResponseWriter().writeAttribute(handlerName, handler, null);
1673+
}
1674+
}
1675+
}
1676+
1677+
public static void addEventListener(FacesContext context, UIComponent component, String clientId, String domEventName, String function) throws IOException {
1678+
StringBuilder script = new StringBuilder("mojarra.ael('")
1679+
.append(clientId != null ? clientId : component.getClientId(context))
1680+
.append("','")
1681+
.append(domEventName)
1682+
.append("',function(event){" + function + "})");
1683+
1684+
if (context.getPartialViewContext().isAjaxRequest()) {
1685+
context.getPartialViewContext().getEvalScripts().add(script.toString());
1686+
}
1687+
else {
1688+
renderScript(context, component, null, script.toString());
1689+
}
1690+
}
1691+
1692+
public static void renderScript(FacesContext context, UIComponent component, String clientId, String script) throws IOException {
1693+
ResponseWriter writer = context.getResponseWriter();
1694+
1695+
writer.startElement("script", component);
1696+
1697+
if (clientId != null) {
1698+
writer.writeAttribute("id", clientId, "id");
1699+
}
1700+
1701+
if (!RenderKitUtils.isOutputHtml5Doctype(context)) {
1702+
writer.writeAttribute("type", ScriptRenderer.DEFAULT_CONTENT_TYPE, "type");
1703+
}
1704+
1705+
writer.writeText(script, component, null);
1706+
writer.endElement("script");
16291707
}
16301708

16311709
// Determines the type of handler to render based on what sorts of

impl/src/main/java/com/sun/faces/renderkit/html_basic/ButtonRenderer.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,12 @@
1818

1919
package com.sun.faces.renderkit.html_basic;
2020

21-
import static com.sun.faces.renderkit.RenderKitUtils.PredefinedPostbackParameter.BEHAVIOR_SOURCE_PARAM;
22-
2321
import java.io.IOException;
2422
import java.util.Collection;
2523
import java.util.List;
2624
import java.util.Map;
2725
import java.util.logging.Level;
2826

29-
import com.sun.faces.RIConstants;
30-
import com.sun.faces.renderkit.Attribute;
31-
import com.sun.faces.renderkit.AttributeManager;
32-
import com.sun.faces.renderkit.RenderKitUtils;
33-
3427
import jakarta.faces.component.UICommand;
3528
import jakarta.faces.component.UIComponent;
3629
import jakarta.faces.component.behavior.ClientBehavior;
@@ -39,6 +32,11 @@
3932
import jakarta.faces.context.ResponseWriter;
4033
import jakarta.faces.event.ActionEvent;
4134

35+
import com.sun.faces.RIConstants;
36+
import com.sun.faces.renderkit.Attribute;
37+
import com.sun.faces.renderkit.AttributeManager;
38+
import com.sun.faces.renderkit.RenderKitUtils;
39+
4240
/**
4341
* <B>ButtonRenderer</B> is a class that renders the current value of <code>UICommand</code> as a Button.
4442
*/
@@ -98,11 +96,6 @@ public void encodeBegin(FacesContext context, UIComponent component) throws IOEx
9896
* when we decide how to do script injection.
9997
*/
10098

101-
Collection<ClientBehaviorContext.Parameter> params = getBehaviorParameters(component);
102-
if (!params.isEmpty() && (type.equals("submit") || type.equals("button"))) {
103-
RenderKitUtils.renderFacesJsIfNecessary(context);
104-
}
105-
10699
String imageSrc = (String) component.getAttributes().get("image");
107100
writer.startElement("input", component);
108101
writeIdAttributeIfNecessary(context, writer, component);
@@ -134,8 +127,6 @@ else if (writer.getContentType().equals(RIConstants.XHTML_CONTENT_TYPE)) {
134127
writer.writeAttribute("class", styleClass, "styleClass");
135128
}
136129

137-
RenderKitUtils.renderOnclick(context, component, params, null, false);
138-
139130
// PENDING(edburns): Prior to i_spec_1111, this element
140131
// was rendered unconditionally
141132

@@ -155,6 +146,15 @@ public void encodeEnd(FacesContext context, UIComponent component) throws IOExce
155146
if (component.getChildCount() > 0) {
156147
context.getResponseWriter().endElement("input");
157148
}
149+
150+
String type = getButtonType(component);
151+
Collection<ClientBehaviorContext.Parameter> params = getBehaviorParameters(component);
152+
153+
if (!params.isEmpty() && (type.equals("submit") || type.equals("button"))) {
154+
RenderKitUtils.renderFacesJsIfNecessary(context);
155+
}
156+
157+
RenderKitUtils.renderOnclickEventListener(context, component, params, null, false);
158158
}
159159

160160
// --------------------------------------------------------- Private Methods

impl/src/main/java/com/sun/faces/renderkit/html_basic/CheckboxRenderer.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
import java.util.Map;
2323
import java.util.logging.Level;
2424

25-
import com.sun.faces.renderkit.Attribute;
26-
import com.sun.faces.renderkit.AttributeManager;
27-
import com.sun.faces.renderkit.RenderKitUtils;
28-
2925
import jakarta.faces.component.UIComponent;
3026
import jakarta.faces.context.FacesContext;
3127
import jakarta.faces.context.ResponseWriter;
3228
import jakarta.faces.convert.ConverterException;
3329

30+
import com.sun.faces.renderkit.Attribute;
31+
import com.sun.faces.renderkit.AttributeManager;
32+
import com.sun.faces.renderkit.RenderKitUtils;
33+
3434
/**
3535
* <B>CheckboxRenderer</B> is a class that renders the current value of <code>UISelectBoolean</code> as a checkbox.
3636
*/
@@ -104,10 +104,10 @@ protected void getEndTextToRender(FacesContext context, UIComponent component, S
104104
RenderKitUtils.renderPassThruAttributes(context, writer, component, ATTRIBUTES, getNonOnClickSelectBehaviors(component));
105105
RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);
106106

107-
RenderKitUtils.renderSelectOnclick(context, component, false);
108-
109107
writer.endElement("input");
110108

109+
RenderKitUtils.renderSelectOnclickEventListener(context, component, null, false);
110+
111111
}
112112

113113
// --------------------------------------------------------- Private Methods

0 commit comments

Comments
 (0)