ICEfaces
  1. ICEfaces
  2. ICE-10750

Duplicate HTML <BR> tags being rendered during BODY element updates

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 4.0, EE-3.3.0.GA_P03
    • Fix Version/s: 4.1, EE-3.3.0.GA_P04
    • Component/s: Framework, ICE-Components
    • Labels:
      None
    • Environment:
      All, EE 3.3.30 P03
    • Assignee Priority:
      P2
    • Support Case References:
    • Workaround Exists:
      Yes
    • Workaround Description:
      Hide
      Use MyFaces JSF OR
      - Preload the ICE Compat library by including any ice: component (hidden if necessary) on each page.
      - When using the javax.faces.FACELETS_DECORATORS context parameter, add a root container as the only child of the body and use it to wrap all the page content inside of it. Do this to every page in the application. This avoids full body updates, which is the only case when <br /> tags get duplicated.
      - Change the ice:outputText to an h:outputText.
      - Do a redirect navigation.
      Show
      Use MyFaces JSF OR - Preload the ICE Compat library by including any ice: component (hidden if necessary) on each page. - When using the javax.faces.FACELETS_DECORATORS context parameter, add a root container as the only child of the body and use it to wrap all the page content inside of it. Do this to every page in the application. This avoids full body updates, which is the only case when <br /> tags get duplicated. - Change the ice:outputText to an h:outputText. - Do a redirect navigation.

      Description

      When displaying HTML tags from a managed bean to the page via an ouputText component, these tags are being duplicated when rendered on the page. This also only happens after the page with the tags is navigated to through a forward. It also only happens if there is an ice:outputText component on the page. The component that shows the HTML tags is an h:outputText with escape="false".

      This also doesn't happen for the EE 3.3.0 P02 release.

        Activity

        Hide
        Arran Mccullough added a comment -

        Attached test case that shows the issue.

        Steps:

        • Load lftest.jsf
        • Click the 'lftest2' button. the page is forwarded to lftest2.jsf
        • When inspecting the page there should only be two <br/> tags between the letters but there is four.
        Show
        Arran Mccullough added a comment - Attached test case that shows the issue. Steps: Load lftest.jsf Click the 'lftest2' button. the page is forwarded to lftest2.jsf When inspecting the page there should only be two <br/> tags between the letters but there is four.
        Hide
        Arturo Zambrano added a comment - - edited

        After some substantial investigation, I've found a workaround and a possible solution and determined that the issue is somewhere in our own bridge. However, it's still not clear where exactly the issue is produced.

        First of all, this issue seems to only affect <br/> tags. I tried using <span>, <button> and <hr> tags, and they don't get duplicated. I also specified this markup right in the xhtml document (as opposed to retrieving it from a bean property), using escaped characters, and the issue can be reproduced as well. In both cases, the response is correct: there are no duplicate tags. They get duplicated when they are taken from the response and inserted into the DOM. Also, it's important to clarify that this happens when the content of the second page is loaded dynamically (asynchronously, without actually navigating to a different URL). It doesn't happen when one navigates directly to the second page (i.e. synchronous update).

        Going back in revisions, this issue is first seen at revision 44114, which is a commit made for ICE-10479, as Arran points out. However, this applies only to this specific test application, since the issue can be reproduced with different markup and using the code from P02 and even P01, and maybe from earlier versions as well.

        The commit mentioned above simply adds icefaces-compat.js and compat.js as resource dependencies to ice:outputText. Many other extended components already had these resource dependencies before that commit, so this issue can be reproduced using other ice:* components instead of ice:outputText. Using an empty ice:panelgroup causes the same issue, even with jars from P02.

        The specific cause of the above is simple. When there's an ice:* component (with the resource dependencies mentioned above) in the second page, there's a full body update. The single <update> tag in the response has the ID 'javax.faces.ViewBody', whereas when there are no ice:* components, the individual nodes are updated separately (i.e. multiple <update> tags). So, when the update is for 'javax.faces.ViewBody', the <br> tags get duplicated.

        The reason why a full-body update is triggered when having certain ice:* tags in the second page is that there's a new node insertion, which is a direct child of the body, and because of that, the domdiff causes an update of the whole body (which is the nearest element with a client ID). The node in question is a <span> that contains a <script> and has the ID '********_icefaces_compat_config'.

        So, a quick workaround is to include an ice:* component in the first page, so this node with the ID '********_icefaces_compat_config' is already there and can be individually updated by looking up its ID, instead of having to update the whole body.

        A possible fix is to make sure to always include this (empty) node mentioned above, even when there are no ice:* components on the page, in order to avoid full-body updates when an ice:* component is introduced. We already do that for the node with the ID 'dynamic-code-compat'.

        The above solution can solve this specific problem of introducing ice:* components in the page. However, there are still ways of reproduce this issue. I'm attaching another test page (lftest3.xhtml), which can be simply inserted into the test application. This page doesn't have any ice:* or ace:* component, just standard h:* components. This page has a boolean checkbox, which is used to render/unrender an h:panelGroup, which is a direct child of h:body. This panel group is initially unrendered, and when the checkbox is checked, this panel group is rendered, and because it's a direct child of the body, this triggers a full body update. By examining the markup, you can see that the <br> tags inside this panel group are duplicated as well. This can be reproduced with eh P01 jars as well.

        Unfortunately, the issue seems to be in our own bridge. I created a pure-JSF test application (attached) with only this page in it, and the issue couldn't be reproduced: the <br> tags weren't duplicated.

        I looked at the code in jsf.js and bridge.js (the uncompressed versions), and I couldn't find a point where tags are explicitly duplicated. It must be a side effect of some other handling or parsing.

        Show
        Arturo Zambrano added a comment - - edited After some substantial investigation, I've found a workaround and a possible solution and determined that the issue is somewhere in our own bridge. However, it's still not clear where exactly the issue is produced. First of all, this issue seems to only affect <br/> tags. I tried using <span>, <button> and <hr> tags, and they don't get duplicated. I also specified this markup right in the xhtml document (as opposed to retrieving it from a bean property), using escaped characters, and the issue can be reproduced as well. In both cases, the response is correct: there are no duplicate tags. They get duplicated when they are taken from the response and inserted into the DOM. Also, it's important to clarify that this happens when the content of the second page is loaded dynamically (asynchronously, without actually navigating to a different URL). It doesn't happen when one navigates directly to the second page (i.e. synchronous update). Going back in revisions, this issue is first seen at revision 44114, which is a commit made for ICE-10479 , as Arran points out. However, this applies only to this specific test application, since the issue can be reproduced with different markup and using the code from P02 and even P01, and maybe from earlier versions as well. The commit mentioned above simply adds icefaces-compat.js and compat.js as resource dependencies to ice:outputText. Many other extended components already had these resource dependencies before that commit, so this issue can be reproduced using other ice:* components instead of ice:outputText. Using an empty ice:panelgroup causes the same issue, even with jars from P02. The specific cause of the above is simple. When there's an ice:* component (with the resource dependencies mentioned above) in the second page, there's a full body update. The single <update> tag in the response has the ID 'javax.faces.ViewBody', whereas when there are no ice:* components, the individual nodes are updated separately (i.e. multiple <update> tags). So, when the update is for 'javax.faces.ViewBody', the <br> tags get duplicated. The reason why a full-body update is triggered when having certain ice:* tags in the second page is that there's a new node insertion, which is a direct child of the body, and because of that, the domdiff causes an update of the whole body (which is the nearest element with a client ID). The node in question is a <span> that contains a <script> and has the ID '********_icefaces_compat_config'. So, a quick workaround is to include an ice:* component in the first page, so this node with the ID '********_icefaces_compat_config' is already there and can be individually updated by looking up its ID, instead of having to update the whole body. A possible fix is to make sure to always include this (empty) node mentioned above, even when there are no ice:* components on the page, in order to avoid full-body updates when an ice:* component is introduced. We already do that for the node with the ID 'dynamic-code-compat'. The above solution can solve this specific problem of introducing ice:* components in the page. However, there are still ways of reproduce this issue. I'm attaching another test page (lftest3.xhtml), which can be simply inserted into the test application. This page doesn't have any ice:* or ace:* component, just standard h:* components. This page has a boolean checkbox, which is used to render/unrender an h:panelGroup, which is a direct child of h:body. This panel group is initially unrendered, and when the checkbox is checked, this panel group is rendered, and because it's a direct child of the body, this triggers a full body update. By examining the markup, you can see that the <br> tags inside this panel group are duplicated as well. This can be reproduced with eh P01 jars as well. Unfortunately, the issue seems to be in our own bridge. I created a pure-JSF test application (attached) with only this page in it, and the issue couldn't be reproduced: the <br> tags weren't duplicated. I looked at the code in jsf.js and bridge.js (the uncompressed versions), and I couldn't find a point where tags are explicitly duplicated. It must be a side effect of some other handling or parsing.
        Hide
        Arturo Zambrano added a comment -

        This issue can also be reproduced with ICEfaces 4.x (using lftest3.xhtml).

        Show
        Arturo Zambrano added a comment - This issue can also be reproduced with ICEfaces 4.x (using lftest3.xhtml).
        Hide
        Arran Mccullough added a comment -

        The new workaround looks to have helped the issue except when the following context param is set:

        <context-param>
        <param-name>javax.faces.FACELETS_DECORATORS</param-name>
        <param-value>com.icesoft.facelets.HtmlTagDecorator</param-value>
        </context-param>

        Show
        Arran Mccullough added a comment - The new workaround looks to have helped the issue except when the following context param is set: <context-param> <param-name>javax.faces.FACELETS_DECORATORS</param-name> <param-value>com.icesoft.facelets.HtmlTagDecorator</param-value> </context-param>
        Hide
        Arturo Zambrano added a comment -

        I couldn't reproduce the issue by simply adding the javax.faces.FACELETS_DECORATORS context parameter, with com.icesoft.facelets.HtmlTagDecorator as its value, to the original test app attached to this JIRA. I couldn't reproduce it either when using the test case that the customer provided.

        However, I was able to reproduce the issue, using said context parameter, by modifying the original test case. I simply added a <br/> tag to lftest2.xhtml as a direct child of the body, and the issue occurred again. This occurred because, when using our HtmlTagDecorator, all plain HTML tags (explicitly declared in the .xhtml document) get an ID attribute and an assigned id value. So, all plain <br /> tags get an ID attribute with a value, and if they are direct children of the body, this will cause a full-body update, which is the only case where <br /> tags get duplicated.

        The workaround for this is to add a main container to every page in the application, in order to avoid full body updates, so this main container is the node that actually gets updated instead of the body. In the original test app, simply add this tag <h:panelGroup layout="block" id="main"></h:panelGroup> as the only direct child of <h:body>, and make sure to put all the page content inside of this tag. No other content should be outside of this tag.

        This workaround works because all pages would have a <div> element with the same ID (in this case, 'main'), and because the navigation is done via dynamic updates (as opposed to redirecting to a new URL), the server will send updates for this node and not for the body, since the domdiff will detect that there's already a node with this ID.

        Note that the original workaround of including an ice:* component in the origin page is still needed.

        Show
        Arturo Zambrano added a comment - I couldn't reproduce the issue by simply adding the javax.faces.FACELETS_DECORATORS context parameter, with com.icesoft.facelets.HtmlTagDecorator as its value, to the original test app attached to this JIRA. I couldn't reproduce it either when using the test case that the customer provided. However, I was able to reproduce the issue, using said context parameter, by modifying the original test case. I simply added a <br/> tag to lftest2.xhtml as a direct child of the body, and the issue occurred again. This occurred because, when using our HtmlTagDecorator, all plain HTML tags (explicitly declared in the .xhtml document) get an ID attribute and an assigned id value. So, all plain <br /> tags get an ID attribute with a value, and if they are direct children of the body, this will cause a full-body update, which is the only case where <br /> tags get duplicated. The workaround for this is to add a main container to every page in the application, in order to avoid full body updates, so this main container is the node that actually gets updated instead of the body. In the original test app, simply add this tag <h:panelGroup layout="block" id="main"></h:panelGroup> as the only direct child of <h:body>, and make sure to put all the page content inside of this tag. No other content should be outside of this tag. This workaround works because all pages would have a <div> element with the same ID (in this case, 'main'), and because the navigation is done via dynamic updates (as opposed to redirecting to a new URL), the server will send updates for this node and not for the body, since the domdiff will detect that there's already a node with this ID. Note that the original workaround of including an ice:* component in the origin page is still needed.
        Hide
        Ken Fyten added a comment -

        Reminder that we should log a new defect in the Mojarra bug tracker for this issue. Also test with MyFaces to see if they behave differently.

        Show
        Ken Fyten added a comment - Reminder that we should log a new defect in the Mojarra bug tracker for this issue. Also test with MyFaces to see if they behave differently.
        Hide
        Mircea Toma added a comment - - edited

        It seems that the 'body' update strategy employed by Mojarra uses a XML DOM parser to generate the new nodes and then adds them one by one. When a br XML node is added to the browser's HTML body element the body ends up having to br nodes. This can easily be tested using Firebug for example.

        Show
        Mircea Toma added a comment - - edited It seems that the 'body' update strategy employed by Mojarra uses a XML DOM parser to generate the new nodes and then adds them one by one. When a br XML node is added to the browser's HTML body element the body ends up having to br nodes. This can easily be tested using Firebug for example.
        Hide
        Mircea Toma added a comment -
        Show
        Mircea Toma added a comment - Created Mojarra issue https://java.net/jira/browse/JAVASERVERFACES-4014 .
        Hide
        Mircea Toma added a comment -

        Running the test with Myfaces does not result in br node duplication.

        Show
        Mircea Toma added a comment - Running the test with Myfaces does not result in br node duplication.

          People

          • Assignee:
            Mircea Toma
            Reporter:
            Arran Mccullough
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: