diff options
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibWeb/DOM/HTMLImageElement.cpp | 108 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/HTMLImageElement.h | 21 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutDocument.cpp | 3 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutImage.cpp | 44 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutImage.h | 9 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/ImageLoader.cpp | 101 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/ImageLoader.h | 59 |
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 }; +}; + +} |