Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -781,15 +781,14 @@ <h4>Example</h4>
</p>

<h3><a id="introatrules">&#64; Rules</a></h3>
<h4>@import</h4>
<p>
Beginning with JavaFX 8u20, the CSS
<a href="http://www.w3.org/TR/CSS21/cascade.html#at-import">@import</a> is partially supported.
Only unconditional import is supported. In other words, the media&#8209;type qualifier is not supported.
Also, the JavaFX CSS parser is non-compliant with regard to where an @import may appear within a stylesheet
(see <a href="http://www.w3.org/TR/CSS21/syndata.html#at-rules">At&#8209;rules</a>).
Users are cautioned that this will be fixed in a future release. Adherence to the W3C standard is strongly advised.
</p>
<h4>@import <a class="grammar" style="font-weight: normal">[ &lt;url&gt; | &lt;string&gt; ] &lt;media-query-list&gt;? ;</a></h4>
<p>The @import rule allows applications to import rules from other stylesheets. Import rules must be placed before
all other @-rules and style rules, and no other rule can be placed between @import rules. If a valid import
rule is encountered, the content of the referenced stylesheet is treated as if it literally appeared at the
location of the @import rule.</p>
<p>An import rule can optionally include a <a href="#figure-media-query-list">media query list</a> that specifies
the conditions under which the imported rules apply, as if they were wrapped in a @media rule with the same
media query list.</p>
<h4>@font-face</h4>
<p>Since JavaFX 8, the implementation partially supports the CSS3 syntax to load a font from a URL using the
<a href="http://www.w3.org/TR/css3-fonts/#the-font-face-rule">@font&#8209;face</a> rule:</p>
Expand Down Expand Up @@ -839,7 +838,7 @@ <h4>@media</h4>
A media query list evaluates to <code>true</code> if <em>any</em> of the media queries is <code>true</code>,
and evaluates to <code>false</code> only if <em>all</em> the media queries are <code>false</code>.
An empty media query list evaluates to <code>true</code>.
<figure style="margin: 0">
<figure style="margin: 0" id="figure-media-query-list">
<img src="media-query.svg" width="210" alt="Media Query List">
<figcaption style="float: left; margin-top: 27px">
<span class="grammar">&lt;media-query-list&gt;:</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,21 @@
import com.sun.javafx.css.parser.Token;
import javafx.application.ColorScheme;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
* Contains the implementations of all supported media feature queries.
*/
final class MediaFeatures {
public final class MediaFeatures {

private MediaFeatures() {}

// Extension point for unit tests to add media features
public static BiFunction<String, Token, MediaQuery> DEFAULT = (featureName, _) -> {
throw new IllegalArgumentException(String.format("Unknown media feature <%s>", featureName));
};

/**
* Returns a {@code MediaQuery} that evaluates the specified feature in a discrete context.
*
Expand Down Expand Up @@ -132,8 +138,7 @@ lowerCaseFeatureName, lowerCaseTextValue(featureValue),
case "-fx-prefers-persistent-scrollbars" -> booleanPreferenceExpression(
lowerCaseFeatureName, featureValue, "persistent", MediaQueryContext::isPersistentScrollBars);

default -> throw new IllegalArgumentException(
String.format("Unknown media feature <%s>", featureName));
default -> DEFAULT.apply(featureName, featureValue);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ public sealed interface MediaQuery
*/
int getContextAwareness();

/**
* Attempts to determine the result of this media query without a context, and returns {@link TriState#TRUE}
* if the query matches for all possible contexts, {@link TriState#FALSE} if it matches for no possible
* context, or {@link TriState#UNKNOWN} if the result depends on the context or cannot be determined.
*
* @return {@link TriState#TRUE} if the query is always true, {@link TriState#FALSE}
* if the query is always false, otherwise {@link TriState#UNKNOWN}
*/
TriState evaluate();

/**
* Evaluates this media query against the provided context.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2026, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.sun.javafx.css.media;

import javafx.css.StyleConverter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;

public final class MediaQueryList extends ArrayList<MediaQuery> {

public MediaQueryList() {}

public MediaQueryList(int capacity) {
super(capacity);
}

/**
* Attempts to determine the result of this media query list without a context, and returns
* {@link TriState#TRUE} if it matches for all possible contexts, {@link TriState#FALSE} if
* it matches for no possible context, or {@link TriState#UNKNOWN} if the result depends on
* the context or cannot be determined.
*
* @return {@link TriState#TRUE} if the media query list is always true, {@link TriState#FALSE}
* if the media query list is always false, otherwise {@link TriState#UNKNOWN}
*/
public TriState evaluate() {
if (isEmpty()) {
return TriState.TRUE;
}

boolean unknown = false;

for (int i = 0, max = size(); i < max; i++) {
switch (get(i).evaluate()) {
case TRUE -> { return TriState.TRUE; }
case UNKNOWN -> unknown = true;
}
}

return unknown ? TriState.UNKNOWN : TriState.FALSE;
}

/**
* Evaluates whether any of the media queries is {@code true}.
*
* @param context the evaluation context
* @return {@code true} if any of the media queries is {@code true} or if the list is empty,
* {@code false} otherwise
*/
public boolean evaluate(MediaQueryContext context) {
if (isEmpty()) {
return true;
}

for (int i = 0, max = size(); i < max; i++) {
MediaQuery query = get(i);
boolean value = query.evaluate(context);
context.notifyQueryEvaluated(query, value);

if (value) {
return true;
}
}

return false;
}

public void writeBinary(DataOutputStream stream, StyleConverter.StringStore stringStore) throws IOException {
stream.writeInt(size());

for (MediaQuery query : this) {
MediaQuerySerializer.writeBinary(query, stream, stringStore);
}
}

public static MediaQueryList readBinary(DataInputStream stream, String[] strings) throws IOException {
int size = stream.readInt();
var queries = new MediaQueryList(size);

for (int i = 0; i < size; i++) {
queries.add(MediaQuerySerializer.readBinary(stream, strings));
}

return queries;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ public MediaQueryParser(BiConsumer<Token, String> errorHandler) {
* }</pre>
*
* @param tokens the token stream
* @return the expression
* @return the media query list
*/
public List<MediaQuery> parseMediaQueryList(List<Token> tokens) {
public MediaQueryList parseMediaQueryList(List<Token> tokens) {
var stream = new TokenStream(tokens);
var expressions = new ArrayList<MediaQuery>();
var expressions = new MediaQueryList();
boolean lastWasComma = false;

while (stream.consume() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,27 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;

/**
* CSS media rules are @-rules that contain media queries. A media query tests the "external"
* configuration of a {@link Scene}, and is independent of its scene graph content.
*/
public final class MediaRule {

private final List<MediaQuery> queries;
private final MediaQueryList queries;
private final MediaRule parent;

public MediaRule(List<MediaQuery> queries, MediaRule parent) {
this.queries = List.copyOf(queries);
public MediaRule(MediaQueryList queries, MediaRule parent) {
this.queries = queries;
this.parent = parent;
}

/**
* Returns the unmodifiable list of media queries.
* Returns the list of media queries.
*
* @return the unmodifiable list of media queries
* @return the list of media queries
*/
public List<MediaQuery> getQueries() {
public MediaQueryList getQueries() {
return queries;
}

Expand All @@ -64,6 +63,31 @@ public MediaRule getParent() {
return parent;
}

/**
* Attempts to determine the result of this media rule without a context, and returns {@link TriState#TRUE}
* if the rule matches for all possible contexts, {@link TriState#FALSE} if it matches for no possible
* context, or {@link TriState#UNKNOWN} if the result depends on the context or cannot be determined.
*
* @return {@link TriState#TRUE} if the rule always matches, {@link TriState#FALSE}
* if the rule never matches, otherwise {@link TriState#UNKNOWN}
*/
public TriState evaluate() {
boolean parentUnknown = false;

if (parent != null) {
switch (parent.evaluate()) {
case FALSE -> { return TriState.FALSE; }
case UNKNOWN -> parentUnknown = true;
}
}

return switch (queries.evaluate()) {
case TRUE -> parentUnknown ? TriState.UNKNOWN : TriState.TRUE;
case FALSE -> TriState.FALSE;
case UNKNOWN -> TriState.UNKNOWN;
};
}

/**
* Evaluates whether any of the media queries is {@code true}.
*
Expand All @@ -76,29 +100,11 @@ public boolean evaluate(MediaQueryContext context) {
return false;
}

if (queries.isEmpty()) {
return true;
}

for (int i = 0, max = queries.size(); i < max; i++) {
MediaQuery query = queries.get(i);
boolean value = query.evaluate(context);
context.notifyQueryEvaluated(query, value);

if (value) {
return true;
}
}

return false;
return queries.evaluate(context);
}

public void writeBinary(DataOutputStream stream, StyleConverter.StringStore stringStore) throws IOException {
stream.writeInt(queries.size());

for (MediaQuery query : queries) {
MediaQuerySerializer.writeBinary(query, stream, stringStore);
}
queries.writeBinary(stream, stringStore);

stream.writeBoolean(parent != null);

Expand All @@ -108,16 +114,11 @@ public void writeBinary(DataOutputStream stream, StyleConverter.StringStore stri
}

public static MediaRule readBinary(DataInputStream stream, String[] strings) throws IOException {
int size = stream.readInt();
var queries = new MediaQuery[size];

for (int i = 0; i < size; i++) {
queries[i] = MediaQuerySerializer.readBinary(stream, strings);
}
MediaQueryList queries = MediaQueryList.readBinary(stream, strings);

boolean hasParent = stream.readBoolean();
MediaRule parentRule = hasParent ? readBinary(stream, strings) : null;

return new MediaRule(List.of(queries), parentRule);
return new MediaRule(queries, parentRule);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2026, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.sun.javafx.css.media;

public enum TriState {
TRUE,
FALSE,
UNKNOWN
}
Loading