mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
LibCore: Add Resource for platform agnostic application resource loading
The first implementation is simply raw files.
This commit is contained in:
parent
f4a89c31c6
commit
0d417cd604
Notes:
sideshowbarker
2024-07-17 09:49:33 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/0d417cd604 Pull-request: https://github.com/SerenityOS/serenity/pull/21302 Reviewed-by: https://github.com/kleinesfilmroellchen Reviewed-by: https://github.com/trflynn89
8 changed files with 393 additions and 0 deletions
|
@ -21,6 +21,9 @@ set(SOURCES
|
|||
Notifier.cpp
|
||||
Process.cpp
|
||||
ProcessStatisticsReader.cpp
|
||||
Resource.cpp
|
||||
ResourceImplementation.cpp
|
||||
ResourceImplementationFile.cpp
|
||||
SecretString.cpp
|
||||
SessionManagement.cpp
|
||||
Socket.cpp
|
||||
|
|
|
@ -32,6 +32,8 @@ class NetworkJob;
|
|||
class NetworkResponse;
|
||||
class Notifier;
|
||||
class ProcessStatisticsReader;
|
||||
class Resource;
|
||||
class ResourceImplementation;
|
||||
class Socket;
|
||||
template<typename Result, typename TError = AK::Error>
|
||||
class Promise;
|
||||
|
|
88
Userland/Libraries/LibCore/Resource.cpp
Normal file
88
Userland/Libraries/LibCore/Resource.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/ResourceImplementation.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Resource::Resource(String path, Scheme scheme, NonnullOwnPtr<Core::MappedFile> file)
|
||||
: m_path(move(path))
|
||||
, m_scheme(scheme)
|
||||
, m_data(move(file))
|
||||
{
|
||||
}
|
||||
|
||||
Resource::Resource(String path, Scheme scheme, ByteBuffer buffer)
|
||||
: m_path(move(path))
|
||||
, m_scheme(scheme)
|
||||
, m_data(move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
Resource::Resource(String path, Scheme scheme, DirectoryTag)
|
||||
: m_path(move(path))
|
||||
, m_scheme(scheme)
|
||||
, m_data(DirectoryTag {})
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Resource>> Resource::load_from_uri(StringView uri)
|
||||
{
|
||||
return ResourceImplementation::the().load_from_uri(uri);
|
||||
}
|
||||
|
||||
[[nodiscard]] String Resource::uri() const
|
||||
{
|
||||
return MUST(String::formatted("{}://{}", m_scheme == Scheme::Resource ? "resource"sv : "file"sv, m_path));
|
||||
}
|
||||
|
||||
[[nodiscard]] Optional<String> Resource::filesystem_path() const
|
||||
{
|
||||
return ResourceImplementation::the().filesystem_path(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] String Resource::filename() const
|
||||
{
|
||||
return MUST(String::from_utf8(LexicalPath(m_path.bytes_as_string_view()).basename()));
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector<String> Resource::children() const
|
||||
{
|
||||
return ResourceImplementation::the().child_names(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] ByteBuffer Resource::clone_data() const
|
||||
{
|
||||
return m_data.visit(
|
||||
[](NonnullOwnPtr<Core::MappedFile> const& file) { return MUST(ByteBuffer::copy(file->bytes())); },
|
||||
[](ByteBuffer const& buffer) { return buffer; },
|
||||
[](DirectoryTag) -> ByteBuffer { VERIFY_NOT_REACHED(); });
|
||||
}
|
||||
|
||||
[[nodiscard]] ByteBuffer Resource::release_data() &&
|
||||
{
|
||||
VERIFY(!m_data.has<DirectoryTag>());
|
||||
|
||||
if (m_data.has<NonnullOwnPtr<Core::MappedFile>>())
|
||||
return MUST(ByteBuffer::copy(m_data.get<NonnullOwnPtr<Core::MappedFile>>()->bytes()));
|
||||
return move(m_data).get<ByteBuffer>();
|
||||
}
|
||||
|
||||
[[nodiscard]] ReadonlyBytes Resource::data() const
|
||||
{
|
||||
return m_data.visit(
|
||||
[](NonnullOwnPtr<Core::MappedFile> const& file) { return file->bytes(); },
|
||||
[](ByteBuffer const& buffer) { return buffer.bytes(); },
|
||||
[](DirectoryTag) -> ReadonlyBytes { VERIFY_NOT_REACHED(); });
|
||||
}
|
||||
|
||||
[[nodiscard]] FixedMemoryStream Resource::stream() const
|
||||
{
|
||||
return FixedMemoryStream(data());
|
||||
}
|
||||
}
|
90
Userland/Libraries/LibCore/Resource.h
Normal file
90
Userland/Libraries/LibCore/Resource.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Span.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
|
||||
namespace Core {
|
||||
class Resource : public RefCounted<Resource> {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<Resource>> load_from_uri(StringView);
|
||||
|
||||
[[nodiscard]] bool is_file() const { return !m_data.has<DirectoryTag>(); }
|
||||
[[nodiscard]] bool is_directory() const { return m_data.has<DirectoryTag>(); }
|
||||
|
||||
[[nodiscard]] String uri() const;
|
||||
[[nodiscard]] String filename() const;
|
||||
[[nodiscard]] Optional<String> filesystem_path() const;
|
||||
|
||||
[[nodiscard]] ByteBuffer clone_data() const;
|
||||
[[nodiscard]] ByteBuffer release_data() &&;
|
||||
[[nodiscard]] ReadonlyBytes data() const;
|
||||
[[nodiscard]] FixedMemoryStream stream() const;
|
||||
|
||||
[[nodiscard]] Vector<String> children() const;
|
||||
// Depth-first
|
||||
template<IteratorFunction<Resource const&> Callback>
|
||||
IterationDecision for_each_descendant(Callback&&) const;
|
||||
|
||||
template<IteratorFunction<Resource const&> Callback>
|
||||
void for_each_descendant_file(Callback&&) const;
|
||||
|
||||
struct DirectoryTag { };
|
||||
|
||||
private:
|
||||
friend class ResourceImplementation;
|
||||
|
||||
enum class Scheme {
|
||||
File,
|
||||
Resource,
|
||||
};
|
||||
|
||||
Resource(String path, Scheme, NonnullOwnPtr<Core::MappedFile>);
|
||||
Resource(String path, Scheme, ByteBuffer);
|
||||
Resource(String path, Scheme, DirectoryTag);
|
||||
|
||||
String m_path; // Relative to scheme root. File: abspath, Resource: resource root
|
||||
Scheme m_scheme;
|
||||
|
||||
Variant<DirectoryTag, NonnullOwnPtr<Core::MappedFile>, ByteBuffer> m_data;
|
||||
};
|
||||
|
||||
template<IteratorFunction<Resource const&> Callback>
|
||||
IterationDecision Resource::for_each_descendant(Callback&& callback) const
|
||||
{
|
||||
auto children = this->children();
|
||||
for (auto const& child : children) {
|
||||
if (auto child_resource = load_from_uri(MUST(String::formatted("{}/{}", uri(), child))); !child_resource.is_error()) {
|
||||
if (callback(*child_resource.value()) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (child_resource.value()->for_each_descendant(callback) == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
template<IteratorFunction<Resource const&> Callback>
|
||||
void Resource::for_each_descendant_file(Callback&& callback) const
|
||||
{
|
||||
for_each_descendant([callback = forward<Callback>(callback)](Resource const& resource) {
|
||||
if (resource.is_directory())
|
||||
return IterationDecision::Continue;
|
||||
return callback(resource);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
100
Userland/Libraries/LibCore/ResourceImplementation.cpp
Normal file
100
Userland/Libraries/LibCore/ResourceImplementation.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/ResourceImplementation.h>
|
||||
#include <LibCore/ResourceImplementationFile.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
static OwnPtr<ResourceImplementation> s_the;
|
||||
|
||||
void ResourceImplementation::install(OwnPtr<ResourceImplementation> the)
|
||||
{
|
||||
s_the = move(the);
|
||||
}
|
||||
|
||||
ResourceImplementation& ResourceImplementation::the()
|
||||
{
|
||||
if (!s_the)
|
||||
install(make<ResourceImplementationFile>("/res"_string));
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
NonnullRefPtr<Resource> ResourceImplementation::make_resource(String full_path, NonnullOwnPtr<Core::MappedFile> file)
|
||||
{
|
||||
return adopt_ref(*new Resource(move(full_path), Resource::Scheme::Resource, move(file)));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Resource> ResourceImplementation::make_resource(String full_path, ByteBuffer buffer)
|
||||
{
|
||||
return adopt_ref(*new Resource(move(full_path), Resource::Scheme::Resource, move(buffer)));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Resource> ResourceImplementation::make_directory_resource(String full_path)
|
||||
{
|
||||
return adopt_ref(*new Resource(move(full_path), Resource::Scheme::Resource, Resource::DirectoryTag {}));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Resource>> ResourceImplementation::load_from_uri(StringView uri)
|
||||
{
|
||||
StringView const file_scheme = "file://"sv;
|
||||
StringView const resource_scheme = "resource://"sv;
|
||||
|
||||
if (uri.starts_with(resource_scheme))
|
||||
return load_from_resource_scheme_uri(uri);
|
||||
|
||||
if (uri.starts_with(file_scheme)) {
|
||||
auto path = uri.substring_view(file_scheme.length());
|
||||
if (is_directory(path))
|
||||
return adopt_ref(*new Resource(TRY(String::from_utf8(path)), Resource::Scheme::File, Resource::DirectoryTag {}));
|
||||
return adopt_ref(*new Resource(TRY(String::from_utf8(path)), Resource::Scheme::File, TRY(MappedFile::map(path))));
|
||||
}
|
||||
|
||||
dbgln("ResourceImplementation: Unknown scheme for {}", uri);
|
||||
return Error::from_string_view("Invalid scheme"sv);
|
||||
}
|
||||
|
||||
Vector<String> ResourceImplementation::child_names(Resource const& resource)
|
||||
{
|
||||
if (!resource.is_directory())
|
||||
return {};
|
||||
|
||||
if (resource.m_scheme == Resource::Scheme::Resource)
|
||||
return child_names_for_resource_scheme(resource);
|
||||
|
||||
VERIFY(resource.m_scheme == Resource::Scheme::File);
|
||||
|
||||
Vector<String> children;
|
||||
Core::DirIterator it(resource.filesystem_path().release_value().to_deprecated_string(), Core::DirIterator::SkipParentAndBaseDir);
|
||||
while (it.has_next())
|
||||
children.append(MUST(String::from_deprecated_string(it.next_path())));
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
Optional<String> ResourceImplementation::filesystem_path(Resource const& resource)
|
||||
{
|
||||
if (resource.m_scheme == Resource::Scheme::Resource)
|
||||
return filesystem_path_for_resource_scheme(resource.m_path);
|
||||
|
||||
VERIFY(resource.m_scheme == Resource::Scheme::File);
|
||||
|
||||
return resource.m_path;
|
||||
}
|
||||
|
||||
// Note: This is a copy of the impl in LibFilesystem, but we can't link that to LibCore
|
||||
bool ResourceImplementation::is_directory(StringView filesystem_path)
|
||||
{
|
||||
auto st_or_error = System::stat(filesystem_path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
}
|
36
Userland/Libraries/LibCore/ResourceImplementation.h
Normal file
36
Userland/Libraries/LibCore/ResourceImplementation.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/Resource.h>
|
||||
|
||||
namespace Core {
|
||||
class ResourceImplementation {
|
||||
public:
|
||||
ErrorOr<NonnullRefPtr<Resource>> load_from_uri(StringView);
|
||||
Vector<String> child_names(Resource const&);
|
||||
Optional<String> filesystem_path(Resource const&);
|
||||
|
||||
virtual ~ResourceImplementation() = default;
|
||||
|
||||
static void install(OwnPtr<ResourceImplementation>);
|
||||
static ResourceImplementation& the();
|
||||
|
||||
protected:
|
||||
virtual ErrorOr<NonnullRefPtr<Resource>> load_from_resource_scheme_uri(StringView) = 0;
|
||||
virtual Vector<String> child_names_for_resource_scheme(Resource const&) = 0;
|
||||
virtual Optional<String> filesystem_path_for_resource_scheme(String const&) = 0;
|
||||
|
||||
static bool is_directory(StringView filesystem_path);
|
||||
|
||||
static NonnullRefPtr<Resource> make_resource(String full_path, NonnullOwnPtr<Core::MappedFile>);
|
||||
static NonnullRefPtr<Resource> make_resource(String full_path, ByteBuffer);
|
||||
static NonnullRefPtr<Resource> make_directory_resource(String full_path);
|
||||
};
|
||||
}
|
48
Userland/Libraries/LibCore/ResourceImplementationFile.cpp
Normal file
48
Userland/Libraries/LibCore/ResourceImplementationFile.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/ResourceImplementationFile.h>
|
||||
|
||||
namespace Core {
|
||||
ResourceImplementationFile::ResourceImplementationFile(String base_directory)
|
||||
: m_base_directory(move(base_directory))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Resource>> ResourceImplementationFile::load_from_resource_scheme_uri(StringView uri)
|
||||
{
|
||||
StringView const resource_scheme = "resource://"sv;
|
||||
|
||||
VERIFY(uri.starts_with(resource_scheme));
|
||||
|
||||
auto path = TRY(String::from_utf8(uri.substring_view(resource_scheme.length())));
|
||||
auto full_path = TRY(String::from_deprecated_string(LexicalPath::join(m_base_directory, path).string()));
|
||||
if (is_directory(full_path))
|
||||
return make_directory_resource(move(path));
|
||||
|
||||
return make_resource(path, TRY(MappedFile::map(full_path)));
|
||||
}
|
||||
|
||||
Vector<String> ResourceImplementationFile::child_names_for_resource_scheme(Resource const& resource)
|
||||
{
|
||||
Vector<String> children;
|
||||
Core::DirIterator it(resource.filesystem_path().release_value().to_deprecated_string(), Core::DirIterator::SkipParentAndBaseDir);
|
||||
while (it.has_next())
|
||||
children.append(MUST(String::from_deprecated_string(it.next_path())));
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
Optional<String> ResourceImplementationFile::filesystem_path_for_resource_scheme(String const& relative_path)
|
||||
{
|
||||
return MUST(String::from_deprecated_string(LexicalPath::join(m_base_directory, relative_path).string()));
|
||||
}
|
||||
|
||||
}
|
26
Userland/Libraries/LibCore/ResourceImplementationFile.h
Normal file
26
Userland/Libraries/LibCore/ResourceImplementationFile.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/ResourceImplementation.h>
|
||||
|
||||
namespace Core {
|
||||
class ResourceImplementationFile : public ResourceImplementation {
|
||||
public:
|
||||
explicit ResourceImplementationFile(String base_directory);
|
||||
|
||||
virtual ErrorOr<NonnullRefPtr<Resource>> load_from_resource_scheme_uri(StringView) override;
|
||||
virtual Vector<String> child_names_for_resource_scheme(Resource const&) override;
|
||||
virtual Optional<String> filesystem_path_for_resource_scheme(String const&) override;
|
||||
|
||||
private:
|
||||
String m_base_directory;
|
||||
};
|
||||
}
|
Loading…
Add table
Reference in a new issue