JDK-7073803 : text/html clipboard Data Flavor is incorrectly returning HTML fragment only
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2011-08-02
  • Updated: 2012-03-20
  • Resolved: 2011-08-04
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Our application supports complex HTML pasting, such as content pasted from Microsoft Word. We use java.awt.datatransfer.Transferable#getTransferData() passing in a DataFlavor from the available clipboard data flavor list that has mime type "text/html".

In previous releases of Java, this returned the entire HTML clipboard contents. In Java 7, this is only returning content between the <!--StartFragment--> and <!--EndFragment--> comment tags.

This is a serious regression as it means we can no longer import correct HTML from complete documents copied to the clipboard.

REGRESSION.  Last worked in version 6u26

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a basic document in Microsoft Word (I used 2010 in my testing, but as far back as 2003 should exhibit this behaviour as well).
2. Select all content in the document, copy
3. Load the HTMLPaste application
4. Paste (ctrl+v should work)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The console output should contain a full HTML document with a large amount of MS Word styles
ACTUAL -
Only the content between the <!--StartFragment--> and <!--EndFragment--> comment tags is printed to the console

ERROR MESSAGES/STACK TRACES THAT OCCUR :
None, it is a regression

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Arrays;

public final class HTMLPaste extends JFrame {

	public HTMLPaste() throws HeadlessException {
		setTitle("Paste test");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		final JEditorPane field = createEditor();
		add(new JScrollPane(field));
		setSize(300, 400);
	}

	private JEditorPane createEditor() {
		final JEditorPane editorPane = new JEditorPane();
		editorPane.setEditorKit(new HTMLEditorKit());
		editorPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        editorPane.setTransferHandler(overrideTransferHandler(editorPane));
        return editorPane;
	}

    private TransferHandler overrideTransferHandler(final JEditorPane editorPane) {
        return new TransferHandler() {
            final TransferHandler delegate = editorPane.getTransferHandler();
            @Override
            public boolean importData(final TransferSupport support) {
                try {

                    readData(support);

                    //do the normal import process
                    return delegate.importData(support);
                } catch (Error e) {
                    e.printStackTrace();
                    throw e;
                } catch (UnsupportedFlavorException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return false;
            }
        };
    }

    private void readData(final TransferHandler.TransferSupport support) throws UnsupportedFlavorException, IOException {
		final DataFlavor[] flavors = support.getDataFlavors();
		System.out.println(Arrays.toString(flavors));
		for (final DataFlavor flavor : flavors) {
			if (flavor.getRepresentationClass() == String.class && flavor.getMimeType().startsWith("text/html")) {
				System.out.println("found html");
                outputData(support, flavor);
				break;
			}
			if (flavor.getRepresentationClass() == String.class && flavor.getMimeType().startsWith("text/plain")) {
				System.out.println("found text");
                outputData(support, flavor);
				break;
			}
		}
	}

    private void outputData(TransferHandler.TransferSupport support, DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        Object out = support.getTransferable().getTransferData(flavor);
        System.out.println("out = " + out);
    }

	public static void main(String[] args) {
		new HTMLPaste().setVisible(true);
	}
}

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

Comments
EVALUATION For now it is not a question to return back in Clipboard/DnD functionality, but to choose an appropriate solution to make customer happy and provide them an access to full HTML context (the task was described in CR 6513578 as supplementary, but was postponed due to low priority). For customer question about why the clipboard behavior was change there is a direct answer:to make clipboard functionality cross-platform. Full HTML content access is a feature of Window platform only. Did we have a right to change an old behavior? Yes, because HTML content provider is not specified in Java doc. So, now there is two way to resolve the situation1. Nano-fix for a customer with a "secret" property to run java with a switch -Dawt.getHTMLall jdk\src\windows\classes\sun\awt\windows\WDataTransferer.java: line 208: - str = new HTMLCodec(str, EHTMLReadMode.HTML_READ_SELECTION); + str = new HTMLCodec(str, System.getProperty("getHTMLall")==null ? EHTMLReadMode.HTML_READ_SELECTION : EHTMLReadMode.HTML_READ_ALL); 2. Nano-fix with custom Flavor support line 208: - str = new HTMLCodec(str, EHTMLReadMode.HTML_READ_SELECTION); +: EHTMLReadMode readMode = EHTMLReadMode.HTML_READ_SELECTION; String how = df.getParameter("part"); String[] typesS = new String[] { "all", "fragment", "selection"}; EHTMLReadMode[] typesE = new EHTMLReadMode[] { EHTMLReadMode.HTML_READ_ALL, EHTMLReadMode.HTML_READ_FRAGMENT, EHTMLReadMode.HTML_READ_SELECTION}; for (int i = 0; i < 3; ++i) { if (typesS[i].equalsIgnoreCase(how)) { readMode = typesE[i]; break; } } str = new HTMLCodec(str, readMode); Test case for second: Main.java: ------------------------ import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; public class Main { public static void main(String[] argv) throws Exception { String mimePrefix = "text/html; Class=" + String.class.getName() + "; charset=UTF-8"; DataFlavor df = new DataFlavor(mimePrefix + ";part=all"); System.out.println((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(df)); df = new DataFlavor(mimePrefix + ";part=fragment"); System.out.println((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(df)); df = new DataFlavor(mimePrefix + ";part=selection"); System.out.println((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(df)); df = new DataFlavor(mimePrefix); //the same as "part=selection" System.out.println((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(df)); } } Please, choose the best way and think about "put" flavor functionality.
21-09-2011