diff options
-rw-r--r-- | Libraries/LibWeb/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/HTMLImageElement.cpp | 53 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/HTMLImageElement.h | 9 | ||||
-rw-r--r-- | Libraries/LibWeb/Forward.h | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/Resource.cpp | 104 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/Resource.h | 105 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/ResourceLoader.cpp | 20 | ||||
-rw-r--r-- | Libraries/LibWeb/Loader/ResourceLoader.h | 3 |
8 files changed, 274 insertions, 23 deletions
diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 1474207ce1..92adb508df 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -83,6 +83,7 @@ set(SOURCES Layout/LayoutWidget.cpp Layout/LineBox.cpp Layout/LineBoxFragment.cpp + Loader/Resource.cpp Loader/ResourceLoader.cpp PageView.cpp Parser/CSSParser.cpp diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.cpp b/Libraries/LibWeb/DOM/HTMLImageElement.cpp index 53157f0e96..c0d9b250ee 100644 --- a/Libraries/LibWeb/DOM/HTMLImageElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLImageElement.cpp @@ -55,30 +55,40 @@ void HTMLImageElement::parse_attribute(const FlyString& name, const String& valu void HTMLImageElement::load_image(const String& src) { URL src_url = document().complete_url(src); - ResourceLoader::the().load(src_url, [this, weak_element = make_weak_ptr()](auto data, auto&) { - if (!weak_element) { - dbg() << "HTMLImageElement: Load completed after element destroyed."; - return; - } - if (data.is_null()) { - dbg() << "HTMLImageElement: Failed to load " << this->src(); - return; - } + auto resource = ResourceLoader::the().load_resource(src_url); + set_resource(resource); +} - m_encoded_data = data; - m_image_decoder = Gfx::ImageDecoder::create(m_encoded_data.data(), m_encoded_data.size()); +void HTMLImageElement::resource_did_load() +{ + ASSERT(resource()); - 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(); - } + if (!resource()->has_encoded_data()) { + dbg() << "HTMLImageElement: Resource did load, but encoded data empty: " << this->src(); + return; + } - document().update_layout(); + dbg() << "HTMLImageElement: Resource did load, encoded data looks tasty: " << this->src(); - dispatch_event(Event::create("load")); - }); + m_image_decoder = Gfx::ImageDecoder::create(resource()->encoded_data()); + + 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::animate() @@ -162,7 +172,8 @@ void HTMLImageElement::set_volatile(Badge<LayoutDocument>, bool v) bool has_image = m_image_decoder->set_nonvolatile(); if (has_image) return; - m_image_decoder = Gfx::ImageDecoder::create(m_encoded_data.data(), m_encoded_data.size()); + ASSERT(resource()); + m_image_decoder = Gfx::ImageDecoder::create(resource()->encoded_data()); } } diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.h b/Libraries/LibWeb/DOM/HTMLImageElement.h index b123612591..e85a5bfb18 100644 --- a/Libraries/LibWeb/DOM/HTMLImageElement.h +++ b/Libraries/LibWeb/DOM/HTMLImageElement.h @@ -30,12 +30,15 @@ #include <LibCore/Forward.h> #include <LibGfx/Forward.h> #include <LibWeb/DOM/HTMLElement.h> +#include <LibWeb/Loader/Resource.h> namespace Web { class LayoutDocument; -class HTMLImageElement : public HTMLElement { +class HTMLImageElement final + : public HTMLElement + , public ResourceClient { public: using WrapperType = Bindings::HTMLImageElementWrapper; @@ -55,6 +58,9 @@ public: void set_volatile(Badge<LayoutDocument>, bool); private: + virtual void resource_did_load() override; + virtual void resource_did_fail() override; + void load_image(const String& src); void animate(); @@ -62,7 +68,6 @@ private: virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override; RefPtr<Gfx::ImageDecoder> m_image_decoder; - ByteBuffer m_encoded_data; size_t m_current_frame_index { 0 }; size_t m_loops_completed { 0 }; diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 00149973de..2b81759bbb 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -51,6 +51,8 @@ class LayoutNode; class MouseEvent; class Node; class Origin; +class Resource; +class ResourceLoader; class Selector; class StyleResolver; class StyleRule; diff --git a/Libraries/LibWeb/Loader/Resource.cpp b/Libraries/LibWeb/Loader/Resource.cpp new file mode 100644 index 0000000000..17e6a3c38d --- /dev/null +++ b/Libraries/LibWeb/Loader/Resource.cpp @@ -0,0 +1,104 @@ +/* + * 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 <LibWeb/DOM/HTMLImageElement.h> +#include <LibWeb/Loader/Resource.h> + +namespace Web { + +NonnullRefPtr<Resource> Resource::create(Badge<ResourceLoader>, const URL& url) +{ + return adopt(*new Resource(url)); +} + +Resource::Resource(const URL& url) + : m_url(url) +{ +} + +Resource::~Resource() +{ +} + +void Resource::did_load(Badge<ResourceLoader>, const ByteBuffer& data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers) +{ + ASSERT(!m_loaded); + m_encoded_data = data; + m_response_headers = headers; + m_loaded = true; + + for_each_client([](auto& client) { + client.resource_did_load(); + }); +} + +void Resource::did_fail(Badge<ResourceLoader>, const String& error) +{ + m_error = error; + m_failed = true; + + for_each_client([](auto& client) { + client.resource_did_fail(); + }); +} + +void Resource::register_client(Badge<ResourceClient>, ResourceClient& client) +{ + ASSERT(!m_clients.contains(&client)); + m_clients.set(&client); +} + +void Resource::unregister_client(Badge<ResourceClient>, ResourceClient& client) +{ + ASSERT(m_clients.contains(&client)); + m_clients.remove(&client); +} + +void ResourceClient::set_resource(Resource* resource) +{ + if (m_resource) + m_resource->unregister_client({}, *this); + m_resource = resource; + if (m_resource) { + m_resource->register_client({}, *this); + + // Make sure that reused resources also have their load callback fired. + if (resource->is_loaded()) + resource_did_load(); + + // Make sure that reused resources also have their fail callback fired. + if (resource->is_failed()) + resource_did_fail(); + } +} + +ResourceClient::~ResourceClient() +{ + if (m_resource) + m_resource->unregister_client({}, *this); +} + +} diff --git a/Libraries/LibWeb/Loader/Resource.h b/Libraries/LibWeb/Loader/Resource.h new file mode 100644 index 0000000000..80a55da64a --- /dev/null +++ b/Libraries/LibWeb/Loader/Resource.h @@ -0,0 +1,105 @@ +/* + * 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/ByteBuffer.h> +#include <AK/HashMap.h> +#include <AK/HashTable.h> +#include <AK/Noncopyable.h> +#include <AK/RefCounted.h> +#include <AK/URL.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class ResourceClient; + +class Resource : public RefCounted<Resource> { + AK_MAKE_NONCOPYABLE(Resource); + AK_MAKE_NONMOVABLE(Resource); + +public: + static NonnullRefPtr<Resource> create(Badge<ResourceLoader>, const URL&); + ~Resource(); + + bool is_loaded() const { return m_loaded; } + + bool is_failed() const { return m_failed; } + const String& error() const { return m_error; } + + bool has_encoded_data() const { return !m_encoded_data.is_null(); } + + const URL& url() const { return m_url; } + const ByteBuffer& encoded_data() const { return m_encoded_data; } + + void register_client(Badge<ResourceClient>, ResourceClient&); + void unregister_client(Badge<ResourceClient>, ResourceClient&); + + template<typename Callback> + void for_each_client(Callback callback) + { + // FIXME: This should use some kind of smart pointer to ResourceClient! + Vector<ResourceClient*, 16> clients_copy; + clients_copy.ensure_capacity(m_clients.size()); + for (auto* client : m_clients) + clients_copy.append(client); + for (auto* client : clients_copy) + callback(*client); + } + + void did_load(Badge<ResourceLoader>, const ByteBuffer& data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers); + void did_fail(Badge<ResourceLoader>, const String& error); + +private: + explicit Resource(const URL&); + + URL m_url; + ByteBuffer m_encoded_data; + bool m_loaded { false }; + bool m_failed { false }; + String m_error; + HashMap<String, String, CaseInsensitiveStringTraits> m_response_headers; + HashTable<ResourceClient*> m_clients; +}; + +class ResourceClient { +public: + virtual ~ResourceClient(); + + virtual void resource_did_load() { } + virtual void resource_did_fail() { } + +protected: + Resource* resource() { return m_resource; } + const Resource* resource() const { return m_resource; } + void set_resource(Resource*); + +private: + RefPtr<Resource> m_resource; +}; + +} diff --git a/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Libraries/LibWeb/Loader/ResourceLoader.cpp index e35a0f30e5..2d39dcf67f 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.cpp +++ b/Libraries/LibWeb/Loader/ResourceLoader.cpp @@ -31,6 +31,7 @@ #include <LibCore/File.h> #include <LibProtocol/Client.h> #include <LibProtocol/Download.h> +#include <LibWeb/Loader/Resource.h> #include <LibWeb/Loader/ResourceLoader.h> namespace Web { @@ -68,6 +69,25 @@ void ResourceLoader::load_sync(const URL& url, Function<void(const ByteBuffer&, loop.exec(); } +RefPtr<Resource> ResourceLoader::load_resource(const URL& url) +{ + if (!url.is_valid()) + return nullptr; + + auto resource = Resource::create({}, url); + + load( + url, + [=](auto& data, auto& headers) { + const_cast<Resource&>(*resource).did_load({}, data, headers); + }, + [=](auto& error) { + const_cast<Resource&>(*resource).did_fail({}, error); + }); + + return resource; +} + void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback) { if (is_port_blocked(url.port())) { diff --git a/Libraries/LibWeb/Loader/ResourceLoader.h b/Libraries/LibWeb/Loader/ResourceLoader.h index e9a9c0d772..d9a696a774 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.h +++ b/Libraries/LibWeb/Loader/ResourceLoader.h @@ -29,6 +29,7 @@ #include <AK/Function.h> #include <AK/URL.h> #include <LibCore/Object.h> +#include <LibWeb/Forward.h> namespace Protocol { class Client; @@ -41,6 +42,8 @@ class ResourceLoader : public Core::Object { public: static ResourceLoader& the(); + RefPtr<Resource> load_resource(const URL&); + void load(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr); void load_sync(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr); |