This is a problem with escaping the CData termination sequence, but there are several facets to the problem.
Keep in mind for the following problems that there are two modes of operation. The initial rendering mode when the output is HTML and the CData termination block has no special meaning and update mode when the updates are sent in XML where it obviously will terminate the update CData section.
- For the purposes of the <h:outputText> component, the solution is to escape the '>' in all cases, thus the string ]]> becomes ]]> in all rendered text. This is appropriate for initial rendering in HTML as well as updates of outputText components written in updates (as well as mirrors what the native JSF components do)
- There is a problem with the <h:inputText> component in ICEFaces, but only on the very first POSTBack. An input Text component displays its 'value' verbatim, so escaping the '>' char doesn't work and will show up as the literal string ]]> because it's rendered in HTML.
Normally we don't render inputText components as part of an <update> block. This is accomplished by setting the POSTBack values into the OLD DOM so that they match the rendered values in the new DOM and thusly don't show up in the nodeDiff array. However, when we render the response from the first POSTback we see the following. What it shows is that some script has been added to the form offsetting each child by 1 basically. The upshot of this process is that the Form is added to the diff block, and this will render out all its children.
Diff in node String value, oldNode: [#text: ], new Node: [#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>], adding newNode
[#text: <script type='text/javascript'>ice.captureSubmit('form1');</script>]
Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null]
Diff in Node Name, oldNode: span, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: span, adding parent: [form: null]
Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
Diff in Node Name, oldNode: input, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: #comment, adding parent: [form: null]
Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: input, new Node: br, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
Diff in Node Name, oldNode: br, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: br, adding parent: [form: null]
Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
Diff in Node Name, oldNode: #comment, new Node: #text, adding parent: [form: null]
Diff in Node Name, oldNode: input, new Node: #comment, adding parent: [form: null]
Diff in Node Id, oldNodeId: form1:reload, new NodeId: form1:_t18, adding parent: [form: null]
Diff in Node Name, oldNode: #text, new Node: input, adding parent: [form: null]
As a result of having the Form in the update, the following update is constructed:
<partial-response><changes><update id="form1"><![CDATA[<form action="/escape/icefaces.jsf" enctype="application/x-www-form-urlencoded" id="form1" method="post" name="form1">
<input name="form1" type="hidden" value="form1" />
<script type='text/javascript'>ice.captureSubmit('form1');</script>
Output: <span id="form1:out1">aaa]]>bbb</span> <br />
Input: <input id="form1:in1" name="form1:in1" type="text" value="aaa]]>bbb" /> <br />
<br />
<input id="form1:reset" name="form1:reset" onclick="return resetpush(this, event);" type="submit" value="reset" />
<input id="form1:_t18" name="form1:_t18" type="submit" value="send it in" /><input id="form1:reload" name="form1:reload" type="submit" value="reload" />
</form>]]></update><update id="javax.faces.ViewState"><![CDATA[3863213446070469989:3513529411051212321]]></update></changes></partial-response>
Here the output Text has been escaped properly, but the inputText causes a problem since this is now XML content. If we escape the '>', the escaped characters show up verbatim, if we don't escape the characters, it terminates the update CData block.
Inputting ']]>' in an inputText in the first POSTback causes the bridge to dispose of the view and a reload to be performed, which leaves us in the position of being vulnerable to the problem repeatedly. However, if the first POSTback doesn't contain ]]>, for all subsequent requests the script block of the form never changes and the update block only contains the outputText string and the application works and supports ]]> properly.
The key factor is that component output is escaped as expected; whether the escaping occurs upon input to the DOM or output from the serializer is an implementation decision.