JDK-8048110 : Using tables in JTextPane leads to infinite loop in FlowLayout.layoutRow
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 7u60,8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2014-06-24
  • Updated: 2015-06-04
  • Resolved: 2014-09-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u80Fixed 8u40Fixed 9 b33Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.7.0_60"
Java(TM) SE Runtime Environment (build 1.7.0_60-b19)
Java HotSpot(TM) Client VM (build 24.60-b09, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
We are using a JTextPane to display HTML content in a Swing application. For this we also use Swing's HTMLEditorKit and HTMLDocument implementation. The application works fine when the document contains simple HTML. 
If we replace a paragraph inside the HTML document with an HTML table by using the HTMLDocument.setInnerHTML method, this method never returns and hangs in an infinite loop.

When searching for a workaround we evaluated using the HTMLDocument.insertAfterStart method instead. This method returns, but the layouting of the simple table fails as there are multiple tables visible now. Thus, using this method (or the related insert... methods) does not solve the problem.

The application works fine with Java releases prior 1.7.0_60.
The problem occurs also with Java 8 and Java 8u5.

REGRESSION.  Last worked in version 7u55

ADDITIONAL REGRESSION INFORMATION: 
java version "1.7.0_55"
Java(TM) SE Runtime Environment (build 1.7.0_55-b13)
Java HotSpot(TM) Client VM (build 24.55-b03, mixed mode, sharing)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the test program below, a Swing frame will be displayed
2. Click the button

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The application displays the simple 2x2 HTML table in the text pane.
ACTUAL -
The application freezes and consumes 100% of a CPU core.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Stack trace of the thread "AWT-EventQueue-0":

java.util.Arrays.copyOf(java.lang.Object[ ], int, java.lang.Class) (line: 2245)
java.util.Arrays.copyOf(java.lang.Object[ ], int)
java.util.Vector.grow(int) (line: 262)
java.util.Vector.ensureCapacityHelper(int) (line: 242)
java.util.Vector.add(java.lang.Object) (line: 778)
javax.swing.text.FlowView$FlowStrategy.layoutRow(javax.swing.text.FlowView, int, int) (line: 563)
javax.swing.text.FlowView$FlowStrategy.layout(javax.swing.text.FlowView) (line: 477)
javax.swing.text.FlowView.layout(int, int) (line: 201)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 366)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 348)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 361)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 334)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 366)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 348)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 361)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 334)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 366)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 348)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.FlowView.layout(int, int) (line: 220)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 366)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 348)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.text.BoxView.updateChildSizes() (line: 366)
javax.swing.text.BoxView.setSpanOnAxis(int, float) (line: 348)
javax.swing.text.BoxView.layout(int, int) (line: 708)
javax.swing.text.BoxView.setSize(float, float) (line: 397)
javax.swing.plaf.basic.BasicTextUI$RootView.setSize(float, float) (line: 1714)
javax.swing.plaf.basic.BasicTextUI$RootView.paint(java.awt.Graphics, java.awt.Shape) (line: 1433)
javax.swing.plaf.basic.BasicTextUI.paintSafely(java.awt.Graphics) (line: 737)
javax.swing.plaf.basic.BasicTextUI.paint(java.awt.Graphics, javax.swing.JComponent) (line: 881)
javax.swing.plaf.basic.BasicTextUI.update(java.awt.Graphics, javax.swing.JComponent) (line: 860)
javax.swing.JComponent.paintComponent(java.awt.Graphics) (line: 778)
javax.swing.JComponent.paint(java.awt.Graphics) (line: 1054)
javax.swing.JComponent.paintToOffscreen(java.awt.Graphics, int, int, int, int, int, int) (line: 5219)
javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(javax.swing.JComponent, java.awt.Image, java.awt.Graphics, int, int, int, int) (line: 1529)
javax.swing.RepaintManager$PaintManager.paint(javax.swing.JComponent, javax.swing.JComponent, java.awt.Graphics, int, int, int, int) (line: 1452)
javax.swing.RepaintManager.paint(javax.swing.JComponent, javax.swing.JComponent, java.awt.Graphics, int, int, int, int) (line: 1249)
javax.swing.JComponent._paintImmediately(int, int, int, int) (line: 5167)
javax.swing.JComponent.paintImmediately(int, int, int, int) (line: 4978)
javax.swing.RepaintManager$3.run() (line: 808)
javax.swing.RepaintManager$3.run()
java.security.AccessController.doPrivileged(java.security.PrivilegedAction, java.security.AccessControlContext)
java.security.ProtectionDomain$1.doIntersectionPrivilege(java.security.PrivilegedAction, java.security.AccessControlContext, java.security.AccessControlContext)
javax.swing.RepaintManager.paintDirtyRegions(java.util.Map) (line: 796)
javax.swing.RepaintManager.paintDirtyRegions() (line: 769)
javax.swing.RepaintManager.prePaintDirtyRegions() (line: 718)
javax.swing.RepaintManager.access$1100(javax.swing.RepaintManager)
javax.swing.RepaintManager$ProcessingRunnable.run() (line: 1677)
java.awt.event.InvocationEvent.dispatch() (line: 312)
java.awt.EventQueue.dispatchEventImpl(java.awt.AWTEvent, java.lang.Object) (line: 733)
java.awt.EventQueue.access$200(java.awt.EventQueue, java.awt.AWTEvent, java.lang.Object)
java.awt.EventQueue$3.run() (line: 694)
java.awt.EventQueue$3.run()
java.security.AccessController.doPrivileged(java.security.PrivilegedAction, java.security.AccessControlContext)
java.security.ProtectionDomain$1.doIntersectionPrivilege(java.security.PrivilegedAction, java.security.AccessControlContext, java.security.AccessControlContext)
java.awt.EventQueue.dispatchEvent(java.awt.AWTEvent) (line: 703)
java.awt.EventDispatchThread.pumpOneEventForFilters(int) (line: 242)
java.awt.EventDispatchThread.pumpEventsForFilter(int, java.awt.Conditional, java.awt.EventFilter) (line: 161)
java.awt.EventDispatchThread.pumpEventsForHierarchy(int, java.awt.Conditional, java.awt.Component) (line: 150)
java.awt.EventDispatchThread.pumpEvents(int, java.awt.Conditional) (line: 146)
java.awt.EventDispatchThread.pumpEvents(java.awt.Conditional) (line: 138)
java.awt.EventDispatchThread.run() (line: 91)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;

public class FlowViewLayoutBug extends JFrame
{
    public static void main(String[] args)
    {
        final FlowViewLayoutBug frame = new FlowViewLayoutBug();
        final JTextPane textPane = new JTextPane();

        HTMLEditorKit editorKit = new HTMLEditorKit();
        textPane.setContentType("text/html");
        textPane.setEditorKit(editorKit);
        textPane.setText("Initial text without table works as expected.<br/>However, as soon as clicking the button, the whole app hangs with 100% CPU consumption.");

        JButton button = new JButton("Click me");
        button.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            { 
                // using an HTML table inside the document causes the problem
                String htmlText = "<table width=\"100%\" cellpadding=\"10\" cellspacing=\"5\" align=\"center\"><tr><th align=\"left\" bgcolor=\"#bec3c6\">Devices</th><th align=\"left\" bgcolor=\"#bec3c6\">State</th></tr><tr><td align=\"left\" bgcolor=\"#bec3c6\">PC</td><td align=\"left\" bgcolor=\"#46a055\">Ok</td></tr></table>";

                try
                {
                    textPane.setDocument(textPane.getEditorKit().createDefaultDocument());
                    HTMLDocument htmlDocument = (HTMLDocument) textPane.getDocument();
                    Element firstParagraph = findFirstElement(textPane.getDocument().getDefaultRootElement(), "p");
                    // this line causes the application to hang in an infinite loop
                    htmlDocument.setInnerHTML(firstParagraph, htmlText);
                    
                    // if we use 'insertAfterStart' instead of 'setInnerHTML', the application does 
                    // not lang, but the layouting of the simple table fails (there are multiple tables visible).
                    // Thus, this is no workaround. 
                    // To reproduce this, uncomment line below and comment line above.
                    //htmlDocument.insertAfterStart(firstParagraph, htmlText);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        });

        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(textPane, BorderLayout.CENTER);
        frame.getContentPane().add(button, BorderLayout.SOUTH);

        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(500, 200));
        frame.setVisible(true);
    }

    private static Element findFirstElement(Element e, String name)
    {
        String elementName = e.getName();

        if (elementName != null && elementName.equalsIgnoreCase(name))
            return e;

        for (int i = 0; i < e.getElementCount(); i++)
        {
            Element result = findFirstElement(e.getElement(i), name);
            if (result != null)
                return result;
        }
        return null;
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
We did not find any workaround.
Displaying HTML content with tables is an essential feature of our application. Thus, we cannot continue until this bug is resolved.



Comments
Suggested fix: The LogicalView.forwardUpdate() should send an update event to the views followed by the changed place, only when an instance of GlyphView has changed; otherwise it should invoke View.forwardUpdate() to handle the update properly.
15-08-2014

This is a regression from the fix JDK-8024395 Improve fix for line break calculations
15-07-2014