mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWeb/CSS: Implement @supports font-format()
and font-tech()
These let authors query whether we support font formats and features.
This commit is contained in:
parent
adfe8a9dcb
commit
b6fb4baeb7
Notes:
github-actions[bot]
2025-03-17 10:01:17 +00:00
Author: https://github.com/AtkinsSJ
Commit: b6fb4baeb7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3941
6 changed files with 242 additions and 2 deletions
|
@ -15,6 +15,7 @@
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||||
|
#include <LibWeb/CSS/FontFace.h>
|
||||||
#include <LibWeb/CSS/MediaList.h>
|
#include <LibWeb/CSS/MediaList.h>
|
||||||
#include <LibWeb/CSS/Parser/Parser.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
#include <LibWeb/CSS/PropertyName.h>
|
#include <LibWeb/CSS/PropertyName.h>
|
||||||
|
@ -274,10 +275,11 @@ OwnPtr<BooleanExpression> Parser::parse_boolean_expression_group(TokenStream<Com
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-conditional-4/#typedef-supports-feature
|
// https://drafts.csswg.org/css-conditional-5/#typedef-supports-feature
|
||||||
OwnPtr<BooleanExpression> Parser::parse_supports_feature(TokenStream<ComponentValue>& tokens)
|
OwnPtr<BooleanExpression> Parser::parse_supports_feature(TokenStream<ComponentValue>& tokens)
|
||||||
{
|
{
|
||||||
// <supports-feature> = <supports-selector-fn> | <supports-decl>
|
// <supports-feature> = <supports-selector-fn> | <supports-font-tech-fn>
|
||||||
|
// | <supports-font-format-fn> | <supports-decl>
|
||||||
auto transaction = tokens.begin_transaction();
|
auto transaction = tokens.begin_transaction();
|
||||||
tokens.discard_whitespace();
|
tokens.discard_whitespace();
|
||||||
auto const& first_token = tokens.consume_a_token();
|
auto const& first_token = tokens.consume_a_token();
|
||||||
|
@ -307,6 +309,36 @@ OwnPtr<BooleanExpression> Parser::parse_supports_feature(TokenStream<ComponentVa
|
||||||
!parse_a_selector_list(selector_tokens, SelectorType::Standalone).is_error());
|
!parse_a_selector_list(selector_tokens, SelectorType::Standalone).is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `<supports-font-tech-fn> = font-tech( <font-tech> )`
|
||||||
|
if (first_token.is_function("font-tech"sv)) {
|
||||||
|
TokenStream tech_tokens { first_token.function().value };
|
||||||
|
tech_tokens.discard_whitespace();
|
||||||
|
auto tech_token = tech_tokens.consume_a_token();
|
||||||
|
tech_tokens.discard_whitespace();
|
||||||
|
if (tech_tokens.has_next_token() || !tech_token.is(Token::Type::Ident))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
auto tech_name = tech_token.token().ident();
|
||||||
|
bool matches = font_tech_is_supported(tech_name);
|
||||||
|
return Supports::FontTech::create(move(tech_name), matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `<supports-font-format-fn> = font-format( <font-format> )`
|
||||||
|
if (first_token.is_function("font-format"sv)) {
|
||||||
|
TokenStream format_tokens { first_token.function().value };
|
||||||
|
format_tokens.discard_whitespace();
|
||||||
|
auto format_token = format_tokens.consume_a_token();
|
||||||
|
format_tokens.discard_whitespace();
|
||||||
|
if (format_tokens.has_next_token() || !format_token.is(Token::Type::Ident))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
auto format_name = format_token.token().ident();
|
||||||
|
bool matches = font_format_is_supported(format_name);
|
||||||
|
return Supports::FontFormat::create(move(format_name), matches);
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,38 @@ void Supports::Selector::dump(StringBuilder& builder, int indent_levels) const
|
||||||
builder.appendff("Selector: `{}` matches={}\n", m_selector, m_matches);
|
builder.appendff("Selector: `{}` matches={}\n", m_selector, m_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MatchResult Supports::FontTech::evaluate(HTML::Window const*) const
|
||||||
|
{
|
||||||
|
return as_match_result(m_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Supports::FontTech::to_string() const
|
||||||
|
{
|
||||||
|
return MUST(String::formatted("font-tech({})", m_tech));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Supports::FontTech::dump(StringBuilder& builder, int indent_levels) const
|
||||||
|
{
|
||||||
|
indent(builder, indent_levels);
|
||||||
|
builder.appendff("FontTech: `{}` matches={}\n", m_tech, m_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchResult Supports::FontFormat::evaluate(HTML::Window const*) const
|
||||||
|
{
|
||||||
|
return as_match_result(m_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Supports::FontFormat::to_string() const
|
||||||
|
{
|
||||||
|
return MUST(String::formatted("font-format({})", m_format));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Supports::FontFormat::dump(StringBuilder& builder, int indent_levels) const
|
||||||
|
{
|
||||||
|
indent(builder, indent_levels);
|
||||||
|
builder.appendff("FontFormat: `{}` matches={}\n", m_format, m_matches);
|
||||||
|
}
|
||||||
|
|
||||||
String Supports::to_string() const
|
String Supports::to_string() const
|
||||||
{
|
{
|
||||||
return m_condition->to_string();
|
return m_condition->to_string();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FlyString.h>
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/RefCounted.h>
|
#include <AK/RefCounted.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
@ -60,6 +61,50 @@ public:
|
||||||
bool m_matches;
|
bool m_matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FontTech final : public BooleanExpression {
|
||||||
|
public:
|
||||||
|
static NonnullOwnPtr<FontTech> create(FlyString tech, bool matches)
|
||||||
|
{
|
||||||
|
return adopt_own(*new FontTech(move(tech), matches));
|
||||||
|
}
|
||||||
|
virtual ~FontTech() override = default;
|
||||||
|
|
||||||
|
virtual MatchResult evaluate(HTML::Window const*) const override;
|
||||||
|
virtual String to_string() const override;
|
||||||
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FontTech(FlyString tech, bool matches)
|
||||||
|
: m_tech(move(tech))
|
||||||
|
, m_matches(matches)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
FlyString m_tech;
|
||||||
|
bool m_matches;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FontFormat final : public BooleanExpression {
|
||||||
|
public:
|
||||||
|
static NonnullOwnPtr<FontFormat> create(FlyString format, bool matches)
|
||||||
|
{
|
||||||
|
return adopt_own(*new FontFormat(move(format), matches));
|
||||||
|
}
|
||||||
|
virtual ~FontFormat() override = default;
|
||||||
|
|
||||||
|
virtual MatchResult evaluate(HTML::Window const*) const override;
|
||||||
|
virtual String to_string() const override;
|
||||||
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FontFormat(FlyString format, bool matches)
|
||||||
|
: m_format(move(format))
|
||||||
|
, m_matches(matches)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
FlyString m_format;
|
||||||
|
bool m_matches;
|
||||||
|
};
|
||||||
|
|
||||||
static NonnullRefPtr<Supports> create(NonnullOwnPtr<BooleanExpression>&& condition)
|
static NonnullRefPtr<Supports> create(NonnullOwnPtr<BooleanExpression>&& condition)
|
||||||
{
|
{
|
||||||
return adopt_ref(*new Supports(move(condition)));
|
return adopt_ref(*new Supports(move(condition)));
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CSS Reftest Reference</title>
|
||||||
|
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
|
||||||
|
<link rel="author" href="http://opera.com" title="Opera Software ASA">
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
background-color:green;
|
||||||
|
height:100px;
|
||||||
|
width:100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||||
|
<div></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>CSS Conditional Test: @supports font-format()</title>
|
||||||
|
<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
|
||||||
|
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#typedef-supports-font-format-fn">
|
||||||
|
<link rel="match" href="../../../../expected/wpt-import/css/css-conditional/at-supports-001-ref.html">
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
background: red;
|
||||||
|
height: 10px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
/* assume all browsers support at least opentype, truetype, woff */
|
||||||
|
@supports font-format(opentype) {
|
||||||
|
#test1 { background: green };
|
||||||
|
}
|
||||||
|
@supports font-format(TrueType) {
|
||||||
|
#test2 { background: green };
|
||||||
|
}
|
||||||
|
@supports font-format(Woff) {
|
||||||
|
#test3 { background: green };
|
||||||
|
}
|
||||||
|
/* forms that should NOT be recognized as supported */
|
||||||
|
@supports not font-format() {
|
||||||
|
#test4 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format(xyzzy) {
|
||||||
|
#test5 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format("opentype") {
|
||||||
|
#test6 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format("truetype") {
|
||||||
|
#test7 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format("woff") {
|
||||||
|
#test8 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format(truetype opentype) {
|
||||||
|
#test9 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-format(truetype, opentype) {
|
||||||
|
#test10 { background: green };
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||||
|
<div id=test1></div>
|
||||||
|
<div id=test2></div>
|
||||||
|
<div id=test3></div>
|
||||||
|
<div id=test4></div>
|
||||||
|
<div id=test5></div>
|
||||||
|
<div id=test6></div>
|
||||||
|
<div id=test7></div>
|
||||||
|
<div id=test8></div>
|
||||||
|
<div id=test9></div>
|
||||||
|
<div id=test10></div>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>CSS Conditional Test: @supports font-tech()</title>
|
||||||
|
<link rel="author" title="Jonathan Kew" href="mailto:jkew@mozilla.com">
|
||||||
|
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#typedef-supports-font-tech-fn">
|
||||||
|
<link rel="match" href="../../../../expected/wpt-import/css/css-conditional/at-supports-001-ref.html">
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
background: red;
|
||||||
|
height: 10px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
/* assume all browsers support at least features-opentype and color-COLRv0 */
|
||||||
|
@supports font-tech(features-opentype) {
|
||||||
|
#test1 { background: green };
|
||||||
|
}
|
||||||
|
@supports font-tech(color-COLRv0) {
|
||||||
|
#test2 { background: green };
|
||||||
|
}
|
||||||
|
/* forms that should NOT be recognized as supported */
|
||||||
|
@supports not font-tech(features-opentype color-COLRv1) {
|
||||||
|
#test3 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech(features-opentype, color-COLRv0) {
|
||||||
|
#test4 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech(features-opentype, features-opentype) {
|
||||||
|
#test5 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech() {
|
||||||
|
#test6 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech(xyzzy) {
|
||||||
|
#test7 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech("features-opentype") {
|
||||||
|
#test8 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech(feature-opentype) {
|
||||||
|
#test9 { background: green };
|
||||||
|
}
|
||||||
|
@supports not font-tech(colors-COLRv0) {
|
||||||
|
#test10 { background: green };
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||||
|
<div id=test1></div>
|
||||||
|
<div id=test2></div>
|
||||||
|
<div id=test3></div>
|
||||||
|
<div id=test4></div>
|
||||||
|
<div id=test5></div>
|
||||||
|
<div id=test6></div>
|
||||||
|
<div id=test7></div>
|
||||||
|
<div id=test8></div>
|
||||||
|
<div id=test9></div>
|
||||||
|
<div id=test10></div>
|
Loading…
Add table
Add a link
Reference in a new issue