From 7af337764eb25d401db7c4b6926e52e0a75bd2b1 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 1 Jun 2020 21:58:29 +0200 Subject: [PATCH] LibWeb: Add a naive Resource cache This patch introduces a caching mechanism in ResourceLoader. It's keyed on a LoadRequest object which is what you provide to load_resource() when you want to load a resource. We currently never prune the cache, so resources will stay in there forever. This is obviously not gonna stay that way, but we're just getting started here. :^) This should drastically reduce the number of requests when loading some sites (like Twitter) that reuse the same images over and over. --- Libraries/LibWeb/DOM/HTMLImageElement.cpp | 5 +- Libraries/LibWeb/Forward.h | 1 + Libraries/LibWeb/Loader/LoadRequest.h | 65 ++++++++++++++++++++++ Libraries/LibWeb/Loader/Resource.cpp | 8 +-- Libraries/LibWeb/Loader/Resource.h | 9 +-- Libraries/LibWeb/Loader/ResourceLoader.cpp | 19 +++++-- Libraries/LibWeb/Loader/ResourceLoader.h | 2 +- 7 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 Libraries/LibWeb/Loader/LoadRequest.h diff --git a/Libraries/LibWeb/DOM/HTMLImageElement.cpp b/Libraries/LibWeb/DOM/HTMLImageElement.cpp index c0d9b250ee..3efea5977e 100644 --- a/Libraries/LibWeb/DOM/HTMLImageElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLImageElement.cpp @@ -54,8 +54,9 @@ void HTMLImageElement::parse_attribute(const FlyString& name, const String& valu void HTMLImageElement::load_image(const String& src) { - URL src_url = document().complete_url(src); - auto resource = ResourceLoader::the().load_resource(src_url); + LoadRequest request; + request.set_url(document().complete_url(src)); + auto resource = ResourceLoader::the().load_resource(request); set_resource(resource); } diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 2b81759bbb..ac10ad7245 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -48,6 +48,7 @@ class PageView; class ImageData; class LayoutDocument; class LayoutNode; +class LoadRequest; class MouseEvent; class Node; class Origin; diff --git a/Libraries/LibWeb/Loader/LoadRequest.h b/Libraries/LibWeb/Loader/LoadRequest.h new file mode 100644 index 0000000000..03652601ce --- /dev/null +++ b/Libraries/LibWeb/Loader/LoadRequest.h @@ -0,0 +1,65 @@ +/* + * 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 +#include + +namespace Web { + +class LoadRequest { +public: + LoadRequest() + { + } + + bool is_valid() const { return m_url.is_valid(); } + + const URL& url() const { return m_url; } + void set_url(const URL& url) { m_url = url; } + + unsigned hash() const { return m_url.to_string().hash(); } + + bool operator==(const LoadRequest& other) const + { + return m_url == other.m_url; + } + +private: + URL m_url; +}; + +} + +namespace AK { + +template<> +struct Traits : public GenericTraits { + static unsigned hash(const Web::LoadRequest& request) { return request.hash(); } +}; + +} diff --git a/Libraries/LibWeb/Loader/Resource.cpp b/Libraries/LibWeb/Loader/Resource.cpp index 17e6a3c38d..e261220b82 100644 --- a/Libraries/LibWeb/Loader/Resource.cpp +++ b/Libraries/LibWeb/Loader/Resource.cpp @@ -29,13 +29,13 @@ namespace Web { -NonnullRefPtr Resource::create(Badge, const URL& url) +NonnullRefPtr Resource::create(Badge, const LoadRequest& request) { - return adopt(*new Resource(url)); + return adopt(*new Resource(request)); } -Resource::Resource(const URL& url) - : m_url(url) +Resource::Resource(const LoadRequest& request) + : m_request(request) { } diff --git a/Libraries/LibWeb/Loader/Resource.h b/Libraries/LibWeb/Loader/Resource.h index 80a55da64a..9af4ecb406 100644 --- a/Libraries/LibWeb/Loader/Resource.h +++ b/Libraries/LibWeb/Loader/Resource.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace Web { @@ -43,7 +44,7 @@ class Resource : public RefCounted { AK_MAKE_NONMOVABLE(Resource); public: - static NonnullRefPtr create(Badge, const URL&); + static NonnullRefPtr create(Badge, const LoadRequest&); ~Resource(); bool is_loaded() const { return m_loaded; } @@ -53,7 +54,7 @@ public: bool has_encoded_data() const { return !m_encoded_data.is_null(); } - const URL& url() const { return m_url; } + const URL& url() const { return m_request.url(); } const ByteBuffer& encoded_data() const { return m_encoded_data; } void register_client(Badge, ResourceClient&); @@ -75,9 +76,9 @@ public: void did_fail(Badge, const String& error); private: - explicit Resource(const URL&); + explicit Resource(const LoadRequest&); - URL m_url; + LoadRequest m_request; ByteBuffer m_encoded_data; bool m_loaded { false }; bool m_failed { false }; diff --git a/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Libraries/LibWeb/Loader/ResourceLoader.cpp index 2d39dcf67f..4a5591fbc6 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.cpp +++ b/Libraries/LibWeb/Loader/ResourceLoader.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -69,15 +70,25 @@ void ResourceLoader::load_sync(const URL& url, Function ResourceLoader::load_resource(const URL& url) +static HashMap> s_resource_cache; + +RefPtr ResourceLoader::load_resource(const LoadRequest& request) { - if (!url.is_valid()) + if (!request.is_valid()) return nullptr; - auto resource = Resource::create({}, url); + auto it = s_resource_cache.find(request); + if (it != s_resource_cache.end()) { + dbg() << "Reusing cached resource for: " << request.url(); + return it->value; + } + + auto resource = Resource::create({}, request); + + s_resource_cache.set(request, resource); load( - url, + request.url(), [=](auto& data, auto& headers) { const_cast(*resource).did_load({}, data, headers); }, diff --git a/Libraries/LibWeb/Loader/ResourceLoader.h b/Libraries/LibWeb/Loader/ResourceLoader.h index d9a696a774..34968790c3 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.h +++ b/Libraries/LibWeb/Loader/ResourceLoader.h @@ -42,7 +42,7 @@ class ResourceLoader : public Core::Object { public: static ResourceLoader& the(); - RefPtr load_resource(const URL&); + RefPtr load_resource(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);