LibWeb: Select the largest decoded favicon as the active page favicon

We currently pick the first favicon in reverse tree order. But we are
encouraged by the spec to pick the most appropraite icon. We now
consider the size of the decoded icon, and choose the largest.
This commit is contained in:
Timothy Flynn 2026-05-28 07:23:00 -04:00 committed by Andreas Kling
parent d3ad37e635
commit 5f3ea017f5
3 changed files with 32 additions and 32 deletions

View file

@ -4983,10 +4983,9 @@ void Document::check_favicon_after_loading_link_resource()
return;
auto favicon_link_elements = HTMLCollection::create(*head_element, HTMLCollection::Scope::Descendants, [](Element const& element) {
if (!is<HTML::HTMLLinkElement>(element))
return false;
return static_cast<HTML::HTMLLinkElement const&>(element).has_loaded_icon();
if (auto const* link_element = as_if<HTML::HTMLLinkElement>(element))
return link_element->has_loaded_icon();
return false;
});
if (favicon_link_elements->length() == 0) {
@ -4994,31 +4993,29 @@ void Document::check_favicon_after_loading_link_resource()
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.
// If multiple icons are provided, the user agent must select the most appropriate icon according to the type,
// media, and sizes attributes. 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.
RefPtr<Gfx::Bitmap const> largest_icon;
for (auto i = favicon_link_elements->length(); i-- > 0;) {
auto favicon_element = favicon_link_elements->item(i);
if (favicon_element == m_active_element.ptr())
auto* link_element = static_cast<HTML::HTMLLinkElement*>(favicon_link_elements->item(i));
if (link_element == m_active_element.ptr())
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;
// If the user agent tries to use an icon but that icon is determined, upon closer examination, to in fact be
// inappropriate (e.g. because it uses an unsupported format), then the user agent must try the
// next-most-appropriate icon as determined by the attributes.
if (auto icon = link_element->load_favicon_if_window_is_active()) {
if (!largest_icon || icon->size().area() > largest_icon->size().area()) {
m_active_favicon = link_element;
largest_icon = move(icon);
}
}
}
dbgln_if(SPAM_DEBUG, "No favicon found to be used");
if (!largest_icon)
dbgln_if(SPAM_DEBUG, "No favicon found to be used");
}
void Document::set_window(HTML::Window& window)

View file

@ -930,9 +930,9 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru
}
}
static NonnullRefPtr<Core::Promise<bool>> decode_favicon(ReadonlyBytes favicon_data, URL::URL const& favicon_url, GC::Ref<DOM::Document> document)
static NonnullRefPtr<Core::Promise<NonnullRefPtr<Gfx::Bitmap const>>> decode_favicon(ReadonlyBytes favicon_data, URL::URL const& favicon_url, GC::Ref<DOM::Document> document)
{
auto promise = Core::Promise<bool>::construct();
auto promise = Core::Promise<NonnullRefPtr<Gfx::Bitmap const>>::construct();
if (favicon_url.basename().ends_with(".svg"sv)) {
auto result = SVG::SVGDecodedImageData::create(document->realm(), document->page(), favicon_url, favicon_data);
@ -951,7 +951,8 @@ static NonnullRefPtr<Core::Promise<bool>> decode_favicon(ReadonlyBytes favicon_d
auto navigable = document->navigable();
if (navigable && navigable->is_traversable())
navigable->traversable_navigable()->page().client().page_did_change_favicon(decoded_frame->bitmap());
promise->resolve(true);
promise->resolve(decoded_frame->bitmap_ref());
return promise;
}
@ -968,7 +969,7 @@ static NonnullRefPtr<Core::Promise<bool>> decode_favicon(ReadonlyBytes favicon_d
if (navigable && navigable->is_traversable())
navigable->traversable_navigable()->page().client().page_did_change_favicon(*favicon_bitmap);
promise->resolve(true);
promise->resolve(favicon_bitmap.release_nonnull());
return {};
};
@ -977,15 +978,17 @@ static NonnullRefPtr<Core::Promise<bool>> decode_favicon(ReadonlyBytes favicon_d
return promise;
}
bool HTMLLinkElement::load_favicon_and_use_if_window_is_active()
RefPtr<Gfx::Bitmap const> HTMLLinkElement::load_favicon_if_window_is_active()
{
if (!has_loaded_icon())
return false;
return {};
// FIXME: Refactor the caller(s) to handle the async nature of image loading
auto promise = decode_favicon(m_loaded_icon->icon, m_loaded_icon->url, document());
auto result = promise->await();
return !result.is_error();
if (auto result = promise->await(); !result.is_error())
return result.release_value();
return {};
}
// https://html.spec.whatwg.org/multipage/links.html#rel-icon:the-link-element-3

View file

@ -39,7 +39,7 @@ public:
bool has_loaded_icon() const;
bool has_icon_keyword() const;
bool load_favicon_and_use_if_window_is_active();
RefPtr<Gfx::Bitmap const> load_favicon_if_window_is_active();
static void load_fallback_favicon_if_needed(GC::Ref<DOM::Document>);