2024-12-20 15:53:18 +00:00
/*
2025-04-10 10:26:22 +01:00
* Copyright ( c ) 2024 - 2025 , Sam Atkins < sam @ ladybird . org >
2024-12-20 15:53:18 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibWeb/CSS/CSSStyleSheet.h>
# include <LibWeb/CSS/Fetch.h>
2025-04-10 12:09:39 +01:00
# include <LibWeb/DOM/Document.h>
2025-08-08 09:55:38 -04:00
# include <LibWeb/DOMURL/DOMURL.h>
2024-12-20 15:53:18 +00:00
# include <LibWeb/Fetch/Fetching/Fetching.h>
2025-04-10 16:04:34 +01:00
# include <LibWeb/HTML/SharedResourceRequest.h>
2025-09-23 23:28:11 +02:00
# include <LibWeb/Painting/ViewportPaintable.h>
2024-12-20 15:53:18 +00:00
namespace Web : : CSS {
2025-12-28 12:12:59 +01:00
// https://drafts.csswg.org/css-values-4/#style-resource-base-url
struct StyleSheetAndURL {
GC : : Ptr < CSSStyleSheet > sheet ;
: : URL : : URL url ;
} ;
static StyleSheetAndURL style_resource_base_url ( RuleOrDeclaration css_rule_or_declaration )
2024-12-20 15:53:18 +00:00
{
2025-12-28 12:12:59 +01:00
// 1. Let sheet be null.
GC : : Ptr < CSSStyleSheet > sheet ;
2025-04-10 12:09:39 +01:00
2025-12-28 12:12:59 +01:00
// 2. If cssRuleOrDeclaration is a CSS declaration block whose parent CSS rule is not null, set cssRuleOrDeclaration to cssRuleOrDeclaration’ s parent CSS rule.
if ( auto * block = css_rule_or_declaration . value . get_pointer < RuleOrDeclaration : : StyleDeclaration > ( ) ) {
if ( block - > parent_rule )
css_rule_or_declaration . value = RuleOrDeclaration : : Rule { block - > parent_rule - > parent_style_sheet ( ) } ;
}
2024-12-20 15:53:18 +00:00
2025-12-28 12:12:59 +01:00
// 3. If cssRuleOrDeclaration is a CSS rule, set sheet to cssRuleOrDeclaration’ s parent style sheet.
if ( auto * rule = css_rule_or_declaration . value . get_pointer < RuleOrDeclaration : : Rule > ( ) ) {
if ( rule - > parent_style_sheet ) {
sheet = rule - > parent_style_sheet ;
}
}
2024-12-20 15:53:18 +00:00
2025-12-28 12:12:59 +01:00
// 4. If sheet is not null:
if ( sheet ) {
// 1. If sheet’ s stylesheet base URL is not null, return sheet’ s stylesheet base URL.
if ( auto base_url = sheet - > base_url ( ) ; base_url . has_value ( ) )
return { sheet , base_url . value ( ) } ;
// 2. If sheet’ s location is not null, return sheet’ s location.
if ( auto location = sheet - > location ( ) ; location . has_value ( ) )
return { sheet , location . value ( ) } ;
}
// 5. Return cssRuleOrDeclaration’ s relevant settings object’ s API base URL.
return { sheet , css_rule_or_declaration . environment_settings_object - > api_base_url ( ) } ;
}
2024-12-20 15:53:18 +00:00
2025-12-28 12:12:59 +01:00
// https://drafts.csswg.org/css-values-4/#resolve-a-style-resource-url
static Optional < : : URL : : URL > resolve_a_style_resource_url ( StyleResourceURL const & url_value , RuleOrDeclaration css_rule_or_declaration )
{
// 1. Let baseURL be the style resource base URL given cssRuleOrDeclaration.
auto [ _ , base_url ] = style_resource_base_url ( css_rule_or_declaration ) ;
// 2. Return the result of the URL parser steps with urlValue’ s url and base.
2025-04-10 10:26:22 +01:00
auto url_string = url_value . visit (
[ ] ( : : URL : : URL const & url ) { return url . to_string ( ) ; } ,
[ ] ( CSS : : URL const & url ) { return url . url ( ) ; } ) ;
2025-12-28 12:12:59 +01:00
return DOMURL : : parse ( url_string , base_url ) ;
}
2025-08-08 09:55:38 -04:00
2025-12-28 12:12:59 +01:00
// https://drafts.csswg.org/css-values-4/#fetch-a-style-resource
static GC : : Ptr < Fetch : : Infrastructure : : Request > fetch_a_style_resource_impl ( StyleResourceURL const & url_value , RuleOrDeclaration css_rule_or_declaration , Fetch : : Infrastructure : : Request : : Destination destination , CorsMode cors_mode )
{
auto & vm = css_rule_or_declaration . environment_settings_object - > vm ( ) ;
2025-11-12 12:06:53 -05:00
2025-12-28 12:12:59 +01:00
// 1. Let parsedUrl be the result of resolving urlValue given cssRuleOrDeclaration. If that failed, return.
auto parsed_url = resolve_a_style_resource_url ( url_value , css_rule_or_declaration ) ;
2025-01-10 04:50:34 +13:00
if ( ! parsed_url . has_value ( ) )
2025-11-12 11:47:27 -05:00
return { } ;
2024-12-20 15:53:18 +00:00
2025-12-28 12:12:59 +01:00
// 2. Let settingsObject be cssRuleOrDeclaration’ s relevant settings object.
auto & environment_settings = * css_rule_or_declaration . environment_settings_object ;
// 3. Let req be a new request whose url is parsedUrl, whose destination is destination, mode is corsMode,
2024-12-20 15:53:18 +00:00
// origin is environmentSettings’ s origin, credentials mode is "same-origin", use-url-credentials flag is set,
// client is environmentSettings, and whose referrer is environmentSettings’ s API base URL.
auto request = Fetch : : Infrastructure : : Request : : create ( vm ) ;
2025-01-10 04:50:34 +13:00
request - > set_url ( parsed_url . release_value ( ) ) ;
2024-12-20 15:53:18 +00:00
request - > set_destination ( destination ) ;
request - > set_mode ( cors_mode = = CorsMode : : Cors ? Fetch : : Infrastructure : : Request : : Mode : : CORS : Fetch : : Infrastructure : : Request : : Mode : : NoCORS ) ;
request - > set_origin ( environment_settings . origin ( ) ) ;
request - > set_credentials_mode ( Fetch : : Infrastructure : : Request : : CredentialsMode : : SameOrigin ) ;
request - > set_use_url_credentials ( true ) ;
request - > set_client ( & environment_settings ) ;
request - > set_referrer ( environment_settings . api_base_url ( ) ) ;
2025-12-28 12:12:59 +01:00
// 4. If corsMode is "no-cors", set req’ s credentials mode to "include".
2025-05-02 17:25:56 +01:00
if ( cors_mode = = CorsMode : : NoCors )
request - > set_credentials_mode ( Fetch : : Infrastructure : : Request : : CredentialsMode : : Include ) ;
2024-12-20 15:53:18 +00:00
2025-12-28 12:12:59 +01:00
// 5. Apply any URL request modifier steps that apply to this request.
2025-05-02 17:25:56 +01:00
if ( auto const * css_url = url_value . get_pointer < CSS : : URL > ( ) )
apply_request_modifiers_from_url_value ( * css_url , request ) ;
2025-12-28 12:12:59 +01:00
// 6. If req’ s mode is "cors", and sheet is not null, then set req’ s referrer to the style resource base URL given cssRuleOrDeclaration. [CSSOM]
// FIXME: Spec issue - sheet is not defined as a variable, we use the sheet determined from 'style resource base URL' instead.
// https://github.com/w3c/csswg-drafts/issues/12288
auto [ sheet , base_url ] = style_resource_base_url ( css_rule_or_declaration ) ;
if ( request - > mode ( ) = = Fetch : : Infrastructure : : Request : : Mode : : CORS & & sheet )
request - > set_referrer ( base_url ) ;
// 7. If sheet’ s origin-clean flag is set, set req’ s initiator type to "css". [CSSOM]
if ( sheet ) {
if ( sheet - > is_origin_clean ( ) )
request - > set_initiator_type ( Fetch : : Infrastructure : : Request : : InitiatorType : : CSS ) ;
} else {
// AD-HOC: If the resource is not associated with a stylesheet, we must still set an initiator type in order
// for this resource to be observable through a PerformanceObserver. WPT relies on this.
request - > set_initiator_type ( Fetch : : Infrastructure : : Request : : InitiatorType : : Script ) ;
2025-04-10 12:09:39 +01:00
}
2024-12-20 15:53:18 +00:00
2025-05-02 17:25:56 +01:00
// 9. Fetch req, with processresponseconsumebody set to processResponse.
2025-04-10 16:04:34 +01:00
// NB: Implemented by caller.
return request ;
}
// https://drafts.csswg.org/css-values-4/#fetch-a-style-resource
2025-12-28 12:12:59 +01:00
GC : : Ptr < Fetch : : Infrastructure : : FetchController > fetch_a_style_resource ( StyleResourceURL const & url_value , RuleOrDeclaration css_rule_or_declaration , Fetch : : Infrastructure : : Request : : Destination destination , CorsMode cors_mode , Fetch : : Infrastructure : : FetchAlgorithms : : ProcessResponseConsumeBodyFunction process_response )
2025-04-10 16:04:34 +01:00
{
2025-12-28 12:12:59 +01:00
auto request = fetch_a_style_resource_impl ( url_value , css_rule_or_declaration , destination , cors_mode ) ;
2025-11-12 11:47:27 -05:00
if ( ! request )
return { } ;
2025-12-28 12:12:59 +01:00
auto & environment_settings = * css_rule_or_declaration . environment_settings_object ;
2025-04-30 12:33:29 +01:00
auto & vm = environment_settings . vm ( ) ;
2025-04-10 16:04:34 +01:00
2025-04-30 12:33:29 +01:00
Fetch : : Infrastructure : : FetchAlgorithms : : Input fetch_algorithms_input { } ;
fetch_algorithms_input . process_response_consume_body = move ( process_response ) ;
2025-04-10 16:04:34 +01:00
2025-04-30 12:33:29 +01:00
return Fetch : : Fetching : : fetch ( environment_settings . realm ( ) , * request , Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm , move ( fetch_algorithms_input ) ) ) ;
2025-04-10 16:04:34 +01:00
}
// https://drafts.csswg.org/css-images-4/#fetch-an-external-image-for-a-stylesheet
2025-12-28 12:12:59 +01:00
GC : : Ptr < HTML : : SharedResourceRequest > fetch_an_external_image_for_a_stylesheet ( StyleResourceURL const & url_value , RuleOrDeclaration declaration , DOM : : Document & document )
2025-04-10 16:04:34 +01:00
{
2025-12-28 12:12:59 +01:00
// To fetch an external image for a stylesheet, given a <url> url and a CSS declaration block declaration, fetch a
// style resource given url, with ruleOrDeclaration being declaration, destination "image", CORS mode "no-cors",
// and processResponse being the following steps given response res and null, failure or a byte stream byteStream:
// If byteStream is a byte stream, load the image from the byte stream.
2025-04-10 16:04:34 +01:00
// NB: We can't directly call fetch_a_style_resource() because we want to make use of SharedResourceRequest to
// deduplicate image requests.
2025-12-28 12:12:59 +01:00
auto request = fetch_a_style_resource_impl ( url_value , declaration , Fetch : : Infrastructure : : Request : : Destination : : Image , CorsMode : : NoCors ) ;
2025-11-12 11:47:27 -05:00
if ( ! request )
return { } ;
2025-04-30 12:33:29 +01:00
2025-12-28 12:12:59 +01:00
auto & realm = document . realm ( ) ;
2025-04-30 12:33:29 +01:00
2025-12-28 12:12:59 +01:00
auto shared_resource_request = HTML : : SharedResourceRequest : : get_or_create ( realm , document . page ( ) , request - > url ( ) ) ;
2025-04-30 12:33:29 +01:00
shared_resource_request - > add_callbacks (
2025-12-28 12:12:59 +01:00
[ & document , weak_document = GC : : Weak { document } ] {
2025-04-30 12:33:29 +01:00
if ( ! weak_document )
return ;
2025-12-28 12:12:59 +01:00
if ( auto navigable = document . navigable ( ) ) {
2026-03-04 16:02:14 +01:00
document . notify_css_background_image_loaded ( ) ;
2025-04-30 12:33:29 +01:00
}
} ,
nullptr ) ;
2025-04-10 16:04:34 +01:00
2025-04-30 12:33:29 +01:00
if ( shared_resource_request - > needs_fetching ( ) )
shared_resource_request - > fetch_resource ( realm , * request ) ;
2024-12-20 15:53:18 +00:00
2025-04-30 12:33:29 +01:00
return shared_resource_request ;
2024-12-20 15:53:18 +00:00
}
2025-05-02 17:25:56 +01:00
// https://drafts.csswg.org/css-values-5/#apply-request-modifiers-from-url-value
void apply_request_modifiers_from_url_value ( URL const & url , GC : : Ref < Fetch : : Infrastructure : : Request > request )
{
// To apply request modifiers from URL value given a request req and a <url> url, call the URL request modifier
// steps for url’ s <request-url-modifier>s in sequence given req.
for ( auto const & request_url_modifier : url . request_url_modifiers ( ) )
request_url_modifier . modify_request ( request ) ;
}
2024-12-20 15:53:18 +00:00
}