mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibWeb: Update displayed favicon when a favicon is loaded
When a favicon has been loaded, trigger a favicon update on document level. Of all the link tags in the header, the last favicon that is load should be shown. When the favicon could not be loaded, load the next icon in reverse tree order.
This commit is contained in:
parent
13847aa7e8
commit
06d9853a8b
Notes:
sideshowbarker
2024-07-18 22:57:59 +09:00
Author: https://github.com/avdg Commit: https://github.com/SerenityOS/serenity/commit/06d9853a8b Pull-request: https://github.com/SerenityOS/serenity/pull/13462 Reviewed-by: https://github.com/TobyAsE Reviewed-by: https://github.com/davidot ✅
4 changed files with 116 additions and 0 deletions
|
@ -51,6 +51,7 @@
|
|||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/HTML/HTMLIFrameElement.h>
|
||||
#include <LibWeb/HTML/HTMLImageElement.h>
|
||||
#include <LibWeb/HTML/HTMLLinkElement.h>
|
||||
#include <LibWeb/HTML/HTMLScriptElement.h>
|
||||
#include <LibWeb/HTML/HTMLTitleElement.h>
|
||||
#include <LibWeb/HTML/MessageEvent.h>
|
||||
|
@ -1560,4 +1561,48 @@ void Document::invalidate_stacking_context_tree()
|
|||
const_cast<Painting::PaintableBox*>(paint_box)->invalidate_stacking_context();
|
||||
}
|
||||
|
||||
void Document::check_favicon_after_loading_link_resource()
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/links.html#rel-icon
|
||||
// NOTE: firefox also load favicons outside the head tag, which is against spec (see table 4.6.7)
|
||||
auto head_element = head();
|
||||
auto favicon_link_elements = HTMLCollection::create(*head_element, [](Element const& element) {
|
||||
if (!is<HTML::HTMLLinkElement>(element))
|
||||
return false;
|
||||
|
||||
return static_cast<HTML::HTMLLinkElement const&>(element).has_loaded_icon();
|
||||
});
|
||||
|
||||
if (favicon_link_elements->length() == 0) {
|
||||
dbgln_if(SPAM_DEBUG, "No favicon found to be used");
|
||||
return;
|
||||
}
|
||||
|
||||
// 4.6.7.8 Link type "icon"
|
||||
//
|
||||
// If there are multiple equally appropriate icons, user agents must use the last one declared
|
||||
// in tree order at the time that the user agent collected the list of icons.
|
||||
//
|
||||
// If multiple icons are provided, the user agent must select the most appropriate icon
|
||||
// according to the type, media, and sizes attributes.
|
||||
//
|
||||
// FIXME: There is no selective behavior yet for favicons.
|
||||
for (auto i = favicon_link_elements->length(); i-- > 0;) {
|
||||
auto favicon_element = favicon_link_elements->item(i);
|
||||
|
||||
if (favicon_element == m_active_element)
|
||||
return;
|
||||
|
||||
// If the user agent tries to use an icon but that icon is determined, upon closer examination,
|
||||
// to in fact be inappropriate (...), then the user agent must try the next-most-appropriate icon
|
||||
// as determined by the attributes.
|
||||
if (static_cast<HTML::HTMLLinkElement*>(favicon_element)->load_favicon_and_use_if_window_is_active()) {
|
||||
m_active_favicon = favicon_element;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dbgln_if(SPAM_DEBUG, "No favicon found to be used");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -339,6 +339,8 @@ public:
|
|||
|
||||
bool in_removed_last_ref() const { return m_in_removed_last_ref; }
|
||||
|
||||
void check_favicon_after_loading_link_resource();
|
||||
|
||||
private:
|
||||
explicit Document(const AK::URL&);
|
||||
|
||||
|
@ -374,6 +376,7 @@ private:
|
|||
RefPtr<CSS::StyleSheetList> m_style_sheets;
|
||||
RefPtr<Node> m_hovered_node;
|
||||
RefPtr<Node> m_inspected_node;
|
||||
RefPtr<Node> m_active_favicon;
|
||||
WeakPtr<HTML::BrowsingContext> m_browsing_context;
|
||||
AK::URL m_url;
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/HTMLLinkElement.h>
|
||||
#include <LibWeb/ImageDecoding.h>
|
||||
#include <LibWeb/Loader/ResourceLoader.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
@ -50,9 +52,18 @@ void HTMLLinkElement::inserted()
|
|||
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)));
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
bool HTMLLinkElement::has_loaded_icon() const
|
||||
{
|
||||
return m_relationship & Relationship::Icon && resource() && resource()->is_loaded() && resource()->has_encoded_data();
|
||||
}
|
||||
|
||||
void HTMLLinkElement::parse_attribute(FlyString const& name, String const& value)
|
||||
{
|
||||
// 4.6.7 Link types - https://html.spec.whatwg.org/multipage/links.html#linkTypes
|
||||
|
@ -91,7 +102,17 @@ void HTMLLinkElement::resource_did_fail()
|
|||
void HTMLLinkElement::resource_did_load()
|
||||
{
|
||||
VERIFY(resource());
|
||||
VERIFY(m_relationship & (Relationship::Stylesheet | Relationship::Icon));
|
||||
|
||||
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);
|
||||
m_document_load_event_delayer.clear();
|
||||
|
||||
if (!resource()->has_encoded_data()) {
|
||||
|
@ -115,4 +136,45 @@ void HTMLLinkElement::resource_did_load()
|
|||
document().style_sheets().add_sheet(sheet.release_nonnull());
|
||||
}
|
||||
|
||||
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;
|
||||
auto decoded_image = Web::image_decoder_client().decode_image(resource()->encoded_data());
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ public:
|
|||
String type() const { return attribute(HTML::AttributeNames::type); }
|
||||
String href() const { return attribute(HTML::AttributeNames::href); }
|
||||
|
||||
bool has_loaded_icon() const;
|
||||
bool load_favicon_and_use_if_window_is_active();
|
||||
|
||||
private:
|
||||
void parse_attribute(FlyString const&, String const&) override;
|
||||
|
||||
|
@ -35,6 +38,9 @@ private:
|
|||
virtual void resource_did_fail() override;
|
||||
virtual void resource_did_load() override;
|
||||
|
||||
void resource_did_load_stylesheet();
|
||||
void resource_did_load_favicon();
|
||||
|
||||
struct Relationship {
|
||||
enum {
|
||||
Alternate = 1 << 0,
|
||||
|
|
Loading…
Reference in a new issue