Skip to content
Permalink
Browse files
8267554: Support loading stylesheets from data-URIs
Reviewed-by: kcr, aghaisas
  • Loading branch information
Michael Strauß authored and kevinrushforth committed Jun 25, 2021
1 parent 3fd4c97 commit 78179be10199291461411f09e4f6c4a1e653b195
@@ -52,6 +52,7 @@
import com.sun.javafx.logging.PlatformLogger;
import com.sun.javafx.logging.PlatformLogger.Level;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.FilePermission;
import java.io.IOException;
@@ -65,6 +66,9 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.DigestInputStream;
@@ -1087,7 +1091,6 @@ private static Stylesheet loadStylesheetUnPrivileged(final String fname) {
// check if url has extension, if not then just url as is and always parse as css text
if (!(fname.endsWith(".css") || fname.endsWith(".bss"))) {
url = getURL(fname);
parse = true;
} else {
final String name = fname.substring(0, fname.length() - 4);

@@ -1100,27 +1103,81 @@ private static Stylesheet loadStylesheetUnPrivileged(final String fname) {
}

if ((url != null) && !parse) {

try {
// RT-36332: if loadBinary throws an IOException, make sure to try .css
stylesheet = Stylesheet.loadBinary(url);
} catch (IOException ioe) {
stylesheet = null;
} catch (IOException ignored) {
}

if (stylesheet == null && (parse = !parse)) {
if (stylesheet == null) {
// If we failed to load the .bss file,
// fall back to the .css file.
// Note that 'parse' is toggled in the test.
url = getURL(fname);
}
}
}

// either we failed to load the .bss file, or parse
// was set to true.
if ((url != null) && parse) {
stylesheet = new CssParser().parse(url);
if (stylesheet == null) {
DataURI dataUri = null;

if (url != null) {
stylesheet = new CssParser().parse(url);
} else {
dataUri = DataURI.tryParse(fname);
}

if (dataUri != null) {
boolean isText =
"text".equalsIgnoreCase(dataUri.getMimeType())
&& ("css".equalsIgnoreCase(dataUri.getMimeSubtype())
|| "plain".equalsIgnoreCase(dataUri.getMimeSubtype()));

boolean isBinary =
"application".equalsIgnoreCase(dataUri.getMimeType())
&& "octet-stream".equalsIgnoreCase(dataUri.getMimeSubtype());

if (isText) {
String charsetName = dataUri.getParameters().get("charset");
Charset charset;

try {
charset = charsetName != null ? Charset.forName(charsetName) : Charset.defaultCharset();
} catch (IllegalCharsetNameException | UnsupportedCharsetException ex) {
String message = String.format(
"Unsupported charset \"%s\" in stylesheet URI \"%s\"", charsetName, dataUri);

if (errors != null) {
errors.add(new CssParser.ParseError(message));
}

if (getLogger().isLoggable(Level.WARNING)) {
getLogger().warning(message);
}

return null;
}

var stylesheetText = new String(dataUri.getData(), charset);
stylesheet = new CssParser().parse(stylesheetText);
} else if (isBinary) {
try (InputStream stream = new ByteArrayInputStream(dataUri.getData())) {
stylesheet = Stylesheet.loadBinary(stream);
}
} else {
String message = String.format("Unexpected MIME type \"%s/%s\" in stylesheet URI \"%s\"",
dataUri.getMimeType(), dataUri.getMimeSubtype(), dataUri);

if (errors != null) {
errors.add(new CssParser.ParseError(message));
}

if (getLogger().isLoggable(Level.WARNING)) {
getLogger().warning(message);
}

return null;
}
}
}

if (stylesheet == null) {
@@ -1169,15 +1226,15 @@ private static Stylesheet loadStylesheetUnPrivileged(final String fname) {
getLogger().info("Could not find stylesheet: " + fname);//, fnfe);
}
} catch (IOException ioe) {
if (errors != null) {
CssParser.ParseError error =
new CssParser.ParseError(
"Could not load stylesheet: " + fname
);
errors.add(error);
}
// For data URIs, use the pretty-printed version for logging
var dataUri = DataURI.tryParse(fname);
String stylesheetName = dataUri != null ? dataUri.toString() : fname;

if (errors != null) {
errors.add(new CssParser.ParseError("Could not load stylesheet: " + stylesheetName));
}
if (getLogger().isLoggable(Level.INFO)) {
getLogger().info("Could not load stylesheet: " + fname);//, ioe);
getLogger().info("Could not load stylesheet: " + stylesheetName);
}
}
return null;
@@ -25,12 +25,14 @@

package javafx.application;

import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Map;

import javafx.application.Preloader.PreloaderNotification;
import javafx.css.Stylesheet;
import javafx.scene.Scene;
import javafx.stage.Stage;

@@ -514,9 +516,19 @@ public static String getUserAgentStylesheet() {
* Setting it on the command line overrides anything set using this method
* in code.
* <p>
* The URL is a hierarchical URI of the form [scheme:][//authority][path]. If the URL
* does not have a [scheme:] component, the URL is considered to be the [path] component only.
* Any leading '/' character of the [path] is ignored and the [path] is treated as a path relative to
* the root of the application's classpath.
* <p>
* The RFC 2397 "data" scheme for URLs is supported in addition to the protocol handlers that
* are registered for the application.
* If a URL uses the "data" scheme and the MIME type is either empty, "text/plain", or "text/css",
* the payload will be interpreted as a CSS file.
* If the MIME type is "application/octet-stream", the payload will be interpreted as a binary
* CSS file (see {@link Stylesheet#convertToBinary(File, File)}).
* <p>
* NOTE: This method must be called on the JavaFX Application Thread.
* </p>
*
*
* @param url The URL to the stylesheet as a String.
* @since JavaFX 8.0
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,6 +41,7 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
@@ -278,25 +279,49 @@ final void readBinary(int bssVersion, DataInputStream is, String[] strings)
* css version or if an I/O error occurs while reading from the stream
*/
public static Stylesheet loadBinary(URL url) throws IOException {
if (url == null) {
return null;
}

try (InputStream stream = url.openStream()) {
return loadBinary(stream, url.toExternalForm());
} catch (FileNotFoundException ex) {
return null;
}
}

if (url == null) return null;
/**
* Loads a binary stylesheet from a stream.
*
* @param stream the input stream
* @return the loaded {@code Stylesheet}
* @throws IOException if the binary stream corresponds to a more recent binary
* css version or if an I/O error occurs while reading from the stream
*
* @since 17
*/
public static Stylesheet loadBinary(InputStream stream) throws IOException {
return loadBinary(stream, null);
}

private static Stylesheet loadBinary(InputStream stream, String uri) throws IOException {
Stylesheet stylesheet = null;

try (DataInputStream dataInputStream =
new DataInputStream(new BufferedInputStream(url.openStream(), 40 * 1024))) {
new DataInputStream(new BufferedInputStream(stream, 40 * 1024))) {

// read file version
final int bssVersion = dataInputStream.readShort();
if (bssVersion > Stylesheet.BINARY_CSS_VERSION) {
throw new IOException(url.toString() + " wrong binary CSS version: "
+ bssVersion + ". Expected version less than or equal to" +
Stylesheet.BINARY_CSS_VERSION);
throw new IOException(
String.format("Wrong binary CSS version %s, expected version less than or equal to %s",
uri != null ? bssVersion + " in stylesheet \"" + uri + "\"" : bssVersion,
Stylesheet.BINARY_CSS_VERSION));
}
// read strings
final String[] strings = StringStore.readBinary(dataInputStream);
// read binary data
stylesheet = new Stylesheet(url.toExternalForm());
stylesheet = new Stylesheet(uri);

try {

@@ -305,7 +330,7 @@ public static Stylesheet loadBinary(URL url) throws IOException {

} catch (Exception e) {

stylesheet = new Stylesheet(url.toExternalForm());
stylesheet = new Stylesheet(uri);

dataInputStream.reset();

@@ -317,9 +342,6 @@ public static Stylesheet loadBinary(URL url) throws IOException {
}
}

} catch (FileNotFoundException fnfe) {
// This comes from url.openStream() and is expected.
// It just means that the .bss file doesn't exist.
}

// return stylesheet
@@ -68,6 +68,7 @@
import javafx.collections.ObservableMap;
import javafx.css.CssMetaData;
import javafx.css.StyleableObjectProperty;
import javafx.css.Stylesheet;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.image.WritableImage;
@@ -83,6 +84,7 @@
import com.sun.javafx.logging.PlatformLogger;
import com.sun.javafx.logging.PlatformLogger.Level;

import java.io.File;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -1620,7 +1622,13 @@ protected void onChanged(Change<String> c) {
* does not have a [scheme:] component, the URL is considered to be the [path] component only.
* Any leading '/' character of the [path] is ignored and the [path] is treated as a path relative to
* the root of the application's classpath.
* </p>
* <p>
* The RFC 2397 "data" scheme for URLs is supported in addition to the protocol handlers that
* are registered for the application.
* If a URL uses the "data" scheme and the MIME type is either empty, "text/plain", or "text/css",
* the payload will be interpreted as a CSS file.
* If the MIME type is "application/octet-stream", the payload will be interpreted as a binary
* CSS file (see {@link Stylesheet#convertToBinary(File, File)}).
* <pre><code>
*
* package com.example.javafx.app;
@@ -1691,13 +1699,22 @@ public final String getUserAgentStylesheet() {
* the platform-default user-agent stylesheet. If the URL does not resolve to a valid location,
* the platform-default user-agent stylesheet will be used.
* <p>
* For additional information about using CSS with the scene graph,
* see the <a href="doc-files/cssref.html">CSS Reference Guide</a>.
* </p>
* @param url The URL is a hierarchical URI of the form [scheme:][//authority][path]. If the URL
* The URL is a hierarchical URI of the form [scheme:][//authority][path]. If the URL
* does not have a [scheme:] component, the URL is considered to be the [path] component only.
* Any leading '/' character of the [path] is ignored and the [path] is treated as a path relative to
* the root of the application's classpath.
* <p>
* The RFC 2397 "data" scheme for URLs is supported in addition to the protocol handlers that
* are registered for the application.
* If a URL uses the "data" scheme and the MIME type is either empty, "text/plain", or "text/css",
* the payload will be interpreted as a CSS file.
* If the MIME type is "application/octet-stream", the payload will be interpreted as a binary
* CSS file (see {@link Stylesheet#convertToBinary(File, File)}).
* <p>
* For additional information about using CSS with the scene graph,
* see the <a href="doc-files/cssref.html">CSS Reference Guide</a>.
*
* @param url the URL of the user-agent stylesheet
* @since JavaFX 8u20
*/
public final void setUserAgentStylesheet(String url) {
@@ -33,11 +33,13 @@
import javafx.application.Platform;
import javafx.beans.NamedArg;
import javafx.beans.property.*;
import javafx.css.Stylesheet;
import javafx.geometry.NodeOrientation;
import javafx.geometry.Point3D;
import javafx.scene.input.PickResult;
import javafx.scene.paint.Paint;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

@@ -681,13 +683,22 @@ public final String getUserAgentStylesheet() {
* the platform-default user-agent stylesheet. If the URL does not resolve to a valid location,
* the platform-default user-agent stylesheet will be used.
* <p>
* For additional information about using CSS with the scene graph,
* see the <a href="doc-files/cssref.html">CSS Reference Guide</a>.
* </p>
* @param url The URL is a hierarchical URI of the form [scheme:][//authority][path]. If the URL
* The URL is a hierarchical URI of the form [scheme:][//authority][path]. If the URL
* does not have a [scheme:] component, the URL is considered to be the [path] component only.
* Any leading '/' character of the [path] is ignored and the [path] is treated as a path relative to
* the root of the application's classpath.
* <p>
* The RFC 2397 "data" scheme for URLs is supported in addition to the protocol handlers that
* are registered for the application.
* If a URL uses the "data" scheme and the MIME type is either empty, "text/plain", or "text/css",
* the payload will be interpreted as a CSS file.
* If the MIME type is "application/octet-stream", the payload will be interpreted as a binary
* CSS file (see {@link Stylesheet#convertToBinary(File, File)}).
* <p>
* For additional information about using CSS with the scene graph,
* see the <a href="doc-files/cssref.html">CSS Reference Guide</a>.
*
* @param url the URL of the user-agent stylesheet
* @since JavaFX 8u20
*/
public final void setUserAgentStylesheet(String url) {

1 comment on commit 78179be

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 78179be Jun 25, 2021

Please sign in to comment.