summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibWeb/DOM/HTMLImageElement.cpp108
-rw-r--r--Libraries/LibWeb/DOM/HTMLImageElement.h21
-rw-r--r--Libraries/LibWeb/Layout/LayoutDocument.cpp3
-rw-r--r--Libraries/LibWeb/Layout/LayoutImage.cpp44
-rw-r--r--Libraries/LibWeb/Layout/LayoutImage.h9
-rw-r--r--Libraries/LibWeb/Loader/ImageLoader.cpp101
-rw-r--r--Libraries/LibWeb/Loader/ImageLoader.h59
7 files changed, 245 insertions, 100 deletions
diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.cpp b/Libraries/LibWeb/DOM/HTMLImageElement.cpp
index 414e5d4b76..e21c66c3fa 100644
--- a/Libraries/LibWeb/DOM/HTMLImageElement.cpp
+++ b/Libraries/LibWeb/DOM/HTMLImageElement.cpp
@@ -40,6 +40,22 @@ HTMLImageElement::HTMLImageElement(Document& document, const FlyString& tag_name
: HTMLElement(document, tag_name)
, m_timer(Core::Timer::construct())
{
+ m_image_loader.on_load = [this] {
+ if (image_decoder() && image_decoder()->is_animated() && image_decoder()->frame_count() > 1) {
+ const auto& first_frame = image_decoder()->frame(0);
+ m_timer->set_interval(first_frame.duration);
+ m_timer->on_timeout = [this] { animate(); };
+ m_timer->start();
+ }
+ this->document().update_layout();
+ dispatch_event(Event::create("load"));
+ };
+
+ m_image_loader.on_fail = [this] {
+ dbg() << "HTMLImageElement: Resource did fail: " << this->src();
+ this->document().update_layout();
+ dispatch_event(Event::create("error"));
+ };
}
HTMLImageElement::~HTMLImageElement()
@@ -49,70 +65,29 @@ HTMLImageElement::~HTMLImageElement()
void HTMLImageElement::parse_attribute(const FlyString& name, const String& value)
{
HTMLElement::parse_attribute(name, value);
- if (name.equals_ignoring_case("src"))
- load_image(value);
-}
-
-void HTMLImageElement::load_image(const String& src)
-{
- LoadRequest request;
- request.set_url(document().complete_url(src));
- set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request));
-}
-
-void HTMLImageElement::resource_did_load()
-{
- ASSERT(resource());
-
- if (!resource()->has_encoded_data()) {
- dbg() << "HTMLImageElement: Resource did load, but encoded data empty: " << this->src();
- return;
- }
-
- dbg() << "HTMLImageElement: Resource did load, encoded data looks tasty: " << this->src();
-
- m_image_decoder = resource()->ensure_decoder();
-
- if (m_image_decoder->is_animated() && m_image_decoder->frame_count() > 1) {
- const auto& first_frame = m_image_decoder->frame(0);
- m_timer->set_interval(first_frame.duration);
- m_timer->on_timeout = [this] { animate(); };
- m_timer->start();
- }
-
- document().update_layout();
- dispatch_event(Event::create("load"));
-}
-
-void HTMLImageElement::resource_did_fail()
-{
- dbg() << "HTMLImageElement: Resource did fail: " << this->src();
- m_image_decoder = nullptr;
- document().update_layout();
- dispatch_event(Event::create("error"));
-}
-void HTMLImageElement::resource_did_replace_decoder()
-{
- m_image_decoder = resource()->ensure_decoder();
+ if (name == HTML::AttributeNames::src)
+ m_image_loader.load(document().complete_url(value));
}
void HTMLImageElement::animate()
{
- if (!layout_node()) {
+ if (!layout_node())
return;
- }
- m_current_frame_index = (m_current_frame_index + 1) % m_image_decoder->frame_count();
- const auto& current_frame = m_image_decoder->frame(m_current_frame_index);
+ auto* decoder = image_decoder();
+ ASSERT(decoder);
+
+ m_current_frame_index = (m_current_frame_index + 1) % decoder->frame_count();
+ const auto& current_frame = decoder->frame(m_current_frame_index);
if (current_frame.duration != m_timer->interval()) {
m_timer->restart(current_frame.duration);
}
- if (m_current_frame_index == m_image_decoder->frame_count() - 1) {
+ if (m_current_frame_index == decoder->frame_count() - 1) {
++m_loops_completed;
- if (m_loops_completed > 0 && m_loops_completed == m_image_decoder->loop_count()) {
+ if (m_loops_completed > 0 && m_loops_completed == decoder->loop_count()) {
m_timer->stop();
}
}
@@ -120,6 +95,7 @@ void HTMLImageElement::animate()
layout_node()->set_needs_display();
}
+#if 0
int HTMLImageElement::preferred_width() const
{
return attribute(HTML::AttributeNames::width).to_int().value_or(m_image_decoder ? m_image_decoder->width() : 0);
@@ -129,6 +105,7 @@ int HTMLImageElement::preferred_height() const
{
return attribute(HTML::AttributeNames::height).to_int().value_or(m_image_decoder ? m_image_decoder->height() : 0);
}
+#endif
RefPtr<LayoutNode> HTMLImageElement::create_layout_node(const StyleProperties* parent_style) const
{
@@ -136,32 +113,27 @@ RefPtr<LayoutNode> HTMLImageElement::create_layout_node(const StyleProperties* p
auto display = style->string_or_fallback(CSS::PropertyID::Display, "inline");
if (display == "none")
return nullptr;
- return adopt(*new LayoutImage(*this, move(style)));
+ return adopt(*new LayoutImage(*this, move(style), m_image_loader));
+}
+
+const Gfx::ImageDecoder* HTMLImageElement::image_decoder() const
+{
+ return m_image_loader.image_decoder();
}
const Gfx::Bitmap* HTMLImageElement::bitmap() const
{
- if (!m_image_decoder)
+ auto* decoder = image_decoder();
+ if (!decoder)
return nullptr;
-
- if (m_image_decoder->is_animated()) {
- return m_image_decoder->frame(m_current_frame_index).image;
- }
-
- return m_image_decoder->bitmap();
+ if (decoder->is_animated())
+ return decoder->frame(m_current_frame_index).image;
+ return decoder->bitmap();
}
void HTMLImageElement::set_visible_in_viewport(Badge<LayoutDocument>, bool visible_in_viewport)
{
- if (m_visible_in_viewport == visible_in_viewport)
- return;
- m_visible_in_viewport = visible_in_viewport;
-
- // FIXME: Don't update volatility every time. If we're here, we're probably scanning through
- // the whole document, updating "is visible in viewport" flags, and this could lead
- // to the same bitmap being marked volatile back and forth unnecessarily.
- if (resource())
- resource()->update_volatility();
+ m_image_loader.set_visible_in_viewport(visible_in_viewport);
}
}
diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.h b/Libraries/LibWeb/DOM/HTMLImageElement.h
index c1b28160e3..6d0434ba52 100644
--- a/Libraries/LibWeb/DOM/HTMLImageElement.h
+++ b/Libraries/LibWeb/DOM/HTMLImageElement.h
@@ -27,18 +27,17 @@
#pragma once
#include <AK/ByteBuffer.h>
+#include <AK/OwnPtr.h>
#include <LibCore/Forward.h>
#include <LibGfx/Forward.h>
#include <LibWeb/DOM/HTMLElement.h>
-#include <LibWeb/Loader/ImageResource.h>
+#include <LibWeb/Loader/ImageLoader.h>
namespace Web {
class LayoutDocument;
-class HTMLImageElement final
- : public HTMLElement
- , public ImageResourceClient {
+class HTMLImageElement final : public HTMLElement {
public:
using WrapperType = Bindings::HTMLImageElementWrapper;
@@ -53,30 +52,20 @@ public:
int preferred_height() const;
const Gfx::Bitmap* bitmap() const;
- const Gfx::ImageDecoder* image_decoder() const { return m_image_decoder; }
+ const Gfx::ImageDecoder* image_decoder() const;
void set_visible_in_viewport(Badge<LayoutDocument>, bool);
private:
- // ^ImageResource
- virtual void resource_did_load() override;
- virtual void resource_did_fail() override;
- virtual void resource_did_replace_decoder() override;
- virtual bool is_visible_in_viewport() const override { return m_visible_in_viewport; }
-
- void load_image(const String& src);
-
void animate();
virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override;
- RefPtr<Gfx::ImageDecoder> m_image_decoder;
+ ImageLoader m_image_loader;
size_t m_current_frame_index { 0 };
size_t m_loops_completed { 0 };
NonnullRefPtr<Core::Timer> m_timer;
-
- bool m_visible_in_viewport { false };
};
template<>
diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp
index 646344b054..5b42311047 100644
--- a/Libraries/LibWeb/Layout/LayoutDocument.cpp
+++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp
@@ -72,7 +72,8 @@ void LayoutDocument::did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect& a_v
{
Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
for_each_in_subtree_of_type<LayoutImage>([&](auto& layout_image) {
- const_cast<HTMLImageElement&>(layout_image.node()).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect()));
+ if (is<HTMLImageElement>(layout_image.node()))
+ const_cast<HTMLImageElement&>(to<HTMLImageElement>(layout_image.node())).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect()));
return IterationDecision::Continue;
});
}
diff --git a/Libraries/LibWeb/Layout/LayoutImage.cpp b/Libraries/LibWeb/Layout/LayoutImage.cpp
index cb14c42a57..c4200faa9e 100644
--- a/Libraries/LibWeb/Layout/LayoutImage.cpp
+++ b/Libraries/LibWeb/Layout/LayoutImage.cpp
@@ -24,15 +24,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <LibGUI/Painter.h>
#include <LibGfx/Font.h>
+#include <LibGfx/ImageDecoder.h>
#include <LibGfx/StylePainter.h>
-#include <LibGUI/Painter.h>
#include <LibWeb/Layout/LayoutImage.h>
namespace Web {
-LayoutImage::LayoutImage(const HTMLImageElement& element, NonnullRefPtr<StyleProperties> style)
+LayoutImage::LayoutImage(const Element& element, NonnullRefPtr<StyleProperties> style, const ImageLoader& image_loader)
: LayoutReplaced(element, move(style))
+ , m_image_loader(image_loader)
{
}
@@ -40,18 +42,31 @@ LayoutImage::~LayoutImage()
{
}
+int LayoutImage::preferred_width() const
+{
+ auto* decoder = m_image_loader.image_decoder();
+ return node().attribute(HTML::AttributeNames::width).to_int().value_or(decoder ? decoder->width() : 0);
+}
+
+int LayoutImage::preferred_height() const
+{
+ auto* decoder = m_image_loader.image_decoder();
+ return node().attribute(HTML::AttributeNames::height).to_int().value_or(decoder ? decoder->height() : 0);
+}
+
void LayoutImage::layout(LayoutMode layout_mode)
{
- if (node().preferred_width() && node().preferred_height()) {
+ if (preferred_width() && preferred_height()) {
set_has_intrinsic_width(true);
set_has_intrinsic_height(true);
- set_intrinsic_width(node().preferred_width());
- set_intrinsic_height(node().preferred_height());
+ set_intrinsic_width(preferred_width());
+ set_intrinsic_height(preferred_height());
} else if (renders_as_alt_text()) {
+ auto& image_element = to<HTMLImageElement>(node());
auto& font = Gfx::Font::default_font();
- auto alt = node().alt();
+ auto alt = image_element.alt();
if (alt.is_empty())
- alt = node().src();
+ alt = image_element.src();
set_width(font.width(alt) + 16);
set_height(font.glyph_height() + 16);
} else {
@@ -74,20 +89,23 @@ void LayoutImage::render(RenderingContext& context)
LayoutReplaced::render(context);
if (renders_as_alt_text()) {
+ auto& image_element = to<HTMLImageElement>(node());
context.painter().set_font(Gfx::Font::default_font());
Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
- auto alt = node().alt();
+ auto alt = image_element.alt();
if (alt.is_empty())
- alt = node().src();
+ alt = image_element.src();
context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right);
- } else if (node().bitmap())
- context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *node().bitmap(), node().bitmap()->rect());
- LayoutReplaced::render(context);
+ } else if (m_image_loader.bitmap()) {
+ context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *m_image_loader.bitmap(), m_image_loader.bitmap()->rect());
+ }
}
bool LayoutImage::renders_as_alt_text() const
{
- return !node().image_decoder();
+ if (is<HTMLImageElement>(node()))
+ return !m_image_loader.image_decoder();
+ return false;
}
}
diff --git a/Libraries/LibWeb/Layout/LayoutImage.h b/Libraries/LibWeb/Layout/LayoutImage.h
index 2112bab079..a972d51167 100644
--- a/Libraries/LibWeb/Layout/LayoutImage.h
+++ b/Libraries/LibWeb/Layout/LayoutImage.h
@@ -35,19 +35,24 @@ class HTMLImageElement;
class LayoutImage : public LayoutReplaced {
public:
- LayoutImage(const HTMLImageElement&, NonnullRefPtr<StyleProperties>);
+ LayoutImage(const Element&, NonnullRefPtr<StyleProperties>, const ImageLoader&);
virtual ~LayoutImage() override;
virtual void layout(LayoutMode = LayoutMode::Default) override;
virtual void render(RenderingContext&) override;
- const HTMLImageElement& node() const { return static_cast<const HTMLImageElement&>(LayoutReplaced::node()); }
+ const Element& node() const { return static_cast<const Element&>(LayoutReplaced::node()); }
bool renders_as_alt_text() const;
private:
virtual const char* class_name() const override { return "LayoutImage"; }
virtual bool is_image() const override { return true; }
+
+ int preferred_width() const;
+ int preferred_height() const;
+
+ const ImageLoader& m_image_loader;
};
template<>
diff --git a/Libraries/LibWeb/Loader/ImageLoader.cpp b/Libraries/LibWeb/Loader/ImageLoader.cpp
new file mode 100644
index 0000000000..df992e5483
--- /dev/null
+++ b/Libraries/LibWeb/Loader/ImageLoader.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibWeb/Loader/ImageLoader.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web {
+
+ImageLoader::ImageLoader()
+{
+}
+
+void ImageLoader::load(const URL& url)
+{
+ LoadRequest request;
+ request.set_url(url);
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request));
+}
+
+void ImageLoader::set_visible_in_viewport(bool visible_in_viewport)
+{
+ if (m_visible_in_viewport == visible_in_viewport)
+ return;
+ m_visible_in_viewport = visible_in_viewport;
+
+ // FIXME: Don't update volatility every time. If we're here, we're probably scanning through
+ // the whole document, updating "is visible in viewport" flags, and this could lead
+ // to the same bitmap being marked volatile back and forth unnecessarily.
+ if (resource())
+ resource()->update_volatility();
+}
+
+void ImageLoader::resource_did_load()
+{
+ ASSERT(resource());
+
+ if (!resource()->mime_type().starts_with("image/")) {
+ if (on_fail)
+ on_fail();
+ return;
+ }
+
+ if (!resource()->has_encoded_data()) {
+ dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url();
+ } else {
+ dbg() << "ImageLoader: Resource did load, has encoded data. URL: " << resource()->url();
+ }
+ m_decoder = resource()->ensure_decoder();
+
+ if (on_load)
+ on_load();
+}
+
+void ImageLoader::resource_did_fail()
+{
+ dbg() << "ImageLoader: Resource did fail. URL: " << resource()->url();
+ if (on_fail)
+ on_fail();
+}
+
+void ImageLoader::resource_did_replace_decoder()
+{
+ m_decoder = resource()->ensure_decoder();
+}
+
+const Gfx::Bitmap* ImageLoader::bitmap() const
+{
+ return m_decoder ? m_decoder->bitmap() : nullptr;
+}
+
+const Gfx::ImageDecoder* ImageLoader::image_decoder() const
+{
+ return m_decoder;
+}
+
+}
diff --git a/Libraries/LibWeb/Loader/ImageLoader.h b/Libraries/LibWeb/Loader/ImageLoader.h
new file mode 100644
index 0000000000..4b0e15ab59
--- /dev/null
+++ b/Libraries/LibWeb/Loader/ImageLoader.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <LibWeb/Loader/ImageResource.h>
+
+namespace Web {
+
+class ImageLoader : public ImageResourceClient {
+public:
+ ImageLoader();
+
+ void load(const URL&);
+
+ const Gfx::Bitmap* bitmap() const;
+ const Gfx::ImageDecoder* image_decoder() const;
+
+ void set_visible_in_viewport(bool);
+
+ Function<void()> on_load;
+ Function<void()> on_fail;
+
+private:
+ // ^ImageResourceClient
+ virtual void resource_did_load() override;
+ virtual void resource_did_fail() override;
+ virtual void resource_did_replace_decoder() override;
+ virtual bool is_visible_in_viewport() const override { return m_visible_in_viewport; }
+
+ RefPtr<Gfx::ImageDecoder> m_decoder;
+ bool m_visible_in_viewport { false };
+};
+
+}