mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
LibWeb/CSS: Parse <url>
as a new CSS::URL type
Our previous approach to `<url>` had a couple of issues: - We'd complete the URL during parsing, when we should actually keep it as the original string until it's used. - There's nowhere for us to store `<url-modifier>`s on a `URL::URL`. So, `CSS::URL` is a solution to this. It holds the original URL string, and later will also hold any modifiers. This commit parses all `<url>`s as `CSS::URL`, but then converts it into a `URL::URL`, so no user code is changed. These will be modified in subsequent commits. For `@namespace`, we were never supposed to complete the URL at all, so this makes that more correct already. However, in practice all `@namespace`s are absolute URLs already, so this should have no observable effects.
This commit is contained in:
parent
c82f4b46a2
commit
7216c6b050
Notes:
github-actions[bot]
2025-04-09 17:47:57 +00:00
Author: https://github.com/AtkinsSJ
Commit: 7216c6b050
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4285
Reviewed-by: https://github.com/tcl3 ✅
7 changed files with 101 additions and 27 deletions
|
@ -195,6 +195,7 @@ set(SOURCES
|
||||||
CSS/Time.cpp
|
CSS/Time.cpp
|
||||||
CSS/Transformation.cpp
|
CSS/Transformation.cpp
|
||||||
CSS/TransitionEvent.cpp
|
CSS/TransitionEvent.cpp
|
||||||
|
CSS/URL.cpp
|
||||||
CSS/VisualViewport.cpp
|
CSS/VisualViewport.cpp
|
||||||
Cookie/Cookie.cpp
|
Cookie/Cookie.cpp
|
||||||
Cookie/ParsedCookie.cpp
|
Cookie/ParsedCookie.cpp
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||||
#include <LibWeb/CSS/Supports.h>
|
#include <LibWeb/CSS/Supports.h>
|
||||||
|
#include <LibWeb/CSS/URL.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
namespace Web::CSS::Parser {
|
namespace Web::CSS::Parser {
|
||||||
|
@ -276,7 +277,7 @@ private:
|
||||||
Optional<GridRepeat> parse_repeat(Vector<ComponentValue> const&);
|
Optional<GridRepeat> parse_repeat(Vector<ComponentValue> const&);
|
||||||
Optional<ExplicitGridTrack> parse_track_sizing_function(ComponentValue const&);
|
Optional<ExplicitGridTrack> parse_track_sizing_function(ComponentValue const&);
|
||||||
|
|
||||||
Optional<::URL::URL> parse_url_function(TokenStream<ComponentValue>&);
|
Optional<URL> parse_url_function(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_url_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_url_value(TokenStream<ComponentValue>&);
|
||||||
|
|
||||||
Optional<ShapeRadius> parse_shape_radius(TokenStream<ComponentValue>&);
|
Optional<ShapeRadius> parse_shape_radius(TokenStream<ComponentValue>&);
|
||||||
|
|
|
@ -153,15 +153,22 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
|
||||||
TokenStream tokens { rule.prelude };
|
TokenStream tokens { rule.prelude };
|
||||||
tokens.discard_whitespace();
|
tokens.discard_whitespace();
|
||||||
|
|
||||||
Optional<::URL::URL> url = parse_url_function(tokens);
|
Optional<URL> url = parse_url_function(tokens);
|
||||||
if (!url.has_value() && tokens.next_token().is(Token::Type::String))
|
if (!url.has_value() && tokens.next_token().is(Token::Type::String))
|
||||||
url = complete_url(tokens.consume_a_token().token().string());
|
url = URL { tokens.consume_a_token().token().string().to_string() };
|
||||||
|
|
||||||
if (!url.has_value()) {
|
if (!url.has_value()) {
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @import rule: Unable to parse `{}` as URL.", tokens.next_token().to_debug_string());
|
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @import rule: Unable to parse `{}` as URL.", tokens.next_token().to_debug_string());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Stop completing the URL here
|
||||||
|
auto resolved_url = complete_url(url->url());
|
||||||
|
if (!resolved_url.has_value()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @import rule: Unable to complete `{}` as URL.", url->url());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
tokens.discard_whitespace();
|
tokens.discard_whitespace();
|
||||||
// FIXME: Implement layer support.
|
// FIXME: Implement layer support.
|
||||||
RefPtr<Supports> supports {};
|
RefPtr<Supports> supports {};
|
||||||
|
@ -191,7 +198,7 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return CSSImportRule::create(url.value(), const_cast<DOM::Document&>(*document()), supports, move(media_query_list));
|
return CSSImportRule::create(resolved_url.release_value(), const_cast<DOM::Document&>(*document()), supports, move(media_query_list));
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<FlyString> Parser::parse_layer_name(TokenStream<ComponentValue>& tokens, AllowBlankLayerName allow_blank_layer_name)
|
Optional<FlyString> Parser::parse_layer_name(TokenStream<ComponentValue>& tokens, AllowBlankLayerName allow_blank_layer_name)
|
||||||
|
@ -435,7 +442,10 @@ GC::Ptr<CSSNamespaceRule> Parser::convert_to_namespace_rule(AtRule const& rule)
|
||||||
|
|
||||||
FlyString namespace_uri;
|
FlyString namespace_uri;
|
||||||
if (auto url = parse_url_function(tokens); url.has_value()) {
|
if (auto url = parse_url_function(tokens); url.has_value()) {
|
||||||
namespace_uri = url.value().to_string();
|
// "A URI string parsed from the URI syntax must be treated as a literal string: as with the STRING syntax, no
|
||||||
|
// URI-specific normalization is applied."
|
||||||
|
// https://drafts.csswg.org/css-namespaces/#syntax
|
||||||
|
namespace_uri = url->url();
|
||||||
} else if (auto& url_token = tokens.consume_a_token(); url_token.is(Token::Type::String)) {
|
} else if (auto& url_token = tokens.consume_a_token(); url_token.is(Token::Type::String)) {
|
||||||
namespace_uri = url_token.token().string();
|
namespace_uri = url_token.token().string();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/GenericLexer.h>
|
#include <AK/GenericLexer.h>
|
||||||
#include <AK/TemporaryChange.h>
|
#include <AK/TemporaryChange.h>
|
||||||
#include <LibURL/URL.h>
|
|
||||||
#include <LibWeb/CSS/FontFace.h>
|
#include <LibWeb/CSS/FontFace.h>
|
||||||
#include <LibWeb/CSS/Parser/Parser.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
#include <LibWeb/CSS/PropertyName.h>
|
#include <LibWeb/CSS/PropertyName.h>
|
||||||
|
@ -2014,9 +2013,13 @@ RefPtr<AbstractImageStyleValue> Parser::parse_image_value(TokenStream<ComponentV
|
||||||
if (url.has_value()) {
|
if (url.has_value()) {
|
||||||
// If the value is a 'url(..)' parse as image, but if it is just a reference 'url(#xx)', leave it alone,
|
// If the value is a 'url(..)' parse as image, but if it is just a reference 'url(#xx)', leave it alone,
|
||||||
// so we can parse as URL further on. These URLs are used as references inside SVG documents for masks.
|
// so we can parse as URL further on. These URLs are used as references inside SVG documents for masks.
|
||||||
if (!url.value().equals(m_url, ::URL::ExcludeFragment::Yes)) {
|
if (!url->url().starts_with('#')) {
|
||||||
|
// FIXME: Stop completing the URL here
|
||||||
|
auto completed_url = complete_url(url->url());
|
||||||
|
if (completed_url.has_value()) {
|
||||||
tokens.discard_a_mark();
|
tokens.discard_a_mark();
|
||||||
return ImageStyleValue::create(url.value());
|
return ImageStyleValue::create(completed_url.release_value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tokens.restore_a_mark();
|
tokens.restore_a_mark();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2562,24 +2565,16 @@ RefPtr<CSSStyleValue> Parser::parse_easing_value(TokenStream<ComponentValue>& to
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<::URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& tokens)
|
Optional<URL> Parser::parse_url_function(TokenStream<ComponentValue>& tokens)
|
||||||
{
|
{
|
||||||
auto transaction = tokens.begin_transaction();
|
auto transaction = tokens.begin_transaction();
|
||||||
auto& component_value = tokens.consume_a_token();
|
auto const& component_value = tokens.consume_a_token();
|
||||||
|
|
||||||
auto convert_string_to_url = [&](StringView url_string) -> Optional<::URL::URL> {
|
|
||||||
auto url = complete_url(url_string);
|
|
||||||
if (url.has_value()) {
|
|
||||||
transaction.commit();
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (component_value.is(Token::Type::Url)) {
|
if (component_value.is(Token::Type::Url)) {
|
||||||
auto url_string = component_value.token().url();
|
transaction.commit();
|
||||||
return convert_string_to_url(url_string);
|
return URL { component_value.token().url().to_string() };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component_value.is_function("url"sv)) {
|
if (component_value.is_function("url"sv)) {
|
||||||
auto const& function_values = component_value.function().value;
|
auto const& function_values = component_value.function().value;
|
||||||
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
|
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
|
||||||
|
@ -2588,8 +2583,8 @@ Optional<::URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& tok
|
||||||
if (value.is(Token::Type::Whitespace))
|
if (value.is(Token::Type::Whitespace))
|
||||||
continue;
|
continue;
|
||||||
if (value.is(Token::Type::String)) {
|
if (value.is(Token::Type::String)) {
|
||||||
auto url_string = value.token().string();
|
transaction.commit();
|
||||||
return convert_string_to_url(url_string);
|
return URL { value.token().string().to_string() };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2603,7 +2598,11 @@ RefPtr<CSSStyleValue> Parser::parse_url_value(TokenStream<ComponentValue>& token
|
||||||
auto url = parse_url_function(tokens);
|
auto url = parse_url_function(tokens);
|
||||||
if (!url.has_value())
|
if (!url.has_value())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return URLStyleValue::create(*url);
|
// FIXME: Stop completing the URL here
|
||||||
|
auto completed_url = complete_url(url->url());
|
||||||
|
if (!completed_url.has_value())
|
||||||
|
return nullptr;
|
||||||
|
return URLStyleValue::create(completed_url.release_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius
|
// https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius
|
||||||
|
@ -3681,7 +3680,11 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
||||||
|
|
||||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||||
auto url = parse_url_function(tokens);
|
auto url = parse_url_function(tokens);
|
||||||
if (!url.has_value() || !url->is_valid())
|
if (!url.has_value())
|
||||||
|
return nullptr;
|
||||||
|
// FIXME: Stop completing the URL here
|
||||||
|
auto completed_url = complete_url(url->url());
|
||||||
|
if (!completed_url.has_value())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Optional<FlyString> format;
|
Optional<FlyString> format;
|
||||||
|
@ -3719,7 +3722,7 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
||||||
// FIXME: [ tech( <font-tech>#)]?
|
// FIXME: [ tech( <font-tech>#)]?
|
||||||
|
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
return FontSourceStyleValue::create(url.release_value(), move(format));
|
return FontSourceStyleValue::create(completed_url.release_value(), move(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<CSSStyleValue> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
|
NonnullRefPtr<CSSStyleValue> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
|
||||||
|
|
31
Libraries/LibWeb/CSS/URL.cpp
Normal file
31
Libraries/LibWeb/CSS/URL.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/CSS/Serialize.h>
|
||||||
|
#include <LibWeb/CSS/URL.h>
|
||||||
|
|
||||||
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
URL::URL(String url)
|
||||||
|
: m_url(move(url))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/cssom-1/#serialize-a-url
|
||||||
|
String URL::to_string() const
|
||||||
|
{
|
||||||
|
// To serialize a URL means to create a string represented by "url(", followed by the serialization of the URL as a string, followed by ")".
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.append("url("sv);
|
||||||
|
serialize_a_string(builder, m_url);
|
||||||
|
builder.append(')');
|
||||||
|
|
||||||
|
return builder.to_string_without_validation();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URL::operator==(URL const&) const = default;
|
||||||
|
|
||||||
|
}
|
27
Libraries/LibWeb/CSS/URL.h
Normal file
27
Libraries/LibWeb/CSS/URL.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/String.h>
|
||||||
|
|
||||||
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-values-4/#urls
|
||||||
|
class URL {
|
||||||
|
public:
|
||||||
|
URL(String url);
|
||||||
|
|
||||||
|
String const& url() const { return m_url; }
|
||||||
|
|
||||||
|
String to_string() const;
|
||||||
|
bool operator==(URL const&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_url;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -269,6 +269,7 @@ class TransformationStyleValue;
|
||||||
class TransitionStyleValue;
|
class TransitionStyleValue;
|
||||||
class UnicodeRangeStyleValue;
|
class UnicodeRangeStyleValue;
|
||||||
class UnresolvedStyleValue;
|
class UnresolvedStyleValue;
|
||||||
|
class URL;
|
||||||
class URLStyleValue;
|
class URLStyleValue;
|
||||||
class VisualViewport;
|
class VisualViewport;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue