2020-01-18 11:38:21 +03:00
/*
2021-03-08 15:40:53 +03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2021-02-21 19:44:17 +03:00
* Copyright ( c ) 2021 , the SerenityOS developers .
2021-11-18 22:22:59 +03:00
* Copyright ( c ) 2021 , Sam Atkins < atkinssj @ serenityos . org >
2020-01-18 11:38:21 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2020-02-15 00:29:06 +03:00
# include <AK/ByteBuffer.h>
2021-11-18 22:22:59 +03:00
# include <AK/Debug.h>
2019-10-07 20:06:47 +03:00
# include <AK/URL.h>
2021-07-30 21:31:46 +03:00
# include <LibWeb/CSS/Parser/Parser.h>
2020-03-07 12:32:51 +03:00
# include <LibWeb/DOM/Document.h>
2020-07-26 16:08:16 +03:00
# include <LibWeb/HTML/HTMLLinkElement.h>
2020-06-01 21:42:50 +03:00
# include <LibWeb/Loader/ResourceLoader.h>
2022-04-03 20:49:38 +03:00
# include <LibWeb/Page/Page.h>
2022-09-16 16:01:47 +03:00
# include <LibWeb/Platform/ImageCodecPlugin.h>
2019-10-07 20:06:47 +03:00
2020-07-28 19:20:36 +03:00
namespace Web : : HTML {
2020-03-07 12:27:02 +03:00
2022-02-18 23:00:52 +03:00
HTMLLinkElement : : HTMLLinkElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2021-02-07 13:20:15 +03:00
: HTMLElement ( document , move ( qualified_name ) )
2019-10-07 20:06:47 +03:00
{
2022-09-03 19:43:24 +03:00
set_prototype ( & window ( ) . cached_web_prototype ( " HTMLLinkElement " ) ) ;
2019-10-07 20:06:47 +03:00
}
2022-03-14 22:21:51 +03:00
HTMLLinkElement : : ~ HTMLLinkElement ( ) = default ;
2019-10-07 20:06:47 +03:00
2021-04-06 19:58:20 +03:00
void HTMLLinkElement : : inserted ( )
2020-06-02 13:53:29 +03:00
{
2021-04-06 19:58:20 +03:00
HTMLElement : : inserted ( ) ;
2020-06-02 13:53:29 +03:00
2021-02-21 19:44:17 +03:00
if ( m_relationship & Relationship : : Stylesheet & & ! ( m_relationship & Relationship : : Alternate ) ) {
2021-11-18 22:22:59 +03:00
auto url = document ( ) . parse_url ( href ( ) ) ;
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Loading import URL: {} " , url ) ;
auto request = LoadRequest : : create_for_url_on_page ( url , document ( ) . page ( ) ) ;
2022-02-15 16:11:51 +03:00
// NOTE: Mark this element as delaying the document load event *before* calling set_resource()
// as it may trigger a synchronous resource_did_load() callback.
2021-11-18 22:22:59 +03:00
m_document_load_event_delayer . emplace ( document ( ) ) ;
2022-02-15 16:11:51 +03:00
set_resource ( ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , request ) ) ;
2022-03-19 16:58:48 +03:00
// NOTE: If we ended up not loading a resource for whatever reason, don't delay the load event.
if ( ! resource ( ) )
m_document_load_event_delayer . clear ( ) ;
2019-10-07 20:06:47 +03:00
}
2021-09-27 03:06:37 +03:00
if ( m_relationship & Relationship : : Preload ) {
// FIXME: Respect the "as" attribute.
LoadRequest request ;
2021-09-28 23:20:10 +03:00
request . set_url ( document ( ) . parse_url ( attribute ( HTML : : AttributeNames : : href ) ) ) ;
2021-09-27 03:06:37 +03:00
m_preload_resource = ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , request ) ;
2021-09-27 23:38:29 +03:00
} else if ( m_relationship & Relationship : : DNSPrefetch ) {
ResourceLoader : : the ( ) . prefetch_dns ( document ( ) . parse_url ( attribute ( HTML : : AttributeNames : : href ) ) ) ;
} else if ( m_relationship & Relationship : : Preconnect ) {
ResourceLoader : : the ( ) . preconnect ( document ( ) . parse_url ( attribute ( HTML : : AttributeNames : : href ) ) ) ;
2022-04-03 20:49:38 +03:00
} else if ( m_relationship & Relationship : : Icon ) {
auto favicon_url = document ( ) . parse_url ( href ( ) ) ;
auto favicon_request = LoadRequest : : create_for_url_on_page ( favicon_url , document ( ) . page ( ) ) ;
set_resource ( ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , favicon_request ) ) ;
2021-09-27 03:06:37 +03:00
}
2019-10-07 20:06:47 +03:00
}
2020-03-07 12:27:02 +03:00
2022-04-03 20:49:38 +03:00
bool HTMLLinkElement : : has_loaded_icon ( ) const
{
return m_relationship & Relationship : : Icon & & resource ( ) & & resource ( ) - > is_loaded ( ) & & resource ( ) - > has_encoded_data ( ) ;
}
2022-04-01 20:58:27 +03:00
void HTMLLinkElement : : parse_attribute ( FlyString const & name , String const & value )
2020-06-15 21:25:25 +03:00
{
2022-04-03 20:39:38 +03:00
// 4.6.7 Link types - https://html.spec.whatwg.org/multipage/links.html#linkTypes
2020-06-15 21:25:25 +03:00
if ( name = = HTML : : AttributeNames : : rel ) {
m_relationship = 0 ;
2022-04-03 20:39:38 +03:00
// Keywords are always ASCII case-insensitive, and must be compared as such.
auto lowercased_value = value . to_lowercase ( ) ;
// 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.
auto parts = lowercased_value . split_view ( ' ' ) ;
2020-06-15 21:25:25 +03:00
for ( auto & part : parts ) {
2021-09-27 23:38:29 +03:00
if ( part = = " stylesheet " sv )
2020-06-15 21:25:25 +03:00
m_relationship | = Relationship : : Stylesheet ;
2021-09-27 23:38:29 +03:00
else if ( part = = " alternate " sv )
2020-06-15 21:25:25 +03:00
m_relationship | = Relationship : : Alternate ;
2021-09-27 23:38:29 +03:00
else if ( part = = " preload " sv )
2021-09-27 03:06:37 +03:00
m_relationship | = Relationship : : Preload ;
2021-09-27 23:38:29 +03:00
else if ( part = = " dns-prefetch " sv )
m_relationship | = Relationship : : DNSPrefetch ;
else if ( part = = " preconnect " sv )
m_relationship | = Relationship : : Preconnect ;
2022-04-03 20:39:38 +03:00
else if ( part = = " icon " sv )
m_relationship | = Relationship : : Icon ;
2020-06-15 21:25:25 +03:00
}
}
}
2021-11-18 22:22:59 +03:00
void HTMLLinkElement : : resource_did_fail ( )
{
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Resource did fail. URL: {} " , resource ( ) - > url ( ) ) ;
m_document_load_event_delayer . clear ( ) ;
}
void HTMLLinkElement : : resource_did_load ( )
{
VERIFY ( resource ( ) ) ;
2022-04-03 20:49:38 +03:00
VERIFY ( m_relationship & ( Relationship : : Stylesheet | Relationship : : Icon ) ) ;
2021-11-18 22:22:59 +03:00
2022-04-03 20:49:38 +03:00
if ( m_relationship & Relationship : : Stylesheet )
resource_did_load_stylesheet ( ) ;
if ( m_relationship & Relationship : : Icon )
resource_did_load_favicon ( ) ;
}
void HTMLLinkElement : : resource_did_load_stylesheet ( )
{
VERIFY ( m_relationship & Relationship : : Stylesheet ) ;
2021-11-18 22:22:59 +03:00
m_document_load_event_delayer . clear ( ) ;
if ( ! resource ( ) - > has_encoded_data ( ) ) {
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Resource did load, no encoded data. URL: {} " , resource ( ) - > url ( ) ) ;
} else {
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Resource did load, has encoded data. URL: {} " , resource ( ) - > url ( ) ) ;
2022-03-20 19:20:59 +03:00
if ( resource ( ) - > mime_type ( ) ! = " text/css " sv ) {
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Resource did load, but MIME type was {} instead of text/css. URL: {} " , resource ( ) - > mime_type ( ) , resource ( ) - > url ( ) ) ;
return ;
}
2021-11-18 22:22:59 +03:00
}
2022-08-07 14:14:54 +03:00
auto * sheet = parse_css_stylesheet ( CSS : : Parser : : ParsingContext ( document ( ) , resource ( ) - > url ( ) ) , resource ( ) - > encoded_data ( ) ) ;
2021-11-18 22:22:59 +03:00
if ( ! sheet ) {
dbgln_if ( CSS_LOADER_DEBUG , " HTMLLinkElement: Failed to parse stylesheet: {} " , resource ( ) - > url ( ) ) ;
return ;
}
sheet - > set_owner_node ( this ) ;
2022-08-07 14:14:54 +03:00
document ( ) . style_sheets ( ) . add_sheet ( * sheet ) ;
2021-11-18 22:22:59 +03:00
}
2022-04-03 20:49:38 +03: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 ( ) ;
}
bool HTMLLinkElement : : load_favicon_and_use_if_window_is_active ( )
{
if ( ! has_loaded_icon ( ) )
return false ;
RefPtr < Gfx : : Bitmap > favicon_bitmap ;
2022-09-16 16:01:47 +03:00
auto decoded_image = Platform : : ImageCodecPlugin : : the ( ) . decode_image ( resource ( ) - > encoded_data ( ) ) ;
2022-04-03 20:49:38 +03:00
if ( ! decoded_image . has_value ( ) | | decoded_image - > frames . is_empty ( ) ) {
dbgln ( " Could not decode favicon {} " , resource ( ) - > url ( ) ) ;
return false ;
}
favicon_bitmap = decoded_image - > frames [ 0 ] . bitmap ;
dbgln_if ( IMAGE_DECODER_DEBUG , " Decoded favicon, {} " , favicon_bitmap - > size ( ) ) ;
auto * page = document ( ) . page ( ) ;
if ( ! page )
return favicon_bitmap ;
if ( document ( ) . browsing_context ( ) = = & page - > top_level_browsing_context ( ) )
if ( favicon_bitmap ) {
page - > client ( ) . page_did_change_favicon ( * favicon_bitmap ) ;
return true ;
}
return false ;
}
2020-03-07 12:27:02 +03:00
}