views:

108

answers:

4

A Swing JLabel automatically interprets any text as HTML content, if it starts with <html>. If the content of this HTML is an image with invalid URL this will cause the whole GUI to hang since the ImageFetche which should load this image will quit by an NPE.

To reproduce this problem simply create a JLabel as follows

new JLabel("<html><img src='http:\\\\invalid\\url'>")

I know there is a client property to prevent the JLabel from interpreting HTML. But JLabel is the default renderer implementation for many Swing components(like JTree, JTable and so on) which makes this a problem for nearly any Swing application which allows user input. So instead of implementing tons of custom renderer I'm searching for a global solution to disable the HTML interpretation.

A: 

For a simple JLabel, you can call the JComponent method

myLabel.putClientProperty("html.disable", Boolean.TRUE);

on the label where you want to disable HTML rendering.

Reference: Impossible to disable HTML Rendering in a JLabel


For something like a JTable, JTree, or JList you'll need to create a custom cell renderer that sets this property. Here's an example (modified from this example) that creates a custom cell renderer for a JList.

import java.awt.Component;
import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;

public class JListTest {
    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        JFrame frame = new JFrame("JList Test");
        frame.setLayout(new FlowLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        String[] selections = { "<html><img src='http:\\\\invalid\\url'>",
                "red", "orange", "dark blue" };
        JList list = new JList(selections);

        // set the list cell renderer to the custom class defined below
        list.setCellRenderer(new MyCellRenderer());

        list.setSelectedIndex(1);
        System.out.println(list.getSelectedValue());
        frame.add(new JScrollPane(list));
        frame.pack();

        frame.setVisible(true);
    }
}


class MyCellRenderer extends JLabel implements ListCellRenderer {
    public MyCellRenderer() {
        setOpaque(true);
        putClientProperty("html.disable", Boolean.TRUE);
    }

    public Component getListCellRendererComponent(
        JList list,
        Object value,
        int index,
        boolean isSelected,
        boolean cellHasFocus)
    {
        setText(value.toString());
        return this;
    }
}

I used the example code from the ListCellRenderer documentation as a starting point for the custom list cell renderer.

When I run the example, you can see that the HTML in the first list entry is rendered instead of being interpreted.

JList Custom Renderer

Bill the Lizard
I know - but this doesn't work for JLabels which are used as TableCellRenderer, ListCellRenderer and so on, unless I write my own implementation for any of them.
tigger
"I know there is a client property to prevent the JLabel from interpreting HTML."
Epaga
A: 

Hanging is probably the least unpleasant behavior. This is why Data Validation is so very important. Just do not allow the users to enter something like that.

Devon_C_Miller
I really think that is the wrong way around.
Tom Hawtin - tackline
The handling of the html in Swing components is delegated to the LAF.So, short of implementing a custom LAF, there is no way to globally disable support for the html tag. And, there is absolutely no reason to allow arbitrary, unvalidated user input. That's just asking for trouble. At the core, every buffer overflow exploit really comes down to someone failed to validate input. OP has demonstrated how a careless/clueless input can hang the system. But consider, what might a malevolent user be able to do?
Devon_C_Miller
A: 

A serious hack, hardly recommendable, but you could create your own JLabel class and "replace" the one provided by the JVM library. You could accomplish this by replacing the .class file in the jar.

EDIT: apparently that breaks the license agreement that comes with the JDK.

So instead do this trick: prepend to the bootclasspath and inject your own JLabel class.

louisgab
+2  A: 

There is a way if you create your own look and feel.
I'm not sure how well this performs is this, but it works. Lets assume you will extend the "Classic Windows" L&F.You need at leas 2 classes One is the Look&Feel itself, lets call it WindowsClassicLookAndFeelExt. You only need to override method initClassDefaults.

package testSwing;

import javax.swing.UIDefaults;
import com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel;

public class WindowsClassicLookAndFeelExt extends WindowsClassicLookAndFeel    {
    @Override protected void initClassDefaults(UIDefaults table){
        super.initClassDefaults(table);
        Object[] uiDefaults = { "LabelUI", WindowsLabelExtUI.class.getCanonicalName()};
        table.putDefaults(uiDefaults);
    }
}

You also need a WindowsLabelExtUI class to manage all JLabels and set the property:

package testSwing;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import com.sun.java.swing.plaf.windows.WindowsLabelUI;

public class WindowsLabelExtUI extends WindowsLabelUI{
    static WindowsLabelExtUI singleton = new WindowsLabelExtUI();

    public static ComponentUI createUI(JComponent c){
        c.putClientProperty("html.disable", Boolean.TRUE);    
        return singleton;
    }
}

And finally a test class when you set the theme as WindowsClassicLookAndFeelExt

package testSwing;

import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.UIManager;


public class Main{
    public static void main(String[] args){
        try{                UIManager.setLookAndFeel(WindowsClassicLookAndFeelExt.class.getCanonicalName());
        }catch (Exception e){
            e.printStackTrace();
        }

        JFrame frame = new JFrame("JList Test");
        frame.setLayout(new FlowLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        String[] selections = {"<html><img src='http:\\\\invalid\\url'>", "<html><H1>Hello</h1></html>", "orange", "dark blue"};

        JList list = new JList(selections);

        list.setSelectedIndex(1);
        System.out.println(list.getSelectedValue());

        JLabel jLabel = new JLabel("<html><h2>standard Label</h2></html>");
        frame.add(new JScrollPane(list));
        frame.add(jLabel);
        frame.pack();

        frame.setVisible(true);
    }
}

And you will see something like

alt text

Pablo Grisafi