Coverage Report - org.apache.myfaces.shared.renderkit.html.HtmlJavaScriptUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
HtmlJavaScriptUtils
8%
15/185
21%
13/60
3.333
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.shared.renderkit.html;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.util.Iterator;
 23  
 import java.util.Map;
 24  
 import java.util.Set;
 25  
 import java.util.logging.Logger;
 26  
 
 27  
 import javax.faces.context.ExternalContext;
 28  
 import javax.faces.context.FacesContext;
 29  
 import javax.faces.context.ResponseWriter;
 30  
 
 31  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 32  
 import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils.ScriptContext;
 33  
 import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
 34  
 import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
 35  
 
 36  0
 public final class HtmlJavaScriptUtils
 37  
 {
 38  1
     private static final Logger log = Logger.getLogger(HtmlJavaScriptUtils.class
 39  
             .getName());
 40  
 
 41  
     private static final String AUTO_SCROLL_PARAM = "autoScroll";
 42  
     private static final String AUTO_SCROLL_FUNCTION = "getScrolling";
 43  
 
 44  
     private static final String SET_HIDDEN_INPUT_FN_NAME = "oamSetHiddenInput";
 45  
     private static final String SET_HIDDEN_INPUT_FN_NAME_JSF2 = "myfaces.oam.setHiddenInput";
 46  
 
 47  
     private static final String FIRST_SUBMIT_SCRIPT_ON_PAGE = "org.apache.MyFaces.FIRST_SUBMIT_SCRIPT_ON_PAGE";
 48  
     private static final String CLEAR_HIDDEN_INPUT_FN_NAME = "oamClearHiddenInput";
 49  
     
 50  
 
 51  
     @SuppressWarnings("unchecked")
 52  
     public static void renderFormSubmitScript(FacesContext facesContext)
 53  
             throws IOException
 54  
     {
 55  0
         if (facesContext.getPartialViewContext() != null && 
 56  
                 (facesContext.getPartialViewContext().isPartialRequest() ||
 57  
                  facesContext.getPartialViewContext().isAjaxRequest() )
 58  
             )
 59  
         {
 60  0
             return;
 61  
         }
 62  
 
 63  0
         Map map = facesContext.getExternalContext().getRequestMap();
 64  0
         Boolean firstScript = (Boolean) map.get(FIRST_SUBMIT_SCRIPT_ON_PAGE);
 65  
 
 66  0
         if (firstScript == null || firstScript.equals(Boolean.TRUE))
 67  
         {
 68  0
             map.put(FIRST_SUBMIT_SCRIPT_ON_PAGE, Boolean.FALSE);
 69  0
             renderFormSubmitScriptIfNecessary(facesContext);
 70  
 
 71  
             //we have to render the config just in case
 72  0
             renderConfigOptionsIfNecessary(facesContext);
 73  
         }
 74  0
     }
 75  
     
 76  
     /**
 77  
      * @param facesContext
 78  
      * @throws IOException
 79  
      */
 80  
     private static void renderFormSubmitScriptIfNecessary(
 81  
             FacesContext facesContext) throws IOException
 82  
     {
 83  0
         ResponseWriter writer = facesContext.getResponseWriter();
 84  0
         ResourceUtils
 85  
                 .renderMyfacesJSInlineIfNecessary(facesContext, writer);
 86  0
     }
 87  
     
 88  
     private static void renderConfigOptionsIfNecessary(FacesContext facesContext)
 89  
             throws IOException
 90  
     {
 91  0
         ResponseWriter writer = facesContext.getResponseWriter();
 92  0
         MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext
 93  
                 .getExternalContext());
 94  0
         ScriptContext script = new ScriptContext();
 95  0
         boolean autoScroll = config.isAutoScroll();
 96  0
         boolean autoSave = JavascriptUtils.isSaveFormSubmitLinkIE(facesContext
 97  
                 .getExternalContext());
 98  
 
 99  0
         if (autoScroll || autoSave)
 100  
         {
 101  0
             script.prettyLine();
 102  0
             script.increaseIndent();
 103  0
             script.append("(!window.myfaces) ? window.myfaces = {} : null;");
 104  0
             script.append("(!myfaces.core) ? myfaces.core = {} : null;");
 105  0
             script.append("(!myfaces.core.config) ? myfaces.core.config = {} : null;");
 106  
         }
 107  
 
 108  0
         if (autoScroll)
 109  
         {
 110  0
             script.append("myfaces.core.config.autoScroll = true;");
 111  
         }
 112  0
         if (autoSave)
 113  
         {
 114  0
             script.append("myfaces.core.config.ieAutoSave = true;");
 115  
         }
 116  0
         if (autoScroll || autoSave)
 117  
         {
 118  0
             writer.startElement(HTML.SCRIPT_ELEM, null);
 119  0
             writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
 120  0
             writer.writeText(script.toString(), null);
 121  0
             writer.endElement(HTML.SCRIPT_ELEM);
 122  
         }
 123  0
     }
 124  
     
 125  
     public static void appendAutoScrollAssignment(StringBuilder onClickValue,
 126  
             String formName)
 127  
     {
 128  0
         appendAutoScrollAssignment(FacesContext.getCurrentInstance(),
 129  
                 new ScriptContext(onClickValue, false), formName);
 130  0
     }
 131  
 
 132  
     /**
 133  
      * Adds the hidden form input value assignment that is necessary for the autoscroll
 134  
      * feature to an html link or button onclick attribute.
 135  
      */
 136  
     public static void appendAutoScrollAssignment(FacesContext context,
 137  
             StringBuilder onClickValue, String formName)
 138  
     {
 139  0
         appendAutoScrollAssignment(context, new ScriptContext(onClickValue,
 140  
                 false), formName);
 141  0
     }
 142  
     
 143  
     private static void appendAutoScrollAssignment(FacesContext context,
 144  
             ScriptContext scriptContext, String formName)
 145  
     {
 146  0
         String formNameStr = formName == null ? "formName" : (new StringBuilder(
 147  
                 "'").append(formName).append("'").toString());
 148  0
         String paramName = new StringBuilder().append("'")
 149  
                 .append(AUTO_SCROLL_PARAM).append("'").toString();
 150  0
         String value = new StringBuilder().append(AUTO_SCROLL_FUNCTION)
 151  
                 .append("()").toString();
 152  
 
 153  0
         scriptContext.prettyLine();
 154  0
         scriptContext.append("if(typeof window." + AUTO_SCROLL_FUNCTION
 155  
                 + "!='undefined')");
 156  0
         scriptContext.append("{");
 157  0
         scriptContext.append(SET_HIDDEN_INPUT_FN_NAME_JSF2);
 158  0
         scriptContext.append("(").append(formNameStr).append(",")
 159  
                 .append(paramName).append(",").append(value).append(");");
 160  0
         scriptContext.append("}");
 161  
 
 162  0
     }
 163  
     
 164  
     public static String getAutoScrollFunction(FacesContext facesContext)
 165  
     {
 166  0
         ScriptContext script = new ScriptContext();
 167  
 
 168  0
         script.prettyLineIncreaseIndent();
 169  
 
 170  0
         script.append("function ");
 171  0
         script.append(AUTO_SCROLL_FUNCTION);
 172  0
         script.append("()");
 173  0
         script.append("{");
 174  0
         script.append("var x = 0; var y = 0;");
 175  0
         script.append("if (self.pageXOffset || self.pageYOffset)");
 176  0
         script.append("{");
 177  0
         script.append("x = self.pageXOffset;");
 178  0
         script.prettyLine();
 179  0
         script.append("y = self.pageYOffset;");
 180  0
         script.append("}");
 181  0
         script.append(" else if ((document.documentElement && document.documentElement.scrollLeft)||"+
 182  
                 "(document.documentElement && document.documentElement.scrollTop))");
 183  0
         script.append("{");
 184  0
         script.append("x = document.documentElement.scrollLeft;");
 185  0
         script.prettyLine();
 186  0
         script.append("y = document.documentElement.scrollTop;");
 187  0
         script.append("}");
 188  0
         script.append(" else if (document.body) ");
 189  0
         script.append("{");
 190  0
         script.append("x = document.body.scrollLeft;");
 191  0
         script.prettyLine();
 192  0
         script.append("y = document.body.scrollTop;");
 193  0
         script.append("}");
 194  0
         script.append("return x + \",\" + y;");
 195  0
         script.append("}");
 196  
 
 197  0
         ExternalContext externalContext = facesContext.getExternalContext();
 198  0
         String oldViewId = JavascriptUtils.getOldViewId(externalContext);
 199  0
         if (oldViewId != null
 200  
                 && oldViewId.equals(facesContext.getViewRoot().getViewId()))
 201  
         {
 202  
             //ok, we stayed on the same page, so let's scroll it to the former place
 203  0
             String scrolling = (String) externalContext
 204  
                     .getRequestParameterMap().get(AUTO_SCROLL_PARAM);
 205  0
             if (scrolling != null && scrolling.length() > 0)
 206  
             {
 207  0
                 double x = 0;
 208  0
                 double y = 0;
 209  0
                 int comma = scrolling.indexOf(',');
 210  0
                 if (comma == -1)
 211  
                 {
 212  0
                     log.warning("Illegal autoscroll request parameter: "
 213  
                             + scrolling);
 214  
                 }
 215  
                 else
 216  
                 {
 217  
                     try
 218  
                     {
 219  
                         //we convert to double against XSS vulnerability
 220  0
                         x = Double.parseDouble(scrolling.substring(0, comma));
 221  
                     }
 222  0
                     catch (NumberFormatException e)
 223  
                     {
 224  0
                         log.warning("Error getting x offset for autoscroll feature. Bad param value: "
 225  
                                 + scrolling);
 226  0
                         x = 0; //ignore false numbers
 227  0
                     }
 228  
 
 229  
                     try
 230  
                     {
 231  
                         //we convert to int against XSS vulnerability
 232  0
                         y = Integer.parseInt(scrolling.substring(comma + 1));
 233  
                     }
 234  0
                     catch (NumberFormatException e)
 235  
                     {
 236  0
                         log.warning("Error getting y offset for autoscroll feature. Bad param value: "
 237  
                                 + scrolling);
 238  0
                         y = 0; //ignore false numbers
 239  0
                     }
 240  
                 }
 241  0
                 script.append("window.scrollTo(").append(String.valueOf(x)).append(",")
 242  
                         .append(String.valueOf(y)).append(");\n");
 243  
             }
 244  
         }
 245  
 
 246  0
         return script.toString();
 247  
     }
 248  
     
 249  
     /**
 250  
      * Renders the hidden form input that is necessary for the autoscroll feature.
 251  
      */
 252  
     public static void renderAutoScrollHiddenInput(FacesContext facesContext,
 253  
             ResponseWriter writer) throws IOException
 254  
     {
 255  0
         writer.startElement(HTML.INPUT_ELEM, null);
 256  0
         writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
 257  0
         writer.writeAttribute(HTML.NAME_ATTR, AUTO_SCROLL_PARAM, null);
 258  0
         writer.endElement(HTML.INPUT_ELEM);
 259  0
     }
 260  
 
 261  
     /**
 262  
      * Renders the autoscroll javascript function.
 263  
      */
 264  
     public static void renderAutoScrollFunction(FacesContext facesContext,
 265  
             ResponseWriter writer) throws IOException
 266  
     {
 267  0
         writer.startElement(HTML.SCRIPT_ELEM, null);
 268  0
         writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,
 269  
                 HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
 270  0
         writer.writeText(getAutoScrollFunction(facesContext), null);
 271  0
         writer.endElement(HTML.SCRIPT_ELEM);
 272  0
     }
 273  
     
 274  
     public static void appendClearHiddenCommandFormParamsFunctionCall(
 275  
             StringBuilder buf, String formName)
 276  
     {
 277  0
         appendClearHiddenCommandFormParamsFunctionCall(new ScriptContext(buf,
 278  
                 false), formName);
 279  0
     }
 280  
     
 281  
     private static void appendClearHiddenCommandFormParamsFunctionCall(
 282  
             ScriptContext context, String formName)
 283  
     {
 284  0
         String functionName = HtmlRendererUtils
 285  
                 .getClearHiddenCommandFormParamsFunctionName(formName);
 286  0
         if (formName == null)
 287  
         {
 288  0
             context.prettyLine();
 289  0
             context.append("var clearFn = ");
 290  0
             context.append(functionName);
 291  0
             context.append(";");
 292  0
             context.prettyLine();
 293  0
             context.append("if(typeof window[clearFn] =='function')");
 294  0
             context.append("{");
 295  0
             context.append("window[clearFn](formName);");
 296  0
             context.append("}");
 297  
         }
 298  
         else
 299  
         {
 300  0
             context.prettyLine();
 301  0
             context.append("if(typeof window.");
 302  0
             context.append(functionName);
 303  0
             context.append("=='function')");
 304  0
             context.append("{");
 305  0
             context.append(functionName).append("('").append(formName)
 306  
                     .append("');");
 307  0
             context.append("}");
 308  
         }
 309  0
     }
 310  
     
 311  
     /**
 312  
      * Prefixes the given String with "clear_" and removes special characters
 313  
      *
 314  
      * @param formName
 315  
      * @return String
 316  
      */
 317  
     public static String getClearHiddenCommandFormParamsFunctionName(
 318  
             String formName)
 319  
     {
 320  0
         final char separatorChar = FacesContext.getCurrentInstance().getNamingContainerSeparatorChar();
 321  0
         if (formName == null)
 322  
         {
 323  0
             return "'" + HtmlRendererUtils.CLEAR_HIDDEN_FIELD_FN_NAME
 324  
                     + "_'+formName.replace(/-/g, '\\$" + separatorChar
 325  
                     + "').replace(/" + separatorChar + "/g,'_')";
 326  
         }
 327  
 
 328  0
         return JavascriptUtils
 329  
                 .getValidJavascriptNameAsInRI(HtmlRendererUtils.CLEAR_HIDDEN_FIELD_FN_NAME + "_"
 330  
                         + formName.replace(separatorChar, '_'));
 331  
     }
 332  
 
 333  
     public static String getClearHiddenCommandFormParamsFunctionNameMyfacesLegacy(
 334  
             String formName)
 335  
     {
 336  0
         return "clear_"
 337  
                 + JavascriptUtils.getValidJavascriptName(formName, false);
 338  
     }
 339  
     
 340  
     /**
 341  
      * Render the javascript function that is called on a click on a commandLink
 342  
      * to clear the hidden inputs. This is necessary because on a browser back,
 343  
      * each hidden input still has it's old value (browser cache!) and therefore
 344  
      * a new submit would cause the according action once more!
 345  
      *
 346  
      * @param writer
 347  
      * @param formName
 348  
      * @param dummyFormParams
 349  
      * @param formTarget
 350  
      * @throws IOException
 351  
      */
 352  
     public static void renderClearHiddenCommandFormParamsFunction(
 353  
             ResponseWriter writer, String formName, Set dummyFormParams,
 354  
             String formTarget) throws IOException
 355  
     {
 356  
         //render the clear hidden inputs javascript function
 357  0
         String functionName = getClearHiddenCommandFormParamsFunctionName(formName);
 358  0
         writer.startElement(HTML.SCRIPT_ELEM, null);
 359  0
         writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
 360  
 
 361  
         // Using writeComment instead of write with <!-- tag
 362  0
         StringBuilder script = new StringBuilder();
 363  0
         script.append("function ");
 364  0
         script.append(functionName);
 365  0
         script.append("() {");
 366  0
         if (dummyFormParams != null)
 367  
         {
 368  0
             script.append("\n  var f = document.forms['");
 369  0
             script.append(formName);
 370  0
             script.append("'];");
 371  0
             int i = 0;
 372  0
             for (Iterator it = dummyFormParams.iterator(); it.hasNext();)
 373  
             {
 374  0
                 String elemVarName = "elem" + i;
 375  0
                 script.append("\n  var ").append(elemVarName).append(" = ");
 376  0
                 script.append("f.elements['").append((String) it.next())
 377  
                         .append("'];");
 378  0
                 script.append("\n  if(typeof ").append(elemVarName)
 379  
                         .append(" !='undefined' && ");
 380  0
                 script.append(elemVarName).append(".nodeName=='INPUT'){");
 381  0
                 script.append("\n   if (").append(elemVarName)
 382  
                         .append(".value != '') {");
 383  0
                 script.append("\n    " + elemVarName + ".value='';");
 384  0
                 script.append("\n   }");
 385  0
                 script.append("\n  }");
 386  0
                 i++;
 387  0
             }
 388  
         }
 389  
         // clear form target
 390  0
         script.append("\n  f.target=");
 391  0
         if (formTarget == null || formTarget.length() == 0)
 392  
         {
 393  
             //Normally one would think that setting target to null has the
 394  
             //desired effect, but once again IE is different...
 395  
             //Setting target to null causes IE to open a new window!
 396  0
             script.append("'';");
 397  
         }
 398  
         else
 399  
         {
 400  0
             script.append("'");
 401  0
             script.append(formTarget);
 402  0
             script.append("';");
 403  
         }
 404  0
         script.append("\n}");
 405  
 
 406  
         //Just to be sure we call this clear method on each load.
 407  
         //Otherwise in the case, that someone submits a form by pressing Enter
 408  
         //within a text input, the hidden inputs won't be cleared!
 409  0
         script.append("\n");
 410  0
         script.append(functionName);
 411  0
         script.append("();");
 412  
 
 413  0
         writer.writeText(script.toString(), null);
 414  0
         writer.endElement(HTML.SCRIPT_ELEM);
 415  0
     }
 416  
     
 417  
     /**
 418  
      * This function correctly escapes the given JavaScript code
 419  
      * for the use in the jsf.util.chain() JavaScript function.
 420  
      * It also handles double-escaping correclty.
 421  
      *
 422  
      * @param javaScript
 423  
      * @return
 424  
      */
 425  
     public static String escapeJavaScriptForChain(String javaScript)
 426  
     {
 427  
         // first replace \' with \\'
 428  
         //String escaped = StringUtils.replace(javaScript, "\\'", "\\\\'");
 429  
 
 430  
         // then replace ' with \'
 431  
         // (this will replace every \' in the original to \\\')
 432  
         //escaped = StringUtils.replace(escaped, '\'', "\\'");
 433  
 
 434  
         //return escaped;
 435  
 
 436  13
         StringBuilder out = null;
 437  149
         for (int pos = 0; pos < javaScript.length(); pos++)
 438  
         {
 439  136
             char c = javaScript.charAt(pos);
 440  
 
 441  136
             if (c == '\\' || c == '\'')
 442  
             {
 443  7
                 if (out == null)
 444  
                 {
 445  3
                     out = new StringBuilder(javaScript.length() + 8);
 446  3
                     if (pos > 0)
 447  
                     {
 448  3
                         out.append(javaScript, 0, pos);
 449  
                     }
 450  
                 }
 451  7
                 out.append('\\');
 452  
             }
 453  136
             if (out != null)
 454  
             {
 455  47
                 out.append(c);
 456  
             }
 457  
         }
 458  
 
 459  13
         if (out == null)
 460  
         {
 461  10
             return javaScript;
 462  
         }
 463  
         else
 464  
         {
 465  3
             return out.toString();
 466  
         }
 467  
     }
 468  
 }