summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibWeb/CMakeLists.txt1
-rw-r--r--Libraries/LibWeb/DOM/HTMLImageElement.cpp53
-rw-r--r--Libraries/LibWeb/DOM/HTMLImageElement.h9
-rw-r--r--Libraries/LibWeb/Forward.h2
-rw-r--r--Libraries/LibWeb/Loader/Resource.cpp104
-rw-r--r--Libraries/LibWeb/Loader/Resource.h105
-rw-r--r--Libraries/LibWeb/Loader/ResourceLoader.cpp20
-rw-r--r--Libraries/LibWeb/Loader/ResourceLoader.h3
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);