mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 12:05:15 +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
3b45ca3a76
commit
b91feeee4f
7 changed files with 101 additions and 27 deletions
|
@ -195,6 +195,7 @@ set(SOURCES
|
|||
CSS/Time.cpp
|
||||
CSS/Transformation.cpp
|
||||
CSS/TransitionEvent.cpp
|
||||
CSS/URL.cpp
|
||||
CSS/VisualViewport.cpp
|
||||
Cookie/Cookie.cpp
|
||||
Cookie/ParsedCookie.cpp
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/Supports.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::CSS::Parser {
|
||||
|
@ -276,7 +277,7 @@ private:
|
|||
Optional<GridRepeat> parse_repeat(Vector<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>&);
|
||||
|
||||
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 };
|
||||
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))
|
||||
url = complete_url(tokens.consume_a_token().token().string());
|
||||
url = URL { tokens.consume_a_token().token().string().to_string() };
|
||||
|
||||
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());
|
||||
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();
|
||||
// FIXME: Implement layer support.
|
||||
RefPtr<Supports> supports {};
|
||||
|
@ -191,7 +198,7 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
|
|||
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)
|
||||
|
@ -435,7 +442,10 @@ GC::Ptr<CSSNamespaceRule> Parser::convert_to_namespace_rule(AtRule const& rule)
|
|||
|
||||
FlyString namespace_uri;
|
||||
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)) {
|
||||
namespace_uri = url_token.token().string();
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <AK/Debug.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/CSS/FontFace.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/PropertyName.h>
|
||||
|
@ -2014,9 +2013,13 @@ RefPtr<AbstractImageStyleValue> Parser::parse_image_value(TokenStream<ComponentV
|
|||
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,
|
||||
// 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)) {
|
||||
tokens.discard_a_mark();
|
||||
return ImageStyleValue::create(url.value());
|
||||
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();
|
||||
return ImageStyleValue::create(completed_url.release_value());
|
||||
}
|
||||
}
|
||||
tokens.restore_a_mark();
|
||||
return nullptr;
|
||||
|
@ -2562,24 +2565,16 @@ RefPtr<CSSStyleValue> Parser::parse_easing_value(TokenStream<ComponentValue>& to
|
|||
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& 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 {};
|
||||
};
|
||||
auto const& component_value = tokens.consume_a_token();
|
||||
|
||||
if (component_value.is(Token::Type::Url)) {
|
||||
auto url_string = component_value.token().url();
|
||||
return convert_string_to_url(url_string);
|
||||
transaction.commit();
|
||||
return URL { component_value.token().url().to_string() };
|
||||
}
|
||||
|
||||
if (component_value.is_function("url"sv)) {
|
||||
auto const& function_values = component_value.function().value;
|
||||
// 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))
|
||||
continue;
|
||||
if (value.is(Token::Type::String)) {
|
||||
auto url_string = value.token().string();
|
||||
return convert_string_to_url(url_string);
|
||||
transaction.commit();
|
||||
return URL { value.token().string().to_string() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2603,7 +2598,11 @@ RefPtr<CSSStyleValue> Parser::parse_url_value(TokenStream<ComponentValue>& token
|
|||
auto url = parse_url_function(tokens);
|
||||
if (!url.has_value())
|
||||
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
|
||||
|
@ -3681,7 +3680,11 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
|||
|
||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||
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;
|
||||
|
||||
Optional<FlyString> format;
|
||||
|
@ -3719,7 +3722,7 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
|||
// FIXME: [ tech( <font-tech>#)]?
|
||||
|
||||
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)
|
||||
|
|
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 UnicodeRangeStyleValue;
|
||||
class UnresolvedStyleValue;
|
||||
class URL;
|
||||
class URLStyleValue;
|
||||
class VisualViewport;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue