mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-24 16:40:21 +00:00
Compare commits
32 commits
ea758d7a74
...
9dda07a421
Author | SHA1 | Date | |
---|---|---|---|
|
9dda07a421 | ||
|
63a5717bc7 | ||
|
c5afe58540 | ||
|
3bcd91b109 | ||
|
7d1291b9f0 | ||
|
6911c45bab | ||
|
879ae94183 | ||
|
7e20f4726f | ||
|
7f72c28e78 | ||
|
d704b61066 | ||
|
b93d8ef875 | ||
|
8a07131229 | ||
|
063cd68bf4 | ||
|
f638f84185 | ||
|
4203b7823f | ||
|
cd446e5e9c | ||
|
ab0dc83d28 | ||
|
6fc06f45c2 | ||
|
e98e9b8e81 | ||
|
4b1deb6fe1 | ||
|
356507284e | ||
|
ec5ea0d686 | ||
|
b3c253e50f | ||
|
d0c0db5bb3 | ||
|
b99a3ec2df | ||
|
74b27d620d | ||
|
dabf3da7e5 | ||
|
11460b3daa | ||
|
43056a8684 | ||
|
a423493dd8 | ||
|
77d205571d | ||
|
84f0b4af2b |
125 changed files with 3909 additions and 406 deletions
|
@ -9,10 +9,8 @@
|
|||
#include <AK/BitmapView.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef DNS_DEBUG
|
||||
# cmakedefine01 DNS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef EDITOR_DEBUG
|
||||
# cmakedefine01 EDITOR_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,15 @@ public:
|
|||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
constexpr IPv6Address(Array<u8, 16> const& data)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
template<SameAs<char const*> T>
|
||||
constexpr IPv6Address(T const&) = delete; // Disable implicit conversion of char const* -> ipv4 -> ipv6
|
||||
|
||||
constexpr IPv6Address(IPv4Address const& ipv4_address)
|
||||
{
|
||||
// IPv4 mapped IPv6 address
|
||||
|
|
|
@ -63,6 +63,11 @@ bool LexicalPath::is_absolute_path(StringView path)
|
|||
return path.starts_with('/');
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return m_string == "/";
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
{
|
||||
Vector<ByteString> vector;
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
|
||||
static bool is_absolute_path(StringView path);
|
||||
bool is_absolute() const { return is_absolute_path(m_string); }
|
||||
bool is_root() const;
|
||||
|
||||
ByteString const& string() const { return m_string; }
|
||||
|
||||
StringView dirname() const { return m_dirname; }
|
||||
|
|
|
@ -45,6 +45,11 @@ bool LexicalPath::is_absolute_path(StringView path)
|
|||
return path.length() >= 2 && path[1] == ':';
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return AK::is_root(m_parts);
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
{
|
||||
Vector<ByteString> vector;
|
||||
|
@ -81,7 +86,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
continue;
|
||||
if (part == ".." && !canonical_parts.is_empty()) {
|
||||
// At the root, .. does nothing.
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
continue;
|
||||
// A .. and a previous non-.. part cancel each other.
|
||||
if (canonical_parts.last() != "..") {
|
||||
|
@ -95,7 +100,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
StringBuilder builder;
|
||||
builder.join('\\', canonical_parts);
|
||||
// "X:" -> "X:\"
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
builder.append('\\');
|
||||
path = builder.to_byte_string();
|
||||
return path == "" ? "." : path;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Testing Ladybird
|
||||
|
||||
Tests are locates in `Tests/`, with a directory for each library.
|
||||
Tests are located in `Tests/`, with a directory for each library.
|
||||
|
||||
Every feature or bug fix added to LibWeb should have a corresponding test in `Tests/LibWeb`.
|
||||
The test should be either a Text, Layout, Ref, or Screenshot test depending on the feature.
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <AK/Assertions.h>
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <LibCompress/Deflate.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
#include <AK/MemoryStream.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Compress {
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
|
|||
|
||||
ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
|
||||
{
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, HIWORD(size), LOWORD(size), NULL);
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, size >> 31 >> 1, size & 0xFFFFFFFF, NULL);
|
||||
if (!map_handle)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Directory.h"
|
||||
#include "DirIterator.h"
|
||||
#include "System.h"
|
||||
#include <dirent.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
@ -31,6 +29,7 @@ Directory::~Directory()
|
|||
MUST(System::close(m_directory_fd));
|
||||
}
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
||||
{
|
||||
if (m_directory_fd == -1)
|
||||
|
@ -38,6 +37,7 @@ ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
|||
TRY(Core::System::fchown(m_directory_fd, uid, gid));
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<bool> Directory::is_valid_directory(int fd)
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ ErrorOr<Directory> Directory::create(LexicalPath path, CreateDirectories create_
|
|||
|
||||
ErrorOr<void> Directory::ensure_directory(LexicalPath const& path, mode_t creation_mode)
|
||||
{
|
||||
if (path.basename() == "/" || path.basename() == ".")
|
||||
if (path.is_root() || path.string() == ".")
|
||||
return {};
|
||||
|
||||
TRY(ensure_directory(path.parent(), creation_mode));
|
||||
|
|
|
@ -10,14 +10,11 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/IterationDecision.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/DirectoryEntry.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace Core {
|
||||
|
@ -51,7 +48,9 @@ public:
|
|||
static ErrorOr<void> for_each_entry(StringView path, DirIterator::Flags, ForEachEntryCallback);
|
||||
ErrorOr<void> for_each_entry(DirIterator::Flags, ForEachEntryCallback);
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> chown(uid_t, gid_t);
|
||||
#endif
|
||||
|
||||
static ErrorOr<bool> is_valid_directory(int fd);
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ class TCPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(ByteString const& host, u16 port);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address, ByteString const&) { return connect(address); }
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd);
|
||||
|
||||
TCPSocket(TCPSocket&& other)
|
||||
|
@ -220,6 +221,7 @@ class UDPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(ByteString const& host, u16 port, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, ByteString const&, Optional<AK::Duration> timeout = {}) { return connect(address, timeout); }
|
||||
|
||||
UDPSocket(UDPSocket&& other)
|
||||
: Socket(static_cast<Socket&&>(other))
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#include <AK/ByteString.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
|
||||
namespace Core::System {
|
||||
|
@ -20,9 +21,21 @@ namespace Core::System {
|
|||
ErrorOr<int> open(StringView path, int options, mode_t mode)
|
||||
{
|
||||
ByteString string_path = path;
|
||||
int rc = _open(string_path.characters(), options, mode);
|
||||
if (rc < 0)
|
||||
return Error::from_syscall("open"sv, -errno);
|
||||
auto sz_path = string_path.characters();
|
||||
int rc = _open(sz_path, options, mode);
|
||||
if (rc < 0) {
|
||||
int error = errno;
|
||||
struct stat st = {};
|
||||
if (::stat(sz_path, &st) == 0 && (st.st_mode & S_IFDIR)) {
|
||||
HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (dir_handle == INVALID_HANDLE_VALUE)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
int dir_fd = _open_osfhandle((intptr_t)dir_handle, 0);
|
||||
if (dir_fd != -1)
|
||||
return dir_fd;
|
||||
}
|
||||
return Error::from_syscall("open"sv, -error);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -133,4 +146,24 @@ ErrorOr<void> unlink(StringView path)
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> mkdir(StringView path, mode_t)
|
||||
{
|
||||
ByteString str = path;
|
||||
if (_mkdir(str.characters()) < 0)
|
||||
return Error::from_syscall("mkdir"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<int> openat(int, StringView, int, mode_t)
|
||||
{
|
||||
dbgln("Core::System::openat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<struct stat> fstatat(int, StringView, int)
|
||||
{
|
||||
dbgln("Core::System::fstatat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
6
Libraries/LibDNS/CMakeLists.txt
Normal file
6
Libraries/LibDNS/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
Message.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibDNS dns)
|
||||
target_link_libraries(LibDNS PRIVATE LibCore)
|
1177
Libraries/LibDNS/Message.cpp
Normal file
1177
Libraries/LibDNS/Message.cpp
Normal file
File diff suppressed because it is too large
Load diff
704
Libraries/LibDNS/Message.h
Normal file
704
Libraries/LibDNS/Message.h
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/MaybeOwned.h>
|
||||
#include <AK/RedBlackTree.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace DNS {
|
||||
namespace Messages {
|
||||
|
||||
struct DomainName;
|
||||
struct ParseContext {
|
||||
CountingStream& stream;
|
||||
NonnullOwnPtr<RedBlackTree<u16, DomainName>> pointers;
|
||||
};
|
||||
|
||||
enum class OpCode : u8;
|
||||
|
||||
struct Options {
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z |AD|CD| RCODE |
|
||||
constexpr inline static u16 QRMask = 0b1000000000000000;
|
||||
constexpr inline static u16 OpCodeMask = 0b0111100000000000;
|
||||
constexpr inline static u16 AuthoritativeAnswerMask = 0b0000010000000000;
|
||||
constexpr inline static u16 TruncatedMask = 0b0000001000000000;
|
||||
constexpr inline static u16 RecursionDesiredMask = 0b0000000100000000;
|
||||
constexpr inline static u16 RecursionAvailableMask = 0b0000000010000000;
|
||||
constexpr inline static u16 AuthenticatedDataMask = 0b0000000000100000;
|
||||
constexpr inline static u16 CheckingDisabledMask = 0b0000000000010000;
|
||||
constexpr inline static u16 ResponseCodeMask = 0b0000000000001111;
|
||||
|
||||
enum class ResponseCode : u16 {
|
||||
NoError = 0,
|
||||
FormatError = 1,
|
||||
ServerFailure = 2,
|
||||
NameError = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
};
|
||||
|
||||
void set_is_question(bool value) { raw = (raw & ~QRMask) | (value ? QRMask : 0); }
|
||||
void set_is_authoritative_answer(bool value) { raw = (raw & ~AuthoritativeAnswerMask) | (value ? AuthoritativeAnswerMask : 0); }
|
||||
void set_is_truncated(bool value) { raw = (raw & ~TruncatedMask) | (value ? TruncatedMask : 0); }
|
||||
void set_recursion_desired(bool value) { raw = (raw & ~RecursionDesiredMask) | (value ? RecursionDesiredMask : 0); }
|
||||
void set_recursion_available(bool value) { raw = (raw & ~RecursionAvailableMask) | (value ? RecursionAvailableMask : 0); }
|
||||
void set_response_code(ResponseCode code) { raw = (raw & ~ResponseCodeMask) | static_cast<u16>(code); }
|
||||
void set_checking_disabled(bool value) { raw = (raw & ~CheckingDisabledMask) | (value ? CheckingDisabledMask : 0); }
|
||||
void set_authenticated_data(bool value) { raw = (raw & ~AuthenticatedDataMask) | (value ? AuthenticatedDataMask : 0); }
|
||||
void set_op_code(OpCode code) { raw = (raw & ~OpCodeMask) | (static_cast<u16>(code) << 11); }
|
||||
|
||||
bool is_question() const { return (raw & QRMask) == 0; }
|
||||
bool is_authoritative_answer() const { return (raw & AuthoritativeAnswerMask) != 0; }
|
||||
bool is_truncated() const { return (raw & TruncatedMask) != 0; }
|
||||
bool recursion_desired() const { return (raw & RecursionDesiredMask) != 0; }
|
||||
bool recursion_available() const { return (raw & RecursionAvailableMask) != 0; }
|
||||
bool checking_disabled() const { return (raw & CheckingDisabledMask) != 0; }
|
||||
bool authenticated_data() const { return (raw & AuthenticatedDataMask) != 0; }
|
||||
ResponseCode response_code() const { return static_cast<ResponseCode>(raw & ResponseCodeMask); }
|
||||
OpCode op_code() const { return static_cast<OpCode>((raw & OpCodeMask) >> 11); }
|
||||
|
||||
String to_string() const;
|
||||
|
||||
NetworkOrdered<u16> raw { 0 };
|
||||
};
|
||||
StringView to_string(Options::ResponseCode);
|
||||
|
||||
struct Header {
|
||||
NetworkOrdered<u16> id;
|
||||
Options options;
|
||||
NetworkOrdered<u16> question_count;
|
||||
NetworkOrdered<u16> answer_count;
|
||||
NetworkOrdered<u16> authority_count;
|
||||
NetworkOrdered<u16> additional_count;
|
||||
};
|
||||
|
||||
struct DomainName {
|
||||
Vector<ByteString> labels;
|
||||
|
||||
static DomainName from_string(StringView);
|
||||
static ErrorOr<DomainName> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
String to_string() const;
|
||||
};
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4.
|
||||
enum class ResourceType : u16 {
|
||||
Reserved = 0, // [RFC6895]
|
||||
A = 1, // a host address [RFC1035]
|
||||
NS = 2, // an authoritative name server [RFC1035]
|
||||
MD = 3, // a mail destination (OBSOLETE - use MX) [RFC1035]
|
||||
MF = 4, // a mail forwarder (OBSOLETE - use MX) [RFC1035]
|
||||
CNAME = 5, // the canonical name for an alias [RFC1035]
|
||||
SOA = 6, // marks the start of a zone of authority [RFC1035]
|
||||
MB = 7, // a mailbox domain name (EXPERIMENTAL) [RFC1035]
|
||||
MG = 8, // a mail group member (EXPERIMENTAL) [RFC1035]
|
||||
MR = 9, // a mail rename domain name (EXPERIMENTAL) [RFC1035]
|
||||
NULL_ = 10, // a null RR (EXPERIMENTAL) [RFC1035]
|
||||
WKS = 11, // a well known service description [RFC1035]
|
||||
PTR = 12, // a domain name pointer [RFC1035]
|
||||
HINFO = 13, // host information [RFC1035]
|
||||
MINFO = 14, // mailbox or mail list information [RFC1035]
|
||||
MX = 15, // mail exchange [RFC1035]
|
||||
TXT = 16, // text strings [RFC1035]
|
||||
RP = 17, // for Responsible Person [RFC1183]
|
||||
AFSDB = 18, // for AFS Data Base location [RFC1183][RFC5864]
|
||||
X25 = 19, // for X.25 PSDN address [RFC1183]
|
||||
ISDN = 20, // for ISDN address [RFC1183]
|
||||
RT = 21, // for Route Through [RFC1183]
|
||||
NSAP = 22, // for NSAP address, NSAP style A record (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
NSAP_PTR = 23, // for domain name pointer, NSAP style (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
SIG = 24, // for security signature [RFC2536][RFC2931][RFC3110][RFC4034]
|
||||
KEY = 25, // for security key [RFC2536][RFC2539][RFC3110][RFC4034]
|
||||
PX = 26, // X.400 mail mapping information [RFC2163]
|
||||
GPOS = 27, // Geographical Position [RFC1712]
|
||||
AAAA = 28, // IP6 Address [RFC3596]
|
||||
LOC = 29, // Location Information [RFC1876]
|
||||
NXT = 30, // Next Domain (OBSOLETE) [RFC2535][RFC3755]
|
||||
EID = 31, // Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
NIMLOC = 32, // Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
SRV = 33, // Server Selection [1][RFC2782]
|
||||
ATMA = 34, // ATM Address "[ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]"
|
||||
NAPTR = 35, // Naming Authority Pointer [RFC3403]
|
||||
KX = 36, // Key Exchanger [RFC2230]
|
||||
CERT = 37, // CERT [RFC4398]
|
||||
A6 = 38, // A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563]
|
||||
DNAME = 39, // DNAME [RFC6672]
|
||||
SINK = 40, // SINK [Donald_E_Eastlake][draft-eastlake-kitchen-sink]
|
||||
OPT = 41, // OPT [RFC3225][RFC6891]
|
||||
APL = 42, // APL [RFC3123]
|
||||
DS = 43, // Delegation Signer [RFC4034]
|
||||
SSHFP = 44, // SSH Key Fingerprint [RFC4255]
|
||||
IPSECKEY = 45, // IPSECKEY [RFC4025]
|
||||
RRSIG = 46, // RRSIG [RFC4034]
|
||||
NSEC = 47, // NSEC [RFC4034][RFC9077]
|
||||
DNSKEY = 48, // DNSKEY [RFC4034]
|
||||
DHCID = 49, // DHCID [RFC4701]
|
||||
NSEC3 = 50, // NSEC3 [RFC5155][RFC9077]
|
||||
NSEC3PARAM = 51, // NSEC3PARAM [RFC5155]
|
||||
TLSA = 52, // TLSA [RFC6698]
|
||||
SMIMEA = 53, // S/MIME cert association [RFC8162]
|
||||
HIP = 55, // Host Identity Protocol [RFC8005]
|
||||
NINFO = 56, // NINFO [Jim_Reid]
|
||||
RKEY = 57, // RKEY [Jim_Reid]
|
||||
TALINK = 58, // Trust Anchor LINK [Wouter_Wijngaards]
|
||||
CDS = 59, // Child DS [RFC7344]
|
||||
CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS [RFC7344]
|
||||
OPENPGPKEY = 61, // OpenPGP Key [RFC7929]
|
||||
CSYNC = 62, // Child-To-Parent Synchronization [RFC7477]
|
||||
ZONEMD = 63, // Message Digest Over Zone Data [RFC8976]
|
||||
SVCB = 64, // General-purpose service binding [RFC9460]
|
||||
HTTPS = 65, // SVCB-compatible type for use with HTTP [RFC9460]
|
||||
SPF = 99, // [RFC7208]
|
||||
UINFO = 100, // [IANA-Reserved]
|
||||
UID = 101, // [IANA-Reserved]
|
||||
GID = 102, // [IANA-Reserved]
|
||||
UNSPEC = 103, // [IANA-Reserved]
|
||||
NID = 104, // [RFC6742]
|
||||
L32 = 105, // [RFC6742]
|
||||
L64 = 106, // [RFC6742]
|
||||
LP = 107, // [RFC6742]
|
||||
EUI48 = 108, // an EUI-48 address [RFC7043]
|
||||
EUI64 = 109, // an EUI-64 address [RFC7043]
|
||||
NXNAME = 128, // NXDOMAIN indicator for Compact Denial of Existence [draft-ietf-dnsop-compact-denial-of-existence-04]
|
||||
TKEY = 249, // Transaction Key [RFC2930]
|
||||
TSIG = 250, // Transaction Signature [RFC8945]
|
||||
IXFR = 251, // incremental transfer [RFC1995]
|
||||
AXFR = 252, // transfer of an entire zone [RFC1035][RFC5936]
|
||||
MAILB = 253, // mailbox-related RRs (MB, MG or MR) [RFC1035]
|
||||
MAILA = 254, // mail agent RRs (OBSOLETE - see MX) [RFC1035]
|
||||
ANY = 255, // A request for some or all records the server has available [RFC1035][RFC6895][RFC8482]
|
||||
URI = 256, // URI [RFC7553]
|
||||
CAA = 257, // Certification Authority Restriction [RFC8659]
|
||||
AVC = 258, // Application Visibility and Control [Wolfgang_Riedel]
|
||||
DOA = 259, // Digital Object Architecture [draft-durand-doa-over-dns]
|
||||
AMTRELAY = 260, // Automatic Multicast Tunneling Relay [RFC8777]
|
||||
RESINFO = 261, // Resolver Information as Key/Value Pairs [RFC9606]
|
||||
WALLET = 262, // Public wallet address [Paul_Hoffman]
|
||||
CLA = 263, // BP Convergence Layer Adapter [draft-johnson-dns-ipn-cla-07]
|
||||
IPN = 264, // BP Node Number [draft-johnson-dns-ipn-cla-07]
|
||||
TA = 32768, // DNSSEC Trust Authorities "[Sam_Weiler][Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]"
|
||||
DLV = 32769, // DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431]
|
||||
};
|
||||
StringView to_string(ResourceType);
|
||||
Optional<ResourceType> resource_type_from_string(StringView);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2.
|
||||
enum class Class : u16 {
|
||||
IN = 1, // the Internet [RFC1035]
|
||||
CH = 3, // the CHAOS class [Moon1981]
|
||||
HS = 4, // Hesiod [Dyer1987]
|
||||
};
|
||||
StringView to_string(Class);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-3.
|
||||
enum class OpCode : u8 {
|
||||
Query = 0, // a standard query (QUERY)
|
||||
IQuery = 1, // an inverse query (IQUERY)
|
||||
Status = 2, // a server status request (STATUS)
|
||||
Notify = 4, // NOTIFY
|
||||
Update = 5, // dynamic update (RFC 2136)
|
||||
DSO = 6, // DNS Stateful Operations (DSO) [RFC8490]
|
||||
Reserved = 7, // [RFC6895]
|
||||
ReservedMask = 15 // [RFC6895]
|
||||
};
|
||||
StringView to_string(OpCode);
|
||||
|
||||
namespace TLSA {
|
||||
|
||||
// Listings from IANA https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml.
|
||||
enum class CertUsage : u8 {
|
||||
CAConstraint = 0,
|
||||
ServiceCertificateConstraint = 1,
|
||||
TrustAnchorAssertion = 2,
|
||||
DomainIssuedCertificate = 3,
|
||||
Private = 255
|
||||
};
|
||||
enum class Selector : u8 {
|
||||
FullCertificate = 0,
|
||||
SubjectPublicKeyInfo = 1,
|
||||
Private = 255
|
||||
};
|
||||
enum class MatchingType : u8 {
|
||||
Full = 0,
|
||||
SHA256 = 1,
|
||||
SHA512 = 2,
|
||||
Private = 255
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DNSSEC {
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class Algorithm : u8 {
|
||||
RSAMD5 = 1, // RSA/MD5 [RFC4034][RFC3110]
|
||||
DSA = 3, // DSA/SHA-1 [RFC3755][RFC2536]
|
||||
RSASHA1 = 5, // RSA/SHA-1 [RFC3110]
|
||||
RSASHA1NSEC3SHA1 = 7, // [RFC5155]
|
||||
RSASHA256 = 8, // RSA/SHA-256 [RFC5702]
|
||||
RSASHA512 = 10, // RSA/SHA-512 [RFC5702]
|
||||
ECDSAP256SHA256 = 13, // ECDSA Curve P-256 with SHA-256 [RFC6605]
|
||||
ECDSAP384SHA384 = 14, // ECDSA Curve P-384 with SHA-384 [RFC6605]
|
||||
ED25519 = 15, // Ed25519 [RFC8080]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(Algorithm algorithm)
|
||||
{
|
||||
switch (algorithm) {
|
||||
case Algorithm::RSAMD5:
|
||||
return "RSAMD5"sv;
|
||||
case Algorithm::DSA:
|
||||
return "DSA"sv;
|
||||
case Algorithm::RSASHA1:
|
||||
return "RSASHA1"sv;
|
||||
case Algorithm::RSASHA1NSEC3SHA1:
|
||||
return "RSASHA1NSEC3SHA1"sv;
|
||||
case Algorithm::RSASHA256:
|
||||
return "RSASHA256"sv;
|
||||
case Algorithm::RSASHA512:
|
||||
return "RSASHA512"sv;
|
||||
case Algorithm::ECDSAP256SHA256:
|
||||
return "ECDSAP256SHA256"sv;
|
||||
case Algorithm::ECDSAP384SHA384:
|
||||
return "ECDSAP384SHA384"sv;
|
||||
case Algorithm::ED25519:
|
||||
return "ED25519"sv;
|
||||
case Algorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class DigestType : u8 {
|
||||
SHA1 = 1, // SHA-1 [RFC3658]
|
||||
SHA256 = 2, // SHA-256 [RFC4509]
|
||||
GOST3411 = 3, // GOST R 34.11-94 [RFC5933]
|
||||
SHA384 = 4, // SHA-384 [RFC6605]
|
||||
SHA512 = 5, // SHA-512 [RFC6605]
|
||||
SHA224 = 6, // SHA-224 [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(DigestType digest_type)
|
||||
{
|
||||
switch (digest_type) {
|
||||
case DigestType::SHA1:
|
||||
return "SHA1"sv;
|
||||
case DigestType::SHA256:
|
||||
return "SHA256"sv;
|
||||
case DigestType::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case DigestType::SHA384:
|
||||
return "SHA384"sv;
|
||||
case DigestType::SHA512:
|
||||
return "SHA512"sv;
|
||||
case DigestType::SHA224:
|
||||
return "SHA224"sv;
|
||||
case DigestType::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class NSEC3HashAlgorithm : u8 {
|
||||
SHA1 = 1, // [RFC5155]
|
||||
SHA256 = 2, // [RFC6605]
|
||||
GOST3411 = 3, // [RFC5933]
|
||||
SHA384 = 4, // [RFC6605]
|
||||
SHA512 = 5, // [RFC6605]
|
||||
SHA224 = 6, // [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(NSEC3HashAlgorithm hash_algorithm)
|
||||
{
|
||||
switch (hash_algorithm) {
|
||||
case NSEC3HashAlgorithm::SHA1:
|
||||
return "SHA1"sv;
|
||||
case NSEC3HashAlgorithm::SHA256:
|
||||
return "SHA256"sv;
|
||||
case NSEC3HashAlgorithm::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case NSEC3HashAlgorithm::SHA384:
|
||||
return "SHA384"sv;
|
||||
case NSEC3HashAlgorithm::SHA512:
|
||||
return "SHA512"sv;
|
||||
case NSEC3HashAlgorithm::SHA224:
|
||||
return "SHA224"sv;
|
||||
case NSEC3HashAlgorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Question {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
|
||||
static ErrorOr<Question> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
};
|
||||
|
||||
namespace Records {
|
||||
|
||||
struct A {
|
||||
IPv4Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::A;
|
||||
static ErrorOr<A> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct AAAA {
|
||||
IPv6Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::AAAA;
|
||||
static ErrorOr<AAAA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct TXT {
|
||||
ByteString content;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::TXT;
|
||||
static ErrorOr<TXT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("Text: '{}'", StringView { content }); }
|
||||
};
|
||||
struct CNAME {
|
||||
DomainName names;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CNAME;
|
||||
static ErrorOr<CNAME> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return names.to_string(); }
|
||||
};
|
||||
struct NS {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NS;
|
||||
static ErrorOr<NS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SOA {
|
||||
DomainName mname;
|
||||
DomainName rname;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SOA;
|
||||
static ErrorOr<SOA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("SOA MName: '{}', RName: '{}', Serial: {}, Refresh: {}, Retry: {}, Expire: {}, Minimum: {}", mname.to_string(), rname.to_string(), serial, refresh, retry, expire, minimum);
|
||||
}
|
||||
};
|
||||
struct MX {
|
||||
u16 preference;
|
||||
DomainName exchange;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::MX;
|
||||
static ErrorOr<MX> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("MX Preference: {}, Exchange: '{}'", preference, exchange.to_string()); }
|
||||
};
|
||||
struct PTR {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::PTR;
|
||||
static ErrorOr<PTR> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SRV {
|
||||
u16 priority;
|
||||
u16 weight;
|
||||
u16 port;
|
||||
DomainName target;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SRV;
|
||||
static ErrorOr<SRV> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("SRV Priority: {}, Weight: {}, Port: {}, Target: '{}'", priority, weight, port, target.to_string()); }
|
||||
};
|
||||
struct DNSKEY {
|
||||
u16 flags;
|
||||
u8 protocol;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
ByteBuffer public_key;
|
||||
|
||||
constexpr static inline u16 FlagSecureEntryPoint = 0b1000000000000000;
|
||||
constexpr static inline u16 FlagZoneKey = 0b0100000000000000;
|
||||
constexpr static inline u16 FlagRevoked = 0b0010000000000000;
|
||||
|
||||
constexpr bool is_secure_entry_point() const { return flags & FlagSecureEntryPoint; }
|
||||
constexpr bool is_zone_key() const { return flags & FlagZoneKey; }
|
||||
constexpr bool is_revoked() const { return flags & FlagRevoked; }
|
||||
constexpr bool is_key_signing_key() const { return is_secure_entry_point() && is_zone_key() && !is_revoked(); }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DNSKEY;
|
||||
static ErrorOr<DNSKEY> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("DNSKEY Flags: {}{}{}{}({}), Protocol: {}, Algorithm: {}, Public Key: {}",
|
||||
is_secure_entry_point() ? "sep "sv : ""sv,
|
||||
is_zone_key() ? "zone "sv : ""sv,
|
||||
is_revoked() ? "revoked "sv : ""sv,
|
||||
is_key_signing_key() ? "ksk "sv : ""sv,
|
||||
flags,
|
||||
protocol,
|
||||
DNSSEC::to_string(algorithm),
|
||||
TRY(encode_base64(public_key)));
|
||||
}
|
||||
};
|
||||
struct CDNSKEY : public DNSKEY {
|
||||
template<typename... Ts>
|
||||
CDNSKEY(Ts&&... args)
|
||||
: DNSKEY(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CDNSKEY;
|
||||
static ErrorOr<CDNSKEY> from_raw(ParseContext& raw) { return DNSKEY::from_raw(raw); }
|
||||
};
|
||||
struct DS {
|
||||
u16 key_tag;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
DNSSEC::DigestType digest_type;
|
||||
ByteBuffer digest;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DS;
|
||||
static ErrorOr<DS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "DS"_string; }
|
||||
};
|
||||
struct CDS : public DS {
|
||||
template<typename... Ts>
|
||||
CDS(Ts&&... args)
|
||||
: DS(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
static constexpr ResourceType type = ResourceType::CDS;
|
||||
static ErrorOr<CDS> from_raw(ParseContext& raw) { return DS::from_raw(raw); }
|
||||
};
|
||||
struct SIG {
|
||||
ResourceType type_covered;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
u8 label_count;
|
||||
u32 original_ttl;
|
||||
UnixDateTime expiration;
|
||||
UnixDateTime inception;
|
||||
u16 key_tag;
|
||||
DomainName signers_name;
|
||||
ByteBuffer signature;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SIG;
|
||||
static ErrorOr<SIG> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
struct RRSIG : public SIG {
|
||||
template<typename... Ts>
|
||||
RRSIG(Ts&&... args)
|
||||
: SIG(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::RRSIG;
|
||||
static ErrorOr<RRSIG> from_raw(ParseContext& raw) { return SIG::from_raw(raw); }
|
||||
};
|
||||
struct NSEC {
|
||||
DomainName next_domain_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC;
|
||||
static ErrorOr<NSEC> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC"_string; }
|
||||
};
|
||||
struct NSEC3 {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
DomainName next_hashed_owner_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3;
|
||||
static ErrorOr<NSEC3> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3"_string; }
|
||||
};
|
||||
struct NSEC3PARAM {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
|
||||
constexpr static inline u8 FlagOptOut = 0b10000000;
|
||||
|
||||
constexpr bool is_opt_out() const { return flags & FlagOptOut; }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3PARAM;
|
||||
static ErrorOr<NSEC3PARAM> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3PARAM"_string; }
|
||||
};
|
||||
struct TLSA {
|
||||
Messages::TLSA::CertUsage cert_usage;
|
||||
Messages::TLSA::Selector selector;
|
||||
Messages::TLSA::MatchingType matching_type;
|
||||
ByteBuffer certificate_association_data;
|
||||
|
||||
static ErrorOr<TLSA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "TLSA"_string; }
|
||||
};
|
||||
struct HINFO {
|
||||
ByteString cpu;
|
||||
ByteString os;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::HINFO;
|
||||
static ErrorOr<HINFO> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("HINFO CPU: '{}', OS: '{}'", StringView { cpu }, StringView { os }); }
|
||||
};
|
||||
struct OPT {
|
||||
struct Option {
|
||||
u16 code;
|
||||
ByteBuffer data;
|
||||
};
|
||||
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | UDP Payload Size |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | Extended RCode | VER | ZZ |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |DO| Z |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | OPT-LEN / OPT-DATA...
|
||||
|
||||
NetworkOrdered<u16> udp_payload_size { 0 };
|
||||
NetworkOrdered<u32> extended_rcode_and_flags { 0 };
|
||||
Vector<Option> options;
|
||||
static constexpr u32 MaskExtendedRCode = 0b11111111000000000000000000000000;
|
||||
static constexpr u32 MaskVersion = 0b00000000111100000000000000000000;
|
||||
static constexpr u32 MaskDO = 0b00000000000000001000000000000000;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::OPT;
|
||||
|
||||
constexpr u8 extended_rcode() const { return (extended_rcode_and_flags & MaskExtendedRCode) >> 24; }
|
||||
constexpr u8 version() const { return (extended_rcode_and_flags & MaskVersion) >> 20; }
|
||||
constexpr bool dnssec_ok() const { return extended_rcode_and_flags & MaskDO; }
|
||||
|
||||
void set_extended_rcode(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskExtendedRCode) | (value << 24); }
|
||||
void set_version(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskVersion) | (value << 20); }
|
||||
void set_dnssec_ok(bool value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskDO) | (value ? MaskDO : 0); }
|
||||
|
||||
static ErrorOr<OPT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.appendff("OPT UDP Payload Size: {}, Extended RCode: {}, Version: {}, DNSSEC OK: {}", udp_payload_size, extended_rcode(), version(), dnssec_ok());
|
||||
for (auto& option : options)
|
||||
builder.appendff(", opt[{} = '{:hex-dump}']", option.code, option.data.bytes());
|
||||
|
||||
return builder.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using Record = Variant<
|
||||
Records::A,
|
||||
Records::AAAA,
|
||||
Records::TXT,
|
||||
Records::CNAME,
|
||||
Records::NS,
|
||||
Records::SOA,
|
||||
Records::MX,
|
||||
Records::PTR,
|
||||
Records::SRV,
|
||||
Records::DNSKEY,
|
||||
Records::CDNSKEY,
|
||||
Records::DS,
|
||||
Records::CDS,
|
||||
Records::RRSIG,
|
||||
Records::NSEC,
|
||||
Records::NSEC3,
|
||||
Records::NSEC3PARAM,
|
||||
Records::TLSA,
|
||||
Records::HINFO,
|
||||
Records::OPT,
|
||||
// TODO: Add more records.
|
||||
ByteBuffer>; // Fallback for unknown records.
|
||||
|
||||
struct ResourceRecord {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
u32 ttl;
|
||||
Record record;
|
||||
Optional<ByteBuffer> raw;
|
||||
|
||||
static ErrorOr<ResourceRecord> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
|
||||
struct ZoneAuthority {
|
||||
DomainName name;
|
||||
ByteString admin_mailbox;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum_ttl;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Header header;
|
||||
Vector<Question> questions;
|
||||
Vector<ResourceRecord> answers;
|
||||
Vector<ResourceRecord> authorities;
|
||||
Vector<ResourceRecord> additional_records;
|
||||
|
||||
static ErrorOr<Message> from_raw(ParseContext&);
|
||||
static ErrorOr<Message> from_raw(Stream&);
|
||||
ErrorOr<size_t> to_raw(ByteBuffer&) const;
|
||||
|
||||
ErrorOr<String> format_for_log() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
488
Libraries/LibDNS/Resolver.h
Normal file
488
Libraries/LibDNS/Resolver.h
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/AtomicRefCounted.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibDNS/Message.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/RWLockProtected.h>
|
||||
|
||||
namespace DNS {
|
||||
class Resolver;
|
||||
|
||||
class LookupResult : public AtomicRefCounted<LookupResult>
|
||||
, public Weakable<LookupResult> {
|
||||
public:
|
||||
explicit LookupResult(Messages::DomainName name)
|
||||
: m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
Vector<Variant<IPv4Address, IPv6Address>> cached_addresses() const
|
||||
{
|
||||
Vector<Variant<IPv4Address, IPv6Address>> result;
|
||||
for (auto& re : m_cached_records) {
|
||||
re.record.record.visit(
|
||||
[&](Messages::Records::A const& a) { result.append(a.address); },
|
||||
[&](Messages::Records::AAAA const& aaaa) { result.append(aaaa.address); },
|
||||
[](auto&) {});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void check_expiration()
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
auto now = Core::DateTime::now();
|
||||
for (size_t i = 0; i < m_cached_records.size();) {
|
||||
auto& record = m_cached_records[i];
|
||||
if (record.expiration.has_value() && record.expiration.value() < now) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Removing expired record for {}", m_name.to_string());
|
||||
m_cached_records.remove(i);
|
||||
} else {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Keeping record for {} (expires in {})", m_name.to_string(), record.expiration.has_value() ? record.expiration.value().to_string() : "never"_string);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cached_records.is_empty())
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
void add_record(Messages::ResourceRecord record)
|
||||
{
|
||||
m_valid = true;
|
||||
auto expiration = record.ttl > 0 ? Optional<Core::DateTime>(Core::DateTime::from_timestamp(Core::DateTime::now().timestamp() + record.ttl)) : OptionalNone();
|
||||
m_cached_records.append({ move(record), move(expiration) });
|
||||
}
|
||||
|
||||
Vector<Messages::ResourceRecord> records() const
|
||||
{
|
||||
Vector<Messages::ResourceRecord> result;
|
||||
for (auto& re : m_cached_records)
|
||||
result.append(re.record);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_record_of_type(Messages::ResourceType type, bool later = false) const
|
||||
{
|
||||
if (later && m_desired_types.contains(type))
|
||||
return true;
|
||||
|
||||
for (auto const& re : m_cached_records) {
|
||||
if (re.record.type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void will_add_record_of_type(Messages::ResourceType type) { m_desired_types.set(type); }
|
||||
|
||||
void set_id(u16 id) { m_id = id; }
|
||||
u16 id() { return m_id; }
|
||||
|
||||
bool is_valid() const { return m_valid; }
|
||||
Messages::DomainName const& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
bool m_valid { false };
|
||||
Messages::DomainName m_name;
|
||||
struct RecordWithExpiration {
|
||||
Messages::ResourceRecord record;
|
||||
Optional<Core::DateTime> expiration;
|
||||
};
|
||||
Vector<RecordWithExpiration> m_cached_records;
|
||||
HashTable<Messages::ResourceType> m_desired_types;
|
||||
u16 m_id { 0 };
|
||||
};
|
||||
|
||||
class Resolver {
|
||||
public:
|
||||
enum class ConnectionMode {
|
||||
TCP,
|
||||
UDP,
|
||||
};
|
||||
|
||||
struct SocketResult {
|
||||
MaybeOwned<Core::Socket> socket;
|
||||
ConnectionMode mode;
|
||||
};
|
||||
|
||||
Resolver(Function<ErrorOr<SocketResult>()> create_socket)
|
||||
: m_pending_lookups(make<RedBlackTree<u16, PendingLookup>>())
|
||||
, m_create_socket(move(create_socket))
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
auto add_v4v6_entry = [&cache](StringView name_string, IPv4Address v4, IPv6Address v6) {
|
||||
auto name = Messages::DomainName::from_string(name_string);
|
||||
auto ptr = make_ref_counted<LookupResult>(name);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::A);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::AAAA);
|
||||
cache.set(name_string, ptr);
|
||||
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { v4 }, .raw = {} });
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { v6 }, .raw = {} });
|
||||
};
|
||||
|
||||
add_v4v6_entry("localhost"sv, { 127, 0, 0, 1 }, IPv6Address::loopback());
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<Empty>> when_socket_ready()
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
m_socket_ready_promises.append(promise);
|
||||
if (has_connection(false)) {
|
||||
promise->resolve({});
|
||||
return promise;
|
||||
}
|
||||
|
||||
if (!has_connection())
|
||||
promise->reject(Error::from_string_literal("Failed to create socket"));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void reset_connection()
|
||||
{
|
||||
m_socket.with_write_locked([&](auto& socket) { socket = {}; });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return expect_cached(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
auto result = lookup_in_cache(name, class_, desired_types);
|
||||
VERIFY(!result.is_null());
|
||||
dbgln_if(DNS_DEBUG, "DNS::expect({}) -> OK", name);
|
||||
return *result;
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup_in_cache(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
return m_cache.with_read_locked([&](auto& cache) -> RefPtr<LookupResult const> {
|
||||
auto it = cache.find(name);
|
||||
if (it == cache.end())
|
||||
return {};
|
||||
|
||||
auto& result = *it->value;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!result.has_record_of_type(type))
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup(move(name), class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
flush_cache();
|
||||
|
||||
auto promise = Core::Promise<NonnullRefPtr<LookupResult const>>::construct();
|
||||
|
||||
if (auto maybe_ipv4 = IPv4Address::from_string(name); maybe_ipv4.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::A)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { maybe_ipv4.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_ipv6 = IPv6Address::from_string(name); maybe_ipv6.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::AAAA)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { maybe_ipv6.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto result = lookup_in_cache(name, class_, desired_types)) {
|
||||
promise->resolve(result.release_nonnull());
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto domain_name = Messages::DomainName::from_string(name);
|
||||
|
||||
if (!has_connection()) {
|
||||
// Use system resolver
|
||||
// FIXME: Use an underlying resolver instead.
|
||||
dbgln_if(DNS_DEBUG, "Not ready to resolve, using system resolver and skipping cache for {}", name);
|
||||
auto record_or_error = Core::Socket::resolve_host(name, Core::Socket::SocketType::Stream);
|
||||
if (record_or_error.is_error()) {
|
||||
promise->reject(record_or_error.release_error());
|
||||
return promise;
|
||||
}
|
||||
auto result = make_ref_counted<LookupResult>(domain_name);
|
||||
auto record = record_or_error.release_value();
|
||||
record.visit(
|
||||
[&](IPv4Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { address }, .raw = {} });
|
||||
},
|
||||
[&](IPv6Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { address }, .raw = {} });
|
||||
});
|
||||
promise->resolve(result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto already_in_cache = false;
|
||||
auto result = m_cache.with_write_locked([&](auto& cache) -> NonnullRefPtr<LookupResult> {
|
||||
auto existing = [&] -> RefPtr<LookupResult> {
|
||||
if (cache.contains(name)) {
|
||||
auto ptr = *cache.get(name);
|
||||
|
||||
already_in_cache = true;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!ptr->has_record_of_type(type, true)) {
|
||||
already_in_cache = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (existing)
|
||||
return *existing;
|
||||
|
||||
auto ptr = make_ref_counted<LookupResult>(domain_name);
|
||||
for (auto const& type : desired_types)
|
||||
ptr->will_add_record_of_type(type);
|
||||
cache.set(name, ptr);
|
||||
return ptr;
|
||||
});
|
||||
|
||||
Optional<u16> cached_result_id;
|
||||
if (already_in_cache) {
|
||||
auto id = result->id();
|
||||
cached_result_id = id;
|
||||
auto existing_promise = m_pending_lookups.with_write_locked([&](auto& lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
if (auto* lookup = lookups->find(id))
|
||||
return lookup->promise;
|
||||
return nullptr;
|
||||
});
|
||||
if (existing_promise)
|
||||
return existing_promise.release_nonnull();
|
||||
|
||||
promise->resolve(*result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Messages::Message query;
|
||||
m_pending_lookups.with_read_locked([&](auto& lookups) {
|
||||
do
|
||||
fill_with_random({ &query.header.id, sizeof(query.header.id) });
|
||||
while (lookups->find(query.header.id) != nullptr);
|
||||
});
|
||||
query.header.question_count = max(1u, desired_types.size());
|
||||
query.header.options.set_response_code(Messages::Options::ResponseCode::NoError);
|
||||
query.header.options.set_recursion_desired(true);
|
||||
query.header.options.set_op_code(Messages::OpCode::Query);
|
||||
for (auto const& type : desired_types) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = domain_name,
|
||||
.type = type,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
if (query.questions.is_empty()) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = Messages::DomainName::from_string(name),
|
||||
.type = Messages::ResourceType::A,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
auto cached_entry = m_pending_lookups.with_write_locked([&](auto& pending_lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
// One more try to make sure we're not overwriting an existing lookup
|
||||
if (cached_result_id.has_value()) {
|
||||
if (auto* lookup = pending_lookups->find(*cached_result_id))
|
||||
return lookup->promise;
|
||||
}
|
||||
|
||||
pending_lookups->insert(query.header.id, { query.header.id, name, result->make_weak_ptr(), promise });
|
||||
return nullptr;
|
||||
});
|
||||
if (cached_entry) {
|
||||
dbgln_if(DNS_DEBUG, "DNS::lookup({}) -> Already in cache", name);
|
||||
return cached_entry.release_nonnull();
|
||||
}
|
||||
|
||||
ByteBuffer query_bytes;
|
||||
MUST(query.to_raw(query_bytes));
|
||||
|
||||
if (m_mode == ConnectionMode::TCP) {
|
||||
auto original_query_bytes = query_bytes;
|
||||
query_bytes = MUST(ByteBuffer::create_uninitialized(query_bytes.size() + sizeof(u16)));
|
||||
NetworkOrdered<u16> size = original_query_bytes.size();
|
||||
query_bytes.overwrite(0, &size, sizeof(size));
|
||||
query_bytes.overwrite(sizeof(size), original_query_bytes.data(), original_query_bytes.size());
|
||||
}
|
||||
|
||||
auto write_result = m_socket.with_write_locked([&](auto& socket) {
|
||||
return (*socket)->write_until_depleted(query_bytes.bytes());
|
||||
});
|
||||
if (write_result.is_error()) {
|
||||
promise->reject(write_result.release_error());
|
||||
return promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingLookup {
|
||||
u16 id { 0 };
|
||||
ByteString name;
|
||||
WeakPtr<LookupResult> result;
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> promise;
|
||||
};
|
||||
|
||||
ErrorOr<Messages::Message> parse_one_message()
|
||||
{
|
||||
if (m_mode == ConnectionMode::UDP)
|
||||
return m_socket.with_write_locked([&](auto& socket) { return Messages::Message::from_raw(**socket); });
|
||||
|
||||
return m_socket.with_write_locked([&](auto& socket) -> ErrorOr<Messages::Message> {
|
||||
if (!TRY((*socket)->can_read_without_blocking()))
|
||||
return Error::from_errno(EAGAIN);
|
||||
|
||||
auto size = TRY((*socket)->template read_value<NetworkOrdered<u16>>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(size));
|
||||
TRY((*socket)->read_until_filled(buffer));
|
||||
FixedMemoryStream stream { static_cast<ReadonlyBytes>(buffer) };
|
||||
return Messages::Message::from_raw(stream);
|
||||
});
|
||||
}
|
||||
|
||||
void process_incoming_messages()
|
||||
{
|
||||
while (true) {
|
||||
if (auto result = m_socket.with_read_locked([](auto& socket) { return (*socket)->can_read_without_blocking(); }); result.is_error() || !result.value())
|
||||
break;
|
||||
auto message_or_err = parse_one_message();
|
||||
if (message_or_err.is_error()) {
|
||||
if (!message_or_err.error().is_errno() || message_or_err.error().code() != EAGAIN)
|
||||
dbgln("DNS: Failed to receive message: {}", message_or_err.error());
|
||||
break;
|
||||
}
|
||||
|
||||
auto message = message_or_err.release_value();
|
||||
auto result = m_pending_lookups.with_write_locked([&](auto& lookups) -> ErrorOr<void> {
|
||||
auto* lookup = lookups->find(message.header.id);
|
||||
if (!lookup)
|
||||
return Error::from_string_literal("No pending lookup found for this message");
|
||||
|
||||
if (lookup->result.is_null())
|
||||
return {}; // Message is a response to a lookup that's been purged from the cache, ignore it
|
||||
|
||||
auto result = lookup->result.strong_ref();
|
||||
for (auto& record : message.answers)
|
||||
result->add_record(move(record));
|
||||
|
||||
lookup->promise->resolve(*result);
|
||||
lookups->remove(message.header.id);
|
||||
return {};
|
||||
});
|
||||
if (result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Received a message with no pending lookup: {}", result.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_connection(bool attempt_restart = true)
|
||||
{
|
||||
auto result = m_socket.with_read_locked(
|
||||
[&](auto& socket) { return socket.has_value() && (*socket)->is_open(); });
|
||||
|
||||
if (attempt_restart && !result && !m_attempting_restart) {
|
||||
TemporaryChange change(m_attempting_restart, true);
|
||||
auto create_result = m_create_socket();
|
||||
if (create_result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Failed to create socket: {}", create_result.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [socket, mode] = MUST(move(create_result));
|
||||
set_socket(move(socket), mode);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_socket(MaybeOwned<Core::Socket> socket, ConnectionMode mode = ConnectionMode::UDP)
|
||||
{
|
||||
m_mode = mode;
|
||||
m_socket.with_write_locked([&](auto& s) {
|
||||
s = move(socket);
|
||||
(*s)->on_ready_to_read = [this] {
|
||||
process_incoming_messages();
|
||||
};
|
||||
(*s)->set_notifications_enabled(true);
|
||||
});
|
||||
|
||||
for (auto& promise : m_socket_ready_promises)
|
||||
promise->resolve({});
|
||||
|
||||
m_socket_ready_promises.clear();
|
||||
}
|
||||
|
||||
void flush_cache()
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
HashTable<ByteString> to_remove;
|
||||
for (auto& entry : cache) {
|
||||
entry.value->check_expiration();
|
||||
if (!entry.value->is_valid())
|
||||
to_remove.set(entry.key);
|
||||
}
|
||||
for (auto const& key : to_remove)
|
||||
cache.remove(key);
|
||||
});
|
||||
}
|
||||
|
||||
Threading::RWLockProtected<HashMap<ByteString, NonnullRefPtr<LookupResult>>> m_cache;
|
||||
Threading::RWLockProtected<NonnullOwnPtr<RedBlackTree<u16, PendingLookup>>> m_pending_lookups;
|
||||
Threading::RWLockProtected<Optional<MaybeOwned<Core::Socket>>> m_socket;
|
||||
Function<ErrorOr<SocketResult>()> m_create_socket;
|
||||
bool m_attempting_restart { false };
|
||||
ConnectionMode m_mode { ConnectionMode::UDP };
|
||||
Vector<NonnullRefPtr<Core::Promise<Empty>>> m_socket_ready_promises;
|
||||
};
|
||||
|
||||
}
|
|
@ -10,8 +10,6 @@
|
|||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC)
|
||||
# include <sys/disk.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
# pragma GCC optimize("O3")
|
||||
#endif
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/Line.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/BitmapSequence.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <AK/Swift.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <ctype.h>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <AK/Format.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SIMD.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Math.h>
|
||||
#include <LibGfx/DeltaE.h>
|
||||
#include <math.h>
|
||||
|
|
|
@ -12,18 +12,13 @@
|
|||
|
||||
#include "DeprecatedPainter.h"
|
||||
#include "Bitmap.h"
|
||||
#include "Font/Font.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Stack.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(AK_COMPILER_GCC)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
|
@ -16,8 +15,6 @@
|
|||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/BoundingBox.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/IntegralMath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
||||
struct hb_font_t;
|
||||
|
||||
|
@ -85,7 +80,7 @@ public:
|
|||
virtual float width(StringView) const = 0;
|
||||
virtual float width(Utf8View const&) const = 0;
|
||||
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const = 0;
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
@ -37,7 +35,7 @@ public:
|
|||
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
||||
virtual float width(StringView) const override;
|
||||
virtual float width(Utf8View const&) const override;
|
||||
virtual FlyString family() const override { return m_typeface->family(); }
|
||||
virtual FlyString const& family() const override { return m_typeface->family(); }
|
||||
|
||||
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <core/SkTypeface.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontData.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
@ -47,7 +45,7 @@ public:
|
|||
virtual u32 glyph_count() const = 0;
|
||||
virtual u16 units_per_em() const = 0;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
virtual u16 weight() const = 0;
|
||||
virtual u16 width() const = 0;
|
||||
virtual u8 slope() const = 0;
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
#include <AK/LsanSuppressions.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Font/TypefaceSkia.h>
|
||||
|
||||
#include <core/SkData.h>
|
||||
#include <core/SkFontMgr.h>
|
||||
#include <core/SkRefCnt.h>
|
||||
#include <core/SkTypeface.h>
|
||||
#ifndef AK_OS_ANDROID
|
||||
# include <ports/SkFontMgr_fontconfig.h>
|
||||
|
@ -114,7 +112,7 @@ void TypefaceSkia::populate_glyph_page(GlyphPage& glyph_page, size_t page_index)
|
|||
}
|
||||
}
|
||||
|
||||
FlyString TypefaceSkia::family() const
|
||||
FlyString const& TypefaceSkia::family() const
|
||||
{
|
||||
if (!m_family.has_value()) {
|
||||
SkString family_name;
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
virtual u32 glyph_count() const override;
|
||||
virtual u16 units_per_em() const override;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override;
|
||||
virtual FlyString family() const override;
|
||||
virtual FlyString const& family() const override;
|
||||
virtual u16 weight() const override;
|
||||
virtual u16 width() const override;
|
||||
virtual u8 slope() const override;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
class Profile;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <LibGfx/ICC/Profile.h>
|
||||
#include <LibGfx/ICC/Tags.h>
|
||||
#include <LibGfx/ICC/WellKnownProfiles.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <LibCompress/Lzw.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||
#include <LibGfx/ImageFormats/PNGLoader.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Error.h>
|
||||
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
|
||||
#include <jxl/decode.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <AK/LEB128.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/ImageFormats/TinyVGLoader.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCompress/DeflateTables.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/WebPSharedLossless.h>
|
||||
|
|
|
@ -13,20 +13,9 @@
|
|||
#include <LibGfx/PathSkia.h>
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkBlurTypes.h>
|
||||
#include <core/SkCanvas.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkMaskFilter.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathBuilder.h>
|
||||
#include <core/SkRRect.h>
|
||||
#include <core/SkSurface.h>
|
||||
#include <effects/SkGradientShader.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <gpu/GrDirectContext.h>
|
||||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <pathops/SkPathOps.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
|
||||
|
@ -16,9 +15,7 @@
|
|||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendContext.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendSurface.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlDirectContext.h>
|
||||
#endif
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/PathSkia.h>
|
||||
#include <core/SkContourMeasure.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <core/SkFont.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathMeasure.h>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "TextLayout.h"
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -7,16 +7,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Utf32View.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/FontCascadeList.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -15,6 +15,19 @@ public:
|
|||
HeaderMap() = default;
|
||||
~HeaderMap() = default;
|
||||
|
||||
HeaderMap(Vector<Header> headers)
|
||||
: m_headers(move(headers))
|
||||
{
|
||||
for (auto& header : m_headers)
|
||||
m_map.set(header.name, header.value);
|
||||
}
|
||||
|
||||
HeaderMap(HeaderMap const&) = default;
|
||||
HeaderMap(HeaderMap&&) = default;
|
||||
|
||||
HeaderMap& operator=(HeaderMap const&) = default;
|
||||
HeaderMap& operator=(HeaderMap&&) = default;
|
||||
|
||||
void set(ByteString name, ByteString value)
|
||||
{
|
||||
m_map.set(name, value);
|
||||
|
@ -56,10 +69,7 @@ template<>
|
|||
inline ErrorOr<HTTP::HeaderMap> decode(Decoder& decoder)
|
||||
{
|
||||
auto headers = TRY(decoder.decode<Vector<HTTP::Header>>());
|
||||
HTTP::HeaderMap header_map;
|
||||
for (auto& header : headers)
|
||||
header_map.set(move(header.name), move(header.value));
|
||||
return header_map;
|
||||
return HTTP::HeaderMap { move(headers) };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ static double parse_date_string(VM& vm, ByteString const& date_string)
|
|||
"%d%t%b%t%Y%t%R"sv, // "01 Jan 2000 08:00"
|
||||
"%A,%t%B%t%e,%t%Y,%t%R%t%Z"sv, // "Tuesday, October 29, 2024, 18:00 UTC"
|
||||
"%B%t%d%t%Y%t%T%t%z"sv, // "November 19 2024 00:00:00 +0900"
|
||||
"%a%t%b%t%e%t%Y"sv // "Wed Nov 20 2024"
|
||||
};
|
||||
|
||||
for (auto const& format : extra_formats) {
|
||||
|
|
|
@ -41,6 +41,7 @@ test("basic functionality", () => {
|
|||
expect(Date.parse("01 February 2013")).toBe(1359698400000);
|
||||
expect(Date.parse("Tuesday, October 29, 2024, 18:00 UTC")).toBe(1730224800000);
|
||||
expect(Date.parse("November 19 2024 00:00:00 +0900")).toBe(1731942000000);
|
||||
expect(Date.parse("Wed Nov 20 2024")).toBe(1732082400000);
|
||||
|
||||
// FIXME: Create a scoped time zone helper when bytecode supports the `using` declaration.
|
||||
setTimeZone(originalTimeZone);
|
||||
|
|
|
@ -123,7 +123,7 @@ void Request::set_up_internal_stream_data(DataReceived on_data_available)
|
|||
};
|
||||
|
||||
m_internal_stream_data->on_finish = [this, user_on_finish = move(user_on_finish)]() {
|
||||
if (!m_internal_stream_data->user_finish_called && m_internal_stream_data->read_stream->is_eof()) {
|
||||
if (!m_internal_stream_data->user_finish_called && (!m_internal_stream_data->read_stream || m_internal_stream_data->read_stream->is_eof())) {
|
||||
m_internal_stream_data->user_finish_called = true;
|
||||
user_on_finish(m_internal_stream_data->total_size, m_internal_stream_data->network_error);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,29 @@ ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, u16 port,
|
|||
return tls_socket;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(Core::SocketAddress address, ByteString const& host, Options options)
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
OwnPtr<Core::Socket> tcp_socket = TRY(Core::TCPSocket::connect(address));
|
||||
TRY(tcp_socket->set_blocking(false));
|
||||
auto tls_socket = make<TLSv12>(move(tcp_socket), move(options));
|
||||
tls_socket->set_sni(host);
|
||||
tls_socket->on_connected = [&] {
|
||||
promise->resolve({});
|
||||
};
|
||||
tls_socket->on_tls_error = [&](auto alert) {
|
||||
tls_socket->try_disambiguate_error();
|
||||
promise->reject(AK::Error::from_string_view(enum_to_string(alert)));
|
||||
};
|
||||
|
||||
TRY(promise->await());
|
||||
|
||||
tls_socket->on_tls_error = nullptr;
|
||||
tls_socket->on_connected = nullptr;
|
||||
tls_socket->m_context.should_expect_successful_read = true;
|
||||
return tls_socket;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<TLSv12>> TLSv12::connect(ByteString const& host, Core::Socket& underlying_stream, Options options)
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
|
@ -271,7 +294,8 @@ bool TLSv12::check_connection_state(bool read)
|
|||
|
||||
ErrorOr<bool> TLSv12::flush()
|
||||
{
|
||||
auto out_bytes = m_context.tls_buffer.bytes();
|
||||
ByteBuffer out = move(m_context.tls_buffer);
|
||||
auto out_bytes = out.bytes();
|
||||
|
||||
if (out_bytes.is_empty())
|
||||
return true;
|
||||
|
@ -298,17 +322,11 @@ ErrorOr<bool> TLSv12::flush()
|
|||
out_bytes = out_bytes.slice(written);
|
||||
} while (!out_bytes.is_empty());
|
||||
|
||||
if (out_bytes.is_empty() && !error.has_value()) {
|
||||
m_context.tls_buffer.clear();
|
||||
if (out_bytes.is_empty() && !error.has_value())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_context.send_retries++ == 10) {
|
||||
// drop the records, we can't send
|
||||
dbgln_if(TLS_DEBUG, "Dropping {} bytes worth of TLS records as max retries has been reached", m_context.tls_buffer.size());
|
||||
m_context.tls_buffer.clear();
|
||||
m_context.send_retries = 0;
|
||||
}
|
||||
if (!out_bytes.is_empty())
|
||||
dbgln("Dropping {} bytes worth of TLS records on the floor", out_bytes.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -357,6 +357,7 @@ public:
|
|||
|
||||
virtual void set_notifications_enabled(bool enabled) override { underlying_stream().set_notifications_enabled(enabled); }
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(Core::SocketAddress, ByteString const& host, Options = {});
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, u16 port, Options = {});
|
||||
static ErrorOr<NonnullOwnPtr<TLSv12>> connect(ByteString const& host, Core::Socket& underlying_stream, Options = {});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibWeb/Animations/Animation.h>
|
||||
|
|
|
@ -150,10 +150,12 @@ public:
|
|||
static float fill_opacity() { return 1.0f; }
|
||||
static CSS::FillRule fill_rule() { return CSS::FillRule::Nonzero; }
|
||||
static CSS::ClipRule clip_rule() { return CSS::ClipRule::Nonzero; }
|
||||
static CSS::LengthPercentage stroke_dashoffset() { return CSS::Length::make_px(0); }
|
||||
static CSS::StrokeLinecap stroke_linecap() { return CSS::StrokeLinecap::Butt; }
|
||||
static CSS::StrokeLinejoin stroke_linejoin() { return CSS::StrokeLinejoin::Miter; }
|
||||
static float stroke_miterlimit() { return 4.0f; }
|
||||
static float stroke_opacity() { return 1.0f; }
|
||||
static CSS::LengthPercentage stroke_width() { return CSS::Length::make_px(1); }
|
||||
static float stop_opacity() { return 1.0f; }
|
||||
static CSS::TextAnchor text_anchor() { return CSS::TextAnchor::Start; }
|
||||
static CSS::Length border_radius() { return Length::make_px(0); }
|
||||
|
@ -478,6 +480,7 @@ public:
|
|||
CSS::FillRule fill_rule() const { return m_inherited.fill_rule; }
|
||||
Optional<SVGPaint> const& stroke() const { return m_inherited.stroke; }
|
||||
float fill_opacity() const { return m_inherited.fill_opacity; }
|
||||
LengthPercentage const& stroke_dashoffset() const { return m_inherited.stroke_dashoffset; }
|
||||
CSS::StrokeLinecap stroke_linecap() const { return m_inherited.stroke_linecap; }
|
||||
CSS::StrokeLinejoin stroke_linejoin() const { return m_inherited.stroke_linejoin; }
|
||||
NumberOrCalculated stroke_miterlimit() const { return m_inherited.stroke_miterlimit; }
|
||||
|
@ -578,11 +581,12 @@ protected:
|
|||
CSS::FillRule fill_rule { InitialValues::fill_rule() };
|
||||
Optional<SVGPaint> stroke;
|
||||
float fill_opacity { InitialValues::fill_opacity() };
|
||||
LengthPercentage stroke_dashoffset { InitialValues::stroke_dashoffset() };
|
||||
CSS::StrokeLinecap stroke_linecap { InitialValues::stroke_linecap() };
|
||||
CSS::StrokeLinejoin stroke_linejoin { InitialValues::stroke_linejoin() };
|
||||
NumberOrCalculated stroke_miterlimit { InitialValues::stroke_miterlimit() };
|
||||
float stroke_opacity { InitialValues::stroke_opacity() };
|
||||
LengthPercentage stroke_width { Length::make_px(1) };
|
||||
LengthPercentage stroke_width { InitialValues::stroke_width() };
|
||||
CSS::TextAnchor text_anchor { InitialValues::text_anchor() };
|
||||
CSS::ClipRule clip_rule { InitialValues::clip_rule() };
|
||||
|
||||
|
@ -826,6 +830,7 @@ public:
|
|||
void set_stroke(SVGPaint value) { m_inherited.stroke = value; }
|
||||
void set_fill_rule(CSS::FillRule value) { m_inherited.fill_rule = value; }
|
||||
void set_fill_opacity(float value) { m_inherited.fill_opacity = value; }
|
||||
void set_stroke_dashoffset(LengthPercentage value) { m_inherited.stroke_dashoffset = value; }
|
||||
void set_stroke_linecap(CSS::StrokeLinecap value) { m_inherited.stroke_linecap = value; }
|
||||
void set_stroke_linejoin(CSS::StrokeLinejoin value) { m_inherited.stroke_linejoin = value; }
|
||||
void set_stroke_miterlimit(NumberOrCalculated value) { m_inherited.stroke_miterlimit = value; }
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
Optional<Percentage> ascent_override() const { return m_ascent_override; }
|
||||
Optional<Percentage> descent_override() const { return m_descent_override; }
|
||||
FontDisplay font_display() const { return m_font_display; }
|
||||
FlyString font_family() const { return m_font_family; }
|
||||
FlyString const& font_family() const { return m_font_family; }
|
||||
Optional<OrderedHashMap<FlyString, i64>> font_feature_settings() const { return m_font_feature_settings; }
|
||||
Optional<FlyString> font_language_override() const { return m_font_language_override; }
|
||||
Optional<FlyString> font_named_instance() const { return m_font_named_instance; }
|
||||
|
|
|
@ -204,15 +204,15 @@ Vector<Token> Tokenizer::tokenize(StringView input, StringView encoding)
|
|||
|
||||
auto decoded_input = MUST(decoder->to_utf8(input));
|
||||
|
||||
// OPTIMIZATION: If the input doesn't contain any CR or FF, we can skip the filtering
|
||||
bool const contains_cr_or_ff = [&] {
|
||||
for (auto byte : decoded_input.bytes()) {
|
||||
if (byte == '\r' || byte == '\f')
|
||||
// OPTIMIZATION: If the input doesn't contain any filterable characters, we can skip the filtering
|
||||
bool const contains_filterable = [&] {
|
||||
for (auto code_point : decoded_input.code_points()) {
|
||||
if (code_point == '\r' || code_point == '\f' || code_point == 0x00 || is_unicode_surrogate(code_point))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (!contains_cr_or_ff) {
|
||||
if (!contains_filterable) {
|
||||
return decoded_input;
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ Vector<Token> Tokenizer::tokenize(StringView input, StringView encoding)
|
|||
} else if (code_point == '\f') {
|
||||
builder.append('\n');
|
||||
// Replace any U+0000 NULL or surrogate code points in input with U+FFFD REPLACEMENT CHARACTER (<28>).
|
||||
} else if (code_point == 0x00 || (code_point >= 0xD800 && code_point <= 0xDFFF)) {
|
||||
} else if (code_point == 0x00 || is_unicode_surrogate(code_point)) {
|
||||
builder.append_code_point(REPLACEMENT_CHARACTER);
|
||||
} else {
|
||||
builder.append_code_point(code_point);
|
||||
|
|
|
@ -2420,6 +2420,18 @@
|
|||
"paint"
|
||||
]
|
||||
},
|
||||
"stroke-dashoffset": {
|
||||
"affects-layout": false,
|
||||
"animation-type": "by-computed-value",
|
||||
"inherited": true,
|
||||
"initial": "0",
|
||||
"valid-types": [
|
||||
"length [0,∞]",
|
||||
"number [0,∞]",
|
||||
"percentage [0,∞]"
|
||||
],
|
||||
"percentages-resolve-to": "length"
|
||||
},
|
||||
"stroke-linecap": {
|
||||
"affects-layout": false,
|
||||
"animation-type": "discrete",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/NonnullRawPtr.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
|
@ -85,12 +86,33 @@
|
|||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
struct FontFaceKey {
|
||||
NonnullRawPtr<FlyString const> family_name;
|
||||
int weight { 0 };
|
||||
int slope { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
// traits for FontFaceKey
|
||||
namespace Detail {
|
||||
template<>
|
||||
inline constexpr bool IsHashCompatible<Web::CSS::FontFaceKey, Web::CSS::OwnFontFaceKey> = true;
|
||||
template<>
|
||||
inline constexpr bool IsHashCompatible<Web::CSS::OwnFontFaceKey, Web::CSS::FontFaceKey> = true;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct Traits<Web::CSS::FontFaceKey> : public DefaultTraits<Web::CSS::FontFaceKey> {
|
||||
static unsigned hash(Web::CSS::FontFaceKey const& key) { return pair_int_hash(key.family_name.hash(), pair_int_hash(key.weight, key.slope)); }
|
||||
static unsigned hash(Web::CSS::FontFaceKey const& key) { return pair_int_hash(key.family_name->hash(), pair_int_hash(key.weight, key.slope)); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<Web::CSS::OwnFontFaceKey> : public DefaultTraits<Web::CSS::OwnFontFaceKey> {
|
||||
static unsigned hash(Web::CSS::OwnFontFaceKey const& key) { return pair_int_hash(key.family_name.hash(), pair_int_hash(key.weight, key.slope)); }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -124,6 +146,29 @@ FlyString const& MatchingRule::qualified_layer_name() const
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
OwnFontFaceKey::OwnFontFaceKey(FontFaceKey const& other)
|
||||
: family_name(other.family_name)
|
||||
, weight(other.weight)
|
||||
, slope(other.slope)
|
||||
{
|
||||
}
|
||||
|
||||
OwnFontFaceKey::operator FontFaceKey() const
|
||||
{
|
||||
return FontFaceKey {
|
||||
family_name,
|
||||
weight,
|
||||
slope
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool OwnFontFaceKey::operator==(FontFaceKey const& other) const
|
||||
{
|
||||
return family_name == other.family_name
|
||||
&& weight == other.weight
|
||||
&& slope == other.slope;
|
||||
}
|
||||
|
||||
static DOM::Element const* element_to_inherit_style_from(DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>);
|
||||
|
||||
StyleComputer::StyleComputer(DOM::Document& document)
|
||||
|
@ -1738,16 +1783,16 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::find_matching_font_weight_desc
|
|||
|
||||
// Partial implementation of the font-matching algorithm: https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm
|
||||
// FIXME: This should be replaced by the full CSS font selection algorithm.
|
||||
RefPtr<Gfx::FontCascadeList const> StyleComputer::font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const
|
||||
RefPtr<Gfx::FontCascadeList const> StyleComputer::font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const
|
||||
{
|
||||
// If a font family match occurs, the user agent assembles the set of font faces in that family and then
|
||||
// narrows the set to a single face using other font properties in the order given below.
|
||||
Vector<MatchingFontCandidate> matching_family_fonts;
|
||||
for (auto const& font_key_and_loader : m_loaded_fonts) {
|
||||
if (font_key_and_loader.key.family_name.equals_ignoring_ascii_case(key.family_name))
|
||||
if (font_key_and_loader.key.family_name.equals_ignoring_ascii_case(family_name))
|
||||
matching_family_fonts.empend(font_key_and_loader.key, const_cast<FontLoaderList*>(&font_key_and_loader.value));
|
||||
}
|
||||
Gfx::FontDatabase::the().for_each_typeface_with_family_name(key.family_name, [&](Gfx::Typeface const& typeface) {
|
||||
Gfx::FontDatabase::the().for_each_typeface_with_family_name(family_name, [&](Gfx::Typeface const& typeface) {
|
||||
matching_family_fonts.empend(
|
||||
FontFaceKey {
|
||||
.family_name = typeface.family(),
|
||||
|
@ -1764,24 +1809,24 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::font_matching_algorithm(FontFa
|
|||
// We don't have complete support of italic and oblique fonts, so matching on font-style can be simplified to:
|
||||
// If a matching slope is found, all faces which don't have that matching slope are excluded from the matching set.
|
||||
auto style_it = find_if(matching_family_fonts.begin(), matching_family_fonts.end(),
|
||||
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.slope == key.slope; });
|
||||
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.slope == slope; });
|
||||
if (style_it != matching_family_fonts.end()) {
|
||||
matching_family_fonts.remove_all_matching([&](auto const& matching_font_candidate) {
|
||||
return matching_font_candidate.key.slope != key.slope;
|
||||
return matching_font_candidate.key.slope != slope;
|
||||
});
|
||||
}
|
||||
// 3. font-weight is matched next.
|
||||
// If the desired weight is inclusively between 400 and 500, weights greater than or equal to the target weight
|
||||
// are checked in ascending order until 500 is hit and checked, followed by weights less than the target weight
|
||||
// in descending order, followed by weights greater than 500, until a match is found.
|
||||
if (key.weight >= 400 && key.weight <= 500) {
|
||||
if (weight >= 400 && weight <= 500) {
|
||||
auto it = find_if(matching_family_fonts.begin(), matching_family_fonts.end(),
|
||||
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= key.weight; });
|
||||
[&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= weight; });
|
||||
for (; it != matching_family_fonts.end() && it->key.weight <= 500; ++it) {
|
||||
if (auto found_font = it->font_with_point_size(font_size_in_pt))
|
||||
return found_font;
|
||||
}
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, weight, font_size_in_pt, false))
|
||||
return found_font;
|
||||
for (; it != matching_family_fonts.end(); ++it) {
|
||||
if (auto found_font = it->font_with_point_size(font_size_in_pt))
|
||||
|
@ -1790,18 +1835,18 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::font_matching_algorithm(FontFa
|
|||
}
|
||||
// If the desired weight is less than 400, weights less than or equal to the desired weight are checked in descending order
|
||||
// followed by weights above the desired weight in ascending order until a match is found.
|
||||
if (key.weight < 400) {
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, true))
|
||||
if (weight < 400) {
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, weight, font_size_in_pt, true))
|
||||
return found_font;
|
||||
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, weight, font_size_in_pt, false))
|
||||
return found_font;
|
||||
}
|
||||
// If the desired weight is greater than 500, weights greater than or equal to the desired weight are checked in ascending order
|
||||
// followed by weights below the desired weight in descending order until a match is found.
|
||||
if (key.weight > 500) {
|
||||
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, key.weight, font_size_in_pt, true))
|
||||
if (weight > 500) {
|
||||
if (auto found_font = find_matching_font_weight_ascending(matching_family_fonts, weight, font_size_in_pt, true))
|
||||
return found_font;
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, key.weight, font_size_in_pt, false))
|
||||
if (auto found_font = find_matching_font_weight_descending(matching_family_fonts, weight, font_size_in_pt, false))
|
||||
return found_font;
|
||||
}
|
||||
return {};
|
||||
|
@ -1963,7 +2008,6 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(
|
|||
.weight = weight,
|
||||
.slope = slope,
|
||||
};
|
||||
|
||||
auto result = Gfx::FontCascadeList::create();
|
||||
if (auto it = m_loaded_fonts.find(key); it != m_loaded_fonts.end()) {
|
||||
auto const& loaders = it->value;
|
||||
|
@ -1974,7 +2018,7 @@ RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(
|
|||
return result;
|
||||
}
|
||||
|
||||
if (auto found_font = font_matching_algorithm(key, font_size_in_pt); found_font && !found_font->is_empty()) {
|
||||
if (auto found_font = font_matching_algorithm(family, weight, slope, font_size_in_pt); found_font && !found_font->is_empty()) {
|
||||
return found_font;
|
||||
}
|
||||
|
||||
|
@ -2800,7 +2844,7 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
|
|||
} else {
|
||||
FontLoaderList loaders;
|
||||
loaders.append(move(loader));
|
||||
const_cast<StyleComputer&>(*this).m_loaded_fonts.set(key, move(loaders));
|
||||
const_cast<StyleComputer&>(*this).m_loaded_fonts.set(OwnFontFaceKey(key), move(loaders));
|
||||
}
|
||||
// Actual object owned by font loader list inside m_loaded_fonts, this isn't use-after-move/free
|
||||
return loader_ref;
|
||||
|
|
|
@ -101,13 +101,20 @@ struct MatchingRule {
|
|||
FlyString const& qualified_layer_name() const;
|
||||
};
|
||||
|
||||
struct FontFaceKey {
|
||||
struct FontFaceKey;
|
||||
|
||||
struct OwnFontFaceKey {
|
||||
explicit OwnFontFaceKey(FontFaceKey const& other);
|
||||
|
||||
operator FontFaceKey() const;
|
||||
|
||||
[[nodiscard]] u32 hash() const { return pair_int_hash(family_name.hash(), pair_int_hash(weight, slope)); }
|
||||
[[nodiscard]] bool operator==(OwnFontFaceKey const& other) const = default;
|
||||
[[nodiscard]] bool operator==(FontFaceKey const& other) const;
|
||||
|
||||
FlyString family_name;
|
||||
int weight { 0 };
|
||||
int slope { 0 };
|
||||
|
||||
[[nodiscard]] u32 hash() const { return pair_int_hash(family_name.hash(), pair_int_hash(weight, slope)); }
|
||||
[[nodiscard]] bool operator==(FontFaceKey const&) const = default;
|
||||
};
|
||||
|
||||
class FontLoader;
|
||||
|
@ -180,7 +187,7 @@ private:
|
|||
void compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const;
|
||||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const;
|
||||
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
|
||||
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_math_depth(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
|
@ -246,7 +253,7 @@ private:
|
|||
GC::Root<CSSStyleSheet> m_user_style_sheet;
|
||||
|
||||
using FontLoaderList = Vector<NonnullOwnPtr<FontLoader>>;
|
||||
HashMap<FontFaceKey, FontLoaderList> m_loaded_fonts;
|
||||
HashMap<OwnFontFaceKey, FontLoaderList> m_loaded_fonts;
|
||||
|
||||
Length::FontMetrics m_default_font_metrics;
|
||||
Length::FontMetrics m_root_element_font_metrics;
|
||||
|
|
|
@ -823,7 +823,13 @@ private:
|
|||
GC::Ptr<Document> m_associated_inert_template_document;
|
||||
GC::Ptr<Document> m_appropriate_template_contents_owner_document;
|
||||
|
||||
HTML::DocumentReadyState m_readiness { HTML::DocumentReadyState::Loading };
|
||||
// https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness
|
||||
// Each Document has a current document readiness, a string, initially "complete".
|
||||
// Spec Note: For Document objects created via the create and initialize a Document object algorithm, this will be
|
||||
// immediately reset to "loading" before any script can observe the value of document.readyState.
|
||||
// This default applies to other cases such as initial about:blank Documents or Documents without a
|
||||
// browsing context.
|
||||
HTML::DocumentReadyState m_readiness { HTML::DocumentReadyState::Complete };
|
||||
String m_content_type { "application/xml"_string };
|
||||
Optional<String> m_pragma_set_default_language;
|
||||
Optional<String> m_encoding;
|
||||
|
|
|
@ -235,10 +235,7 @@ WebIDL::ExceptionOr<void> Node::normalize()
|
|||
Vector<Text*> nodes;
|
||||
|
||||
auto* current_node = node.previous_sibling();
|
||||
while (current_node) {
|
||||
if (!current_node->is_text())
|
||||
break;
|
||||
|
||||
while (current_node && current_node->is_exclusive_text()) {
|
||||
nodes.append(static_cast<Text*>(current_node));
|
||||
current_node = current_node->previous_sibling();
|
||||
}
|
||||
|
@ -247,10 +244,7 @@ WebIDL::ExceptionOr<void> Node::normalize()
|
|||
nodes.reverse();
|
||||
|
||||
current_node = node.next_sibling();
|
||||
while (current_node) {
|
||||
if (!current_node->is_text())
|
||||
break;
|
||||
|
||||
while (current_node && current_node->is_exclusive_text()) {
|
||||
nodes.append(static_cast<Text*>(current_node));
|
||||
current_node = current_node->next_sibling();
|
||||
}
|
||||
|
@ -291,7 +285,7 @@ WebIDL::ExceptionOr<void> Node::normalize()
|
|||
auto* current_node = node.next_sibling();
|
||||
|
||||
// 6. While currentNode is an exclusive Text node:
|
||||
while (current_node && is<Text>(*current_node)) {
|
||||
while (current_node && current_node->is_exclusive_text()) {
|
||||
// 1. For each live range whose start node is currentNode, add length to its start offset and set its start node to node.
|
||||
for (auto& range : Range::live_ranges()) {
|
||||
if (range->start_container() == current_node)
|
||||
|
@ -951,7 +945,7 @@ WebIDL::ExceptionOr<GC::Ref<Node>> Node::replace_child(GC::Ref<Node> node, GC::R
|
|||
} else if (is<DocumentType>(*node)) {
|
||||
// DocumentType
|
||||
// parent has a doctype child that is not child, or an element is preceding child.
|
||||
if (first_child_of_type<DocumentType>() != node || child->has_preceding_node_of_type_in_tree_order<Element>())
|
||||
if (first_child_of_type<DocumentType>() != child || child->has_preceding_node_of_type_in_tree_order<Element>())
|
||||
return WebIDL::HierarchyRequestError::create(realm(), "Invalid node type for insertion"_string);
|
||||
}
|
||||
}
|
||||
|
@ -1057,15 +1051,21 @@ WebIDL::ExceptionOr<GC::Ref<Node>> Node::clone_node(Document* document, bool clo
|
|||
// Set copy’s namespace, namespace prefix, local name, and value to those of node.
|
||||
auto& attr = static_cast<Attr&>(*this);
|
||||
copy = attr.clone(*document);
|
||||
}
|
||||
// NOTE: is<Text>() currently returns true only for text nodes, not for descendant types of Text.
|
||||
else if (is<Text>(this) || is<CDATASection>(this)) {
|
||||
} else if (is<Text>(this)) {
|
||||
// Text
|
||||
auto& text = static_cast<Text&>(*this);
|
||||
|
||||
// Set copy’s data to that of node.
|
||||
auto text_copy = realm().create<Text>(*document, text.data());
|
||||
copy = move(text_copy);
|
||||
copy = [&]() -> GC::Ref<Text> {
|
||||
switch (type()) {
|
||||
case NodeType::TEXT_NODE:
|
||||
return realm().create<Text>(*document, text.data());
|
||||
case NodeType::CDATA_SECTION_NODE:
|
||||
return realm().create<CDATASection>(*document, text.data());
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}();
|
||||
} else if (is<Comment>(this)) {
|
||||
// Comment
|
||||
auto comment = verify_cast<Comment>(this);
|
||||
|
|
|
@ -111,6 +111,7 @@ public:
|
|||
NodeType type() const { return m_type; }
|
||||
bool is_element() const { return type() == NodeType::ELEMENT_NODE; }
|
||||
bool is_text() const { return type() == NodeType::TEXT_NODE || type() == NodeType::CDATA_SECTION_NODE; }
|
||||
bool is_exclusive_text() const { return type() == NodeType::TEXT_NODE; }
|
||||
bool is_document() const { return type() == NodeType::DOCUMENT_NODE; }
|
||||
bool is_document_type() const { return type() == NodeType::DOCUMENT_TYPE_NODE; }
|
||||
bool is_comment() const { return type() == NodeType::COMMENT_NODE; }
|
||||
|
|
|
@ -703,13 +703,13 @@ void HTMLImageElement::add_callbacks_to_image_request(GC::Ref<ImageRequest> imag
|
|||
// 3. Add the image to the list of available images using the key key, with the ignore higher-layer caching flag set.
|
||||
document().list_of_available_images().add(key, *image_data, true);
|
||||
|
||||
set_needs_style_update(true);
|
||||
document().set_needs_layout();
|
||||
|
||||
// 4. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element.
|
||||
if (!maybe_omit_events || previous_url != url_string)
|
||||
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load));
|
||||
|
||||
set_needs_style_update(true);
|
||||
document().set_needs_layout();
|
||||
|
||||
if (image_data->is_animated() && image_data->frame_count() > 1) {
|
||||
m_current_frame_index = 0;
|
||||
m_animation_timer->set_interval(image_data->frame_duration(0));
|
||||
|
|
|
@ -895,7 +895,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
|
|||
// If the latter condition occurs, then abort fetchController, and return. Otherwise, proceed onward.
|
||||
if (navigation_id.has_value() && (!navigable->ongoing_navigation().has<String>() || navigable->ongoing_navigation().get<String>() != *navigation_id)) {
|
||||
fetch_controller->abort(realm, {});
|
||||
return Empty {};
|
||||
return Navigable::NullOrError {};
|
||||
}
|
||||
|
||||
// 8. If request's body is null, then set entry's document state's resource to null.
|
||||
|
@ -1010,9 +1010,9 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
|
|||
if (response_holder->response()->network_error_message().has_value() && !response_holder->response()->network_error_message().value().is_null())
|
||||
return response_holder->response()->network_error_message().value();
|
||||
else
|
||||
return Empty {};
|
||||
return Navigable::NullOrError {};
|
||||
} else if (location_url.is_error() || (location_url.value().has_value() && Fetch::Infrastructure::is_fetch_scheme(location_url.value().value().scheme())))
|
||||
return Empty {};
|
||||
return Navigable::NullOrError {};
|
||||
|
||||
// 22. Assert: locationURL is null and response is not a network error.
|
||||
VERIFY(!location_url.value().has_value());
|
||||
|
@ -1079,7 +1079,7 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
|
|||
// FIXME: 1. Assert: this is running in parallel.
|
||||
|
||||
// 2. Assert: if navigationParams is non-null, then navigationParams's response is non-null.
|
||||
if (!navigation_params.has<Empty>() && !navigation_params.has<NullWithError>())
|
||||
if (!navigation_params.has<NullOrError>())
|
||||
VERIFY(navigation_params.has<GC::Ref<NavigationParams>>() && navigation_params.get<GC::Ref<NavigationParams>>()->response);
|
||||
|
||||
// 3. Let currentBrowsingContext be navigable's active browsing context.
|
||||
|
@ -1089,7 +1089,7 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
|
|||
auto document_resource = entry->document_state()->resource();
|
||||
|
||||
// 5. If navigationParams is null, then:
|
||||
if (navigation_params.has<Empty>() || navigation_params.has<NullWithError>()) {
|
||||
if (navigation_params.has<NullOrError>()) {
|
||||
// 1. If documentResource is a string, then set navigationParams to the result
|
||||
// of creating navigation params from a srcdoc resource given entry, navigable,
|
||||
// targetSnapshotParams, navigationId, and navTimingType.
|
||||
|
@ -1160,9 +1160,9 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
|
|||
// - FIXME: the result of should navigation response to navigation request of type in target be blocked by Content Security Policy? given navigationParams's request, navigationParams's response, navigationParams's policy container's CSP list, cspNavigationType, and navigable is "Blocked";
|
||||
// - FIXME: navigationParams's reserved environment is non-null and the result of checking a navigation response's adherence to its embedder policy given navigationParams's response, navigable, and navigationParams's policy container's embedder policy is false; or
|
||||
// - FIXME: the result of checking a navigation response's adherence to `X-Frame-Options` given navigationParams's response, navigable, navigationParams's policy container's CSP list, and navigationParams's origin is false,
|
||||
if (navigation_params.has<Empty>() || navigation_params.has<NullWithError>()) {
|
||||
if (navigation_params.has<NullOrError>()) {
|
||||
// 1. Set entry's document state's document to the result of creating a document for inline content that doesn't have a DOM, given navigable, null, and navTimingType. The inline content should indicate to the user the sort of error that occurred.
|
||||
auto error_message = navigation_params.has<NullWithError>() ? navigation_params.get<NullWithError>() : "Unknown error"sv;
|
||||
auto error_message = navigation_params.get<NullOrError>().value_or("Unknown error"sv);
|
||||
|
||||
auto error_html = load_error_page(entry->url(), error_message).release_value_but_fixme_should_propagate_errors();
|
||||
entry->document_state()->set_document(create_document_for_inline_content(this, navigation_id, [error_html](auto& document) {
|
||||
|
@ -1178,7 +1178,7 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
|
|||
saveExtraDocumentState = false;
|
||||
|
||||
// 4. If navigationParams is not null, then:
|
||||
if (!navigation_params.has<Empty>() && !navigation_params.has<NullWithError>()) {
|
||||
if (!navigation_params.has<NullOrError>()) {
|
||||
// FIXME: 1. Run the environment discarding steps for navigationParams's reserved environment.
|
||||
// FIXME: 2. Invoke WebDriver BiDi navigation failed with currentBrowsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is navigationParams's response's URL.
|
||||
}
|
||||
|
@ -1210,7 +1210,7 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
|
|||
|
||||
// 3. If entry's document state's request referrer is "client", and navigationParams is a navigation params (i.e., neither null nor a non-fetch scheme navigation params), then:
|
||||
if (entry->document_state()->request_referrer() == Fetch::Infrastructure::Request::Referrer::Client
|
||||
&& (!navigation_params.has<Empty>() && !navigation_params.has<NullWithError>() && navigation_params.has<GC::Ref<NonFetchSchemeNavigationParams>>())) {
|
||||
&& (!navigation_params.has<NullOrError>() && navigation_params.has<GC::Ref<NonFetchSchemeNavigationParams>>())) {
|
||||
// 1. Assert: navigationParams's request is not null.
|
||||
VERIFY(navigation_params.has<GC::Ref<NavigationParams>>() && navigation_params.get<GC::Ref<NavigationParams>>()->request);
|
||||
|
||||
|
@ -1463,7 +1463,7 @@ WebIDL::ExceptionOr<void> Navigable::navigate(NavigateParams params)
|
|||
history_entry->set_document_state(document_state);
|
||||
|
||||
// 7. Let navigationParams be null.
|
||||
NavigationParamsVariant navigation_params = Empty {};
|
||||
NavigationParamsVariant navigation_params = Navigable::NullOrError {};
|
||||
|
||||
// FIXME: 8. If response is non-null:
|
||||
if (response) {
|
||||
|
|
|
@ -54,8 +54,8 @@ class Navigable : public JS::Cell {
|
|||
public:
|
||||
virtual ~Navigable() override;
|
||||
|
||||
using NullWithError = StringView;
|
||||
using NavigationParamsVariant = Variant<Empty, NullWithError, GC::Ref<NavigationParams>, GC::Ref<NonFetchSchemeNavigationParams>>;
|
||||
using NullOrError = Optional<StringView>;
|
||||
using NavigationParamsVariant = Variant<NullOrError, GC::Ref<NavigationParams>, GC::Ref<NonFetchSchemeNavigationParams>>;
|
||||
|
||||
ErrorOr<void> initialize_navigable(GC::Ref<DocumentState> document_state, GC::Ptr<Navigable> parent);
|
||||
|
||||
|
@ -132,7 +132,7 @@ public:
|
|||
SourceSnapshotParams const& source_snapshot_params,
|
||||
TargetSnapshotParams const& target_snapshot_params,
|
||||
Optional<String> navigation_id = {},
|
||||
NavigationParamsVariant navigation_params = Empty {},
|
||||
NavigationParamsVariant navigation_params = Navigable::NullOrError {},
|
||||
CSPNavigationType csp_navigation_type = CSPNavigationType::Other,
|
||||
bool allow_POST = false,
|
||||
GC::Ptr<GC::Function<void()>> completion_steps = {});
|
||||
|
|
|
@ -644,7 +644,7 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
|
|||
// targetSnapshotParams, with allowPOST set to allowPOST and completionSteps set to queue a global task on the navigation and traversal task source given
|
||||
// navigable's active window to run afterDocumentPopulated.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(this->heap(), [populated_target_entry, potentially_target_specific_source_snapshot_params, target_snapshot_params, this, allow_POST, navigable, after_document_populated = GC::create_function(this->heap(), move(after_document_populated))] {
|
||||
navigable->populate_session_history_entry_document(populated_target_entry, *potentially_target_specific_source_snapshot_params, target_snapshot_params, {}, Empty {}, CSPNavigationType::Other, allow_POST, GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable {
|
||||
navigable->populate_session_history_entry_document(populated_target_entry, *potentially_target_specific_source_snapshot_params, target_snapshot_params, {}, Navigable::NullOrError {}, CSPNavigationType::Other, allow_POST, GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable {
|
||||
VERIFY(active_window());
|
||||
queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(this->heap(), [after_document_populated, populated_target_entry]() mutable {
|
||||
after_document_populated->function()(true, populated_target_entry);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/GridFormattingContext.h>
|
||||
|
|
|
@ -854,6 +854,17 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
|||
computed_values.set_fill_rule(*fill_rule);
|
||||
|
||||
computed_values.set_fill_opacity(computed_style.fill_opacity());
|
||||
|
||||
auto const& stroke_dashoffset = computed_style.property(CSS::PropertyID::StrokeDashoffset);
|
||||
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
||||
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
||||
if (stroke_dashoffset.is_number())
|
||||
computed_values.set_stroke_dashoffset(CSS::Length::make_px(CSSPixels::nearest_value_for(stroke_dashoffset.as_number().number())));
|
||||
else if (stroke_dashoffset.is_length())
|
||||
computed_values.set_stroke_dashoffset(stroke_dashoffset.as_length().length());
|
||||
else if (stroke_dashoffset.is_percentage())
|
||||
computed_values.set_stroke_dashoffset(CSS::LengthPercentage { stroke_dashoffset.as_percentage().percentage() });
|
||||
|
||||
if (auto stroke_linecap = computed_style.stroke_linecap(); stroke_linecap.has_value())
|
||||
computed_values.set_stroke_linecap(stroke_linecap.value());
|
||||
if (auto stroke_linejoin = computed_style.stroke_linejoin(); stroke_linejoin.has_value())
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Gradients.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibWeb/Painting/SVGPathPaintable.h>
|
||||
#include <LibWeb/Painting/SVGSVGPaintable.h>
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ void SVGGraphicsElement::apply_presentational_hints(CSS::StyleProperties& style)
|
|||
NamedPropertyID(CSS::PropertyID::Fill),
|
||||
// FIXME: The `stroke` attribute and CSS `stroke` property are not the same! But our support is limited enough that they are equivalent for now.
|
||||
NamedPropertyID(CSS::PropertyID::Stroke),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeDashoffset),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinecap),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
|
||||
|
@ -266,13 +267,10 @@ Optional<float> SVGGraphicsElement::stroke_opacity() const
|
|||
return layout_node()->computed_values().stroke_opacity();
|
||||
}
|
||||
|
||||
Optional<float> SVGGraphicsElement::stroke_width() const
|
||||
float SVGGraphicsElement::resolve_relative_to_viewport_size(CSS::LengthPercentage const& length_percentage) const
|
||||
{
|
||||
if (!layout_node())
|
||||
return {};
|
||||
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
||||
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
||||
auto width = layout_node()->computed_values().stroke_width();
|
||||
// Resolved relative to the "Scaled viewport size": https://www.w3.org/TR/2017/WD-fill-stroke-3-20170413/#scaled-viewport-size
|
||||
// FIXME: This isn't right, but it's something.
|
||||
CSSPixels viewport_width = 0;
|
||||
|
@ -284,7 +282,21 @@ Optional<float> SVGGraphicsElement::stroke_width() const
|
|||
}
|
||||
}
|
||||
auto scaled_viewport_size = (viewport_width + viewport_height) * CSSPixels(0.5);
|
||||
return width.to_px(*layout_node(), scaled_viewport_size).to_double();
|
||||
return length_percentage.to_px(*layout_node(), scaled_viewport_size).to_double();
|
||||
}
|
||||
|
||||
Optional<float> SVGGraphicsElement::stroke_dashoffset() const
|
||||
{
|
||||
if (!layout_node())
|
||||
return {};
|
||||
return resolve_relative_to_viewport_size(layout_node()->computed_values().stroke_dashoffset());
|
||||
}
|
||||
|
||||
Optional<float> SVGGraphicsElement::stroke_width() const
|
||||
{
|
||||
if (!layout_node())
|
||||
return {};
|
||||
return resolve_relative_to_viewport_size(layout_node()->computed_values().stroke_width());
|
||||
}
|
||||
|
||||
// https://svgwg.org/svg2-draft/types.html#__svg__SVGGraphicsElement__getBBox
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
|
||||
Optional<Gfx::Color> fill_color() const;
|
||||
Optional<Gfx::Color> stroke_color() const;
|
||||
Optional<float> stroke_dashoffset() const;
|
||||
Optional<float> stroke_width() const;
|
||||
Optional<float> fill_opacity() const;
|
||||
Optional<CSS::StrokeLinecap> stroke_linecap() const;
|
||||
|
@ -94,6 +95,7 @@ protected:
|
|||
|
||||
private:
|
||||
virtual bool is_svg_graphics_element() const final { return true; }
|
||||
float resolve_relative_to_viewport_size(CSS::LengthPercentage const& length_percentage) const;
|
||||
};
|
||||
|
||||
Gfx::AffineTransform transform_from_transform_list(ReadonlySpan<Transform> transform_list);
|
||||
|
|
|
@ -62,7 +62,7 @@ void XMLDocumentBuilder::set_source(ByteString source)
|
|||
m_document->set_source(MUST(String::from_byte_string(source)));
|
||||
}
|
||||
|
||||
void XMLDocumentBuilder::set_doctype(XML::Doctype doctype)
|
||||
void XMLDocumentBuilder::doctype(XML::Doctype const& doctype)
|
||||
{
|
||||
if (m_document->doctype()) {
|
||||
return;
|
||||
|
@ -73,13 +73,13 @@ void XMLDocumentBuilder::set_doctype(XML::Doctype doctype)
|
|||
document_type->set_name(name);
|
||||
|
||||
if (doctype.external_id.has_value()) {
|
||||
auto external_id = doctype.external_id.release_value();
|
||||
auto const& external_id = *doctype.external_id;
|
||||
|
||||
auto system_id = MUST(AK::String::from_byte_string(external_id.system_id.system_literal));
|
||||
document_type->set_system_id(system_id);
|
||||
|
||||
if (external_id.public_id.has_value()) {
|
||||
auto public_id = MUST(AK::String::from_byte_string(external_id.public_id.release_value().public_literal));
|
||||
auto public_id = MUST(AK::String::from_byte_string(external_id.public_id->public_literal));
|
||||
document_type->set_public_id(public_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
|
||||
private:
|
||||
virtual void set_source(ByteString) override;
|
||||
virtual void set_doctype(XML::Doctype) override;
|
||||
virtual void doctype(XML::Doctype const&) override;
|
||||
virtual void element_start(XML::Name const& name, HashMap<XML::Name, ByteString> const& attributes) override;
|
||||
virtual void element_end(XML::Name const& name) override;
|
||||
virtual void text(StringView data) override;
|
||||
|
|
|
@ -72,6 +72,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
Optional<StringView> profile_process;
|
||||
Optional<StringView> webdriver_content_ipc_path;
|
||||
Optional<StringView> user_agent_preset;
|
||||
Optional<StringView> dns_server_address;
|
||||
Optional<u16> dns_server_port;
|
||||
bool use_dns_over_tls = false;
|
||||
bool log_all_js_exceptions = false;
|
||||
bool enable_idl_tracing = false;
|
||||
bool enable_http_cache = false;
|
||||
|
@ -101,6 +104,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
|
||||
args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig");
|
||||
args_parser.add_option(collect_garbage_on_every_allocation, "Collect garbage after every JS heap allocation", "collect-garbage-on-every-allocation", 'g');
|
||||
args_parser.add_option(dns_server_address, "Set the DNS server address", "dns-server", 0, "host|address");
|
||||
args_parser.add_option(dns_server_port, "Set the DNS server port", "dns-port", 0, "port (default: 53 or 853 if --dot)");
|
||||
args_parser.add_option(use_dns_over_tls, "Use DNS over TLS", "dot");
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||
.help_string = "Name of the User-Agent preset to use in place of the default User-Agent",
|
||||
|
@ -120,6 +126,9 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
if (force_new_process)
|
||||
disable_sql_database = true;
|
||||
|
||||
if (!dns_server_port.has_value())
|
||||
dns_server_port = use_dns_over_tls ? 853 : 53;
|
||||
|
||||
Optional<ProcessType> debug_process_type;
|
||||
Optional<ProcessType> profile_process_type;
|
||||
|
||||
|
@ -140,6 +149,11 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
|
|||
.disable_sql_database = disable_sql_database ? DisableSQLDatabase::Yes : DisableSQLDatabase::No,
|
||||
.debug_helper_process = move(debug_process_type),
|
||||
.profile_helper_process = move(profile_process_type),
|
||||
.dns_settings = (dns_server_address.has_value()
|
||||
? (use_dns_over_tls
|
||||
? DNSSettings(DNSOverTLS(dns_server_address.release_value(), *dns_server_port))
|
||||
: DNSSettings(DNSOverUDP(dns_server_address.release_value(), *dns_server_port)))
|
||||
: SystemDNS {}),
|
||||
};
|
||||
|
||||
if (webdriver_content_ipc_path.has_value())
|
||||
|
|
|
@ -163,7 +163,19 @@ ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process()
|
|||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<Requests::RequestClient>("RequestServer"sv, move(arguments));
|
||||
auto client = TRY(launch_server_process<Requests::RequestClient>("RequestServer"sv, move(arguments)));
|
||||
WebView::Application::chrome_options().dns_settings.visit(
|
||||
[](WebView::SystemDNS) {},
|
||||
[&](WebView::DNSOverTLS const& dns_over_tls) {
|
||||
dbgln("Setting DNS server to {}:{} with TLS", dns_over_tls.server_address, dns_over_tls.port);
|
||||
client->async_set_dns_server(dns_over_tls.server_address, dns_over_tls.port, true);
|
||||
},
|
||||
[&](WebView::DNSOverUDP const& dns_over_udp) {
|
||||
dbgln("Setting DNS server to {}:{}", dns_over_udp.server_address, dns_over_udp.port);
|
||||
client->async_set_dns_server(dns_over_udp.server_address, dns_over_udp.port, false);
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client()
|
||||
|
|
|
@ -45,6 +45,18 @@ enum class EnableAutoplay {
|
|||
Yes,
|
||||
};
|
||||
|
||||
struct SystemDNS { };
|
||||
struct DNSOverTLS {
|
||||
ByteString server_address;
|
||||
u16 port;
|
||||
};
|
||||
struct DNSOverUDP {
|
||||
ByteString server_address;
|
||||
u16 port;
|
||||
};
|
||||
|
||||
using DNSSettings = Variant<SystemDNS, DNSOverTLS, DNSOverUDP>;
|
||||
|
||||
struct ChromeOptions {
|
||||
Vector<URL::URL> urls;
|
||||
Vector<ByteString> raw_urls;
|
||||
|
@ -58,6 +70,7 @@ struct ChromeOptions {
|
|||
Optional<ProcessType> debug_helper_process {};
|
||||
Optional<ProcessType> profile_helper_process {};
|
||||
Optional<ByteString> webdriver_content_ipc_path {};
|
||||
DNSSettings dns_settings { SystemDNS {} };
|
||||
};
|
||||
|
||||
enum class IsLayoutTestMode {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/PathFontProvider.h>
|
||||
#include <LibWebView/Plugins/FontPlugin.h>
|
||||
|
|
|
@ -182,9 +182,6 @@ ErrorOr<void, ParseError> Parser::parse_with_listener(Listener& listener)
|
|||
if (result.is_error())
|
||||
m_listener->error(result.error());
|
||||
m_listener->document_end();
|
||||
if (m_doctype.has_value()) {
|
||||
m_listener->set_doctype(m_doctype.release_value());
|
||||
}
|
||||
m_root_node.clear();
|
||||
return result;
|
||||
}
|
||||
|
@ -621,7 +618,8 @@ ErrorOr<void, ParseError> Parser::parse_doctype_decl()
|
|||
TRY(expect(">"sv));
|
||||
|
||||
rollback.disarm();
|
||||
m_doctype = move(doctype);
|
||||
if (m_listener)
|
||||
m_listener->doctype(doctype);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ struct Listener {
|
|||
virtual ~Listener() { }
|
||||
|
||||
virtual void set_source(ByteString) { }
|
||||
virtual void set_doctype(XML::Doctype) { }
|
||||
virtual void document_start() { }
|
||||
virtual void document_end() { }
|
||||
virtual void doctype(Doctype const&) { }
|
||||
virtual void element_start(Name const&, HashMap<Name, ByteString> const&) { }
|
||||
virtual void element_end(Name const&) { }
|
||||
virtual void text(StringView) { }
|
||||
|
|
|
@ -8,6 +8,7 @@ set(CSS_LOADER_DEBUG ON)
|
|||
set(CSS_PARSER_DEBUG ON)
|
||||
set(CSS_TOKENIZER_DEBUG ON)
|
||||
set(CSS_TRANSITIONS_DEBUG ON)
|
||||
set(DNS_DEBUG ON)
|
||||
set(EDITOR_DEBUG ON)
|
||||
set(EMOJI_DEBUG ON)
|
||||
set(FILE_WATCHER_DEBUG ON)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue