MYFACES-4280 allow behavior scripts as function instead of string
diff --git a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
index ace25c1..9406e33 100755
--- a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
+++ b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
@@ -822,6 +822,19 @@
public static final String WEBSOCKET_MAX_CONNECTIONS = "org.apache.myfaces.WEBSOCKET_MAX_CONNECTIONS";
public static final Integer WEBSOCKET_MAX_CONNECTIONS_DEFAULT = 5000;
+
+ /**
+ * Defines if the clientbehavior scripts are passed as string or function to the jsf.util.chain.
+ * "As string" is actually the default behavior of both MyFaces (until 3.0) and Mojarra.
+ * "As function" is quite usefull for CSP as no string needs to be evaluated as function.
+ *
+ * Our jsf.util.chain supports both of course.
+ */
+ @JSFWebConfigParam(name="org.apache.myfaces.RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING", since="3.0", defaultValue = "false")
+ public static final String RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING = "org.apache.myfaces.RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING";
+ public static final boolean RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT = false;
+
+
// we need it, applicationImpl not ready probably
private ProjectStage projectStage = ProjectStage.Production;
private boolean strictJsf2AllowSlashLibraryName;
@@ -902,6 +915,7 @@
private boolean resourceCacheLastModified = RESOURCE_CACHE_LAST_MODIFIED_DEFAULT;
private boolean logWebContextParams = false;
private int websocketMaxConnections = WEBSOCKET_MAX_CONNECTIONS_DEFAULT;
+ private boolean renderClientBehaviorScriptsAsString = RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT;
private static final boolean MYFACES_IMPL_AVAILABLE;
private static final boolean RI_IMPL_AVAILABLE;
@@ -1316,6 +1330,9 @@
cfg.websocketMaxConnections = getInt(extCtx, WEBSOCKET_MAX_CONNECTIONS,
WEBSOCKET_MAX_CONNECTIONS_DEFAULT);
+ cfg.renderClientBehaviorScriptsAsString = getBoolean(extCtx, RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING,
+ RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT);
+
return cfg;
}
@@ -1781,5 +1798,9 @@
return websocketMaxConnections;
}
+ public boolean isRenderClientBehaviorScriptsAsString()
+ {
+ return renderClientBehaviorScriptsAsString;
+ }
}
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/ClientBehaviorRendererUtils.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/ClientBehaviorRendererUtils.java
index 439bda7..3aa334d 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/ClientBehaviorRendererUtils.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/ClientBehaviorRendererUtils.java
@@ -30,12 +30,15 @@
import javax.faces.component.behavior.ClientBehaviorHint;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.FacesContext;
+import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.renderkit.RendererUtils;
import org.apache.myfaces.renderkit.html.util.JavascriptContext;
import org.apache.myfaces.util.lang.StringUtils;
public class ClientBehaviorRendererUtils
{
+ private static final boolean RENDER_AS_STRING = false;
+
public static void decodeClientBehaviors(FacesContext facesContext, UIComponent component)
{
if (!(component instanceof ClientBehaviorHolder))
@@ -132,6 +135,7 @@
* renderer default script
*
* @param eventName event name ("onclick" etc...)
+ * @param config the {@link MyfacesConfig}
* @param uiComponent the component which has the attachement (or should have)
* @param facesContext the facesContext
* @param params params map of params which have to be dragged into the request
@@ -139,7 +143,9 @@
* an empty string if none is present
*/
private static boolean getClientBehaviorScript(FacesContext facesContext,
- UIComponent uiComponent, String sourceId, String eventName,
+ MyfacesConfig config,
+ UIComponent uiComponent,
+ String sourceId, String eventName,
Map<String, List<ClientBehavior>> clientBehaviors,
JavascriptContext target,
Collection<ClientBehaviorContext.Parameter> params)
@@ -176,7 +182,7 @@
{
ClientBehavior clientBehavior = attachedEventBehaviors.get(i);
submitting = appendClientBehaviourScript(target, context,
- submitting, i < (size -1), clientBehavior);
+ submitting, i < (size -1), clientBehavior, config);
}
}
else
@@ -186,7 +192,7 @@
{
ClientBehavior clientBehavior = clientIterator.next();
submitting = appendClientBehaviourScript(target, context, submitting,
- clientIterator.hasNext(), clientBehavior);
+ clientIterator.hasNext(), clientBehavior, config);
}
}
@@ -194,16 +200,15 @@
}
private static boolean appendClientBehaviourScript(JavascriptContext target, ClientBehaviorContext context,
- boolean submitting, boolean hasNext, ClientBehavior clientBehavior)
+ boolean submitting, boolean hasNext, ClientBehavior clientBehavior, MyfacesConfig config)
{
String script = clientBehavior.getScript(context);
+
// The script _can_ be null, and in fact is for <f:ajax disabled="true" />
if (script != null)
{
- //either strings or functions, but I assume string is more appropriate
- //since it allows access to the
- //origin as this!
- target.append('\'' + escapeJavaScriptForChain(script) + '\'');
+ addFunction(script, target, config);
+
if (hasNext)
{
target.append(", ");
@@ -221,7 +226,8 @@
}
public static String buildBehaviorChain(FacesContext facesContext,
- UIComponent uiComponent, String eventName,
+ UIComponent uiComponent,
+ String eventName,
Collection<ClientBehaviorContext.Parameter> params,
Map<String, List<ClientBehavior>> clientBehaviors,
String userEventCode, String serverEventCode)
@@ -232,23 +238,24 @@
}
public static String buildBehaviorChain(FacesContext facesContext,
- UIComponent uiComponent, String sourceId, String eventName,
+ UIComponent uiComponent,
+ String sourceId, String eventName,
Collection<ClientBehaviorContext.Parameter> params,
Map<String, List<ClientBehavior>> clientBehaviors,
String userEventCode, String serverEventCode)
{
+ MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext);
+
List<String> functions = new ArrayList<>(3);
if (StringUtils.isNotBlank(userEventCode))
{
- // escape every ' in the user event code since it will
- // be a string attribute of jsf.util.chain
- functions.add('\'' + escapeJavaScriptForChain(userEventCode) + '\'');
+ addFunction(userEventCode, functions, config);
}
JavascriptContext chainContext = new JavascriptContext();
JavascriptContext behaviorContext = new JavascriptContext();
- getClientBehaviorScript(facesContext, uiComponent, sourceId,
+ getClientBehaviorScript(facesContext, config, uiComponent, sourceId,
eventName, clientBehaviors, behaviorContext, params);
String behaviorScript = behaviorContext.toString();
@@ -258,7 +265,7 @@
}
if (StringUtils.isNotBlank(serverEventCode))
{
- functions.add('\'' + escapeJavaScriptForChain(serverEventCode) + '\'');
+ addFunction(serverEventCode, functions, config);
}
// It's possible that there are no behaviors to render.
@@ -292,19 +299,6 @@
return chainContext.toString();
}
- /**
- * @param facesContext
- * @param uiComponent
- * @param eventName1
- * @param params1
- * @param eventName2
- * @param params2
- * @param clientBehaviors
- * @param userEventCode
- * @param serverEventCode
-
- * @return
- */
public static String buildBehaviorChain(FacesContext facesContext,
UIComponent uiComponent,
String eventName1,
@@ -315,7 +309,8 @@
String userEventCode,
String serverEventCode)
{
- return buildBehaviorChain(facesContext, uiComponent, null,
+ return buildBehaviorChain(facesContext,
+ uiComponent, null,
eventName1, params1,
eventName2, params2,
clientBehaviors, userEventCode, serverEventCode);
@@ -332,21 +327,23 @@
String userEventCode,
String serverEventCode)
{
+ MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext);
+
List<String> functions = new ArrayList<>(3);
if (StringUtils.isNotBlank(userEventCode))
{
- functions.add('\'' + escapeJavaScriptForChain(userEventCode) + '\'');
+ addFunction(userEventCode, functions, config);
}
JavascriptContext chainContext = new JavascriptContext();
JavascriptContext behaviorContext1 = new JavascriptContext();
- boolean submitting1 = getClientBehaviorScript(facesContext,
+ boolean submitting1 = getClientBehaviorScript(facesContext, config,
uiComponent, sourceId, eventName1, clientBehaviors,
behaviorContext1, params1);
JavascriptContext behaviorContext2 = new JavascriptContext();
- boolean submitting2 = getClientBehaviorScript(facesContext,
+ boolean submitting2 = getClientBehaviorScript(facesContext, config,
uiComponent, sourceId, eventName2, clientBehaviors,
behaviorContext2, params2);
@@ -367,7 +364,7 @@
if (StringUtils.isNotBlank(serverEventCode))
{
- functions.add('\'' + escapeJavaScriptForChain(serverEventCode) + '\'');
+ addFunction(serverEventCode, functions, config);
}
// It's possible that there are no behaviors to render. For example, if we have
@@ -453,4 +450,43 @@
return out.toString();
}
}
+
+ private static void addFunction(String function, List<String> functions, MyfacesConfig config)
+ {
+ if (StringUtils.isNotBlank(function))
+ {
+ // either strings or functions are allowed
+ if (config.isRenderClientBehaviorScriptsAsString())
+ {
+ // escape every ' in the user event code since it will be a string attribute of jsf.util.chain
+ functions.add('\'' + escapeJavaScriptForChain(function) + '\'');
+ }
+ else
+ {
+ functions.add("function(event){" + function + "}");
+ }
+ }
+ }
+
+ private static void addFunction(String function, JavascriptContext target, MyfacesConfig config)
+ {
+ if (StringUtils.isNotBlank(function))
+ {
+ // either strings or functions are allowed
+ if (config.isRenderClientBehaviorScriptsAsString())
+ {
+ // escape every ' in the user event code since it will be a string attribute of jsf.util.chain
+ target.append('\'');
+ target.append(escapeJavaScriptForChain(function));
+ target.append('\'');
+ }
+ else
+ {
+ target.append("function(event){");
+ target.append(function);
+ target.append('}');
+ }
+ }
+ }
+
}