JDK-6817933 : Setting the background of an HTML Widget changes the native Windows JFileChooser
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6u10
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2009-03-16
  • Updated: 2013-04-22
  • Resolved: 2013-01-24
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 8
8 b75Fixed
Description
FULL PRODUCT VERSION :
java version "1.6.0_12"
Java(TM) SE Runtime Environment (build 1.6.0_12-b04)
Java HotSpot(TM) Client VM (build 11.2-b01, mixed mode, sharing)
Also
java version "1.5.0_17"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_17-b04)
Java HotSpot(TM) Client VM (build 1.5.0_17-b04, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 5.2.3790]
Microsoft Windows [Version 6.0.6001]

A DESCRIPTION OF THE PROBLEM :
Using style sheets to set the background color of the html widget also sets the background
of the JToggleButton in the Windows Places Bar in the Windows
native file chooser (WindowsFileChooserUI).  The Windows Places Bar is the area on the
left that has  text for "My Recent Documents", "Desktop", "My Documents",
"My Computer" etc.  When we set the html "body" style, this affects the background
of the text.  It looks like this is a bug in the WindowsPlacesBar code,

http://sun.calstatela.edu/~cysun/documentation/java/1.5.0-source/j2se/src/share/classes/sun/swing/WindowsPlacesBar.java
looks like:

            if (isXPPlatform) {
                buttons[i].setIconTextGap(2);
                buttons[i].setMargin(new Insets(2, 2, 2, 2));
                buttons[i].setText("<html><center>"+folderName+"</center></html>");
            }


So, the problem is that the text in the Windows Places Bar is html and setting
the body background color affects it.
The text of the button in the Windows Places Bar defaults to white, so if the
background is set to white, then white boxes appear in the Windows native browser.

One possible fix would be to not use <html>...</html> in this context.
It is not clear to me where the white color for the text is comming from


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Under Windows 2000 Server, or Windows Vista, create a stylesheet file called
HTMLCSSWindowsFileChooserBug.css that contains:
--start--
BODY {
   background: #ff0000;
}
--end--
Compile the test case:
bash-3.2 c:/Program\ Files/Java/jdk1.6.0_12/bin/javac HTMLCSSWindowsFileChooserBug.java

Then run the test case:
bash-3.2$ c:/Program\ Files/Java/jdk1.6.0_12/bin/java -classpath . HTMLCSSWindowsFileChooserBug

Select File -> Open

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Setting the background of an HTMLEditorKit should not cause the native Windows File
Chooser to have red boxes in the Windows File Places Bar.

ACTUAL -
On the left hand side of the native Window File Chooser, there are red boxes behind
the white lettering describing the Windows Places such as "My Documents".  If the
background was set to white in the .css file, then the boxes would be white and the text
would be invisible.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// Illustrates bug
// http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3801
// where setting the stylesheet of an HTMLEditorKit changes
// the Windows native look and feel JFileChooser.
// What happens is that the text of the Windows Places Bar,
// which usually has icons and text for "My Recent Documents"
// "Desktop", "My Documents", "My Computer" etc. gets changed
// so that the text background is the same as the HTML body background
// which is set the same as the style sheet.

// Based on code from http://www.cusc.ctu.edu.vn/forum/index.php?PHPSESSID=0020d8bc5a9edaa9a320d7aae1f81df4&topic=167.msg458

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;
import java.util.*;

import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.text.html.*;

public class HTMLCSSWindowsFileChooserBug extends JFrame {

   protected JTextPane editor;
   protected HTMLDocument doc;
   protected HTMLEditorKit kit;
   protected JFileChooser chooser;

   public HTMLCSSWindowsFileChooserBug() throws Exception {
      super("HTMLEditorKit/JFileChooser/Stylesheet bug");
      setSize(650, 400);

      editor = new JTextPane();
      kit = new HTMLEditorKit();
      editor.setEditorKit(kit);

      // Set the style sheet
      HTMLDocument doc = (HTMLDocument) editor.getDocument();
      StyleSheet styleSheet = doc.getStyleSheet();
      Class refClass = Class.forName("HTMLCSSWindowsFileChooserBug");
      String styleSheetFileName =  "HTMLCSSWindowsFileChooserBug.css";
      URL _styleSheetURL = refClass.getClassLoader().getResource(
              styleSheetFileName);
      if (_styleSheetURL == null) {
          throw new Exception("Failed to read " + styleSheetFileName);
      }
      styleSheet.importStyleSheet(_styleSheetURL);
      kit.setStyleSheet(styleSheet);

      JScrollPane ps = new JScrollPane(editor);
      getContentPane().add(ps, BorderLayout.CENTER);

      JMenuBar menuBar = createMenuBar();
      setJMenuBar(menuBar);

      chooser = new JFileChooser();

      doc = (HTMLDocument)kit.createDefaultDocument();

      editor.setDocument(doc);

      WindowListener wndCloser = new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      };
      addWindowListener(wndCloser);
   }

   protected JMenuBar createMenuBar() {
      JMenuBar menuBar = new JMenuBar();

      JMenu mFile = new JMenu("File");

      Action actionOpen = new AbstractAction("Open...") {
         public void actionPerformed(ActionEvent e) {
             chooser.showOpenDialog(HTMLCSSWindowsFileChooserBug.this);
         }
      };
      JMenuItem item = new JMenuItem(actionOpen);
      mFile.add(item);

      menuBar.add(mFile);
      return menuBar;
   }

   public static void main(String argv[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

                    HTMLCSSWindowsFileChooserBug frame = new HTMLCSSWindowsFileChooserBug();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
	    }
	    });
   }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
1. The most obvious workaround is to not set the background to white.
This is not acceptable to the customer as some of the pages rendered by the
application have non-white backgrounds.

2. Disabling the Windows Places Bar is another possibility, though users expect
this functionality in the FileChooser.

http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.5/ suggests:

> 4723745: Metal & Windows FileChooserUI classes cannot be used without
> ShellFolder
> Description:
>
> Since 1.4, the JFileChooser code has too strong a dependence on the native
> layer. An attempt was made to bypass this class for programs that use a
> customized "virtual" filesystem, but the algorithm is not 100% reliable.
> To toggle this behavior, use the client property FileChooser.useShellFolder,
> for example, to turn off use of the native set this to false:
>
>    jFileChooser.putClientProperty("FileChooser.useShellFolder",
>                 Boolean.FALSE);

My solution was to modify the class that sets renders the HTML so that
before JFileChooser is called, the background is saved, then the background is
set to the value of a property, "ToolBar.shadow", and then original background
is restored.

My hacky code is:
 Color background = null;
        try {
            // Get the background color of the HTML widget.
            AttributeSet bodyAttribute = (AttributeSet) styleSheet.getStyle("body")
                .getAttribute(javax.swing.text.StyleConstants.ResolveAttribute);
            background = styleSheet.getBackground(bodyAttribute);
        } catch (Exception ex) {
            System.err.println("Problem getting background color");
            ex.printStackTrace();
        }

	try {
            try {
                // Get the color of the ToolBar shadow and use it.
                Color shadow = UIManager.getColor("ToolBar.shadow");
                String rgb = Integer.toHexString(shadow.getRGB());
                styleSheet.addRule("BODY {background: #"
                        + rgb.substring(2, rgb.length())
                        + ";}");
                _HTMLEditorKit.setStyleSheet(styleSheet);
	    } catch (Exception ex) {
                System.err.println("Problem setting background color");
                ex.printStackTrace();
            }
           // The line below is what actually invokes JFileChooser
	    super._open();
	} finally {
            try {
                if (background != null) {
                    // Restore the background color.
                    String rgb = Integer.toHexString(background.getRGB());
                    styleSheet.addRule("BODY {background: #"
                            + rgb.substring(2, rgb.length())
                            + ";}");
                    _HTMLEditorKit.setStyleSheet(styleSheet);
                }
	    } catch (Exception ex) {
                System.out.println("Problem restoring background color.");
                ex.printStackTrace();
            }
	}

Comments
http://cr.openjdk.java.net/~malenkov/6817933.1/
24-01-2013

It is very safe to remove hmtl/text from the sun.swing.WindowsPlacesBar class: buttons[i] = new JToggleButton(folderName, icon); - if (isXPPlatform) { - buttons[i].setText("<html><center>"+folderName+"</center></html>"); - } if (isXPStyle) { buttons[i].putClientProperty("XPStyle.subAppName", "placesbar"); } else { Color fgColor = new Color(UIManager.getColor("List.selectionForeground").getRGB()); buttons[i].setContentAreaFilled(false); buttons[i].setForeground(fgColor); } But the fix above does not solve the problem with other components and other look-and-feels. The following example shows how to affect a button with html/text: import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; public class Test6817933 { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { StyleSheet css = new StyleSheet(); css.addRule("BODY {background:#ff0000;}"); HTMLEditorKit kit = new HTMLEditorKit(); kit.setStyleSheet(css); JFrame frame = new JFrame("JButton affected by HTMLEditorKit"); frame.add(new JButton("<html>BUTTON</html>")); frame.setSize(640, 480); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setVisible(true); } }); } } Optionally, we could close this bug as Not an Issue, because the specification of the HTMLEditorKit.setStyleSheet (and getStyleSheet) method says that the styleSheet property is be shared between instances of the HTMLEditorKit class.
21-01-2013

The reason is that the following instance method with static behavior: javax.swing.text.html.HTMLEditorKit#getStyleSheet() import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; public class Test6817933 { public static void main(String[] args) throws Exception { HTMLEditorKit first = new HTMLEditorKit(); first.setStyleSheet(new StyleSheet()); HTMLEditorKit second = new HTMLEditorKit(); second.setStyleSheet(new StyleSheet()); if (first.getStyleSheet() == second.getStyleSheet()){ throw new Error("instance method with static behavior"); } } }
18-01-2013

A simplified test is added.
18-01-2013

Seems that WindowsPlacesBar from JFileChooser uses CSS from the last HTMLEditorKit.
18-01-2013