2020-01-18 09:38:21 +01:00
/*
2025-02-27 15:30:26 +01:00
* Copyright ( c ) 2018 - 2025 , Andreas Kling < andreas @ ladybird . org >
2021-02-21 18:44:17 +02:00
* Copyright ( c ) 2021 , the SerenityOS developers .
2021-11-18 19:22:59 +00:00
* Copyright ( c ) 2021 , Sam Atkins < atkinssj @ serenityos . org >
2023-01-14 19:05:28 +05:30
* Copyright ( c ) 2023 , Srikavin Ramkumar < me @ srikavin . me >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2020-02-14 22:29:06 +01:00
# include <AK/ByteBuffer.h>
2021-11-18 19:22:59 +00:00
# include <AK/Debug.h>
2023-07-04 09:50:47 +02:00
# include <LibTextCodec/Decoder.h>
2024-03-18 16:22:27 +13:00
# include <LibURL/URL.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLLinkElementPrototype.h>
2025-01-15 15:06:39 -07:00
# include <LibWeb/Bindings/PrincipalHostDefined.h>
2021-07-30 19:31:46 +01:00
# include <LibWeb/CSS/Parser/Parser.h>
2024-05-16 06:02:56 +01:00
# include <LibWeb/DOM/DOMTokenList.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Document.h>
2023-01-14 19:05:28 +05:30
# include <LibWeb/DOM/Event.h>
2023-03-19 15:13:45 +01:00
# include <LibWeb/DOM/ShadowRoot.h>
2025-01-21 18:03:37 +13:00
# include <LibWeb/DOMURL/DOMURL.h>
2023-01-14 19:05:28 +05:30
# include <LibWeb/Fetch/Fetching/Fetching.h>
# include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
2024-09-22 13:29:49 +02:00
# include <LibWeb/Fetch/Infrastructure/FetchController.h>
2023-01-14 19:05:28 +05:30
# include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
# include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
# include <LibWeb/HTML/EventNames.h>
2020-07-26 15:08:16 +02:00
# include <LibWeb/HTML/HTMLLinkElement.h>
2023-01-14 19:05:28 +05:30
# include <LibWeb/HTML/PotentialCORSRequest.h>
2023-08-27 17:06:39 +02:00
# include <LibWeb/HTML/TraversableNavigable.h>
2022-10-01 18:29:18 +01:00
# include <LibWeb/Infra/CharacterTypes.h>
2023-11-19 18:10:36 +13:00
# include <LibWeb/Infra/Strings.h>
2020-06-01 20:42:50 +02:00
# include <LibWeb/Loader/ResourceLoader.h>
2022-04-03 19:49:38 +02:00
# include <LibWeb/Page/Page.h>
2022-09-16 15:01:47 +02:00
# include <LibWeb/Platform/ImageCodecPlugin.h>
2019-10-07 19:06:47 +02:00
2020-07-28 18:20:36 +02:00
namespace Web : : HTML {
2020-03-07 10:27:02 +01:00
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( HTMLLinkElement ) ;
2023-11-19 19:47:52 +01:00
2022-02-18 21:00:52 +01:00
HTMLLinkElement : : HTMLLinkElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2021-02-07 11:20:15 +01:00
: HTMLElement ( document , move ( qualified_name ) )
2019-10-07 19:06:47 +02:00
{
}
2022-03-14 13:21:51 -06:00
HTMLLinkElement : : ~ HTMLLinkElement ( ) = default ;
2019-10-07 19:06:47 +02:00
2023-08-07 08:41:28 +02:00
void HTMLLinkElement : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLLinkElement ) ;
2023-01-10 06:28:20 -05:00
}
2025-01-23 17:37:18 +01:00
void HTMLLinkElement : : removed_from ( Node * old_parent , Node & old_root )
2024-04-21 19:46:37 +02:00
{
2025-01-23 17:37:18 +01:00
Base : : removed_from ( old_parent , old_root ) ;
2024-04-21 19:46:37 +02:00
if ( m_loaded_style_sheet ) {
document_or_shadow_root_style_sheets ( ) . remove_a_css_style_sheet ( * m_loaded_style_sheet ) ;
m_loaded_style_sheet = nullptr ;
}
}
2021-04-06 17:58:20 +01:00
void HTMLLinkElement : : inserted ( )
2020-06-02 12:53:29 +02:00
{
2021-04-06 17:58:20 +01:00
HTMLElement : : inserted ( ) ;
2020-06-02 12:53:29 +02:00
2024-04-24 17:57:18 +00:00
if ( ! document ( ) . browsing_context ( ) ) {
return ;
}
2024-04-16 21:02:50 +01:00
if ( m_relationship & Relationship : : Stylesheet ) {
2023-01-14 19:05:28 +05:30
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
// The appropriate times to fetch and process this type of link are:
// - When the external resource link is created on a link element that is already browsing-context connected.
// - When the external resource link's link element becomes browsing-context connected.
fetch_and_process_linked_resource ( ) ;
2019-10-07 19:06:47 +02:00
}
2021-09-27 02:06:37 +02:00
2023-01-14 19:05:28 +05:30
// FIXME: Follow spec for fetching and processing these attributes as well
2021-09-27 02:06:37 +02:00
if ( m_relationship & Relationship : : Preload ) {
2025-01-23 19:40:57 +13:00
if ( auto maybe_href = document ( ) . encoding_parse_url ( get_attribute_value ( HTML : : AttributeNames : : href ) ) ; maybe_href . has_value ( ) ) {
// FIXME: Respect the "as" attribute.
LoadRequest request ;
request . set_url ( maybe_href . value ( ) ) ;
2025-01-15 15:06:39 -07:00
request . set_page ( Bindings : : principal_host_defined_page ( HTML : : principal_realm ( realm ( ) ) ) ) ;
2025-01-23 19:40:57 +13:00
set_resource ( ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , request ) ) ;
}
2021-09-28 00:08:29 +03:30
} else if ( m_relationship & Relationship : : DNSPrefetch ) {
2025-01-23 19:40:57 +13:00
if ( auto dns_prefetch_url = document ( ) . encoding_parse_url ( get_attribute_value ( HTML : : AttributeNames : : href ) ) ; dns_prefetch_url . has_value ( ) ) {
ResourceLoader : : the ( ) . prefetch_dns ( dns_prefetch_url . value ( ) ) ;
}
2021-09-28 00:08:29 +03:30
} else if ( m_relationship & Relationship : : Preconnect ) {
2025-01-23 19:40:57 +13:00
if ( auto maybe_href = document ( ) . encoding_parse_url ( get_attribute_value ( HTML : : AttributeNames : : href ) ) ; maybe_href . has_value ( ) ) {
ResourceLoader : : the ( ) . preconnect ( maybe_href . value ( ) ) ;
}
2022-04-03 19:49:38 +02:00
} else if ( m_relationship & Relationship : : Icon ) {
2025-01-23 19:40:57 +13:00
if ( auto favicon_url = document ( ) . encoding_parse_url ( href ( ) ) ; favicon_url . has_value ( ) ) {
auto favicon_request = LoadRequest : : create_for_url_on_page ( favicon_url . value ( ) , & document ( ) . page ( ) ) ;
set_resource ( ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , favicon_request ) ) ;
}
2021-09-27 02:06:37 +02:00
}
2019-10-07 19:06:47 +02:00
}
2020-03-07 10:27:02 +01:00
2024-06-04 08:04:44 +01:00
WebIDL : : ExceptionOr < void > HTMLLinkElement : : set_as ( String const & value )
{
return set_attribute ( HTML : : AttributeNames : : as , move ( value ) ) ;
}
2024-05-16 06:02:56 +01:00
// https://html.spec.whatwg.org/multipage/semantics.html#dom-link-rellist
2024-11-15 04:01:23 +13:00
GC : : Ref < DOM : : DOMTokenList > HTMLLinkElement : : rel_list ( )
2024-05-16 06:02:56 +01:00
{
// The relList IDL attribute must reflect the rel content attribute.
if ( ! m_rel_list )
m_rel_list = DOM : : DOMTokenList : : create ( * this , HTML : : AttributeNames : : rel ) ;
2024-05-18 05:54:15 +01:00
return * m_rel_list ;
2024-05-16 06:02:56 +01:00
}
2024-11-18 07:00:59 +13:00
// https://html.spec.whatwg.org/multipage/semantics.html#dom-link-sizes
GC : : Ref < DOM : : DOMTokenList > HTMLLinkElement : : sizes ( )
{
// The size IDL attribute must reflect the size content attribute.
if ( ! m_sizes )
m_sizes = DOM : : DOMTokenList : : create ( * this , HTML : : AttributeNames : : sizes ) ;
return * m_sizes ;
}
2022-04-03 19:49:38 +02:00
bool HTMLLinkElement : : has_loaded_icon ( ) const
{
return m_relationship & Relationship : : Icon & & resource ( ) & & resource ( ) - > is_loaded ( ) & & resource ( ) - > has_encoded_data ( ) ;
}
2024-11-14 08:14:16 -05:00
void HTMLLinkElement : : attribute_changed ( FlyString const & name , Optional < String > const & old_value , Optional < String > const & value , Optional < FlyString > const & namespace_ )
2020-06-15 20:25:25 +02:00
{
2024-11-14 08:14:16 -05:00
Base : : attribute_changed ( name , old_value , value , namespace_ ) ;
2023-06-06 07:07:27 +02:00
2025-02-21 10:19:43 +00:00
// https://html.spec.whatwg.org/multipage/semantics.html#processing-the-type-attribute:attr-link-type
if ( name = = HTML : : AttributeNames : : type ) {
if ( value . has_value ( ) )
m_mime_type = value - > to_ascii_lowercase ( ) ;
else {
m_mime_type = { } ;
}
return ;
}
2022-04-03 19:39:38 +02:00
// 4.6.7 Link types - https://html.spec.whatwg.org/multipage/links.html#linkTypes
2025-01-20 16:26:59 +11:00
auto old_relationship = m_relationship ;
2020-06-15 20:25:25 +02:00
if ( name = = HTML : : AttributeNames : : rel ) {
m_relationship = 0 ;
2022-04-03 19:39:38 +02:00
// Keywords are always ASCII case-insensitive, and must be compared as such.
2024-10-14 10:51:15 +02:00
auto lowercased_value = value . value_or ( String { } ) . to_ascii_lowercase ( ) ;
2022-04-03 19:39:38 +02:00
// To determine which link types apply to a link, a, area, or form element,
// the element's rel attribute must be split on ASCII whitespace.
// The resulting tokens are the keywords for the link types that apply to that element.
2023-11-19 18:10:36 +13:00
auto parts = lowercased_value . bytes_as_string_view ( ) . split_view_if ( Infra : : is_ascii_whitespace ) ;
2020-06-15 20:25:25 +02:00
for ( auto & part : parts ) {
2021-09-28 00:08:29 +03:30
if ( part = = " stylesheet " sv )
2020-06-15 20:25:25 +02:00
m_relationship | = Relationship : : Stylesheet ;
2021-09-28 00:08:29 +03:30
else if ( part = = " alternate " sv )
2020-06-15 20:25:25 +02:00
m_relationship | = Relationship : : Alternate ;
2021-09-28 00:08:29 +03:30
else if ( part = = " preload " sv )
2021-09-27 02:06:37 +02:00
m_relationship | = Relationship : : Preload ;
2021-09-28 00:08:29 +03:30
else if ( part = = " dns-prefetch " sv )
m_relationship | = Relationship : : DNSPrefetch ;
else if ( part = = " preconnect " sv )
m_relationship | = Relationship : : Preconnect ;
2022-04-03 19:39:38 +02:00
else if ( part = = " icon " sv )
m_relationship | = Relationship : : Icon ;
2020-06-15 20:25:25 +02:00
}
2024-05-16 06:02:56 +01:00
if ( m_rel_list )
m_rel_list - > associated_attribute_changed ( value . value_or ( String { } ) ) ;
2020-06-15 20:25:25 +02:00
}
2022-11-20 01:46:25 +01:00
2024-04-16 21:02:50 +01:00
// https://html.spec.whatwg.org/multipage/semantics.html#the-link-element:explicitly-enabled
// Whenever the disabled attribute is removed, set the link element's explicitly enabled attribute to true.
if ( ! value . has_value ( ) & & name = = HTML : : AttributeNames : : disabled )
m_explicitly_enabled = true ;
if ( m_relationship & Relationship : : Stylesheet ) {
2025-02-01 15:47:02 +11:00
if ( name = = HTML : : AttributeNames : : disabled & & m_loaded_style_sheet ) {
2024-04-15 21:42:46 +01:00
document_or_shadow_root_style_sheets ( ) . remove_a_css_style_sheet ( * m_loaded_style_sheet ) ;
2025-02-01 15:47:02 +11:00
m_loaded_style_sheet = nullptr ;
}
2023-01-14 19:05:28 +05:30
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
// The appropriate times to fetch and process this type of link are:
if (
2024-04-26 09:29:27 +02:00
is_browsing_context_connected ( )
& & (
2025-01-20 16:26:59 +11:00
// AD-HOC: When the link element's type becomes a stylesheet
! ( old_relationship & Relationship : : Stylesheet ) | |
2024-04-26 09:29:27 +02:00
// - When the href attribute of the link element of an external resource link that is already browsing-context connected is changed.
name = = AttributeNames : : href | |
// - When the disabled attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
name = = AttributeNames : : disabled | |
// - When the crossorigin attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
name = = AttributeNames : : crossorigin
// FIXME: - When the type attribute of the link element of an external resource link that is already browsing-context connected is set or changed to a value that does not or no longer matches the Content-Type metadata of the previous obtained external resource, if any.
// FIXME: - When the type attribute of the link element of an external resource link that is already browsing-context connected, but was previously not obtained due to the type attribute specifying an unsupported type, is removed or changed.
) ) {
2023-01-14 19:05:28 +05:30
fetch_and_process_linked_resource ( ) ;
}
2024-08-10 18:07:02 -06:00
if ( name = = HTML : : AttributeNames : : media & & m_loaded_style_sheet ) {
m_loaded_style_sheet - > set_media ( value . value_or ( String { } ) ) ;
}
2023-01-14 19:05:28 +05:30
}
2020-06-15 20:25:25 +02:00
}
2021-11-18 19:22:59 +00:00
void HTMLLinkElement : : resource_did_fail ( )
{
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Resource did fail. URL: {} " , resource ( ) - > url ( ) ) ;
2023-06-06 07:03:30 +02:00
if ( m_relationship & Relationship : : Preload ) {
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-06-06 07:03:30 +02:00
}
2021-11-18 19:22:59 +00:00
}
void HTMLLinkElement : : resource_did_load ( )
{
VERIFY ( resource ( ) ) ;
2023-01-14 19:05:28 +05:30
if ( m_relationship & Relationship : : Icon ) {
2022-04-03 19:49:38 +02:00
resource_did_load_favicon ( ) ;
2023-01-14 19:05:28 +05:30
m_document_load_event_delayer . clear ( ) ;
}
2023-06-06 07:03:30 +02:00
if ( m_relationship & Relationship : : Preload ) {
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2023-06-06 07:03:30 +02:00
}
2022-04-03 19:49:38 +02:00
}
2023-01-14 19:05:28 +05:30
// https://html.spec.whatwg.org/multipage/semantics.html#create-link-options-from-element
HTMLLinkElement : : LinkProcessingOptions HTMLLinkElement : : create_link_options ( )
2022-04-03 19:49:38 +02:00
{
2023-01-14 19:05:28 +05:30
// 1. Let document be el's node document.
auto & document = this - > document ( ) ;
2021-11-18 19:22:59 +00:00
2023-01-14 19:05:28 +05:30
// 2. Let options be a new link processing options with
LinkProcessingOptions options ;
// FIXME: destination the result of translating the state of el's as attribute
// crossorigin the state of el's crossorigin content attribute
2023-10-01 17:46:26 +13:00
options . crossorigin = cors_setting_attribute_from_keyword ( get_attribute ( AttributeNames : : crossorigin ) ) ;
2024-05-31 21:18:55 +01:00
// referrer policy the state of el's referrerpolicy content attribute
options . referrer_policy = ReferrerPolicy : : from_string ( get_attribute ( AttributeNames : : referrerpolicy ) . value_or ( " " _string ) ) . value_or ( ReferrerPolicy : : ReferrerPolicy : : EmptyString ) ;
2023-01-14 19:05:28 +05:30
// FIXME: source set el's source set
2024-08-10 20:23:13 -06:00
// base URL document's document base URL
options . base_url = document . base_url ( ) ;
2023-01-14 19:05:28 +05:30
// origin document's origin
options . origin = document . origin ( ) ;
// environment document's relevant settings object
options . environment = & document . relevant_settings_object ( ) ;
// policy container document's policy container
options . policy_container = document . policy_container ( ) ;
// document document
options . document = & document ;
// FIXME: cryptographic nonce metadata The current value of el's [[CryptographicNonce]] internal slot
2024-05-30 21:33:43 +01:00
// fetch priority the state of el's fetchpriority content attribute
options . fetch_priority = Fetch : : Infrastructure : : request_priority_from_string ( get_attribute_value ( HTML : : AttributeNames : : fetchpriority ) ) . value_or ( Fetch : : Infrastructure : : Request : : Priority : : Auto ) ;
2023-01-14 19:05:28 +05:30
// 3. If el has an href attribute, then set options's href to the value of el's href attribute.
2023-10-01 17:46:26 +13:00
if ( auto maybe_href = get_attribute ( AttributeNames : : href ) ; maybe_href . has_value ( ) )
options . href = maybe_href . value ( ) ;
2023-01-14 19:05:28 +05:30
// 4. If el has an integrity attribute, then set options's integrity to the value of el's integrity content attribute.
2023-10-01 17:46:26 +13:00
if ( auto maybe_integrity = get_attribute ( AttributeNames : : integrity ) ; maybe_integrity . has_value ( ) )
options . integrity = maybe_integrity . value ( ) ;
2023-01-14 19:05:28 +05:30
// 5. If el has a type attribute, then set options's type to the value of el's type attribute.
2023-10-01 17:46:26 +13:00
if ( auto maybe_type = get_attribute ( AttributeNames : : type ) ; maybe_type . has_value ( ) )
options . type = maybe_type . value ( ) ;
2023-01-14 19:05:28 +05:30
// FIXME: 6. Assert: options's href is not the empty string, or options's source set is not null.
// A link element with neither an href or an imagesrcset does not represent a link.
// 7. Return options.
return options ;
}
// https://html.spec.whatwg.org/multipage/semantics.html#create-a-link-request
2024-11-15 04:01:23 +13:00
GC : : Ptr < Fetch : : Infrastructure : : Request > HTMLLinkElement : : create_link_request ( HTMLLinkElement : : LinkProcessingOptions const & options )
2023-01-14 19:05:28 +05:30
{
// 1. Assert: options's href is not the empty string.
2024-05-30 21:31:14 +01:00
VERIFY ( ! options . href . is_empty ( ) ) ;
2023-01-14 19:05:28 +05:30
2024-05-30 21:31:14 +01:00
// FIXME: 2. If options's destination is null, then return null.
2023-01-14 19:05:28 +05:30
2024-05-30 21:31:14 +01:00
// 3. Let url be the result of encoding-parsing a URL given options's href, relative to options's base URL.
2024-12-06 16:24:08 -05:00
// FIXME: Spec issue: We should be parsing this URL relative to a document or environment settings object.
// https://github.com/whatwg/html/issues/9715
2025-01-21 18:03:37 +13:00
auto url = DOMURL : : parse ( options . href , options . base_url ) ;
2024-05-30 21:31:14 +01:00
// 4. If url is failure, then return null.
2025-01-22 17:35:52 +13:00
if ( ! url . has_value ( ) )
2023-01-14 19:05:28 +05:30
return nullptr ;
2024-05-30 21:31:14 +01:00
// 5. Let request be the result of creating a potential-CORS request given url, options's destination, and options's crossorigin.
2025-01-22 17:35:52 +13:00
auto request = create_potential_CORS_request ( vm ( ) , * url , options . destination , options . crossorigin ) ;
2023-01-14 19:05:28 +05:30
2024-05-30 21:31:14 +01:00
// 6. Set request's policy container to options's policy container.
2024-11-25 14:30:12 +00:00
request - > set_policy_container ( GC : : Ref { * options . policy_container } ) ;
2023-01-14 19:05:28 +05:30
2024-05-30 21:31:14 +01:00
// 7. Set request's integrity metadata to options's integrity.
2023-01-14 19:05:28 +05:30
request - > set_integrity_metadata ( options . integrity ) ;
2024-05-30 21:31:14 +01:00
// 8. Set request's cryptographic nonce metadata to options's cryptographic nonce metadata.
2023-01-14 19:05:28 +05:30
request - > set_cryptographic_nonce_metadata ( options . cryptographic_nonce_metadata ) ;
2024-05-30 21:31:14 +01:00
// 9. Set request's referrer policy to options's referrer policy.
2023-01-14 19:05:28 +05:30
request - > set_referrer_policy ( options . referrer_policy ) ;
2024-05-30 21:31:14 +01:00
// 10. Set request's client to options's environment.
2023-01-14 19:05:28 +05:30
request - > set_client ( options . environment ) ;
2024-05-30 21:31:14 +01:00
// 11. Set request's priority to options's fetch priority.
request - > set_priority ( options . fetch_priority ) ;
// 12. Return request.
2023-01-14 19:05:28 +05:30
return request ;
}
// https://html.spec.whatwg.org/multipage/semantics.html#fetch-and-process-the-linked-resource
void HTMLLinkElement : : fetch_and_process_linked_resource ( )
{
default_fetch_and_process_linked_resource ( ) ;
}
// https://html.spec.whatwg.org/multipage/semantics.html#default-fetch-and-process-the-linked-resource
void HTMLLinkElement : : default_fetch_and_process_linked_resource ( )
{
// https://html.spec.whatwg.org/multipage/semantics.html#the-link-element:attr-link-href-4
// If both the href and imagesrcset attributes are absent, then the element does not define a link.
// FIXME: Support imagesrcset attribute
if ( ! has_attribute ( AttributeNames : : href ) | | href ( ) . is_empty ( ) )
return ;
// 1. Let options be the result of creating link options from el.
auto options = create_link_options ( ) ;
// 2. Let request be the result of creating a link request given options.
auto request = create_link_request ( options ) ;
// 3. If request is null, then return.
if ( request = = nullptr ) {
return ;
}
// FIXME: 4. Set request's synchronous flag.
// 5. Run the linked resource fetch setup steps, given el and request. If the result is false, then return.
if ( ! linked_resource_fetch_setup_steps ( * request ) )
return ;
// 6. Set request's initiator type to "css" if el's rel attribute contains the keyword stylesheet; "link" otherwise.
if ( m_relationship & Relationship : : Stylesheet ) {
request - > set_initiator_type ( Fetch : : Infrastructure : : Request : : InitiatorType : : CSS ) ;
2021-11-18 19:22:59 +00:00
} else {
2023-01-14 19:05:28 +05:30
request - > set_initiator_type ( Fetch : : Infrastructure : : Request : : InitiatorType : : Link ) ;
}
2022-03-20 17:20:59 +01:00
2023-01-14 19:05:28 +05:30
// 7. Fetch request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence bodyBytes:
2023-05-26 09:51:53 -04:00
Fetch : : Infrastructure : : FetchAlgorithms : : Input fetch_algorithms_input { } ;
fetch_algorithms_input . process_response_consume_body = [ this , hr = options ] ( auto response , auto body_bytes ) {
// FIXME: If the response is CORS cross-origin, we must use its internal response to query any of its data. See:
// https://github.com/whatwg/html/issues/9355
response = response - > unsafe_response ( ) ;
// 1. Let success be true.
bool success = true ;
// 2. If either of the following conditions are met:
// - bodyBytes is null or failure; or
// - response's status is not an ok status,
if ( body_bytes . template has < Empty > ( ) | | body_bytes . template has < Fetch : : Infrastructure : : FetchAlgorithms : : ConsumeBodyFailureTag > ( ) | | ! Fetch : : Infrastructure : : is_ok_status ( response - > status ( ) ) ) {
// then set success to false.
success = false ;
}
// FIXME: 3. Otherwise, wait for the link resource's critical subresources to finish loading.
// 4. Process the linked resource given el, success, response, and bodyBytes.
process_linked_resource ( success , response , body_bytes ) ;
} ;
2024-09-22 13:29:49 +02:00
if ( m_fetch_controller )
m_fetch_controller - > abort ( realm ( ) , { } ) ;
m_fetch_controller = MUST ( Fetch : : Fetching : : fetch ( realm ( ) , * request , Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm ( ) , move ( fetch_algorithms_input ) ) ) ) ;
2023-01-14 19:05:28 +05:30
}
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:process-the-linked-resource
void HTMLLinkElement : : process_stylesheet_resource ( bool success , Fetch : : Infrastructure : : Response const & response , Variant < Empty , Fetch : : Infrastructure : : FetchAlgorithms : : ConsumeBodyFailureTag , ByteBuffer > body_bytes )
{
// 1. If the resource's Content-Type metadata is not text/css, then set success to false.
2025-02-21 10:19:43 +00:00
auto mime_type_string = m_mime_type ;
if ( ! mime_type_string . has_value ( ) ) {
auto extracted_mime_type = response . header_list ( ) - > extract_mime_type ( ) ;
if ( extracted_mime_type . has_value ( ) )
mime_type_string = extracted_mime_type - > essence ( ) ;
}
if ( mime_type_string ! = " text/css " sv ) {
2023-01-14 19:05:28 +05:30
success = false ;
}
// FIXME: 2. If el no longer creates an external resource link that contributes to the styling processing model,
// or if, since the resource in question was fetched, it has become appropriate to fetch it again, then return.
// 3. If el has an associated CSS style sheet, remove the CSS style sheet.
if ( m_loaded_style_sheet ) {
2024-04-15 21:42:46 +01:00
document_or_shadow_root_style_sheets ( ) . remove_a_css_style_sheet ( * m_loaded_style_sheet ) ;
2023-01-14 19:05:28 +05:30
m_loaded_style_sheet = nullptr ;
2021-11-18 19:22:59 +00:00
}
2023-01-14 19:05:28 +05:30
// 4. If success is true, then:
if ( success ) {
// 1. Create a CSS style sheet with the following properties:
// type
// text/css
// location
2024-08-21 14:49:08 +01:00
// response's URL list[0]
2023-01-14 19:05:28 +05:30
// owner node
// element
// media
// The media attribute of element.
// title
// The title attribute of element, if element is in a document tree, or the empty string otherwise.
// alternate flag
// Set if the link is an alternative style sheet and element's explicitly enabled is false; unset otherwise.
// origin-clean flag
// Set if the resource is CORS-same-origin; unset otherwise.
// parent CSS style sheet
// owner CSS rule
// null
// disabled flag
// Left at its default value.
// CSS rules
// Left uninitialized.
//
// The CSS environment encoding is the result of running the following steps: [CSSSYNTAX]
// 1. If the element has a charset attribute, get an encoding from that attribute's value. If that succeeds, return the resulting encoding. [ENCODING]
// 2. Otherwise, return the document's character encoding. [DOM]
2022-11-20 01:46:25 +01:00
2023-10-10 15:00:58 +03:30
Optional < String > encoding ;
if ( auto charset = attribute ( HTML : : AttributeNames : : charset ) ; charset . has_value ( ) )
encoding = charset . release_value ( ) ;
2023-07-04 09:50:47 +02:00
2023-10-10 15:00:58 +03:30
if ( ! encoding . has_value ( ) )
encoding = document ( ) . encoding_or_default ( ) ;
auto decoder = TextCodec : : decoder_for ( * encoding ) ;
2023-07-04 09:50:47 +02:00
if ( ! decoder . has_value ( ) ) {
// If we don't support the encoding yet, let's error out instead of trying to decode it as something it's most likely not.
dbgln ( " FIXME: Style sheet encoding '{}' is not supported yet " , encoding ) ;
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-01-14 19:05:28 +05:30
} else {
2023-07-04 09:50:47 +02:00
auto const & encoded_string = body_bytes . get < ByteBuffer > ( ) ;
auto maybe_decoded_string = TextCodec : : convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark ( * decoder , encoded_string ) ;
if ( maybe_decoded_string . is_error ( ) ) {
2024-03-18 16:22:27 +13:00
dbgln ( " Style sheet {} claimed to be '{}' but decoding failed " , response . url ( ) . value_or ( URL : : URL ( ) ) , encoding ) ;
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-07-04 09:50:47 +02:00
} else {
auto const decoded_string = maybe_decoded_string . release_value ( ) ;
2025-02-05 12:08:27 +00:00
m_loaded_style_sheet = parse_css_stylesheet ( CSS : : Parser : : ParsingParams ( document ( ) , * response . url ( ) ) , decoded_string ) ;
2023-07-04 09:50:47 +02:00
if ( m_loaded_style_sheet ) {
2024-08-21 14:49:08 +01:00
Optional < String > location ;
if ( ! response . url_list ( ) . is_empty ( ) )
2024-12-03 22:31:33 +13:00
location = response . url_list ( ) . first ( ) . to_string ( ) ;
2024-08-21 14:49:08 +01:00
2024-04-15 21:42:46 +01:00
document ( ) . style_sheets ( ) . create_a_css_style_sheet (
" text/css " _string ,
this ,
attribute ( HTML : : AttributeNames : : media ) . value_or ( { } ) ,
in_a_document_tree ( ) ? attribute ( HTML : : AttributeNames : : title ) . value_or ( { } ) : String { } ,
2024-04-16 21:02:50 +01:00
m_relationship & Relationship : : Alternate & & ! m_explicitly_enabled ,
2024-04-15 21:42:46 +01:00
true ,
2024-08-21 14:49:08 +01:00
move ( location ) ,
2024-04-15 21:42:46 +01:00
nullptr ,
nullptr ,
* m_loaded_style_sheet ) ;
2023-07-04 09:50:47 +02:00
} else {
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Failed to parse stylesheet: {} " , resource ( ) - > url ( ) ) ;
}
// 2. Fire an event named load at el.
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2023-07-04 09:50:47 +02:00
}
2022-11-20 01:46:25 +01:00
}
2023-01-14 19:05:28 +05:30
}
// 5. Otherwise, fire an event named error at el.
else {
2023-08-13 13:05:26 +02:00
dispatch_event ( * DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2021-11-18 19:22:59 +00:00
}
2023-01-14 19:05:28 +05:30
// FIXME: 6. If el contributes a script-blocking style sheet, then:
// FIXME: 1. Assert: el's node document's script-blocking style sheet counter is greater than 0.
// FIXME: 2. Decrement el's node document's script-blocking style sheet counter by 1.
// 7. Unblock rendering on el.
2025-02-27 15:30:26 +01:00
unblock_rendering ( ) ;
2023-01-14 19:05:28 +05:30
m_document_load_event_delayer . clear ( ) ;
}
// https://html.spec.whatwg.org/multipage/semantics.html#process-the-linked-resource
void HTMLLinkElement : : process_linked_resource ( bool success , Fetch : : Infrastructure : : Response const & response , Variant < Empty , Fetch : : Infrastructure : : FetchAlgorithms : : ConsumeBodyFailureTag , ByteBuffer > body_bytes )
{
if ( m_relationship & Relationship : : Stylesheet )
process_stylesheet_resource ( success , response , body_bytes ) ;
}
// https://html.spec.whatwg.org/multipage/semantics.html#linked-resource-fetch-setup-steps
bool HTMLLinkElement : : linked_resource_fetch_setup_steps ( Fetch : : Infrastructure : : Request & request )
{
if ( m_relationship & Relationship : : Stylesheet )
return stylesheet_linked_resource_fetch_setup_steps ( request ) ;
return true ;
}
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:linked-resource-fetch-setup-steps
bool HTMLLinkElement : : stylesheet_linked_resource_fetch_setup_steps ( Fetch : : Infrastructure : : Request & request )
{
// 1. If el's disabled attribute is set, then return false.
if ( has_attribute ( AttributeNames : : disabled ) )
return false ;
// FIXME: 2. If el contributes a script-blocking style sheet, increment el's node document's script-blocking style sheet counter by 1.
// 3. If el's media attribute's value matches the environment and el is potentially render-blocking, then block rendering on el.
// FIXME: Check media attribute value.
2025-02-27 15:30:26 +01:00
if ( is_potentially_render_blocking ( ) )
block_rendering ( ) ;
2023-01-14 19:05:28 +05:30
m_document_load_event_delayer . emplace ( document ( ) ) ;
// 4. If el is currently render-blocking, then set request's render-blocking to true.
2025-02-27 15:30:26 +01:00
if ( document ( ) . is_render_blocking_element ( * this ) )
request . set_render_blocking ( true ) ;
2023-01-14 19:05:28 +05:30
// 5. Return true.
return true ;
2021-11-18 19:22:59 +00:00
}
2025-02-27 15:30:26 +01:00
void HTMLLinkElement : : set_parser_document ( Badge < HTMLParser > , GC : : Ref < DOM : : Document > document )
{
m_parser_document = document - > make_weak_ptr < DOM : : Document > ( ) ;
}
bool HTMLLinkElement : : is_implicitly_potentially_render_blocking ( ) const
{
// A link element of this type is implicitly potentially render-blocking if the element was created by its node document's parser.
return & document ( ) = = m_parser_document ;
}
2022-04-03 19:49:38 +02:00
void HTMLLinkElement : : resource_did_load_favicon ( )
{
VERIFY ( m_relationship & ( Relationship : : Icon ) ) ;
if ( ! resource ( ) - > has_encoded_data ( ) ) {
dbgln_if ( SPAM_DEBUG , " Favicon downloaded, no encoded data " ) ;
return ;
}
dbgln_if ( SPAM_DEBUG , " Favicon downloaded, {} bytes from {} " , resource ( ) - > encoded_data ( ) . size ( ) , resource ( ) - > url ( ) ) ;
document ( ) . check_favicon_after_loading_link_resource ( ) ;
}
2024-11-15 04:01:23 +13:00
static NonnullRefPtr < Core : : Promise < Web : : Platform : : DecodedImage > > decode_favicon ( ReadonlyBytes favicon_data , URL : : URL const & favicon_url , GC : : Ref < DOM : : Document > document )
2022-04-03 19:49:38 +02:00
{
2024-04-19 15:55:54 -06:00
auto on_failed_decode = [ favicon_url ] ( [[maybe_unused]] Error & error ) {
dbgln_if ( IMAGE_DECODER_DEBUG , " Failed to decode favicon {}: {} " , favicon_url , error ) ;
} ;
2024-11-15 04:01:23 +13:00
auto on_successful_decode = [ document = GC : : Root ( document ) ] ( Web : : Platform : : DecodedImage & decoded_image ) - > ErrorOr < void > {
2024-04-19 15:55:54 -06:00
auto favicon_bitmap = decoded_image . frames [ 0 ] . bitmap ;
dbgln_if ( IMAGE_DECODER_DEBUG , " Decoded favicon, {} " , favicon_bitmap - > size ( ) ) ;
2022-04-03 19:49:38 +02:00
2024-08-21 14:52:51 +02:00
auto navigable = document - > navigable ( ) ;
2024-04-19 15:55:54 -06:00
if ( navigable & & navigable - > is_traversable ( ) )
navigable - > traversable_navigable ( ) - > page ( ) . client ( ) . page_did_change_favicon ( * favicon_bitmap ) ;
return { } ;
} ;
2022-04-03 19:49:38 +02:00
2024-04-19 15:55:54 -06:00
auto promise = Platform : : ImageCodecPlugin : : the ( ) . decode_image ( favicon_data , move ( on_successful_decode ) , move ( on_failed_decode ) ) ;
2022-04-03 19:49:38 +02:00
2024-04-19 15:55:54 -06:00
return promise ;
2023-10-30 10:47:34 -04:00
}
2022-04-03 19:49:38 +02:00
2023-10-30 10:47:34 -04:00
bool HTMLLinkElement : : load_favicon_and_use_if_window_is_active ( )
{
if ( ! has_loaded_icon ( ) )
return false ;
2024-04-19 15:55:54 -06:00
// FIXME: Refactor the caller(s) to handle the async nature of image loading
2024-08-21 14:52:51 +02:00
auto promise = decode_favicon ( resource ( ) - > encoded_data ( ) , resource ( ) - > url ( ) , document ( ) ) ;
2024-04-19 15:55:54 -06:00
auto result = promise - > await ( ) ;
return ! result . is_error ( ) ;
2023-10-30 10:47:34 -04:00
}
// https://html.spec.whatwg.org/multipage/links.html#rel-icon:the-link-element-3
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < void > HTMLLinkElement : : load_fallback_favicon_if_needed ( GC : : Ref < DOM : : Document > document )
2023-10-30 10:47:34 -04:00
{
auto & realm = document - > realm ( ) ;
auto & vm = realm . vm ( ) ;
// In the absence of a link with the icon keyword, for Document objects whose URL's scheme is an HTTP(S) scheme,
// user agents may instead run these steps in parallel:
if ( document - > has_active_favicon ( ) )
return { } ;
if ( ! document - > url ( ) . scheme ( ) . is_one_of ( " http " sv , " https " sv ) )
return { } ;
// 1. Let request be a new request whose URL is the URL record obtained by resolving the URL "/favicon.ico" against
// the Document object's URL, client is the Document object's relevant settings object, destination is "image",
// synchronous flag is set, credentials mode is "include", and whose use-URL-credentials flag is set.
// NOTE: Fetch requests no longer have a synchronous flag, see https://github.com/whatwg/fetch/pull/1165
auto request = Fetch : : Infrastructure : : Request : : create ( vm ) ;
2025-01-23 19:40:57 +13:00
request - > set_url ( * document - > parse_url ( " /favicon.ico " sv ) ) ;
2023-10-30 10:47:34 -04:00
request - > set_client ( & document - > relevant_settings_object ( ) ) ;
request - > set_destination ( Fetch : : Infrastructure : : Request : : Destination : : Image ) ;
request - > set_credentials_mode ( Fetch : : Infrastructure : : Request : : CredentialsMode : : Include ) ;
request - > set_use_url_credentials ( true ) ;
// 2. Let response be the result of fetching request.
Fetch : : Infrastructure : : FetchAlgorithms : : Input fetch_algorithms_input { } ;
2024-11-15 04:01:23 +13:00
fetch_algorithms_input . process_response = [ document , request ] ( GC : : Ref < Fetch : : Infrastructure : : Response > response ) {
2023-10-30 10:47:34 -04:00
auto & realm = document - > realm ( ) ;
2024-11-15 04:01:23 +13:00
auto global = GC : : Ref { realm . global_object ( ) } ;
2023-10-30 10:47:34 -04:00
2024-11-15 04:01:23 +13:00
auto process_body = GC : : create_function ( realm . heap ( ) , [ document , request ] ( ByteBuffer body ) {
2024-08-21 14:52:51 +02:00
( void ) decode_favicon ( body , request - > url ( ) , document ) ;
2024-04-23 10:25:20 +02:00
} ) ;
2024-11-15 04:01:23 +13:00
auto process_body_error = GC : : create_function ( realm . heap ( ) , [ ] ( JS : : Value ) {
2024-04-23 10:25:20 +02:00
} ) ;
2023-10-30 10:47:34 -04:00
2024-07-06 14:42:57 -05:00
// Check for failed favicon response
if ( ! Fetch : : Infrastructure : : is_ok_status ( response - > status ( ) ) | | ! response - > body ( ) ) {
return ;
}
2023-10-30 10:47:34 -04:00
// 3. Use response's unsafe response as an icon as if it had been declared using the icon keyword.
2024-01-05 22:38:29 +01:00
if ( auto body = response - > unsafe_response ( ) - > body ( ) )
2024-04-26 14:57:40 -04:00
body - > fully_read ( realm , process_body , process_body_error , global ) ;
2023-10-30 10:47:34 -04:00
} ;
TRY ( Fetch : : Fetching : : fetch ( realm , request , Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm , move ( fetch_algorithms_input ) ) ) ) ;
return { } ;
2022-04-03 19:49:38 +02:00
}
2022-11-20 01:46:25 +01:00
void HTMLLinkElement : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-09-22 13:29:49 +02:00
visitor . visit ( m_fetch_controller ) ;
2022-11-20 01:46:25 +01:00
visitor . visit ( m_loaded_style_sheet ) ;
2024-05-17 14:06:27 -07:00
visitor . visit ( m_rel_list ) ;
2024-11-18 07:00:59 +13:00
visitor . visit ( m_sizes ) ;
2022-11-20 01:46:25 +01:00
}
2020-03-07 10:27:02 +01:00
}