Serenity Operating System
1/*
2 * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibWeb/HTML/BrowsingContext.h>
8#include <LibWeb/Layout/ImageBox.h>
9#include <LibWeb/Painting/ImagePaintable.h>
10#include <LibWeb/Platform/FontPlugin.h>
11
12namespace Web::Layout {
13
14ImageBox::ImageBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style, ImageLoader const& image_loader)
15 : ReplacedBox(document, element, move(style))
16 , m_image_loader(image_loader)
17{
18 browsing_context().register_viewport_client(*this);
19}
20
21ImageBox::~ImageBox() = default;
22
23void ImageBox::finalize()
24{
25 Base::finalize();
26
27 // NOTE: We unregister from the browsing context in finalize() to avoid trouble
28 // in the scenario where our BrowsingContext has already been swept by GC.
29 browsing_context().unregister_viewport_client(*this);
30}
31
32int ImageBox::preferred_width() const
33{
34 return dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width());
35}
36
37int ImageBox::preferred_height() const
38{
39 return dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height());
40}
41
42void ImageBox::prepare_for_replaced_layout()
43{
44 if (!m_image_loader.has_loaded_or_failed()) {
45 set_intrinsic_width(0);
46 set_intrinsic_height(0);
47 } else {
48 if (m_image_loader.width()) {
49 set_intrinsic_width(m_image_loader.width());
50 }
51 if (m_image_loader.height()) {
52 set_intrinsic_height(m_image_loader.height());
53 }
54
55 if (m_image_loader.width() && m_image_loader.height()) {
56 set_intrinsic_aspect_ratio((float)m_image_loader.width() / (float)m_image_loader.height());
57 } else {
58 set_intrinsic_aspect_ratio({});
59 }
60 }
61
62 if (renders_as_alt_text()) {
63 auto& image_element = verify_cast<HTML::HTMLImageElement>(dom_node());
64 auto& font = Platform::FontPlugin::the().default_font();
65 auto alt = image_element.alt();
66 if (alt.is_empty())
67 alt = image_element.src();
68
69 CSSPixels alt_text_width = 0;
70 if (!m_cached_alt_text_width.has_value())
71 m_cached_alt_text_width = font.width(alt);
72 alt_text_width = m_cached_alt_text_width.value();
73
74 set_intrinsic_width(alt_text_width + 16);
75 set_intrinsic_height(font.pixel_size() + 16);
76 }
77
78 if (!has_intrinsic_width() && !has_intrinsic_height()) {
79 // FIXME: Do something.
80 }
81}
82
83void ImageBox::dom_node_did_update_alt_text(Badge<HTML::HTMLImageElement>)
84{
85 m_cached_alt_text_width = {};
86}
87
88bool ImageBox::renders_as_alt_text() const
89{
90 if (is<HTML::HTMLImageElement>(dom_node()))
91 return !m_image_loader.has_image();
92 return false;
93}
94
95void ImageBox::browsing_context_did_set_viewport_rect(CSSPixelRect const& viewport_rect)
96{
97 m_image_loader.set_visible_in_viewport(paint_box() && viewport_rect.intersects(paint_box()->absolute_rect()));
98}
99
100JS::GCPtr<Painting::Paintable> ImageBox::create_paintable() const
101{
102 return Painting::ImagePaintable::create(*this);
103}
104
105}