Search code examples
internet-explorerjsfiframeindexoutofboundsexceptionstate-saving

Submitting form in iframe in IE11 throws java.lang.StringIndexOutOfBoundsException while restoring JSF state


After the user get's redirected from a session bean, the following exception is thrown:

StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1911
 at com.sun.faces.renderkit.ServerSideStateHelper.getState(ServerSideStateHelper.java:266
 at com.sun.faces.renderkit.ResponseStateManagerImpl.getState(ResponseStateManagerImpl.java:100
 at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:227
 at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:188
 at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123
 at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:453
 at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:148
 at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:303
 at org.omnifaces.viewhandler.RestorableViewHandler.restoreView(RestorableViewHandler.java:66
 at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:303
 at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:192
 at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101
 at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116
 at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593
 at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175
 at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655
 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161
 at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231
 at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317
 at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195
 at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860
 at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757
 at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056
 at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229
 at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137
 at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104
 at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90
 at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79
 at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54
 at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59
 at com.sun.grizzly.ContextTask.run(ContextTask.java:71
 at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532
 at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513
 at java.lang.Thread.run(Thread.java:745)

The button code uses the actionListener attribute:

actionListener="#{aController.aMethod}"

The controller performs the redirect like this:

ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
String encodedURL = response.encodeURL(context.getRequestContextPath() + "/page.xhtml?faces-redirect=true");
context.redirect(encodedURL);

My session config:

<session-config>
    <session-timeout>60</session-timeout>
    <tracking-mode>URL</tracking-mode>
</session-config>

The page only contains one single form (as well as the generated HTML output), defined:

<form id="wrapper" name="wrapper" method="post" action="/ACETOwebkalender/a/b.xhtml" enctype="application/x-www-form-urlencoded">

The outer pages uses the Slider Revolution Responsive WordPress Plugin, which uses jQuery.ajax. May this produce the issue?

This issue only appears in IE11 and Safari, when the page is embedded in another page using iFrame.


Solution

  • Fixed the bug for Safari with the following code snippet on every single page:

        <script>
        window.onload = function() {
            if (top != self) { // If true, then page is been requested inside an iframe.
                var jsessionid = '#{session.id}';
                var forms = document.forms;
    
                for (i = 0; i &lt; forms.length; i++) {
                    forms[i].action += ';JSESSIONID=' + jsessionid;
              }
            }
        };
    </script>
    

    And for IE, I updated the web.xml to following:

    ....
    <filter>
        <filter-name>P3P Response Filter</filter-name>
        <filter-class>util.ResponseFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>P3P Response Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <session-config>
        <session-timeout>60</session-timeout>
        <cookie-config>
            <http-only>true</http-only>
            <secure>true</secure>
        </cookie-config>
        <tracking-mode>COOKIE</tracking-mode>
        <tracking-mode>URL</tracking-mode>
    </session-config>
    ...
    

    Where ResponseFilter.class is:

    import java.io.IOException;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;
    
    public class ResponseFilter implements Filter {
    
     @Override
     public void init(FilterConfig filterConfig) throws ServletException {}
    
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletResponse resp = (HttpServletResponse) response;
      resp.addHeader("P3P", "CP=\"DC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");
      chain.doFilter(request, resp);
     }
    
     @Override
     public void destroy() {}
    }