diff --git a/Base/home/anon/www/many-buggies.html b/Base/home/anon/www/many-buggies.html
new file mode 100644
index 0000000000..aa012e9595
--- /dev/null
+++ b/Base/home/anon/www/many-buggies.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html
index 4a7fa634ce..46e8aa17af 100644
--- a/Base/home/anon/www/welcome.html
+++ b/Base/home/anon/www/welcome.html
@@ -28,6 +28,7 @@ span#ua {
Your user agent is:
Some small test pages:
+ - many buggies
- system palette color css extension
- link inside display: inline-block
- setInterval() test
diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt
index 92adb508df..023a60be8b 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/ImageResource.cpp
Loader/Resource.cpp
Loader/ResourceLoader.cpp
PageView.cpp
diff --git a/Libraries/LibWeb/CSS/StyleValue.cpp b/Libraries/LibWeb/CSS/StyleValue.cpp
index 8414a1280c..7f31757f25 100644
--- a/Libraries/LibWeb/CSS/StyleValue.cpp
+++ b/Libraries/LibWeb/CSS/StyleValue.cpp
@@ -293,7 +293,7 @@ ImageStyleValue::ImageStyleValue(const URL& url, Document& document)
{
LoadRequest request;
request.set_url(url);
- set_resource(ResourceLoader::the().load_resource(request));
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
}
void ImageStyleValue::resource_did_load()
diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.cpp b/Libraries/LibWeb/DOM/HTMLImageElement.cpp
index 7b9b992494..689f9e821a 100644
--- a/Libraries/LibWeb/DOM/HTMLImageElement.cpp
+++ b/Libraries/LibWeb/DOM/HTMLImageElement.cpp
@@ -56,7 +56,7 @@ void HTMLImageElement::load_image(const String& src)
{
LoadRequest request;
request.set_url(document().complete_url(src));
- set_resource(ResourceLoader::the().load_resource(request));
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request));
}
void HTMLImageElement::resource_did_load()
@@ -70,7 +70,8 @@ void HTMLImageElement::resource_did_load()
dbg() << "HTMLImageElement: Resource did load, encoded data looks tasty: " << this->src();
- m_image_decoder = Gfx::ImageDecoder::create(resource()->encoded_data());
+ ASSERT(!m_image_decoder);
+ 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);
@@ -91,6 +92,11 @@ void HTMLImageElement::resource_did_fail()
dispatch_event(Event::create("error"));
}
+void HTMLImageElement::resource_did_replace_decoder()
+{
+ m_image_decoder = resource()->ensure_decoder();
+}
+
void HTMLImageElement::animate()
{
if (!layout_node()) {
@@ -161,19 +167,17 @@ const Gfx::Bitmap* HTMLImageElement::bitmap() const
return m_image_decoder->bitmap();
}
-void HTMLImageElement::set_volatile(Badge, bool v)
+void HTMLImageElement::set_visible_in_viewport(Badge, bool visible_in_viewport)
{
- if (!m_image_decoder)
+ if (m_visible_in_viewport == visible_in_viewport)
return;
- if (v) {
- m_image_decoder->set_volatile();
- return;
- }
- bool has_image = m_image_decoder->set_nonvolatile();
- if (has_image)
- return;
- ASSERT(resource());
- m_image_decoder = Gfx::ImageDecoder::create(resource()->encoded_data());
+ 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();
}
}
diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.h b/Libraries/LibWeb/DOM/HTMLImageElement.h
index e85a5bfb18..0d4f2ed4c3 100644
--- a/Libraries/LibWeb/DOM/HTMLImageElement.h
+++ b/Libraries/LibWeb/DOM/HTMLImageElement.h
@@ -30,7 +30,7 @@
#include
#include
#include
-#include
+#include
namespace Web {
@@ -38,7 +38,7 @@ class LayoutDocument;
class HTMLImageElement final
: public HTMLElement
- , public ResourceClient {
+ , public ImageResourceClient {
public:
using WrapperType = Bindings::HTMLImageElementWrapper;
@@ -55,11 +55,14 @@ public:
const Gfx::Bitmap* bitmap() const;
const Gfx::ImageDecoder* image_decoder() const { return m_image_decoder; }
- void set_volatile(Badge, bool);
+ void set_visible_in_viewport(Badge, 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);
@@ -72,6 +75,8 @@ private:
size_t m_current_frame_index { 0 };
size_t m_loops_completed { 0 };
NonnullRefPtr m_timer;
+
+ bool m_visible_in_viewport { false };
};
template<>
diff --git a/Libraries/LibWeb/DOM/HTMLLinkElement.cpp b/Libraries/LibWeb/DOM/HTMLLinkElement.cpp
index 2133728203..4ccc36bc5b 100644
--- a/Libraries/LibWeb/DOM/HTMLLinkElement.cpp
+++ b/Libraries/LibWeb/DOM/HTMLLinkElement.cpp
@@ -76,7 +76,7 @@ void HTMLLinkElement::load_stylesheet(const URL& url)
{
LoadRequest request;
request.set_url(url);
- set_resource(ResourceLoader::the().load_resource(request));
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
}
}
diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp
index 278a509c92..6a14af9b0b 100644
--- a/Libraries/LibWeb/Layout/LayoutDocument.cpp
+++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp
@@ -63,7 +63,7 @@ void LayoutDocument::did_set_viewport_rect(Badge, const Gfx::Rect& a_view
{
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([&](auto& layout_image) {
- const_cast(layout_image.node()).set_volatile({}, !viewport_rect.intersects(layout_image.rect()));
+ const_cast(layout_image.node()).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.rect()));
return IterationDecision::Continue;
});
}
diff --git a/Libraries/LibWeb/Loader/ImageResource.cpp b/Libraries/LibWeb/Loader/ImageResource.cpp
new file mode 100644
index 0000000000..efacec3aa1
--- /dev/null
+++ b/Libraries/LibWeb/Loader/ImageResource.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, Andreas Kling
+ * 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
+#include
+#include
+#include
+
+namespace Web {
+
+ImageResource::ImageResource(const LoadRequest& request)
+ : Resource(Type::Image, request)
+{
+}
+
+ImageResource::~ImageResource()
+{
+}
+
+Gfx::ImageDecoder& ImageResource::ensure_decoder()
+{
+ if (!m_decoder)
+ m_decoder = Gfx::ImageDecoder::create(encoded_data());
+ return *m_decoder;
+}
+
+void ImageResource::update_volatility()
+{
+ if (!m_decoder)
+ return;
+
+ bool visible_in_viewport = false;
+ for_each_client([&](auto& client) {
+ if (static_cast(client).is_visible_in_viewport())
+ visible_in_viewport = true;
+ });
+
+ if (!visible_in_viewport) {
+ m_decoder->set_volatile();
+ return;
+ }
+
+ bool still_has_decoded_image = m_decoder->set_nonvolatile();
+ if (still_has_decoded_image)
+ return;
+
+ m_decoder = nullptr;
+ for_each_client([&](auto& client) {
+ static_cast(client).resource_did_replace_decoder();
+ });
+}
+
+ImageResourceClient::~ImageResourceClient()
+{
+}
+
+}
diff --git a/Libraries/LibWeb/Loader/ImageResource.h b/Libraries/LibWeb/Loader/ImageResource.h
new file mode 100644
index 0000000000..d505c2a59d
--- /dev/null
+++ b/Libraries/LibWeb/Loader/ImageResource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, Andreas Kling
+ * 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
+
+namespace Web {
+
+class ImageResource final : public Resource {
+ friend class Resource;
+
+public:
+ virtual ~ImageResource() override;
+ Gfx::ImageDecoder& ensure_decoder();
+
+ void update_volatility();
+
+private:
+ explicit ImageResource(const LoadRequest&);
+ RefPtr m_decoder;
+};
+
+class ImageResourceClient : public ResourceClient {
+public:
+ virtual ~ImageResourceClient();
+
+ virtual bool is_visible_in_viewport() const { return false; }
+ virtual void resource_did_replace_decoder() {}
+
+protected:
+ ImageResource* resource() { return static_cast(ResourceClient::resource()); }
+ const ImageResource* resource() const { return static_cast(ResourceClient::resource()); }
+};
+
+}
diff --git a/Libraries/LibWeb/Loader/Resource.cpp b/Libraries/LibWeb/Loader/Resource.cpp
index 2b4e0d61a7..26992d7af7 100644
--- a/Libraries/LibWeb/Loader/Resource.cpp
+++ b/Libraries/LibWeb/Loader/Resource.cpp
@@ -30,13 +30,16 @@
namespace Web {
-NonnullRefPtr Resource::create(Badge, const LoadRequest& request)
+NonnullRefPtr Resource::create(Badge, Type type, const LoadRequest& request)
{
- return adopt(*new Resource(request));
+ if (type == Type::Image)
+ return adopt(*new ImageResource(request));
+ return adopt(*new Resource(type, request));
}
-Resource::Resource(const LoadRequest& request)
+Resource::Resource(Type type, const LoadRequest& request)
: m_request(request)
+ , m_type(type)
{
}
diff --git a/Libraries/LibWeb/Loader/Resource.h b/Libraries/LibWeb/Loader/Resource.h
index 932e58c124..0ccfda79a4 100644
--- a/Libraries/LibWeb/Loader/Resource.h
+++ b/Libraries/LibWeb/Loader/Resource.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
@@ -46,8 +47,13 @@ class Resource : public RefCounted {
AK_MAKE_NONMOVABLE(Resource);
public:
- static NonnullRefPtr create(Badge, const LoadRequest&);
- ~Resource();
+ enum class Type {
+ Generic,
+ Image,
+ };
+
+ static NonnullRefPtr create(Badge, Type, const LoadRequest&);
+ virtual ~Resource();
bool is_loaded() const { return m_loaded; }
@@ -67,11 +73,13 @@ public:
void did_load(Badge, const ByteBuffer& data, const HashMap& headers);
void did_fail(Badge, const String& error);
-private:
- explicit Resource(const LoadRequest&);
+protected:
+ explicit Resource(Type, const LoadRequest&);
+private:
LoadRequest m_request;
ByteBuffer m_encoded_data;
+ Type m_type { Type::Generic };
bool m_loaded { false };
bool m_failed { false };
String m_error;
diff --git a/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Libraries/LibWeb/Loader/ResourceLoader.cpp
index 4a5591fbc6..62a8804e25 100644
--- a/Libraries/LibWeb/Loader/ResourceLoader.cpp
+++ b/Libraries/LibWeb/Loader/ResourceLoader.cpp
@@ -72,7 +72,7 @@ void ResourceLoader::load_sync(const URL& url, Function> s_resource_cache;
-RefPtr ResourceLoader::load_resource(const LoadRequest& request)
+RefPtr ResourceLoader::load_resource(Resource::Type type, const LoadRequest& request)
{
if (!request.is_valid())
return nullptr;
@@ -83,7 +83,7 @@ RefPtr ResourceLoader::load_resource(const LoadRequest& request)
return it->value;
}
- auto resource = Resource::create({}, request);
+ auto resource = Resource::create({}, type, request);
s_resource_cache.set(request, resource);
diff --git a/Libraries/LibWeb/Loader/ResourceLoader.h b/Libraries/LibWeb/Loader/ResourceLoader.h
index 34968790c3..bd3591e5d9 100644
--- a/Libraries/LibWeb/Loader/ResourceLoader.h
+++ b/Libraries/LibWeb/Loader/ResourceLoader.h
@@ -29,7 +29,7 @@
#include
#include
#include
-#include
+#include
namespace Protocol {
class Client;
@@ -42,7 +42,7 @@ class ResourceLoader : public Core::Object {
public:
static ResourceLoader& the();
- RefPtr load_resource(const LoadRequest&);
+ RefPtr load_resource(Resource::Type, const LoadRequest&);
void load(const URL&, Function& response_headers)> success_callback, Function error_callback = nullptr);
void load_sync(const URL&, Function& response_headers)> success_callback, Function error_callback = nullptr);