Compare commits

...

154 commits

Author SHA1 Message Date
stasoid
cf70aef726
Merge 6394b12880 into 001df24935 2024-11-21 14:29:44 +01:00
Pavel Shliak
001df24935 LibWebSocket: Clean up #include directives
Some checks are pending
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
b60cb699a9 LibMedia: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
8d13115d9a LibCrypto: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
cd14b215d1 LibDNS: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
35764db0b7 LibWasm: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
caf7983039 LibHTTP: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Pavel Shliak
cdb54fe504 LibRegex: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-21 14:08:33 +01:00
Psychpsyo
7f989765f5 LibWeb: Fix MouseEvent position values
The clientX and clientY values are, as per the spec, the offset from
the viewport.
This makes them actually be that and also fixes up the calculations
for offsetX, offsetY, pageX and pageY.
I assume all of these got messed up in some sort of refactor in the
past.

The spec comment from the now-removed
compute_mouse_event_client_offset() function sadly has no convenient
place to be anymore so, for now, it is just gone as well.
Personally, I think it'd make sense to refactor a lot of this file so
that not every mouse event repeats a large chunk of (almost) identical
code. That way there'd be a nice place to put the comment without
repeating it all over the file.
But that is out of the scope of this PR.

Also: I know, offsetX and Y are not fully fixed yet, they still
don't ignore the element's CSS transforms but I am working on that
in a new PR.
2024-11-21 13:22:22 +01:00
Aliaksandr Kalenik
41c172c663 LibWeb: Allow custom properties in getPropertyPriority() 2024-11-21 13:16:08 +01:00
Aliaksandr Kalenik
ac5699c8fc LibWeb: Allow custom properties in CSSStyleDeclaration.removeProperty() 2024-11-21 13:16:08 +01:00
Aliaksandr Kalenik
ce26e5d757 LibWeb: Allow custom properties in CSSStyleDeclaration.getPropertyValue 2024-11-21 13:16:08 +01:00
Aliaksandr Kalenik
3a2cc1aa20 LibWeb: Allow custom properties in CSSStyleDeclaration.setProperty()
This change fixes unhoverable toolbar on https://excalidraw.com/
The problem was that React.js uses setProperty() to add style properties
specified in the "style" attribute in the virtual DOM, and we were
failing to add the CSS variable used to set the "pointer-events" value
to "all".
2024-11-21 13:16:08 +01:00
Aliaksandr Kalenik
0448d4d609 LibWeb: Update property_id_from_string() generator to handle ::Custom 2024-11-21 13:16:08 +01:00
Lucas CHOLLET
a1687854ab LibWeb/CSS: Use double in CSSHWB::to_color()
See previous the commit description for more details about the floating
points operations.

The hwb test cases in `css-color-functions` are now rendered identically
to what firefox does (I haven't checked the others tests, but they
aren't affected by this commit).
2024-11-21 11:59:44 +00:00
Lucas CHOLLET
d1120e1809 LibWeb: Make CSSColorValue resolvers return a double
Without this change the math in `CSSHWB::to_color()` is lacking some
precision to generate the correct value to hand to `Color::from_hsv()`.

More precisely, when converting `hwb(120 20 30)`, the HSV's value would
be calculated as `1 - .3`. However, it turns out that `1 - .3f != .7f`
and `1 - .3f` gives bad results down the road in `Color::from_hsv()`.

This example actually only requires `resolve_with_reference_value()` to
return a double. I changed the two others for symmetry.
2024-11-21 11:59:44 +00:00
Lucas CHOLLET
248e4bb517 LibGfx: Round values in Color::with_opacity()
`svg-gradient-userSpaceOnUse` is now rendered to something closer to
what firefox does, so it is at least some progress.
2024-11-21 11:59:44 +00:00
Lucas CHOLLET
b4ba65c6e5 LibGfx: Round values in Color::from_hsv() 2024-11-21 11:59:44 +00:00
devgianlu
009f328308 LibWeb: Implement ECDH.generateKey 2024-11-21 11:45:22 +01:00
Pavel Shliak
d55caff227 LibFileSystem: Fix Windows build
This reverts part of  b3c253e50f
2024-11-21 11:15:49 +01:00
Feng Yu
570c5f8df2 Documentation: Fix broken link in README 2024-11-21 10:42:45 +01:00
Shannon Booth
d6bcd3fb0b LibWeb: Make CallbackType take a realm instead of settings object
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
In line with the ShadowRealm proposal changes in the WebIDL spec:
webidl#1437 and supporting changes in HTML spec.

This is required for ShadowRealms as they have no relevant settings
object on the shadow realm, so fixes a crash in the QueueingStrategy
test in this commit.
2024-11-20 18:01:21 -07:00
Shannon Booth
d527c5df5d LibWeb: Allow using queuing strategies on globals other than Window
These interfaces are exposed on *, meaning it should work for workers
and our newly added shadow realm global object by being stored on the
universal global scope mixin.
2024-11-20 18:01:21 -07:00
rmg-x
13f349aea2 LibWeb/Fetch: Implement blob range section of scheme fetch specification 2024-11-21 00:26:58 +00:00
rmg-x
84f673515b LibWeb/XHR: Use normalized header value when validating contents
This was not matching specification and caused valid requests to be
rejected.
2024-11-21 00:26:58 +00:00
rmg-x
4e48298414 LibWeb/Fetch: Implement build_content_range(start, end, full_length) 2024-11-21 00:26:58 +00:00
rmg-x
bf5cf720b5 LibWeb/Fetch: Bring parse_single_range_header_value() up to spec
The previous implementation wasn't using the latest specification steps.
2024-11-21 00:26:58 +00:00
Timothy Flynn
488034477a Revert "LibWeb: Set doctype node immediately while parsing XML document"
This reverts commit cd446e5e9c.

This broke about 20k WPT subtests, all related to XML parsing. See:
https://wpt.fyi/results/html/the-xhtml-syntax/parsing-xhtml-documents?diff=&filter=ADC&run_id=5154815472828416&run_id=5090731742199808
2024-11-20 19:11:56 -05:00
Timothy Flynn
f57ff63432 LibJS: Implement Temporal.Duration.prototype.valueOf 2024-11-20 19:04:30 -05:00
Timothy Flynn
c715711f88 LibJS: Implement Temporal.Duration.prototype.total
Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.prototype.total (and its invoked AOs) are left
unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
5689621c2b LibJS: Implement Temporal.Duration.prototype.round
Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.prototype.round (and its invoked AOs) are left
unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
4742775262 LibJS: Implement stringification Temporal.Duration prototypes 2024-11-20 19:04:30 -05:00
Timothy Flynn
a80523be18 LibJS: Implement mathematical Temporal.Duration prototypes
Includes:
Temporal.Duration.prototype.negated
Temporal.Duration.prototype.abs
Temporal.Duration.prototype.add
Temporal.Duration.prototype.subtract
2024-11-20 19:04:30 -05:00
Timothy Flynn
55c81482b0 LibJS: Implement Temporal.Duration.prototype.with 2024-11-20 19:04:30 -05:00
Timothy Flynn
dfaa3bf649 LibJS: Implement Temporal.Duration.prototype.sign/blank 2024-11-20 19:04:30 -05:00
Timothy Flynn
5fe0d3352d LibJS: Implement the Temporal.Duration constructor
This also includes a stubbed Temporal.Duration.prototype.

Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.compare (and its invoked AOs) are left unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
eca378a7a3 LibJS: Restore some Temporal numeric constants
And add few ad-hoc constants for convenience.
2024-11-20 19:04:30 -05:00
Timothy Flynn
e4e05837e1 LibJS: Return a GC::Ref from Temporal::get_options_object
The Object returned here is always non-null.
2024-11-20 19:04:30 -05:00
Timothy Flynn
c8d2404230 LibJS: Update spec steps for the few remaining Temporal AOs 2024-11-20 19:04:30 -05:00
Timothy Flynn
d368fcadac LibJS: Update spec link for Temporal [ %Symbol.toStringTag% ] 2024-11-20 19:04:30 -05:00
Timothy Flynn
f7517c5b8d LibJS: Remove our existing Temporal implementation
Our Temporal implementation is woefully out of date. The spec has been
so vastly rewritten that it is unfortunately not practical to update our
implementation in-place. Even just removing Temporal objects that were
removed from the spec, or updating any of the simpler remaining objects,
has proven to be a mess in previous attempts.

So, this removes our Temporal implementation. AOs used by other specs
are left intact.
2024-11-20 19:04:30 -05:00
Timothy Flynn
b94307583b LibCrypto: Add user-defined literals to convert numbers to a BigInt
It is much more convenient to define constants with:

    1000_bigint

Than with:

    Crypto::UnsignedBigInteger { 1000 }
2024-11-20 19:04:30 -05:00
Timothy Flynn
e236f1d2ae LibCrypto: Define UnsignedBigInteger::operator<=
We have all comparison operators except less-than-or-equal already.
2024-11-20 19:04:30 -05:00
Ali Mohammad Pur
63a5717bc7 LibDNS: Immediately resolve IPv4/IPv6 "hostnames" if A/AAAA is queried
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
This makes e.g. lookup(192.168.1.1, A) resolve to the IP instead of
querying DNS for the IP.
2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
c5afe58540 LibDNS: Add a default entry for localhost
In the future, we may want to parse /etc/hosts (or equivalent) into the
cache; this commit only adds localhost to make the normal workflow work.
2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
3bcd91b109 LibDNS: Hide some debug logs behind DNS_DEBUG 2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
7d1291b9f0 RequestServer: Implement the ResolveOnly EnsureConnection level 2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
6911c45bab LibDNS: Respect records' TTL in the resolver cache 2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
879ae94183 LibRequests: Don't crash on requests without a read stream finishing
This can now happen due to the hostname not existing, as RS explicitly
performs DNS resolution before setting up the response pipe.
2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
7e20f4726f LibDNS+LibWeb+Ladybird+RequestServer: Let there be DNS over TLS
This commit adds our own DNS resolver, with the aim of implementing DoT
(and eventually DoH, maybe even DNSSEC etc.)
2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
7f72c28e78 LibHTTP: Make HeaderMap movable and copyable 2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
d704b61066 LibCore+LibTLS: Add an API for connect()'ing 'with hostname
This just unifies the API for all three sockets (UDP, TCP and TLS)
2024-11-20 21:37:58 +01:00
Ali Mohammad Pur
b93d8ef875 AK: Disable implicit conversion from char* -> ipv4 -> ipv6
This is a footgun with some massive bullets.
2024-11-20 21:37:58 +01:00
Pavel Shliak
8a07131229 LibGfx: Clean up #include directives
We actually include what we use where we use it.
This change aims to improve the speed of incremental builds.
2024-11-20 21:13:23 +01:00
Andreas Kling
063cd68bf4 LibWeb: Mark image elements for layout before firing their load event
This removes a long-standing source of flakiness seen for example in
WPT's /referrer-policy/ tests.
2024-11-20 19:04:37 +01:00
Luke Wilde
f638f84185 LibWeb: Make default document readiness be "complete"
This is required by mini Cloudflare invisible challenges, as it will
only run if the readyState is not "loading". If it is "loading", then
it waits for readystatechange to check that it's not "loading" anymore.

Initial about:blank iframes do not go through the full navigation and
thus don't go through HTMLParser::the_end, which sets the ready state
to something other than "loading". Therefore, the challenge would never
run, as readyState would never change.

Seen on https://discord.com/login
2024-11-20 16:20:28 +01:00
Andreas Kling
4203b7823f LibWeb: Fix incorrect exception on replaceChild() with doctypes
We were checking for presence of the wrong child in the parent.
2024-11-20 16:10:57 +01:00
Andreas Kling
cd446e5e9c LibWeb: Set doctype node immediately while parsing XML document
Instead of deferring it to the end of parsing, where scripts that
were expecting to look at the doctype may have already run.
2024-11-20 16:10:57 +01:00
Andreas Kling
ab0dc83d28 LibWeb: Make Node.normalize() ignore CDATASection nodes
We hadn't modeled the "exclusive text node" concept correctly.
2024-11-20 16:10:57 +01:00
Nico Weber
6fc06f45c2 LibWeb: Plumbing for svg stroke-dashoffset 2024-11-20 15:57:37 +01:00
Nicolas Ramz
e98e9b8e81 Documentation: Fix typo in Testing markdown 2024-11-20 15:48:13 +01:00
Gingeh
4b1deb6fe1 LibWeb: Don't skip filtering when CSS contains null or surrogates 2024-11-20 15:47:19 +01:00
Jonne Ransijn
356507284e LibWeb: Compare font keys by reference
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
`StyleComputer::font_matching_algorithm` was creating a copy of a
`FlyString` every time a `MatchingFontCandidate` was constructed or
copied, causing millions of unnecessairy reference updates when a
lot of fonts are loaded.

While a more permanent solution would be to not load so many unused
fonts, let's do the right thing and remove the unnecessairy copies of
`FlyString`.
2024-11-20 15:38:03 +01:00
Jonne Ransijn
ec5ea0d686 LibGfx: Return family names by reference to avoid unnecessairy cloning 2024-11-20 15:38:03 +01:00
Pavel Shliak
b3c253e50f LibFileSystem: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-20 15:17:53 +01:00
Pavel Shliak
d0c0db5bb3 LibCompress: Clean up #include directives
This change aims to improve the speed of incremental builds.
2024-11-20 15:17:31 +01:00
Timothy Flynn
b99a3ec2df LibWeb: Clone CDATASection nodes with the correct node type
We were cloning these as plain Text nodes, but the clone must also be a
CDATASection node.
2024-11-20 15:15:56 +01:00
rmg-x
74b27d620d LibJS: Parse dates like "Wed Nov 20 2024" 2024-11-20 09:20:48 +00:00
stasoid
dabf3da7e5 LibCore: Fix bug in CreateFileMapping call
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
size >> 31 >> 1 is used instead of size >> 32 to support 32-bit Windows
(size_t is 32 bit there, and you cannot shift 32-bit value by 32 bits
on x86)
This is equivalent to sizeof(size) == 4 ? 0 : size >> 32
2024-11-19 22:07:01 -07:00
stasoid
11460b3daa LibCore: Fix ConfigFile.cpp compilation on Windows 2024-11-19 22:07:01 -07:00
stasoid
43056a8684 LibCore: Port Directory to Windows 2024-11-19 22:07:01 -07:00
stasoid
a423493dd8 AK: Add LexicalPath::is_root() 2024-11-19 22:07:01 -07:00
stasoid
77d205571d LibCore/System: Add mkdir, openat (stub), fstatat (stub) for Windows
Also support directories in open().
2024-11-19 22:07:01 -07:00
Psychpsyo
f09ed59351 LibWeb: Add the search element 2024-11-19 23:30:43 +00:00
stasoid
866609c682 LibCore: Make Process::wait_for_termination return exit code
Some checks are pending
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
2024-11-19 14:40:03 -07:00
stasoid
3468a83e45 LibCore: Port Process to Windows
Windows doesn't have a concept of zombie children, hence:
* `disown` not needed
* we need a process handle because otherwise if the process have ended
  by the time `wait_for_termination` is called
  its pid may be reassigned to other process
2024-11-19 14:40:03 -07:00
stasoid
4a731b3858 LibCore/Process: Make all spawn overloads return ErrorOr<Process> 2024-11-19 14:40:03 -07:00
stasoid
ddd15e96b6 AK: Define pid_t on Windows 2024-11-19 14:40:03 -07:00
stasoid
61d52c8a3f LibCore: Remove Process::spawn(StringView, ReadonlySpan<char const*>) 2024-11-19 14:40:03 -07:00
stasoid
33e7d6121b LibFileSystem: Port to Windows 2024-11-19 14:35:52 -07:00
stasoid
d87144fde2 LibFileSystem: Remove some unused functions 2024-11-19 14:35:52 -07:00
stasoid
69f5f40617 AK: Add static bool LexicalPath::is_absolute_path(StringView path); 2024-11-19 14:35:52 -07:00
stasoid
a828a0e158 LibCore/System: Port getcwd, stat, rmdir, unlink to Windows 2024-11-19 14:35:52 -07:00
Andrew Kaster
4b4a6991e3 Tests: Use ABI entry point for swift-testing tests
Avoid the unstable SwiftPM entry point in favor of the stable ABI entry
point.
2024-11-19 14:32:11 -07:00
Andrew Kaster
fca6fd0b85 LibGC: Add Swift bindings to the GC heap
This includes a protocol for creating LibGC Heap allocated Swift
objects. Pay no attention to the Unmanaged shenanigans, they are
all behind the curtain.
2024-11-19 14:32:11 -07:00
Andrew Kaster
829391e714 LibGC: Add a ForeignCell class for ownership of non-C++ objects
This will allow us to use the GC to manage the lifetime of objects
that are not C++ objects, such as Swift objects. In the future we
could expand this cursed FFI to other languages as well.
2024-11-19 14:32:11 -07:00
Andrew Kaster
726f2cfb11 LibGC: Expose deferred state publicly, annotate DeferGC for Swift
While we don't want arbitrary callers deferring GC, we do want
deferral to be available to the Swift. In order for Swift to
understand the RAII nature of DeferGC, we need to mark it as
non-copyable.
2024-11-19 14:32:11 -07:00
Andrew Kaster
32cf4d1e29 AK: Add missing swift/bridging empty defines for non-Swift compilers 2024-11-19 14:32:11 -07:00
Andrew Kaster
d5fb48a6f5 LibGC: Add missing Types.h to forwarding header
The forwarding header was not including any other headers, but still
relied on a definition of size_t.
2024-11-19 14:32:11 -07:00
Andrew Kaster
458167935c AK: Add an extension to construct an AK.String from a Swift.String 2024-11-19 14:32:11 -07:00
Andrew Kaster
a95f761cb4 AK: Include missing StdLibExtras from NeverDestroyed 2024-11-19 14:32:11 -07:00
Pavel Shliak
6033349574 LibWeb: Do not crash when Radial Gradient height is 0 2024-11-19 22:31:51 +01:00
Pavel Shliak
ed409eacf5 LibGfx: Remove unused Bitmap loaders 2024-11-19 21:48:45 +01:00
Andreas Kling
6ffc7ea36d LibWeb: Make Node::is_text() return true for CDATASection nodes
CDATASection inherits from Text, and so it was incorrect for them to
claim not to be Text nodes.

This fixes at least two WPT subtests. :^)

It also exposed a bug in the DOM Parsing and Serialization spec,
where we're not told how to serialize CDATASection nodes.

Spec bug: https://github.com/w3c/DOM-Parsing/issues/38
2024-11-19 19:24:37 +00:00
Andreas Kling
564dc0a434 LibWeb: Use correct factory function when cloning a Document node
Cloning an XMLDocument should produce a new XMLDocument. Same for
HTMLDocument.

This fixes at least one WPT test, which we're also importing. :^)
2024-11-19 19:24:37 +00:00
Aliaksandr Kalenik
24a6fd3d76 Tests/LibWeb: Rebaseline WebAnimations/misc/steps-serialization.html
Fixes failing test introduced by
b342758dbf
2024-11-19 16:13:38 +01:00
Aliaksandr Kalenik
c47d19d05a LibWeb: Update LegendBox and FieldSetBox to use GC namespace
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
Fixes broken build.
2024-11-19 14:51:42 +01:00
Kostya Farber
a820308a02 LibWeb: Add layout objects for fieldset and legend
Add the boilerplate code for the layout objects that represent the
`<fieldset>` and `<legend>` elements. Using these, we can make progress
towards laying out these two elements per the spec at
https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements.
2024-11-19 14:31:03 +01:00
Pavel Shliak
b342758dbf LibWeb: Fix extra validation for EasingStyleValue intervals 2024-11-19 14:10:53 +01:00
Psychpsyo
801499f13e LibWeb: Fix crash from text inside SVG 2024-11-19 13:41:15 +01:00
Valtteri Koskivuori
135daeb8bb LibCompress: Don't assume zlib header is available right away
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
Instead of checking the header in ZlibDecompressor::create(), we now
check it in read_some() when it is called for the first time. This
resolves a FIXME in the new DecompressionStream implementation.
2024-11-18 19:55:46 -05:00
Timothy Flynn
be09893fa7 AK+LibJS: Don't use Temporal for console.time() and console.timeLog()
We don't need nanosecond precision here anyways, as we only display
millisecond resolution.

This uses our simple duration formatter from AK, which is updated to
accept a Duration here. This method did not have any users after the
move from Serenity.
2024-11-18 17:46:41 -05:00
Timothy Flynn
8bd394f349 LibJS: Use an Intl prototype for a Function.prototype.toString test
The Temporal.TimeZone object no longer exists in the Temporal spec.
2024-11-18 17:46:41 -05:00
Timothy Flynn
ed76e1ed4b LibJS: Use Date for timing test-js tests
Stop relying on Temporal, at least temporarily. The classes used here
will soon be removed (until they are implemented again from scratch).
2024-11-18 17:46:41 -05:00
Timothy Flynn
dd6acfecd4 LibJS: Don't use Temporal to parse UTC offset strings
The production for these strings has been removed from Temporal. This
implements a separate parser in accordance with the Date spec.
2024-11-18 17:46:41 -05:00
Timothy Flynn
f88826691c LibJS: Remove usage of obsolete Duration record from Intl.DurationFormat
The Duration record no longer exists in Temporal. Implement it according
to the DurationFormat spec to prepare for its removal from our Temporal
implementation.

We also implement the DurationSign AO here as well, as the Temporal
implementation will now require a Temporal.Duration JS object.
2024-11-18 17:46:41 -05:00
Timothy Flynn
59e0b7ccb7 LibJS: Remove unused Temporal inclusion from Intl 2024-11-18 17:46:41 -05:00
Pavel Shliak
8cd514d83c LibWeb: Correct serialization of steps() easing functions 2024-11-18 17:39:03 -05:00
Tim Ledbetter
7fe110225b LibWeb: Use correct specifier to pad font language override value 2024-11-18 17:38:03 -05:00
Aliaksandr Kalenik
96a35767b6 LibWeb: Implement mask-image CSS property support
Implemented by reusing AddMask display list item that was initially
added for `background-clip` property.

Progress on flashlight effect on https://null.com/games/athena-crisis
2024-11-18 22:58:58 +01:00
Andrew Kaster
7b7bb60393 CMake: Store the CMake sauce to add no-as-needed libraries to cache
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
This was causing issues for my Ubuntu 24.04 build when building
the Distribution preset, so just stash this constant config in
the CMake cache to not worry about it anymore.
2024-11-18 14:19:24 -07:00
Andreas Kling
e28e4f6700 LibWeb: Update handling of "once" event listeners now that spec is fixed
https://github.com/whatwg/dom/issues/1323 was fixed, and the solution
ended up slightly different from what we had, so let's follow the spec.
2024-11-18 20:20:57 +01:00
Aliaksandr Kalenik
9f541c363d LibWeb: Allow stacking context to only be created by PaintableBox
For a while we used the wider Paintable type for stacking context,
because it was allowed to be created by InlinePaintable and
PaintableBox. Now, when InlinePaintable type is gone, we can use more
specific PaintableBox type for a stacking context.
2024-11-18 20:07:30 +01:00
Pavel Shliak
ed80e929e5 LibGfx: Sync to_skia_color_type 2024-11-18 19:17:51 +01:00
Shannon Booth
66530086a4 LibWeb: Add MediaSourceExtensions events
Some checks are pending
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
Continuing the boilerplate for these interfaces.
2024-11-18 10:58:21 +00:00
Lucien Fiorini
ff791a63fc LibJS: Add fast paths for get and set on float typed arrays 2024-11-18 09:12:05 +01:00
Psychpsyo
3856dd946b LibWeb: Prevent checkboxes from firing change events when losing focus
This is because toggling the checkbox is committing the value.
2024-11-18 09:04:11 +01:00
Andrew Kaster
c898ee90cf js: Don't destroy the JS VM on shutdown
This avoids a crash in the fully static distribution build, due to
static init order fiasco.
2024-11-18 08:23:08 +01:00
rmg-x
8d511b2f7b RequestServer: Clear "Content-Type" header when one isn't provided
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
libcurl will automatically set the Content-Type header when using the
CURLOPT_POSTFIELDS option to "application/x-www-form-urlencoded".

See: https://curl.se/libcurl/c/CURLOPT_POSTFIELDS.html

The following WPT cases now pass (8 tests):
http://wpt.live/xhr/send-blob-with-no-mime-type.html
2024-11-18 02:03:11 +01:00
Andreas Kling
3e8c8b185e LibWeb: Use WindowProxy instead of Window in UI Events IDL
I believe this is an error in the UI Events spec, and it should be
updated to match the HTML spec (which uses WindowProxy everywhere).

This fixes a bunch of issues already covered by existing WPT tests.

Spec bug: https://github.com/w3c/uievents/issues/388

Note that WebKit has been using WindowProxy instead of Window in
UI Events IDL since 2018:
816158b4aa
2024-11-17 23:47:24 +01:00
Timothy Flynn
5bcba896c2 LibWeb: Implement the DecompressionStream interface 2024-11-17 22:37:45 +01:00
Timothy Flynn
638a8aecad LibWeb: Implement the CompressionStream interface 2024-11-17 22:37:45 +01:00
Timothy Flynn
c0da3e356a LibWeb: Add a couple ad-hoc BufferSource AOs
These helpers will be used by CompressionStream/DecompressionStream.
2024-11-17 22:37:45 +01:00
Timothy Flynn
35ba7c7e00 LibWeb: Add the IDL for the GenericTransformStream mixin 2024-11-17 22:37:45 +01:00
Timothy Flynn
5a2260a0bc LibWeb: Return the readable stream error directly instead of as a string
This error is not a string object, it's e.g. a JS::TypeError. This now
matches similar handling of writable stream errors.
2024-11-17 22:37:45 +01:00
Timothy Flynn
fd15910adf LibCompress: Do not verify that zlib & deflate compressors are finished
These compressors will be used by w3c's CompressionStream, which can run
arbitrary JS, and thus never reach their "finish" steps. Let's not crash
the WebContent process if that happens.
2024-11-17 22:37:45 +01:00
Timothy Flynn
355ce72c06 LibCompress: Allow using GzipCompressor in a streaming fashion
GzipCompressor is currently written assuming that it's write_some method
is only called once. When we use this class for LibWeb, we may very well
receive data to compress in small chunks. So this patch makes us write
the gzip header and footer only once, which now resembles the zlib and
deflate compressors.
2024-11-17 22:37:45 +01:00
Timothy Flynn
b11fdea175 LibCompress: Add a forwarding header
Currently, just with the types needed for the w3c Compression spec.
2024-11-17 22:37:45 +01:00
Timothy Flynn
a91af764f6 Utilities: Remove unused compression utilities
Some LibCompress API changes for LibWeb will make these utilities a bit
difficult to keep up to date. Given that these are unused anways, let's
just not bother.
2024-11-17 22:37:45 +01:00
Timothy Flynn
077ae6efa1 headless-browser: Create the expectation directory if it doesn't exist
This is convenient when adding tests to a new folder.
2024-11-17 22:37:45 +01:00
Lucas CHOLLET
6affbf78c2 LibGfx: Adjust matrices for XYZ -> sRGB conversions
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
TL;DR: There are two available sets of coefficients for the conversion
matrices from XYZ to sRGB. We switched from one set to the other, which
is what the WPT tests are expecting.

All RGB color spaces, like display-p3 or rec2020, are defined by their
three color chromacities and a white point. This is also the case for
the video color space Rec. 709, from which the sRGB color space is
derived. The sRGB specification is however a bit different.

In 1996, when formalizing the sRGB spec the authors published a draft
that is still available here [1]. In this document, they also provide
the matrix to convert from the XYZ color space to sRGB. This matrix can
be verified quite easily by using the usual math equations. But hold on,
here come the plot twist: at the time of publication, the spec contained
a different matrix than the one in the draft (the spec is obviously
behind a pay wall, but the numbers are also reported in this official
document [2]). This official matrix, is at a first glance simply a
wrongly rounded version of the one in the draft publication. It however
has some interesting properties: it can be inverted twice (so a
roundtrip) in 8 bits and not suffer from any errors from the
calculations.

So, we are here with two versions of the XYZ -> sRGB matrix, the one
from the spec, which is:
 - better for computations in 8 bits,
 - and official. This is the one that, by authority, we should use.
And a second version, that can be found in the draft, which:
 - makes sense, as directly derived from the chromacities,
 - is publicly available,
 - and (thus?) used in most places.

The old coefficients were the one from the spec, this commit change them
for the one derived from the mathematical formulae. The Python script to
compute these values is available at the end of the commit description.

More details about this subject can be found here [3].

[1] https://www.w3.org/Graphics/Color/sRGB.html
[2] https://color.org/chardata/rgb/sRGB.pdf
[3] https://photosauce.net/blog/post/making-a-minimal-srgb-icc-profile-part-3-choose-your-colors-carefully

The Python script:

```python
# http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html

from numpy.typing import NDArray
import numpy as np

### sRGB
# https://www.w3.org/TR/css-color-4/#predefined-sRGB
srgb_r_chromacity = np.array([0.640, 0.330])
srgb_g_chromacity = np.array([0.300, 0.600])
srgb_b_chromacity = np.array([0.150, 0.060])
##

## White points
white_point_d50 = np.array([0.345700, 0.358500])
white_point_d65 = np.array([0.312700, 0.329000])
#

r_chromacity = srgb_r_chromacity
g_chromacity = srgb_g_chromacity
b_chromacity = srgb_b_chromacity
white_point = white_point_d65

def tristmimulus_vector(chromacity: NDArray) -> NDArray:
    return np.array([
        chromacity[0] /chromacity[1],
        1,
        (1 - chromacity[0] - chromacity[1]) / chromacity[1]
    ])

tristmimulus_matrix = np.hstack((
    tristmimulus_vector(r_chromacity).reshape(3, 1),
    tristmimulus_vector(g_chromacity).reshape(3, 1),
    tristmimulus_vector(b_chromacity).reshape(3, 1),
))

scaling_factors = (np.linalg.inv(tristmimulus_matrix) @
                   tristmimulus_vector(white_point))

M = tristmimulus_matrix * scaling_factors

np.set_printoptions(formatter={'float_kind':'{:.6f}'.format})
xyz_65_to_srgb = np.linalg.inv(M)

# http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
# Let's convert from D50 to D65 using the Bradford method.
m_a = np.array([
    [0.8951000, 0.2664000, -0.1614000],
    [-0.7502000, 1.7135000, 0.0367000],
    [0.0389000, -0.0685000, 1.0296000]
])

cone_response_source = m_a @ tristmimulus_vector(white_point_d50)
cone_response_destination = m_a @ tristmimulus_vector(white_point_d65)

cone_response_ratio = cone_response_destination / cone_response_source
m = np.linalg.inv(m_a) @ np.diagflat(cone_response_ratio) @ m_a

D50_to_D65 = m
xyz_50_to_srgb = xyz_65_to_srgb @ D50_to_D65

print(xyz_50_to_srgb)
print(xyz_65_to_srgb)
```
2024-11-17 22:18:40 +01:00
Shannon Booth
fd0c63b338 LibJS: Align spec comments for ShadowRealm for HostInitializeShadowRealm
The proposed changes have been merged into the proposal with:

https://github.com/tc39/proposal-shadowrealm/commit/f20d02
2024-11-17 22:15:22 +01:00
Psychpsyo
e8c228fb93 LibWeb: Properly escape URL on error page 2024-11-17 22:14:14 +01:00
Shannon Booth
634823d5b4 LibWeb: Implement HTMLIFrameElement.sandbox 2024-11-17 22:12:29 +01:00
Shannon Booth
a4b43cae9a LibWeb: Implement HTMLLinkElement.sizes 2024-11-17 22:12:29 +01:00
Shannon Booth
061ac1f8c7 Tests: Import WPT test for DOMTokenList coverage on attributes 2024-11-17 22:12:29 +01:00
Ali Mohammad Pur
5a4d657a4e LibRegex: Avoid generating ForkJumps when jumping to the next alt block
Fixes #2398.
2024-11-17 20:12:39 +01:00
Shannon Booth
b264d18ad1 LibWeb: Fix missing auxiliary logic for cross document navigation
I noticed this missing check when trying to debug an unrelated issue. I
don't know what it could fix, but this seems like an oversight.
2024-11-17 11:37:43 -05:00
Ali Mohammad Pur
00bc22c332 LibRegex: Don't immediately ignore TempInverse in optimizer
fe46b2c141 added the reset-temp-inverse flag, but set it up so all
tempinverse ops were negated at the start of the next op; this commit
makes it so these flags actually persist for one op and not zero.

Fixes #2296.
2024-11-17 09:03:29 -05:00
Andreas Kling
69c84d3f63 LibWeb: Make sure we don't fire "once" event listeners twice
Spec bug: https://github.com/whatwg/dom/issues/1323
2024-11-17 14:56:35 +01:00
Andreas Kling
aa9ed71ff3 Tests: Import a bunch of WPT tests from /dom/events 2024-11-17 14:56:35 +01:00
Shannon Booth
0339ece565 LibWeb: Add missing initialize call to WritableStreamDefaultController 2024-11-17 08:51:41 -05:00
Shannon Booth
98dadb0ce6 LibWeb: Always return a rejected Promise for functions which throw
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
We were previously throwing an exception if the generated code was
throwing an exception before it hit the implementation of the interface.
Instead, we are meant to catch any exception, and wrap that in a
rejected promise.

For example, this was impacting the fixed test in this commit as an
exception was being thrown when invoking WebIDL::convert_to_int<T>
as the given number was out of range, and the [EnforceRange]
extended attribute decorates that attribute.

This same type of case is seen for a few tests in WPT.
2024-11-16 18:33:58 +01:00
Shannon Booth
8d93cac983 LibWeb: Return GC::Ref for some Stream promise returning functions
These will never return null.
2024-11-16 18:33:58 +01:00
Shannon Booth
ab309dcc58 LibWeb: Change URL parsing sequence for window open steps
This adapts to the latest HTML spec which fixed the issue we had
reported of:

https://github.com/whatwg/html/commit/316b83
2024-11-16 18:22:35 +01:00
sideshowbarker
ed7ec7a0f8 LibWeb: Fix accname computation for all aria-labelledby cases
This change ensures that:

- if an element for which an accessible name otherwise wouldn’t be
  computed is referenced in an aria-labelledby value, the accessible
  name for the element will be computed as expected.

- if an element has both an aria-label value and also an
  aria-labelledby value, the text from the aria-label value gets
  included in the computation of the element’s accessible name.

Otherwise, without this change, some elements with aria-labelledby
values will unexpectedly end up without accessible names, and some
elements with aria-label values will unexpectedly not have that
aria-label value included in the element’s accessible name.
2024-11-16 18:21:37 +01:00
Andreas Kling
13bd52249d LibWeb: Make Selection APIs throw on DocumentType node inputs
This matches the behavior of all major engines, and is covered by
hundreds of subtests in WPT.

Spec PR: https://github.com/w3c/selection-api/pull/342
2024-11-16 14:39:55 +01:00
Jonne Ransijn
c3783cf3bd LibIDL: Fix use-after-free in GenerateWindowOrWorkerInterfaces
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
`lexical_bases` was storing `StringView`s into `ByteString`s returned
from `LexicalPath::string()` that might no longer exist.
2024-11-16 10:30:14 +01:00
Lucas CHOLLET
63873f3809 LibWeb/CSS: Add support for the rec2020 color space in color()
This color space is often used as a reference in WPT tests, having
support for it makes us pass 15 new tests:
  - css/css-color/rec2020-001.html
  - css/css-color/rec2020-002.html
  - css/css-color/rec2020-003.html
  - css/css-color/rec2020-004.html
  - css/css-color/rec2020-005.html
  - css/css-color/predefined-011.html
  - css/css-color/predefined-012.html
2024-11-16 10:29:46 +01:00
Lucas CHOLLET
2173219eac LibGfx: Fix parameters name of Color::from_linear_srgb
And sort the declarations alphabetically while touching it.
2024-11-16 10:29:46 +01:00
Lucas CHOLLET
0b9c4b8adc LibWeb/CSS: Add support for the prophoto-rgb color space in color()
That makes us pass the following WPT tests:
 - css/css-color/prophoto-rgb-001.html
 - css/css-color/prophoto-rgb-002.html
 - css/css-color/prophoto-rgb-003.html
 - css/css-color/prophoto-rgb-004.html
 - css/css-color/prophoto-rgb-005.html
 - css/css-color/predefined-009.html
 - css/css-color/predefined-010.html
2024-11-16 10:29:46 +01:00
Lucas CHOLLET
596a4e55dd LibWeb/CSS: Add support for the display-p3 color space in color()
This color space is often used as a reference in WPT tests, having
support for it makes us pass 15 new tests:
  - css/css-color/display-p3-001.html
  - css/css-color/display-p3-002.html
  - css/css-color/display-p3-003.html
  - css/css-color/display-p3-004.html
  - css/css-color/display-p3-005.html
  - css/css-color/display-p3-006.html
  - css/css-color/lab-008.html
  - css/css-color/lch-008.html
  - css/css-color/oklab-008.html
  - css/css-color/oklch-008.html
  - css/css-color/predefined-005.html
  - css/css-color/predefined-006.html
  - css/css-color/xyz-005.html
  - css/css-color/xyz-d50-005.html
  - css/css-color/xyz-d65-005.html
2024-11-16 10:29:46 +01:00
Lucas CHOLLET
f949334a9a LibGfx: Use a more explicit formula in Color::from_linear_srgb
NFC. I prefer having the constants expanded, it makes it easier to trace
them back to the initial formula.
2024-11-16 10:29:46 +01:00
Lucas CHOLLET
a59d9a3986 LibWeb/CSS: Add support for the a98-rgb color space in color()
This makes us pass the following WPT tests:
 - css/css-color/a98rgb-001.html
 - css/css-color/a98rgb-002.html
 - css/css-color/a98rgb-003.html
 - css/css-color/a98rgb-004.html
 - css/css-color/predefined-007.html
 - css/css-color/predefined-008.html
2024-11-16 10:29:46 +01:00
963 changed files with 21167 additions and 32738 deletions

View file

@ -23,6 +23,14 @@ extension Swift.String {
} }
} }
extension AK.String {
public init(swiftString: consuming Swift.String) {
self.init() // Create empty string first, using default constructor
swiftString.withUTF8 { buffer in
self = AK.String.from_utf8_without_validation(AK.ReadonlyBytes(buffer.baseAddress!, buffer.count))
}
}
}
extension AK.StringView: ExpressibleByStringLiteral { extension AK.StringView: ExpressibleByStringLiteral {
public typealias StringLiteralType = Swift.StaticString public typealias StringLiteralType = Swift.StaticString

View file

@ -9,10 +9,8 @@
#include <AK/BitmapView.h> #include <AK/BitmapView.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/Optional.h>
#include <AK/Platform.h> #include <AK/Platform.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
#include <AK/Try.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <AK/kmalloc.h> #include <AK/kmalloc.h>

View file

@ -50,6 +50,10 @@
# cmakedefine01 CSS_TRANSITIONS_DEBUG # cmakedefine01 CSS_TRANSITIONS_DEBUG
#endif #endif
#ifndef DNS_DEBUG
# cmakedefine01 DNS_DEBUG
#endif
#ifndef EDITOR_DEBUG #ifndef EDITOR_DEBUG
# cmakedefine01 EDITOR_DEBUG # cmakedefine01 EDITOR_DEBUG
#endif #endif

View file

@ -29,6 +29,15 @@ public:
m_data[i] = data[i]; 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) constexpr IPv6Address(IPv4Address const& ipv4_address)
{ {
// IPv4 mapped IPv6 address // IPv4 mapped IPv6 address

View file

@ -58,9 +58,14 @@ LexicalPath::LexicalPath(ByteString path)
} }
} }
bool LexicalPath::is_absolute() const bool LexicalPath::is_absolute_path(StringView path)
{ {
return m_string.starts_with('/'); return path.starts_with('/');
}
bool LexicalPath::is_root() const
{
return m_string == "/";
} }
Vector<ByteString> LexicalPath::parts() const Vector<ByteString> LexicalPath::parts() const

View file

@ -26,7 +26,10 @@ public:
explicit LexicalPath(ByteString); explicit LexicalPath(ByteString);
bool is_absolute() const; 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; } ByteString const& string() const { return m_string; }
StringView dirname() const { return m_dirname; } StringView dirname() const { return m_dirname; }

View file

@ -10,14 +10,9 @@
namespace AK { namespace AK {
static bool is_absolute_path(StringView path)
{
return path.length() >= 2 && path[1] == ':';
}
static bool is_root(auto const& parts) static bool is_root(auto const& parts)
{ {
return parts.size() == 1 && is_absolute_path(parts[0]); return parts.size() == 1 && LexicalPath::is_absolute_path(parts[0]);
} }
LexicalPath::LexicalPath(ByteString path) LexicalPath::LexicalPath(ByteString path)
@ -45,9 +40,14 @@ LexicalPath::LexicalPath(ByteString path)
} }
} }
bool LexicalPath::is_absolute() const bool LexicalPath::is_absolute_path(StringView path)
{ {
return is_absolute_path(m_string); return path.length() >= 2 && path[1] == ':';
}
bool LexicalPath::is_root() const
{
return AK::is_root(m_parts);
} }
Vector<ByteString> LexicalPath::parts() const Vector<ByteString> LexicalPath::parts() const
@ -86,7 +86,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
continue; continue;
if (part == ".." && !canonical_parts.is_empty()) { if (part == ".." && !canonical_parts.is_empty()) {
// At the root, .. does nothing. // At the root, .. does nothing.
if (is_root(canonical_parts)) if (AK::is_root(canonical_parts))
continue; continue;
// A .. and a previous non-.. part cancel each other. // A .. and a previous non-.. part cancel each other.
if (canonical_parts.last() != "..") { if (canonical_parts.last() != "..") {
@ -100,7 +100,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
StringBuilder builder; StringBuilder builder;
builder.join('\\', canonical_parts); builder.join('\\', canonical_parts);
// "X:" -> "X:\" // "X:" -> "X:\"
if (is_root(canonical_parts)) if (AK::is_root(canonical_parts))
builder.append('\\'); builder.append('\\');
path = builder.to_byte_string(); path = builder.to_byte_string();
return path == "" ? "." : path; return path == "" ? "." : path;

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/StdLibExtras.h>
#include <AK/Types.h> #include <AK/Types.h>
namespace AK { namespace AK {

View file

@ -73,16 +73,20 @@ String human_readable_size_long(u64 size, UseThousandsSeparator use_thousands_se
return MUST(String::formatted("{} ({} bytes)", human_readable_size_string, size)); return MUST(String::formatted("{} ({} bytes)", human_readable_size_string, size));
} }
String human_readable_time(i64 time_in_seconds) String human_readable_time(Duration duration)
{ {
auto days = time_in_seconds / 86400; auto milliseconds = duration.to_milliseconds();
time_in_seconds = time_in_seconds % 86400;
auto hours = time_in_seconds / 3600; auto days = milliseconds / 86400000;
time_in_seconds = time_in_seconds % 3600; milliseconds = milliseconds % 86400000;
auto minutes = time_in_seconds / 60; auto hours = milliseconds / 3600000;
time_in_seconds = time_in_seconds % 60; milliseconds = milliseconds % 3600000;
auto minutes = milliseconds / 60000;
milliseconds = milliseconds % 60000;
auto seconds = static_cast<double>(milliseconds) / 1000.0;
StringBuilder builder; StringBuilder builder;
@ -95,7 +99,7 @@ String human_readable_time(i64 time_in_seconds)
if (minutes > 0) if (minutes > 0)
builder.appendff("{} minute{} ", minutes, minutes == 1 ? "" : "s"); builder.appendff("{} minute{} ", minutes, minutes == 1 ? "" : "s");
builder.appendff("{} second{}", time_in_seconds, time_in_seconds == 1 ? "" : "s"); builder.appendff("{:.3} second{}", seconds, seconds == 1.0 ? "" : "s");
return MUST(builder.to_string()); return MUST(builder.to_string());
} }

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <AK/String.h> #include <AK/String.h>
#include <AK/Time.h>
namespace AK { namespace AK {
@ -24,7 +25,7 @@ String human_readable_size(u64 size, HumanReadableBasedOn based_on = HumanReadab
String human_readable_quantity(u64 quantity, HumanReadableBasedOn based_on = HumanReadableBasedOn::Base2, StringView unit = "B"sv, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No); String human_readable_quantity(u64 quantity, HumanReadableBasedOn based_on = HumanReadableBasedOn::Base2, StringView unit = "B"sv, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No);
String human_readable_size_long(u64 size, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No); String human_readable_size_long(u64 size, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No);
String human_readable_time(i64 time_in_seconds); String human_readable_time(Duration);
String human_readable_digital_time(i64 time_in_seconds); String human_readable_digital_time(i64 time_in_seconds);
} }

View file

@ -18,4 +18,11 @@
# define SWIFT_CONFORMS_TO_PROTOCOL(protocol) # define SWIFT_CONFORMS_TO_PROTOCOL(protocol)
# define SWIFT_COMPUTED_PROPERTY # define SWIFT_COMPUTED_PROPERTY
# define SWIFT_MUTATING # define SWIFT_MUTATING
# define SWIFT_UNCHECKED_SENDABLE
# define SWIFT_NONCOPYABLE
# define SWIFT_NONESCAPABLE
# define SWIFT_ESCAPABLE
# define SWIFT_ESCAPABLE_IF(...)
# define SWIFT_RETURNS_RETAINED
# define SWIFT_RETURNS_UNRETAINED
#endif #endif

View file

@ -147,6 +147,7 @@ using __ptrdiff_t = __PTRDIFF_TYPE__;
# if defined(AK_OS_WINDOWS) # if defined(AK_OS_WINDOWS)
using ssize_t = AK::Detail::MakeSigned<size_t>; using ssize_t = AK::Detail::MakeSigned<size_t>;
using mode_t = unsigned short; using mode_t = unsigned short;
using pid_t = int;
# endif # endif
#endif #endif

View file

@ -35,5 +35,5 @@ you are welcome to ask on [Discord](../README.md#get-in-touch-and-participate).
* [LibWeb: From Loading to Painting](LibWebFromLoadingToPainting.md) * [LibWeb: From Loading to Painting](LibWebFromLoadingToPainting.md)
* [LibWeb: Browsing Contexts and Navigables](BrowsingContextsAndNavigables.md) * [LibWeb: Browsing Contexts and Navigables](BrowsingContextsAndNavigables.md)
* [How to Add an IDL File](AddNewIDLFile.md) * [How to Add an IDL File](AddNewIDLFile.md)
* [LibWeb Code Style & Patterns](Browser/Patterns.md) * [LibWeb Code Style & Patterns](LibWebPatterns.md)
* [CSS Generated Files](CSSGeneratedFiles.md) * [CSS Generated Files](CSSGeneratedFiles.md)

View file

@ -1,6 +1,6 @@
# Testing Ladybird # 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`. 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. The test should be either a Text, Layout, Ref, or Screenshot test depending on the feature.

View file

@ -9,8 +9,6 @@
#include <AK/Assertions.h> #include <AK/Assertions.h>
#include <AK/BinarySearch.h> #include <AK/BinarySearch.h>
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <string.h>
#include <LibCompress/Deflate.h> #include <LibCompress/Deflate.h>
#include <LibCompress/Huffman.h> #include <LibCompress/Huffman.h>
@ -480,10 +478,7 @@ DeflateCompressor::DeflateCompressor(NonnullOwnPtr<LittleEndianOutputBitStream>
m_distance_frequencies.fill(0); m_distance_frequencies.fill(0);
} }
DeflateCompressor::~DeflateCompressor() DeflateCompressor::~DeflateCompressor() = default;
{
VERIFY(m_finished);
}
ErrorOr<Bytes> DeflateCompressor::read_some(Bytes) ErrorOr<Bytes> DeflateCompressor::read_some(Bytes)
{ {

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Compress {
class DeflateCompressor;
class DeflateDecompressor;
class GzipCompressor;
class GzipDecompressor;
class ZlibCompressor;
class ZlibDecompressor;
}

View file

@ -11,9 +11,6 @@
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <AK/String.h> #include <AK/String.h>
#include <LibCore/DateTime.h> #include <LibCore/DateTime.h>
#include <LibCore/File.h>
#include <LibCore/MappedFile.h>
#include <LibCore/System.h>
namespace Compress { namespace Compress {
@ -189,8 +186,25 @@ ErrorOr<size_t> GzipDecompressor::write_some(ReadonlyBytes)
return Error::from_errno(EBADF); return Error::from_errno(EBADF);
} }
GzipCompressor::GzipCompressor(MaybeOwned<Stream> stream) ErrorOr<NonnullOwnPtr<GzipCompressor>> GzipCompressor::create(MaybeOwned<Stream> output_stream)
: m_output_stream(move(stream)) {
BlockHeader header;
header.identification_1 = 0x1f;
header.identification_2 = 0x8b;
header.compression_method = 0x08;
header.flags = 0;
header.modification_time = 0;
header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression
header.operating_system = 3; // unix
TRY(output_stream->write_until_depleted({ &header, sizeof(header) }));
auto deflate_compressor = TRY(DeflateCompressor::construct(MaybeOwned(*output_stream)));
return adopt_own(*new GzipCompressor { move(output_stream), move(deflate_compressor) });
}
GzipCompressor::GzipCompressor(MaybeOwned<Stream> output_stream, NonnullOwnPtr<DeflateCompressor> deflate_compressor)
: m_output_stream(move(output_stream))
, m_deflate_compressor(move(deflate_compressor))
{ {
} }
@ -201,25 +215,27 @@ ErrorOr<Bytes> GzipCompressor::read_some(Bytes)
ErrorOr<size_t> GzipCompressor::write_some(ReadonlyBytes bytes) ErrorOr<size_t> GzipCompressor::write_some(ReadonlyBytes bytes)
{ {
BlockHeader header; VERIFY(!m_finished);
header.identification_1 = 0x1f;
header.identification_2 = 0x8b; TRY(m_deflate_compressor->write_until_depleted(bytes));
header.compression_method = 0x08; m_total_bytes += bytes.size();
header.flags = 0; m_crc32.update(bytes);
header.modification_time = 0;
header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression
header.operating_system = 3; // unix
TRY(m_output_stream->write_until_depleted({ &header, sizeof(header) }));
auto compressed_stream = TRY(DeflateCompressor::construct(MaybeOwned(*m_output_stream)));
TRY(compressed_stream->write_until_depleted(bytes));
TRY(compressed_stream->final_flush());
Crypto::Checksum::CRC32 crc32;
crc32.update(bytes);
TRY(m_output_stream->write_value<LittleEndian<u32>>(crc32.digest()));
TRY(m_output_stream->write_value<LittleEndian<u32>>(bytes.size()));
return bytes.size(); return bytes.size();
} }
ErrorOr<void> GzipCompressor::finish()
{
VERIFY(!m_finished);
m_finished = true;
TRY(m_deflate_compressor->final_flush());
TRY(m_output_stream->write_value<LittleEndian<u32>>(m_crc32.digest()));
TRY(m_output_stream->write_value<LittleEndian<u32>>(m_total_bytes));
return {};
}
bool GzipCompressor::is_eof() const bool GzipCompressor::is_eof() const
{ {
return true; return true;
@ -237,12 +253,14 @@ void GzipCompressor::close()
ErrorOr<ByteBuffer> GzipCompressor::compress_all(ReadonlyBytes bytes) ErrorOr<ByteBuffer> GzipCompressor::compress_all(ReadonlyBytes bytes)
{ {
auto output_stream = TRY(try_make<AllocatingMemoryStream>()); auto output_stream = TRY(try_make<AllocatingMemoryStream>());
GzipCompressor gzip_stream { MaybeOwned<Stream>(*output_stream) }; auto gzip_stream = TRY(GzipCompressor::create(MaybeOwned { *output_stream }));
TRY(gzip_stream.write_until_depleted(bytes)); TRY(gzip_stream->write_until_depleted(bytes));
TRY(gzip_stream->finish());
auto buffer = TRY(ByteBuffer::create_uninitialized(output_stream->used_buffer_size())); auto buffer = TRY(ByteBuffer::create_uninitialized(output_stream->used_buffer_size()));
TRY(output_stream->read_until_filled(buffer.bytes())); TRY(output_stream->read_until_filled(buffer.bytes()));
return buffer; return buffer;
} }

View file

@ -49,7 +49,7 @@ public:
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override; virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
virtual bool is_eof() const override; virtual bool is_eof() const override;
virtual bool is_open() const override { return true; } virtual bool is_open() const override { return true; }
virtual void close() override {}; virtual void close() override { }
static ErrorOr<ByteBuffer> decompress_all(ReadonlyBytes); static ErrorOr<ByteBuffer> decompress_all(ReadonlyBytes);
@ -83,7 +83,7 @@ private:
class GzipCompressor final : public Stream { class GzipCompressor final : public Stream {
public: public:
GzipCompressor(MaybeOwned<Stream>); static ErrorOr<NonnullOwnPtr<GzipCompressor>> create(MaybeOwned<Stream>);
virtual ErrorOr<Bytes> read_some(Bytes) override; virtual ErrorOr<Bytes> read_some(Bytes) override;
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override; virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
@ -93,8 +93,17 @@ public:
static ErrorOr<ByteBuffer> compress_all(ReadonlyBytes bytes); static ErrorOr<ByteBuffer> compress_all(ReadonlyBytes bytes);
ErrorOr<void> finish();
private: private:
GzipCompressor(MaybeOwned<Stream>, NonnullOwnPtr<DeflateCompressor>);
MaybeOwned<Stream> m_output_stream; MaybeOwned<Stream> m_output_stream;
NonnullOwnPtr<DeflateCompressor> m_deflate_compressor;
Crypto::Checksum::CRC32 m_crc32;
size_t m_total_bytes { 0 };
bool m_finished { false };
}; };
} }

View file

@ -17,31 +17,35 @@ namespace Compress {
ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Stream> stream) ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Stream> stream)
{ {
auto header = TRY(stream->read_value<ZlibHeader>()); return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(move(stream)));
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported");
if (header.present_dictionary)
return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported");
if (header.as_u16 % 31 != 0)
return Error::from_string_literal("Zlib error correction code does not match");
auto bit_stream = make<LittleEndianInputBitStream>(move(stream));
auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream)));
return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(header, move(deflate_stream)));
} }
ZlibDecompressor::ZlibDecompressor(ZlibHeader header, NonnullOwnPtr<Stream> stream) ZlibDecompressor::ZlibDecompressor(MaybeOwned<Stream> stream)
: m_header(header) : m_has_seen_header(false)
, m_stream(move(stream)) , m_stream(move(stream))
{ {
} }
ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes) ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes)
{ {
if (!m_has_seen_header) {
auto header = TRY(m_stream->read_value<ZlibHeader>());
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported");
if (header.present_dictionary)
return Error::from_string_literal("Zlib compression with a pre-defined dictionary is currently not supported");
if (header.as_u16 % 31 != 0)
return Error::from_string_literal("Zlib error correction code does not match");
auto bit_stream = make<LittleEndianInputBitStream>(move(m_stream));
auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream)));
m_stream = move(deflate_stream);
m_has_seen_header = true;
}
return m_stream->read_some(bytes); return m_stream->read_some(bytes);
} }
@ -84,10 +88,7 @@ ZlibCompressor::ZlibCompressor(MaybeOwned<Stream> stream, NonnullOwnPtr<Stream>
{ {
} }
ZlibCompressor::~ZlibCompressor() ZlibCompressor::~ZlibCompressor() = default;
{
VERIFY(m_finished);
}
ErrorOr<void> ZlibCompressor::write_header(ZlibCompressionMethod compression_method, ZlibCompressionLevel compression_level) ErrorOr<void> ZlibCompressor::write_header(ZlibCompressionMethod compression_method, ZlibCompressionLevel compression_level)
{ {

View file

@ -55,10 +55,10 @@ public:
virtual void close() override; virtual void close() override;
private: private:
ZlibDecompressor(ZlibHeader, NonnullOwnPtr<Stream>); ZlibDecompressor(MaybeOwned<Stream>);
ZlibHeader m_header; bool m_has_seen_header { false };
NonnullOwnPtr<Stream> m_stream; MaybeOwned<Stream> m_stream;
}; };
class ZlibCompressor : public Stream { class ZlibCompressor : public Stream {

View file

@ -28,7 +28,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size) 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) if (!map_handle)
return Error::from_windows_error(GetLastError()); return Error::from_windows_error(GetLastError());

View file

@ -12,8 +12,6 @@
#include <LibCore/Directory.h> #include <LibCore/Directory.h>
#include <LibCore/StandardPaths.h> #include <LibCore/StandardPaths.h>
#include <LibCore/System.h> #include <LibCore/System.h>
#include <pwd.h>
#include <sys/types.h>
namespace Core { namespace Core {

View file

@ -4,10 +4,8 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include "Directory.h" #include <LibCore/Directory.h>
#include "DirIterator.h" #include <LibCore/System.h>
#include "System.h"
#include <dirent.h>
namespace Core { namespace Core {
@ -31,6 +29,7 @@ Directory::~Directory()
MUST(System::close(m_directory_fd)); MUST(System::close(m_directory_fd));
} }
#ifndef AK_OS_WINDOWS
ErrorOr<void> Directory::chown(uid_t uid, gid_t gid) ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
{ {
if (m_directory_fd == -1) 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)); TRY(Core::System::fchown(m_directory_fd, uid, gid));
return {}; return {};
} }
#endif
ErrorOr<bool> Directory::is_valid_directory(int fd) 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) ErrorOr<void> Directory::ensure_directory(LexicalPath const& path, mode_t creation_mode)
{ {
if (path.basename() == "/" || path.basename() == ".") if (path.is_root() || path.string() == ".")
return {}; return {};
TRY(ensure_directory(path.parent(), creation_mode)); TRY(ensure_directory(path.parent(), creation_mode));

View file

@ -10,14 +10,11 @@
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/Format.h> #include <AK/Format.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/IterationDecision.h>
#include <AK/LexicalPath.h> #include <AK/LexicalPath.h>
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/Optional.h>
#include <LibCore/DirIterator.h> #include <LibCore/DirIterator.h>
#include <LibCore/DirectoryEntry.h> #include <LibCore/DirectoryEntry.h>
#include <LibCore/File.h> #include <LibCore/File.h>
#include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
namespace Core { namespace Core {
@ -51,7 +48,9 @@ public:
static ErrorOr<void> for_each_entry(StringView path, DirIterator::Flags, ForEachEntryCallback); static ErrorOr<void> for_each_entry(StringView path, DirIterator::Flags, ForEachEntryCallback);
ErrorOr<void> for_each_entry(DirIterator::Flags, ForEachEntryCallback); ErrorOr<void> for_each_entry(DirIterator::Flags, ForEachEntryCallback);
#ifndef AK_OS_WINDOWS
ErrorOr<void> chown(uid_t, gid_t); ErrorOr<void> chown(uid_t, gid_t);
#endif
static ErrorOr<bool> is_valid_directory(int fd); static ErrorOr<bool> is_valid_directory(int fd);

View file

@ -61,6 +61,24 @@ struct ArgvList {
} }
}; };
Process::Process(Process&& other)
: m_pid(exchange(other.m_pid, 0))
, m_should_disown(exchange(other.m_should_disown, false))
{
}
Process& Process::operator=(Process&& other)
{
m_pid = exchange(other.m_pid, 0);
m_should_disown = exchange(other.m_should_disown, false);
return *this;
}
Process::~Process()
{
(void)disown();
}
Process Process::current() Process Process::current()
{ {
auto p = Process { getpid() }; auto p = Process { getpid() };
@ -121,7 +139,7 @@ ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
return Process { pid }; return Process { pid };
} }
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child) ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child)
{ {
auto process = TRY(spawn({ auto process = TRY(spawn({
.executable = path, .executable = path,
@ -131,14 +149,11 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> argument
if (keep_as_child == KeepAsChild::No) if (keep_as_child == KeepAsChild::No)
TRY(process.disown()); TRY(process.disown());
else {
// FIXME: This won't be needed if return value is changed to Process. return process;
process.m_should_disown = false;
}
return process.pid();
} }
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child) ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child)
{ {
Vector<ByteString> backing_strings; Vector<ByteString> backing_strings;
backing_strings.ensure_capacity(arguments.size()); backing_strings.ensure_capacity(arguments.size());
@ -153,29 +168,8 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> argument
if (keep_as_child == KeepAsChild::No) if (keep_as_child == KeepAsChild::No)
TRY(process.disown()); TRY(process.disown());
else
process.m_should_disown = false;
return process.pid();
}
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<char const*> arguments, ByteString working_directory, KeepAsChild keep_as_child) return process;
{
Vector<ByteString> backing_strings;
backing_strings.ensure_capacity(arguments.size());
for (auto const& argument : arguments)
backing_strings.append(argument);
auto process = TRY(spawn({
.executable = path,
.arguments = backing_strings,
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : Optional<ByteString> { working_directory },
}));
if (keep_as_child == KeepAsChild::No)
TRY(process.disown());
else
process.m_should_disown = false;
return process.pid();
} }
ErrorOr<String> Process::get_name() ErrorOr<String> Process::get_name()
@ -321,6 +315,11 @@ void Process::wait_for_debugger_and_break()
} }
} }
pid_t Process::pid() const
{
return m_pid;
}
ErrorOr<void> Process::disown() ErrorOr<void> Process::disown()
{ {
if (m_pid != 0 && m_should_disown) { if (m_pid != 0 && m_should_disown) {
@ -336,19 +335,19 @@ ErrorOr<void> Process::disown()
} }
} }
ErrorOr<bool> Process::wait_for_termination() ErrorOr<int> Process::wait_for_termination()
{ {
VERIFY(m_pid > 0); VERIFY(m_pid > 0);
bool exited_with_code_0 = true; int exit_code = -1;
int status; int status;
if (waitpid(m_pid, &status, 0) == -1) if (waitpid(m_pid, &status, 0) == -1)
return Error::from_syscall("waitpid"sv, errno); return Error::from_syscall("waitpid"sv, errno);
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
exited_with_code_0 &= WEXITSTATUS(status) == 0; exit_code = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
exited_with_code_0 = false; exit_code = 128 + WTERMSIG(status);
} else if (WIFSTOPPED(status)) { } else if (WIFSTOPPED(status)) {
// This is only possible if the child process is being traced by us. // This is only possible if the child process is being traced by us.
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@ -357,7 +356,7 @@ ErrorOr<bool> Process::wait_for_termination()
} }
m_should_disown = false; m_should_disown = false;
return exited_with_code_0; return exit_code;
} }
} }

View file

@ -53,33 +53,15 @@ public:
No No
}; };
Process(Process&& other) Process(Process&& other);
: m_pid(exchange(other.m_pid, 0)) Process& operator=(Process&& other);
, m_should_disown(exchange(other.m_should_disown, false)) ~Process();
{
}
Process& operator=(Process&& other)
{
m_pid = exchange(other.m_pid, 0);
m_should_disown = exchange(other.m_should_disown, false);
return *this;
}
~Process()
{
(void)disown();
}
static ErrorOr<Process> spawn(ProcessSpawnOptions const& options); static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
static Process current(); static Process current();
// FIXME: Make the following 2 functions return Process instance or delete them. static ErrorOr<Process> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No); static ErrorOr<Process> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
// FIXME: Remove this. char const* should not exist on this level of abstraction.
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<char const*> arguments = {}, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
static ErrorOr<String> get_name(); static ErrorOr<String> get_name();
enum class SetThreadName { enum class SetThreadName {
@ -91,15 +73,17 @@ public:
static void wait_for_debugger_and_break(); static void wait_for_debugger_and_break();
static ErrorOr<bool> is_being_debugged(); static ErrorOr<bool> is_being_debugged();
pid_t pid() const { return m_pid; } pid_t pid() const;
#ifndef AK_OS_WINDOWS
ErrorOr<void> disown(); ErrorOr<void> disown();
#endif
// FIXME: Make it return an exit code. ErrorOr<int> wait_for_termination();
ErrorOr<bool> wait_for_termination();
private: private:
Process(pid_t pid) #ifndef AK_OS_WINDOWS
Process(pid_t pid = -1)
: m_pid(pid) : m_pid(pid)
, m_should_disown(true) , m_should_disown(true)
{ {
@ -107,6 +91,14 @@ private:
pid_t m_pid; pid_t m_pid;
bool m_should_disown; bool m_should_disown;
#else
Process(void* handle = 0)
: m_handle(handle)
{
}
void* m_handle;
#endif
}; };
} }

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023-2024, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2024, stasoid <stasoid@yahoo.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <AK/Utf16View.h>
#include <LibCore/Process.h>
#include <windows.h>
namespace Core {
Process::Process(Process&& other)
: m_handle(exchange(other.m_handle, nullptr))
{
}
Process& Process::operator=(Process&& other)
{
m_handle = exchange(other.m_handle, nullptr);
return *this;
}
Process::~Process()
{
if (m_handle)
CloseHandle(m_handle);
}
Process Process::current()
{
return GetCurrentProcess();
}
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
{
// file actions are not supported
VERIFY(options.file_actions.is_empty());
char const* program_path = 0;
StringBuilder builder;
if (options.search_for_executable_in_path)
builder.appendff("\"{}\" ", options.executable);
else
program_path = options.executable.characters();
builder.join(' ', options.arguments);
builder.append('\0');
ByteBuffer command_line = TRY(builder.to_byte_buffer());
auto curdir = options.working_directory.has_value() ? options.working_directory->characters() : 0;
STARTUPINFO startup_info = {};
PROCESS_INFORMATION process_info = {};
BOOL result = CreateProcess(
program_path,
(char*)command_line.data(),
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
curdir,
&startup_info,
&process_info);
if (!result)
return Error::from_windows_error(GetLastError());
CloseHandle(process_info.hThread);
return Process(process_info.hProcess);
}
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild)
{
return spawn({
.executable = path,
.arguments = Vector<ByteString> { arguments },
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
});
}
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild)
{
Vector<ByteString> backing_strings;
backing_strings.ensure_capacity(arguments.size());
for (auto argument : arguments)
backing_strings.append(argument);
return spawn({
.executable = path,
.arguments = backing_strings,
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
});
}
// Get the full path of the executable file of the current process
ErrorOr<String> Process::get_name()
{
wchar_t path[MAX_PATH] = {};
DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH);
if (!length)
return Error::from_windows_error(GetLastError());
return String::from_utf16(Utf16View { { (u16*)path, length } });
}
ErrorOr<void> Process::set_name(StringView, SetThreadName)
{
// Process::set_name() probably cannot be implemented on Windows.
return {};
}
ErrorOr<bool> Process::is_being_debugged()
{
return IsDebuggerPresent();
}
// Forces the process to sleep until a debugger is attached, then breaks.
void Process::wait_for_debugger_and_break()
{
bool print_message = true;
for (;;) {
if (IsDebuggerPresent()) {
DebugBreak();
return;
}
if (print_message) {
dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), GetCurrentProcessId());
print_message = false;
}
Sleep(100);
}
}
pid_t Process::pid() const
{
return GetProcessId(m_handle);
}
ErrorOr<int> Process::wait_for_termination()
{
auto result = WaitForSingleObject(m_handle, INFINITE);
if (result == WAIT_FAILED)
return Error::from_windows_error(GetLastError());
DWORD exit_code = 0;
if (!GetExitCodeProcess(m_handle, &exit_code))
return Error::from_windows_error(GetLastError());
return exit_code;
}
}

View file

@ -159,6 +159,7 @@ class TCPSocket final : public Socket {
public: public:
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(ByteString const& host, u16 port); 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);
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address, ByteString const&) { return connect(address); }
static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd); static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd);
TCPSocket(TCPSocket&& other) TCPSocket(TCPSocket&& other)
@ -220,6 +221,7 @@ class UDPSocket final : public Socket {
public: public:
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(ByteString const& host, u16 port, Optional<AK::Duration> timeout = {}); 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, 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) UDPSocket(UDPSocket&& other)
: Socket(static_cast<Socket&&>(other)) : Socket(static_cast<Socket&&>(other))

View file

@ -12,7 +12,8 @@
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/ScopeGuard.h> #include <AK/ScopeGuard.h>
#include <LibCore/System.h> #include <LibCore/System.h>
#include <WinSock2.h> #include <Windows.h>
#include <direct.h>
#include <io.h> #include <io.h>
namespace Core::System { namespace Core::System {
@ -20,9 +21,21 @@ namespace Core::System {
ErrorOr<int> open(StringView path, int options, mode_t mode) ErrorOr<int> open(StringView path, int options, mode_t mode)
{ {
ByteString string_path = path; ByteString string_path = path;
int rc = _open(string_path.characters(), options, mode); auto sz_path = string_path.characters();
if (rc < 0) int rc = _open(sz_path, options, mode);
return Error::from_syscall("open"sv, -errno); 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; return rc;
} }
@ -88,4 +101,69 @@ ErrorOr<void> ioctl(int, unsigned, ...)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
ErrorOr<ByteString> getcwd()
{
auto* cwd = _getcwd(nullptr, 0);
if (!cwd)
return Error::from_syscall("getcwd"sv, -errno);
ByteString string_cwd(cwd);
free(cwd);
return string_cwd;
}
ErrorOr<struct stat> stat(StringView path)
{
if (path.is_null())
return Error::from_syscall("stat"sv, -EFAULT);
struct stat st = {};
ByteString path_string = path;
if (::stat(path_string.characters(), &st) < 0)
return Error::from_syscall("stat"sv, -errno);
return st;
}
ErrorOr<void> rmdir(StringView path)
{
if (path.is_null())
return Error::from_errno(EFAULT);
ByteString path_string = path;
if (_rmdir(path_string.characters()) < 0)
return Error::from_syscall("rmdir"sv, -errno);
return {};
}
ErrorOr<void> unlink(StringView path)
{
if (path.is_null())
return Error::from_errno(EFAULT);
ByteString path_string = path;
if (_unlink(path_string.characters()) < 0)
return Error::from_syscall("unlink"sv, -errno);
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();
}
} }

View file

@ -8,7 +8,6 @@
#include <AK/Time.h> #include <AK/Time.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto::ASN1 { namespace Crypto::ASN1 {

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/BitmapView.h> #include <AK/BitmapView.h>
#include <AK/Result.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibCrypto/ASN1/ASN1.h> #include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h>

View file

@ -6,9 +6,8 @@
#pragma once #pragma once
#include <AK/ByteBuffer.h>
#include <AK/Span.h> #include <AK/Span.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/DER.h>
namespace Crypto { namespace Crypto {

View file

@ -10,7 +10,6 @@
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/Endian.h> #include <AK/Endian.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto::Authentication { namespace Crypto::Authentication {

View file

@ -6,9 +6,8 @@
*/ */
#include "UnsignedBigIntegerAlgorithms.h" #include "UnsignedBigIntegerAlgorithms.h"
#include <AK/BigIntBase.h>
#include <AK/BuiltinWrappers.h>
#include <AK/NumericLimits.h> #include <AK/NumericLimits.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto { namespace Crypto {

View file

@ -7,7 +7,6 @@
#include "UnsignedBigIntegerAlgorithms.h" #include "UnsignedBigIntegerAlgorithms.h"
#include <AK/BigIntBase.h> #include <AK/BigIntBase.h>
#include <AK/BuiltinWrappers.h>
namespace Crypto { namespace Crypto {

View file

@ -9,7 +9,6 @@
#include <AK/Concepts.h> #include <AK/Concepts.h>
#include <AK/Span.h> #include <AK/Span.h>
#include <AK/String.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto { namespace Crypto {

View file

@ -601,6 +601,11 @@ bool UnsignedBigInteger::operator<(UnsignedBigInteger const& other) const
return false; return false;
} }
bool UnsignedBigInteger::operator<=(UnsignedBigInteger const& other) const
{
return *this < other || *this == other;
}
bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const
{ {
return *this != other && !(*this < other); return *this != other && !(*this < other);

View file

@ -9,7 +9,6 @@
#pragma once #pragma once
#include <AK/BigIntBase.h> #include <AK/BigIntBase.h>
#include <AK/ByteBuffer.h>
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/Concepts.h> #include <AK/Concepts.h>
#include <AK/Span.h> #include <AK/Span.h>
@ -128,6 +127,7 @@ public:
[[nodiscard]] bool operator==(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator==(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator<=(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
@ -171,8 +171,26 @@ struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&); ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&);
}; };
inline Crypto::UnsignedBigInteger inline Crypto::UnsignedBigInteger operator""_bigint(char const* string, size_t length)
operator""_bigint(char const* string, size_t length)
{ {
return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length })); return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length }));
} }
inline Crypto::UnsignedBigInteger operator""_bigint(unsigned long long value)
{
auto result = Crypto::UnsignedBigInteger { static_cast<u64>(value) };
VERIFY(!result.is_invalid());
return result;
}
inline Crypto::UnsignedBigInteger operator""_bigint(long double value)
{
VERIFY(value >= 0);
VERIFY(value < static_cast<long double>(NumericLimits<double>::max()));
auto result = Crypto::UnsignedBigInteger { static_cast<double>(value) };
VERIFY(!result.is_invalid());
return result;
}

View file

@ -5,7 +5,6 @@
*/ */
#include <AK/Array.h> #include <AK/Array.h>
#include <AK/NumericLimits.h>
#include <AK/Span.h> #include <AK/Span.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibCrypto/Checksum/CRC32.h> #include <LibCrypto/Checksum/CRC32.h>

View file

@ -8,7 +8,6 @@
#pragma once #pragma once
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/Vector.h>
#include <LibCrypto/Cipher/Cipher.h> #include <LibCrypto/Cipher/Cipher.h>
#include <LibCrypto/Cipher/Mode/CBC.h> #include <LibCrypto/Cipher/Mode/CBC.h>
#include <LibCrypto/Cipher/Mode/CTR.h> #include <LibCrypto/Cipher/Mode/CTR.h>

View file

@ -6,7 +6,7 @@
#pragma once #pragma once
#include <AK/ByteBuffer.h> #include <AK/Span.h>
namespace Crypto::Cipher { namespace Crypto::Cipher {

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/Optional.h>
#include <AK/Span.h> #include <AK/Span.h>
#include <AK/Types.h> #include <AK/Types.h>

View file

@ -6,8 +6,6 @@
#pragma once #pragma once
#include <AK/Random.h>
namespace Crypto::Curves { namespace Crypto::Curves {
class Curve25519 { class Curve25519 {

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/ByteBuffer.h> #include <AK/ByteBuffer.h>
#include <LibCrypto/Curves/EllipticCurve.h>
namespace Crypto::Curves { namespace Crypto::Curves {

View file

@ -5,7 +5,6 @@
*/ */
#include <AK/ByteReader.h> #include <AK/ByteReader.h>
#include <AK/Endian.h>
#include <AK/Random.h> #include <AK/Random.h>
#include <LibCrypto/Curves/Curve25519.h> #include <LibCrypto/Curves/Curve25519.h>
#include <LibCrypto/Curves/X25519.h> #include <LibCrypto/Curves/X25519.h>

View file

@ -5,6 +5,7 @@
*/ */
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/Random.h>
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h> #include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
#include <LibCrypto/NumberTheory/ModularFunctions.h> #include <LibCrypto/NumberTheory/ModularFunctions.h>

View file

@ -6,7 +6,6 @@
#pragma once #pragma once
#include <AK/Random.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto::NumberTheory { namespace Crypto::NumberTheory {

View 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

File diff suppressed because it is too large Load diff

702
Libraries/LibDNS/Message.h Normal file
View file

@ -0,0 +1,702 @@
/*
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Base64.h>
#include <AK/IPv4Address.h>
#include <AK/IPv6Address.h>
#include <AK/RedBlackTree.h>
#include <AK/Time.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;
};
}
}

489
Libraries/LibDNS/Resolver.h Normal file
View file

@ -0,0 +1,489 @@
/*
* 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/MaybeOwned.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;
};
}

View file

@ -1,7 +1,12 @@
set(SOURCES set (SOURCES FileSystem.cpp)
FileSystem.cpp if (NOT WIN32)
TempFile.cpp list(APPEND SOURCES TempFile.cpp)
) endif()
serenity_lib(LibFileSystem filesystem) serenity_lib(LibFileSystem filesystem)
target_link_libraries(LibFileSystem PRIVATE LibCoreMinimal) target_link_libraries(LibFileSystem PRIVATE LibCoreMinimal)
if (WIN32)
find_path(DIRENT_INCLUDE_DIR dirent.h REQUIRED)
target_include_directories(LibFileSystem PRIVATE ${DIRENT_INCLUDE_DIR})
endif()

View file

@ -10,12 +10,13 @@
#include <LibCore/DirIterator.h> #include <LibCore/DirIterator.h>
#include <LibCore/System.h> #include <LibCore/System.h>
#include <LibFileSystem/FileSystem.h> #include <LibFileSystem/FileSystem.h>
#include <limits.h>
#if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC) #if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC)
# include <sys/disk.h> # include <sys/disk.h>
#elif defined(AK_OS_LINUX) #elif defined(AK_OS_LINUX)
# include <linux/fs.h> # include <linux/fs.h>
#elif defined(AK_OS_WINDOWS)
# include <dirent.h>
#endif #endif
// On Linux distros that use glibc `basename` is defined as a macro that expands to `__xpg_basename`, so we undefine it // On Linux distros that use glibc `basename` is defined as a macro that expands to `__xpg_basename`, so we undefine it
@ -32,16 +33,19 @@ ErrorOr<ByteString> current_working_directory()
ErrorOr<ByteString> absolute_path(StringView path) ErrorOr<ByteString> absolute_path(StringView path)
{ {
#ifndef AK_OS_WINDOWS
if (exists(path)) if (exists(path))
return real_path(path); return real_path(path);
#endif
if (path.starts_with("/"sv)) if (LexicalPath::is_absolute_path(path))
return LexicalPath::canonicalized_path(path); return LexicalPath::canonicalized_path(path);
auto working_directory = TRY(current_working_directory()); auto working_directory = TRY(current_working_directory());
return LexicalPath::absolute_path(working_directory, path); return LexicalPath::absolute_path(working_directory, path);
} }
#ifndef AK_OS_WINDOWS
ErrorOr<ByteString> real_path(StringView path) ErrorOr<ByteString> real_path(StringView path)
{ {
if (path.is_null()) if (path.is_null())
@ -56,6 +60,13 @@ ErrorOr<ByteString> real_path(StringView path)
return ByteString { real_path, strlen(real_path) }; return ByteString { real_path, strlen(real_path) };
} }
#else
// NOTE: real_path on Windows does not resolve symlinks
ErrorOr<ByteString> real_path(StringView path)
{
return absolute_path(path);
}
#endif
bool exists(StringView path) bool exists(StringView path)
{ {
@ -67,60 +78,6 @@ bool exists(int fd)
return !Core::System::fstat(fd).is_error(); return !Core::System::fstat(fd).is_error();
} }
bool is_device(StringView path)
{
auto st_or_error = Core::System::stat(path);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
}
bool is_device(int fd)
{
auto st_or_error = Core::System::fstat(fd);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
}
bool is_block_device(StringView path)
{
auto st_or_error = Core::System::stat(path);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISBLK(st.st_mode);
}
bool is_block_device(int fd)
{
auto st_or_error = Core::System::fstat(fd);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISBLK(st.st_mode);
}
bool is_char_device(StringView path)
{
auto st_or_error = Core::System::stat(path);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISCHR(st.st_mode);
}
bool is_char_device(int fd)
{
auto st_or_error = Core::System::fstat(fd);
if (st_or_error.is_error())
return false;
auto st = st_or_error.release_value();
return S_ISCHR(st.st_mode);
}
bool is_regular_file(StringView path) bool is_regular_file(StringView path)
{ {
auto st_or_error = Core::System::stat(path); auto st_or_error = Core::System::stat(path);
@ -157,6 +114,16 @@ bool is_directory(int fd)
return S_ISDIR(st.st_mode); return S_ISDIR(st.st_mode);
} }
#ifdef AK_OS_WINDOWS
bool is_link(StringView path)
{
ByteString string_path = path;
auto attr = GetFileAttributes(string_path.characters());
if (attr == INVALID_FILE_ATTRIBUTES)
return false;
return attr & FILE_ATTRIBUTE_REPARSE_POINT;
}
#else
bool is_link(StringView path) bool is_link(StringView path)
{ {
auto st_or_error = Core::System::lstat(path); auto st_or_error = Core::System::lstat(path);
@ -232,13 +199,13 @@ ErrorOr<void> copy_file(StringView destination_path, StringView source_path, str
if (has_flag(preserve_mode, PreserveMode::Timestamps)) { if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
struct timespec times[2] = { struct timespec times[2] = {
#if defined(AK_OS_MACOS) || defined(AK_OS_IOS) # if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
source_stat.st_atimespec, source_stat.st_atimespec,
source_stat.st_mtimespec, source_stat.st_mtimespec,
#else # else
source_stat.st_atim, source_stat.st_atim,
source_stat.st_mtim, source_stat.st_mtim,
#endif # endif
}; };
TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0)); TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0));
} }
@ -280,13 +247,13 @@ ErrorOr<void> copy_directory(StringView destination_path, StringView source_path
if (has_flag(preserve_mode, PreserveMode::Timestamps)) { if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
struct timespec times[2] = { struct timespec times[2] = {
#if defined(AK_OS_MACOS) || defined(AK_OS_IOS) # if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
source_stat.st_atimespec, source_stat.st_atimespec,
source_stat.st_mtimespec, source_stat.st_mtimespec,
#else # else
source_stat.st_atim, source_stat.st_atim,
source_stat.st_mtim, source_stat.st_mtim,
#endif # endif
}; };
TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0)); TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0));
} }
@ -338,6 +305,34 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
return Core::System::unlink(source_path); return Core::System::unlink(source_path);
} }
bool can_delete_or_move(StringView path)
{
VERIFY(!path.is_empty());
auto directory = LexicalPath::dirname(path);
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
if (!directory_has_write_access)
return false;
auto stat_or_empty = [](StringView path) {
auto stat_or_error = Core::System::stat(path);
if (stat_or_error.is_error()) {
struct stat stat { };
return stat;
}
return stat_or_error.release_value();
};
auto directory_stat = stat_or_empty(directory);
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
if (!is_directory_sticky)
return true;
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
auto user_id = geteuid();
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
}
#endif // !AK_OS_WINDOWS
ErrorOr<void> remove(StringView path, RecursionMode mode) ErrorOr<void> remove(StringView path, RecursionMode mode)
{ {
if (is_directory(path) && mode == RecursionMode::Allowed) { if (is_directory(path) && mode == RecursionMode::Allowed) {
@ -368,88 +363,4 @@ ErrorOr<off_t> size_from_fstat(int fd)
return st.st_size; return st.st_size;
} }
ErrorOr<off_t> block_device_size_from_ioctl(StringView path)
{
if (!path.characters_without_null_termination())
return Error::from_syscall("ioctl"sv, -EFAULT);
ByteString path_string = path;
int fd = open(path_string.characters(), O_RDONLY);
if (fd < 0)
return Error::from_errno(errno);
off_t size = TRY(block_device_size_from_ioctl(fd));
if (close(fd) != 0)
return Error::from_errno(errno);
return size;
}
ErrorOr<off_t> block_device_size_from_ioctl(int fd)
{
#if defined(AK_OS_MACOS)
u64 block_count = 0;
u32 block_size = 0;
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count));
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKSIZE, &block_size));
return static_cast<off_t>(block_count * block_size);
#elif defined(AK_OS_FREEBSD) || defined(AK_OS_NETBSD)
off_t size = 0;
TRY(Core::System::ioctl(fd, DIOCGMEDIASIZE, &size));
return size;
#elif defined(AK_OS_LINUX)
u64 size = 0;
TRY(Core::System::ioctl(fd, BLKGETSIZE64, &size));
return static_cast<off_t>(size);
#else
// FIXME: Add support for more platforms.
(void)fd;
return Error::from_string_literal("Platform does not support getting block device size");
#endif
}
bool can_delete_or_move(StringView path)
{
VERIFY(!path.is_empty());
auto directory = LexicalPath::dirname(path);
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
if (!directory_has_write_access)
return false;
auto stat_or_empty = [](StringView path) {
auto stat_or_error = Core::System::stat(path);
if (stat_or_error.is_error()) {
struct stat stat { };
return stat;
}
return stat_or_error.release_value();
};
auto directory_stat = stat_or_empty(directory);
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
if (!is_directory_sticky)
return true;
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
auto user_id = geteuid();
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
}
ErrorOr<ByteString> read_link(StringView link_path)
{
return Core::System::readlink(link_path);
}
ErrorOr<void> link_file(StringView destination_path, StringView source_path)
{
return TRY(Core::System::symlink(source_path, TRY(get_duplicate_file_name(destination_path))));
}
bool looks_like_shared_library(StringView path)
{
return path.ends_with(".so"sv) || path.contains(".so."sv);
}
} }

View file

@ -11,7 +11,6 @@
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <LibCore/File.h> #include <LibCore/File.h>
#include <sys/stat.h>
namespace FileSystem { namespace FileSystem {
@ -31,15 +30,6 @@ bool is_regular_file(int fd);
bool is_directory(StringView path); bool is_directory(StringView path);
bool is_directory(int fd); bool is_directory(int fd);
bool is_device(StringView path);
bool is_device(int fd);
bool is_block_device(StringView path);
bool is_block_device(int fd);
bool is_char_device(StringView path);
bool is_char_device(int fd);
bool is_link(StringView path); bool is_link(StringView path);
bool is_link(int fd); bool is_link(int fd);
@ -73,13 +63,6 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
ErrorOr<void> remove(StringView path, RecursionMode); ErrorOr<void> remove(StringView path, RecursionMode);
ErrorOr<off_t> size_from_stat(StringView path); ErrorOr<off_t> size_from_stat(StringView path);
ErrorOr<off_t> size_from_fstat(int fd); ErrorOr<off_t> size_from_fstat(int fd);
ErrorOr<off_t> block_device_size_from_ioctl(StringView path);
ErrorOr<off_t> block_device_size_from_ioctl(int fd);
bool can_delete_or_move(StringView path); bool can_delete_or_move(StringView path);
ErrorOr<ByteString> read_link(StringView link_path);
ErrorOr<void> link_file(StringView destination_path, StringView source_path);
bool looks_like_shared_library(StringView path);
} }

View file

@ -3,6 +3,7 @@ set(SOURCES
Cell.cpp Cell.cpp
CellAllocator.cpp CellAllocator.cpp
ConservativeVector.cpp ConservativeVector.cpp
ForeignCell.cpp
Root.cpp Root.cpp
Heap.cpp Heap.cpp
HeapBlock.cpp HeapBlock.cpp
@ -12,3 +13,12 @@ set(SOURCES
serenity_lib(LibGC gc) serenity_lib(LibGC gc)
target_link_libraries(LibGC PRIVATE LibCore) target_link_libraries(LibGC PRIVATE LibCore)
if (ENABLE_SWIFT)
generate_clang_module_map(LibGC)
target_sources(LibGC PRIVATE
Heap+Swift.swift
)
target_link_libraries(LibGC PRIVATE AK)
add_swift_target_properties(LibGC LAGOM_LIBRARIES AK)
endif()

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <AK/Swift.h>
#include <LibGC/Heap.h> #include <LibGC/Heap.h>
namespace GC { namespace GC {
@ -25,6 +26,6 @@ public:
private: private:
Heap& m_heap; Heap& m_heap;
}; } SWIFT_NONCOPYABLE;
} }

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/DeferGC.h>
#include <LibGC/ForeignCell.h>
#include <LibGC/Heap.h>
namespace GC {
void* ForeignCell::foreign_data()
{
// !!!
auto offset = round_up_to_power_of_two(sizeof(ForeignCell), m_vtable.alignment);
return static_cast<void*>(reinterpret_cast<u8*>(this) + offset);
}
ForeignCell::ForeignCell(ForeignCell::Vtable vtable)
: m_vtable(move(vtable))
{
if (m_vtable.initialize)
m_vtable.initialize(foreign_data(), m_vtable.class_metadata_pointer, *this);
}
ForeignCell::~ForeignCell()
{
if (m_vtable.destroy)
m_vtable.destroy(foreign_data(), m_vtable.class_metadata_pointer);
}
Ref<ForeignCell> ForeignCell::create(Heap& heap, size_t size, ForeignCell::Vtable vtable)
{
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap.
// We can't guarantee that the ForeignCell will be stashed in a proper ForeignRef/ForeignPtr or similar
// foreign type until after all the dust has settled on both sides of the FFI boundary.
VERIFY(heap.is_gc_deferred());
VERIFY(is_power_of_two(vtable.alignment));
auto& allocator = heap.allocator_for_size(sizeof(ForeignCell) + round_up_to_power_of_two(size, vtable.alignment));
auto* memory = allocator.allocate_cell(heap);
auto* foreign_cell = new (memory) ForeignCell(move(vtable));
return *foreign_cell;
}
void ForeignCell::finalize()
{
Base::finalize();
if (m_vtable.finalize)
m_vtable.finalize(foreign_data(), m_vtable.class_metadata_pointer);
}
void ForeignCell::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
if (m_vtable.visit_edges)
m_vtable.visit_edges(foreign_data(), m_vtable.class_metadata_pointer, visitor);
}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Swift.h>
#include <AK/TypeCasts.h>
#include <LibGC/Cell.h>
#include <LibGC/DeferGC.h>
namespace GC {
template<typename T>
struct ForeignRef;
template<typename T>
struct ForeignPtr;
#define FOREIGN_CELL(class_, base_class) \
using Base = base_class; \
friend class GC::Heap;
class ForeignCell : public Cell {
FOREIGN_CELL(ForeignCell, Cell);
public:
struct Vtable {
// Holds a pointer to the foreign vtable information such as
// a jclass in Java, or a Swift type metadata pointer
void* class_metadata_pointer = nullptr;
// FIXME: FlyString? The class name must be owned by the ForeignCell so it can vend StringViews
// We should properly cache the name and class info pointer to avoid string churn
String class_name;
size_t alignment { 1 };
void (*initialize)(void* thiz, void* clazz, Ref<Cell>);
void (*destroy)(void* thiz, void* clazz);
void (*finalize)(void* thiz, void* clazz);
void (*visit_edges)(void* thiz, void* clazz, Cell::Visitor&);
};
static Ref<ForeignCell> create(Heap&, size_t size, Vtable);
void* foreign_data() SWIFT_RETURNS_INDEPENDENT_VALUE; // technically lying to swift, but it's fiiiiine
// ^Cell
virtual void finalize() override;
virtual void visit_edges(Cell::Visitor& visitor) override;
virtual StringView class_name() const override { return m_vtable.class_name; }
~ForeignCell();
private:
ForeignCell(Vtable vtable);
Vtable m_vtable;
} SWIFT_IMMORTAL_REFERENCE;
template<typename T>
struct ForeignRef {
friend struct ForeignPtr<T>;
template<typename... Args>
static ForeignRef allocate(Heap& heap, Args... args)
{
DeferGC const defer_gc(heap);
auto* cell = T::create(&heap, forward<Args>(args)...);
if constexpr (IsSame<decltype(cell), Cell*>) {
return ForeignRef(*verify_cast<ForeignCell>(cell));
} else {
static_assert(IsSame<decltype(cell), void*>);
auto* cast_cell = static_cast<Cell*>(cell);
return ForeignRef(*verify_cast<ForeignCell>(cast_cell));
}
}
ForeignRef() = delete;
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
ForeignRef(ForeignCell& cell)
: m_cell(cell)
{
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
m_data = static_cast<T*>(m_cell->foreign_data());
}
~ForeignRef() = default;
ForeignRef(ForeignRef const& other) = default;
ForeignRef& operator=(ForeignRef const& other) = default;
RETURNS_NONNULL T* operator->() const { return m_data; }
[[nodiscard]] T& operator*() const { return *m_data; }
RETURNS_NONNULL T* ptr() const { return m_data; }
RETURNS_NONNULL operator T*() const { return m_data; }
operator T&() const { return *m_data; }
Ref<ForeignCell> cell() const { return m_cell; }
void visit_edges(Cell::Visitor& visitor)
{
visitor.visit(m_cell);
}
private:
Ref<ForeignCell> m_cell;
T* m_data { nullptr };
};
template<typename T>
struct ForeignPtr {
constexpr ForeignPtr() = default;
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
ForeignPtr(ForeignCell& cell)
: m_cell(&cell)
{
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
m_data = static_cast<T*>(m_cell->foreign_data());
}
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
ForeignPtr(ForeignCell* cell)
: m_cell(cell)
{
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
m_data = m_cell ? static_cast<T*>(m_cell->foreign_data()) : nullptr;
}
ForeignPtr(ForeignRef<T> const& other)
: m_cell(other.m_cell)
, m_data(other.m_data)
{
}
ForeignPtr(nullptr_t)
: m_cell(nullptr)
{
}
ForeignPtr(ForeignPtr const& other) = default;
ForeignPtr& operator=(ForeignPtr const& other) = default;
T* operator->() const
{
ASSERT(m_cell && m_data);
return m_data;
}
[[nodiscard]] T& operator*() const
{
ASSERT(m_cell && m_data);
return *m_data;
}
operator T*() const { return m_data; }
T* ptr() const { return m_data; }
explicit operator bool() const { return !!m_cell; }
bool operator!() const { return !m_cell; }
Ptr<ForeignCell> cell() const { return m_cell; }
void visit_edges(Cell::Visitor& visitor)
{
visitor.visit(m_cell);
}
private:
Ptr<ForeignCell> m_cell;
T* m_data { nullptr };
};
}

View file

@ -6,11 +6,14 @@
#pragma once #pragma once
#include <AK/Types.h>
namespace GC { namespace GC {
class Cell; class Cell;
class CellAllocator; class CellAllocator;
class DeferGC; class DeferGC;
class ForeignCell;
class RootImpl; class RootImpl;
class Heap; class Heap;
class HeapBlock; class HeapBlock;

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
import AK
@_exported import GCCxx
extension GC.Heap {
public func withDeferredGC<R, E>(_ body: () throws(E) -> R) throws(E) -> R {
let deferredRAII = GC.DeferGC(self)
_ = deferredRAII
return try body()
}
}
// FIXME: Cell and Cell::Visitor are not imported properly, so we have to treat them as OpaquePointer
public protocol HeapAllocatable {
static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
init(cell: OpaquePointer)
func finalize()
func visitEdges(_ visitor: OpaquePointer)
var cell: OpaquePointer { get }
}
// FIXME: Figure out why other modules can't conform to HeapAllocatable
public struct HeapString: HeapAllocatable {
public var string: Swift.String
public init(cell: OpaquePointer) {
self.cell = cell
self.string = ""
}
// FIXME: HeapAllocatable cannot be exposed to C++ yet, so we're off to void* paradise
public static func create(on heap: GC.Heap, string: Swift.String) -> OpaquePointer {
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
precondition(heap.is_gc_deferred())
let heapString = allocate(on: heap)
heapString.pointee.string = string
return heapString.pointee.cell
}
public var cell: OpaquePointer
}
// Here be dragons
func asTypeMetadataPointer(_ type: Any.Type) -> UnsafeMutableRawPointer {
unsafeBitCast(type, to: UnsafeMutableRawPointer.self)
}
func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapAllocatable.Type {
let typeObject = unsafeBitCast(typeMetadata, to: Any.Type.self)
guard let type = typeObject as? any HeapAllocatable.Type else {
fatalError("Passed foreign class but it wasn't a Swift type!")
}
return type
}
extension HeapAllocatable {
fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: OpaquePointer) {
this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell))
}
fileprivate static func destroyFromFFI(at this: UnsafeMutableRawPointer) {
this.assumingMemoryBound(to: Self.self).deinitialize(count: 1)
}
fileprivate static func finalizeFromFFI(at this: UnsafeMutableRawPointer) {
this.assumingMemoryBound(to: Self.self).pointee.finalize()
}
fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: OpaquePointer) {
this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor)
}
public static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self> {
let vtable = GC.ForeignCell.Vtable(
class_metadata_pointer: asTypeMetadataPointer(Self.self),
class_name: AK.String(swiftString: Swift.String(describing: Self.self)),
alignment: MemoryLayout<Self>.alignment,
initialize: { this, typeMetadata, cell in
asHeapAllocatableType(typeMetadata!).initializeFromFFI(at: this!, cell: cell.ptr())
},
destroy: { this, typeMetadata in
asHeapAllocatableType(typeMetadata!).destroyFromFFI(at: this!)
},
finalize: { this, typeMetadata in
asHeapAllocatableType(typeMetadata!).finalizeFromFFI(at: this!)
},
visit_edges: nil
)
let cell = GC.ForeignCell.create(heap, MemoryLayout<Self>.stride, vtable)
return cell.pointee.foreign_data().assumingMemoryBound(to: Self.self)
}
public func finalize() {}
public func visitEdges(_ visitor: OpaquePointer) {}
}

View file

@ -13,6 +13,7 @@
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
#include <AK/StackInfo.h> #include <AK/StackInfo.h>
#include <AK/Swift.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibCore/Forward.h> #include <LibCore/Forward.h>
@ -73,10 +74,13 @@ public:
void uproot_cell(Cell* cell); void uproot_cell(Cell* cell);
bool is_gc_deferred() const { return m_gc_deferrals > 0; }
private: private:
friend class MarkingVisitor; friend class MarkingVisitor;
friend class GraphConstructorVisitor; friend class GraphConstructorVisitor;
friend class DeferGC; friend class DeferGC;
friend class ForeignCell;
void defer_gc(); void defer_gc();
void undefer_gc(); void undefer_gc();
@ -147,7 +151,7 @@ private:
bool m_collecting_garbage { false }; bool m_collecting_garbage { false };
StackInfo m_stack_info; StackInfo m_stack_info;
AK::Function<void(HashMap<Cell*, GC::HeapRoot>&)> m_gather_embedder_roots; AK::Function<void(HashMap<Cell*, GC::HeapRoot>&)> m_gather_embedder_roots;
}; } SWIFT_IMMORTAL_REFERENCE;
inline void Heap::did_create_root(Badge<RootImpl>, RootImpl& impl) inline void Heap::did_create_root(Badge<RootImpl>, RootImpl& impl)
{ {

View file

@ -10,11 +10,7 @@
# pragma GCC optimize("O3") # pragma GCC optimize("O3")
#endif #endif
#include <AK/Function.h>
#include <AK/NumericLimits.h>
#include <LibGfx/AntiAliasingPainter.h> #include <LibGfx/AntiAliasingPainter.h>
#include <LibGfx/DeprecatedPainter.h>
#include <LibGfx/Line.h>
namespace Gfx { namespace Gfx {

View file

@ -9,9 +9,7 @@
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
#include <LibGfx/DeprecatedPath.h> #include <LibGfx/DeprecatedPath.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
#include <LibGfx/LineStyle.h>
#include <LibGfx/PaintStyle.h> #include <LibGfx/PaintStyle.h>
#include <LibGfx/Quad.h>
#include <LibGfx/WindingRule.h> #include <LibGfx/WindingRule.h>
namespace Gfx { namespace Gfx {

View file

@ -7,16 +7,8 @@
*/ */
#include <AK/Bitmap.h> #include <AK/Bitmap.h>
#include <AK/ByteString.h>
#include <AK/Checked.h> #include <AK/Checked.h>
#include <AK/LexicalPath.h>
#include <AK/Memory.h>
#include <AK/MemoryStream.h>
#include <LibCore/File.h>
#include <LibCore/MappedFile.h>
#include <LibCore/MimeData.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ShareableBitmap.h> #include <LibGfx/ShareableBitmap.h>
#include <errno.h> #include <errno.h>
@ -105,30 +97,6 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, Alpha
return adopt_ref(*new Bitmap(format, alpha_type, size, pitch, data, move(destruction_callback))); return adopt_ref(*new Bitmap(format, alpha_type, size, pitch, data, move(destruction_callback)));
} }
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, Optional<IntSize> ideal_size)
{
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
return load_from_file(move(file), path, ideal_size);
}
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(NonnullOwnPtr<Core::File> file, StringView path, Optional<IntSize> ideal_size)
{
auto mapped_file = TRY(Core::MappedFile::map_from_file(move(file), path));
auto mime_type = Core::guess_mime_type_based_on_filename(path);
return load_from_bytes(mapped_file->bytes(), ideal_size, mime_type);
}
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_bytes(ReadonlyBytes bytes, Optional<IntSize> ideal_size, Optional<ByteString> mine_type)
{
if (auto decoder = TRY(ImageDecoder::try_create_for_raw_bytes(bytes, mine_type))) {
auto frame = TRY(decoder->frame(0, ideal_size));
if (auto& bitmap = frame.image)
return bitmap.release_nonnull();
}
return Error::from_string_literal("Gfx::Bitmap unable to load from file");
}
Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback) Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback)
: m_size(size) : m_size(size)
, m_data(data) , m_data(data)

View file

@ -7,11 +7,9 @@
#pragma once #pragma once
#include <AK/Forward.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/RefCounted.h> #include <AK/RefCounted.h>
#include <LibCore/AnonymousBuffer.h> #include <LibCore/AnonymousBuffer.h>
#include <LibCore/Forward.h>
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
#include <LibGfx/Rect.h> #include <LibGfx/Rect.h>
@ -70,9 +68,6 @@ public:
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, AlphaType, IntSize); [[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, AlphaType, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, AlphaType, IntSize); [[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, AlphaType, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback = {}); [[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback = {});
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(StringView path, Optional<IntSize> ideal_size = {});
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(NonnullOwnPtr<Core::File>, StringView path, Optional<IntSize> ideal_size = {});
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_bytes(ReadonlyBytes, Optional<IntSize> ideal_size = {}, Optional<ByteString> mine_type = {});
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); [[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const; ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const;

View file

@ -5,7 +5,6 @@
*/ */
#include <AK/Checked.h> #include <AK/Checked.h>
#include <AK/Forward.h>
#include <LibCore/AnonymousBuffer.h> #include <LibCore/AnonymousBuffer.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/BitmapSequence.h> #include <LibGfx/BitmapSequence.h>

View file

@ -73,6 +73,7 @@ set(SOURCES
set(SWIFT_EXCLUDE_HEADERS set(SWIFT_EXCLUDE_HEADERS
MetalContext.h MetalContext.h
VulkanContext.h VulkanContext.h
SkiaUtils.h
) )
if (APPLE) if (APPLE)

View file

@ -13,7 +13,6 @@
#include <AK/Swift.h> #include <AK/Swift.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
#include <LibGfx/SystemTheme.h>
#include <LibIPC/Decoder.h> #include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h> #include <LibIPC/Encoder.h>
#include <ctype.h> #include <ctype.h>
@ -399,7 +398,9 @@ Vector<Color> Color::tints(u32 steps, float max) const
Color Color::from_linear_srgb(float red, float green, float blue, float alpha) Color Color::from_linear_srgb(float red, float green, float blue, float alpha)
{ {
auto linear_to_srgb = [](float c) { auto linear_to_srgb = [](float c) {
return c >= 0.0031308f ? 1.055f * pow(c, 0.4166666f) - 0.055f : 12.92f * c; if (c <= 0.04045 / 12.92)
return c * 12.92;
return pow(c, 10. / 24) * 1.055 - 0.055;
}; };
red = linear_to_srgb(red) * 255.f; red = linear_to_srgb(red) * 255.f;
@ -413,24 +414,112 @@ Color Color::from_linear_srgb(float red, float green, float blue, float alpha)
clamp(lroundf(alpha * 255.f), 0, 255)); clamp(lroundf(alpha * 255.f), 0, 255));
} }
// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb
Color Color::from_a98rgb(float r, float g, float b, float alpha)
{
auto to_linear = [](float c) {
return pow(c, 563. / 256);
};
auto linear_r = to_linear(r);
auto linear_g = to_linear(g);
auto linear_b = to_linear(b);
float x = 0.57666904 * linear_r + 0.18555824 * linear_g + 0.18822865 * linear_b;
float y = 0.29734498 * linear_r + 0.62736357 * linear_g + 0.07529146 * linear_b;
float z = 0.02703136 * linear_r + 0.07068885 * linear_g + 0.99133754 * linear_b;
return from_xyz65(x, y, z, alpha);
}
// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb
Color Color::from_display_p3(float r, float g, float b, float alpha)
{
auto to_linear = [](float c) {
if (c < 0.04045)
return c / 12.92;
return pow((c + 0.055) / (1.055), 2.4);
};
auto linear_r = to_linear(r);
auto linear_g = to_linear(g);
auto linear_b = to_linear(b);
float x = 0.48657095 * linear_r + 0.26566769 * linear_g + 0.19821729 * linear_b;
float y = 0.22897456 * linear_r + 0.69173852 * linear_g + 0.07928691 * linear_b;
float z = 0.00000000 * linear_r + 0.04511338 * linear_g + 1.04394437 * linear_b;
return from_xyz65(x, y, z, alpha);
}
// https://www.w3.org/TR/css-color-4/#predefined-prophoto-rgb
Color Color::from_pro_photo_rgb(float r, float g, float b, float alpha)
{
auto to_linear = [](float c) -> float {
u8 sign = c < 0 ? -1 : 1;
float absolute = abs(c);
if (absolute <= 16. / 252)
return c / 16;
return sign * pow(c, 1.8);
};
auto linear_r = to_linear(r);
auto linear_g = to_linear(g);
auto linear_b = to_linear(b);
float x = 0.79776664 * linear_r + 0.13518130 * linear_g + 0.03134773 * linear_b;
float y = 0.28807483 * linear_r + 0.71183523 * linear_g + 0.00008994 * linear_b;
float z = 0.00000000 * linear_r + 0.00000000 * linear_g + 0.82510460 * linear_b;
return from_xyz50(x, y, z, alpha);
}
// https://www.w3.org/TR/css-color-4/#predefined-rec2020
Color Color::from_rec2020(float r, float g, float b, float alpha)
{
auto to_linear = [](float c) -> float {
auto constexpr alpha = 1.09929682680944;
auto constexpr beta = 0.018053968510807;
u8 sign = c < 0 ? -1 : 1;
auto absolute = abs(c);
if (absolute < beta * 4.5)
return c / 4.5;
return sign * (pow((absolute + alpha - 1) / alpha, 1 / 0.45));
};
auto linear_r = to_linear(r);
auto linear_g = to_linear(g);
auto linear_b = to_linear(b);
float x = 0.63695805 * linear_r + 0.14461690 * linear_g + 0.16888098 * linear_b;
float y = 0.26270021 * linear_r + 0.67799807 * linear_g + 0.05930172 * linear_b;
float z = 0.00000000 * linear_r + 0.02807269 * linear_g + 1.06098506 * linear_b;
return from_xyz65(x, y, z, alpha);
}
Color Color::from_xyz50(float x, float y, float z, float alpha) Color Color::from_xyz50(float x, float y, float z, float alpha)
{ {
// See commit description for these values // See commit description for these values.
float red = 3.13397926 * x - 1.61689519 * y - 0.49070587 * z; float r = +3.134136 * x - 1.617386 * y - 0.490662 * z;
float green = -0.97840009 * x + 1.91589112 * y + 0.03339256 * z; float g = -0.978795 * x + 1.916254 * y + 0.033443 * z;
float blue = 0.07200357 * x - 0.22897505 * y + 1.40517398 * z; float b = +0.071955 * x - 0.228977 * y + 1.405386 * z;
return from_linear_srgb(red, green, blue, alpha); return from_linear_srgb(r, g, b, alpha);
} }
Color Color::from_xyz65(float x, float y, float z, float alpha) Color Color::from_xyz65(float x, float y, float z, float alpha)
{ {
// https://en.wikipedia.org/wiki/SRGB#From_CIE_XYZ_to_sRGB // See commit description for these values.
float red = 3.2406 * x - 1.5372 * y - 0.4986 * z; float r = +3.240970 * x - 1.537383 * y - 0.498611 * z;
float green = -0.9689 * x + 1.8758 * y + 0.0415 * z; float g = -0.969244 * x + 1.875968 * y + 0.041555 * z;
float blue = 0.0557 * x - 0.2040 * y + 1.0570 * z; float b = +0.055630 * x - 0.203977 * y + 1.056972 * z;
return from_linear_srgb(red, green, blue, alpha); return from_linear_srgb(r, g, b, alpha);
} }
Color Color::from_lab(float L, float a, float b, float alpha) Color Color::from_lab(float L, float a, float b, float alpha)

View file

@ -12,7 +12,6 @@
#include <AK/Format.h> #include <AK/Format.h>
#include <AK/Forward.h> #include <AK/Forward.h>
#include <AK/Math.h> #include <AK/Math.h>
#include <AK/SIMD.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
#include <LibIPC/Forward.h> #include <LibIPC/Forward.h>
@ -182,10 +181,14 @@ public:
return Color(r_u8, g_u8, b_u8, a_u8); return Color(r_u8, g_u8, b_u8, a_u8);
} }
static Color from_a98rgb(float r, float g, float b, float alpha = 1.0f);
static Color from_display_p3(float r, float g, float b, float alpha = 1.0f);
static Color from_lab(float L, float a, float b, float alpha = 1.0f); static Color from_lab(float L, float a, float b, float alpha = 1.0f);
static Color from_linear_srgb(float r, float g, float b, float alpha = 1.0f);
static Color from_pro_photo_rgb(float r, float g, float b, float alpha = 1.0f);
static Color from_rec2020(float r, float g, float b, float alpha = 1.0f);
static Color from_xyz50(float x, float y, float z, float alpha = 1.0f); static Color from_xyz50(float x, float y, float z, float alpha = 1.0f);
static Color from_xyz65(float x, float y, float z, float alpha = 1.0f); static Color from_xyz65(float x, float y, float z, float alpha = 1.0f);
static Color from_linear_srgb(float x, float y, float z, float alpha = 1.0f);
// https://bottosson.github.io/posts/oklab/ // https://bottosson.github.io/posts/oklab/
static constexpr Color from_oklab(float L, float a, float b, float alpha = 1.0f) static constexpr Color from_oklab(float L, float a, float b, float alpha = 1.0f)
@ -397,7 +400,8 @@ public:
constexpr Color with_opacity(float opacity) const constexpr Color with_opacity(float opacity) const
{ {
return with_alpha(alpha() * opacity); VERIFY(opacity >= 0 && opacity <= 1);
return with_alpha(static_cast<u8>(round(alpha() * opacity)));
} }
constexpr Color darkened(float amount = 0.5f) const constexpr Color darkened(float amount = 0.5f) const
@ -546,9 +550,9 @@ public:
break; break;
} }
u8 out_r = (u8)(r * 255); auto out_r = static_cast<u8>(round(r * 255));
u8 out_g = (u8)(g * 255); auto out_g = static_cast<u8>(round(g * 255));
u8 out_b = (u8)(b * 255); auto out_b = static_cast<u8>(round(b * 255));
return Color(out_r, out_g, out_b); return Color(out_r, out_g, out_b);
} }

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Format.h>
#include <AK/Math.h> #include <AK/Math.h>
#include <LibGfx/DeltaE.h> #include <LibGfx/DeltaE.h>
#include <math.h> #include <math.h>

View file

@ -12,18 +12,13 @@
#include "DeprecatedPainter.h" #include "DeprecatedPainter.h"
#include "Bitmap.h" #include "Bitmap.h"
#include "Font/Font.h"
#include <AK/Assertions.h> #include <AK/Assertions.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/Math.h> #include <AK/Math.h>
#include <AK/Memory.h> #include <AK/Memory.h>
#include <AK/Stack.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
#include <AK/Utf8View.h>
#include <LibGfx/DeprecatedPath.h> #include <LibGfx/DeprecatedPath.h>
#include <LibGfx/Palette.h> #include <LibGfx/ScalingMode.h>
#include <LibGfx/Quad.h>
#include <LibGfx/TextLayout.h>
#include <stdio.h> #include <stdio.h>
#if defined(AK_COMPILER_GCC) #if defined(AK_COMPILER_GCC)

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/Forward.h> #include <AK/Forward.h>
#include <AK/Memory.h>
#include <AK/NonnullRefPtr.h> #include <AK/NonnullRefPtr.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
@ -16,8 +15,6 @@
#include <LibGfx/PaintStyle.h> #include <LibGfx/PaintStyle.h>
#include <LibGfx/Point.h> #include <LibGfx/Point.h>
#include <LibGfx/Rect.h> #include <LibGfx/Rect.h>
#include <LibGfx/ScalingMode.h>
#include <LibGfx/Size.h>
#include <LibGfx/WindingRule.h> #include <LibGfx/WindingRule.h>
namespace Gfx { namespace Gfx {

View file

@ -6,12 +6,9 @@
#include <AK/Math.h> #include <AK/Math.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/TypeCasts.h>
#include <LibGfx/BoundingBox.h> #include <LibGfx/BoundingBox.h>
#include <LibGfx/DeprecatedPainter.h> #include <LibGfx/DeprecatedPainter.h>
#include <LibGfx/DeprecatedPath.h> #include <LibGfx/DeprecatedPath.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/TextLayout.h>
namespace Gfx { namespace Gfx {

View file

@ -6,7 +6,6 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>

View file

@ -4,9 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Array.h> #include <AK/Memory.h>
#include <AK/Debug.h>
#include <AK/IntegralMath.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibGfx/AntiAliasingPainter.h> #include <LibGfx/AntiAliasingPainter.h>
#include <LibGfx/DeprecatedPainter.h> #include <LibGfx/DeprecatedPainter.h>

View file

@ -7,15 +7,10 @@
#pragma once #pragma once
#include <AK/Bitmap.h>
#include <AK/ByteReader.h>
#include <AK/RefCounted.h> #include <AK/RefCounted.h>
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibCore/MappedFile.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Size.h>
struct hb_font_t; struct hb_font_t;
@ -85,7 +80,7 @@ public:
virtual float width(StringView) const = 0; virtual float width(StringView) const = 0;
virtual float width(Utf8View const&) 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; virtual NonnullRefPtr<Font> with_size(float point_size) const = 0;

View file

@ -6,9 +6,6 @@
#pragma once #pragma once
#include <AK/HashMap.h>
#include <AK/Noncopyable.h>
#include <AK/RefCounted.h>
#include <LibCore/Resource.h> #include <LibCore/Resource.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>

View file

@ -8,7 +8,6 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <LibGfx/Font/Typeface.h> #include <LibGfx/Font/Typeface.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>

View file

@ -9,7 +9,6 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/Typeface.h> #include <LibGfx/Font/Typeface.h>

View file

@ -8,8 +8,6 @@
#pragma once #pragma once
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Font/Font.h> #include <LibGfx/Font/Font.h>
#include <LibGfx/Font/Typeface.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 u8 baseline() const override { return m_point_height; } // FIXME: Read from font
virtual float width(StringView) const override; virtual float width(StringView) const override;
virtual float width(Utf8View const&) 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<ScaledFont> scaled_with_size(float point_size) const;
virtual NonnullRefPtr<Font> with_size(float point_size) const override; virtual NonnullRefPtr<Font> with_size(float point_size) const override;

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <core/SkTypeface.h>
#include <harfbuzz/hb.h> #include <harfbuzz/hb.h>
#include <LibGfx/Font/ScaledFont.h> #include <LibGfx/Font/ScaledFont.h>

View file

@ -7,9 +7,7 @@
#pragma once #pragma once
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/Noncopyable.h>
#include <AK/RefCounted.h> #include <AK/RefCounted.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/FontData.h> #include <LibGfx/Font/FontData.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
@ -47,7 +45,7 @@ public:
virtual u32 glyph_count() const = 0; virtual u32 glyph_count() const = 0;
virtual u16 units_per_em() const = 0; virtual u16 units_per_em() const = 0;
virtual u32 glyph_id_for_code_point(u32 code_point) 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 weight() const = 0;
virtual u16 width() const = 0; virtual u16 width() const = 0;
virtual u8 slope() const = 0; virtual u8 slope() const = 0;

View file

@ -6,12 +6,10 @@
#include <AK/LsanSuppressions.h> #include <AK/LsanSuppressions.h>
#include <LibGfx/Font/FontDatabase.h> #include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/TypefaceSkia.h> #include <LibGfx/Font/TypefaceSkia.h>
#include <core/SkData.h> #include <core/SkData.h>
#include <core/SkFontMgr.h> #include <core/SkFontMgr.h>
#include <core/SkRefCnt.h>
#include <core/SkTypeface.h> #include <core/SkTypeface.h>
#ifndef AK_OS_ANDROID #ifndef AK_OS_ANDROID
# include <ports/SkFontMgr_fontconfig.h> # 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()) { if (!m_family.has_value()) {
SkString family_name; SkString family_name;

View file

@ -19,7 +19,7 @@ public:
virtual u32 glyph_count() const override; virtual u32 glyph_count() const override;
virtual u16 units_per_em() const override; virtual u16 units_per_em() const override;
virtual u32 glyph_id_for_code_point(u32 code_point) 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 weight() const override;
virtual u16 width() const override; virtual u16 width() const override;
virtual u8 slope() const override; virtual u8 slope() const override;

View file

@ -6,8 +6,6 @@
#pragma once #pragma once
#include <AK/ByteBuffer.h>
namespace Gfx::ICC { namespace Gfx::ICC {
class Profile; class Profile;

View file

@ -7,7 +7,6 @@
#include <LibGfx/ICC/Profile.h> #include <LibGfx/ICC/Profile.h>
#include <LibGfx/ICC/Tags.h> #include <LibGfx/ICC/Tags.h>
#include <LibGfx/ICC/WellKnownProfiles.h> #include <LibGfx/ICC/WellKnownProfiles.h>
#include <time.h>
namespace Gfx::ICC { namespace Gfx::ICC {

View file

@ -7,9 +7,7 @@
#pragma once #pragma once
#include <AK/BitStream.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/Optional.h>
#include <AK/Types.h> #include <AK/Types.h>
namespace Gfx { namespace Gfx {

View file

@ -17,7 +17,6 @@
#include <LibCompress/Lzw.h> #include <LibCompress/Lzw.h>
#include <LibGfx/ImageFormats/GIFLoader.h> #include <LibGfx/ImageFormats/GIFLoader.h>
#include <LibGfx/Painter.h> #include <LibGfx/Painter.h>
#include <string.h>
namespace Gfx { namespace Gfx {

View file

@ -7,7 +7,6 @@
#pragma once #pragma once
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/ImageFormats/ImageDecoder.h> #include <LibGfx/ImageFormats/ImageDecoder.h>
namespace Gfx { namespace Gfx {

View file

@ -4,14 +4,12 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ByteBuffer.h>
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibGfx/ImageFormats/BMPLoader.h> #include <LibGfx/ImageFormats/BMPLoader.h>
#include <LibGfx/ImageFormats/ICOLoader.h> #include <LibGfx/ImageFormats/ICOLoader.h>
#include <LibGfx/ImageFormats/PNGLoader.h> #include <LibGfx/ImageFormats/PNGLoader.h>
#include <string.h>
namespace Gfx { namespace Gfx {

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/LexicalPath.h>
#include <LibGfx/ImageFormats/AVIFLoader.h> #include <LibGfx/ImageFormats/AVIFLoader.h>
#include <LibGfx/ImageFormats/BMPLoader.h> #include <LibGfx/ImageFormats/BMPLoader.h>
#include <LibGfx/ImageFormats/GIFLoader.h> #include <LibGfx/ImageFormats/GIFLoader.h>

View file

@ -6,7 +6,6 @@
#pragma once #pragma once
#include <AK/ByteBuffer.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <AK/RefCounted.h> #include <AK/RefCounted.h>

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Debug.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <LibGfx/ImageFormats/JPEGXLLoader.h> #include <LibGfx/ImageFormats/JPEGXLLoader.h>
#include <jxl/decode.h> #include <jxl/decode.h>

View file

@ -6,7 +6,6 @@
#pragma once #pragma once
#include <AK/MemoryStream.h>
#include <LibGfx/ImageFormats/ImageDecoder.h> #include <LibGfx/ImageFormats/ImageDecoder.h>
namespace Gfx { namespace Gfx {

View file

@ -10,7 +10,6 @@
#include <AK/LEB128.h> #include <AK/LEB128.h>
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <AK/Variant.h> #include <AK/Variant.h>
#include <LibCore/File.h>
#include <LibGfx/AntiAliasingPainter.h> #include <LibGfx/AntiAliasingPainter.h>
#include <LibGfx/DeprecatedPainter.h> #include <LibGfx/DeprecatedPainter.h>
#include <LibGfx/ImageFormats/TinyVGLoader.h> #include <LibGfx/ImageFormats/TinyVGLoader.h>

View file

@ -10,7 +10,6 @@
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
#include <LibGfx/ImageFormats/WebPWriterLossless.h> #include <LibGfx/ImageFormats/WebPWriterLossless.h>
#include <LibGfx/Point.h>
namespace Gfx { namespace Gfx {

View file

@ -8,11 +8,9 @@
#include <AK/BitStream.h> #include <AK/BitStream.h>
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/Endian.h>
#include <AK/HashTable.h> #include <AK/HashTable.h>
#include <AK/MemoryStream.h> #include <AK/MemoryStream.h>
#include <AK/QuickSort.h> #include <AK/QuickSort.h>
#include <LibCompress/DeflateTables.h>
#include <LibCompress/Huffman.h> #include <LibCompress/Huffman.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/ImageFormats/WebPSharedLossless.h> #include <LibGfx/ImageFormats/WebPSharedLossless.h>

View file

@ -6,6 +6,7 @@
#include <LibGfx/ImmutableBitmap.h> #include <LibGfx/ImmutableBitmap.h>
#include <LibGfx/PaintingSurface.h> #include <LibGfx/PaintingSurface.h>
#include <LibGfx/SkiaUtils.h>
#include <core/SkBitmap.h> #include <core/SkBitmap.h>
#include <core/SkImage.h> #include <core/SkImage.h>
@ -60,23 +61,6 @@ Color ImmutableBitmap::get_pixel(int x, int y) const
return m_impl->source.get<NonnullRefPtr<Gfx::Bitmap>>()->get_pixel(x, y); return m_impl->source.get<NonnullRefPtr<Gfx::Bitmap>>()->get_pixel(x, y);
} }
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
{
switch (format) {
case Gfx::BitmapFormat::Invalid:
return kUnknown_SkColorType;
case Gfx::BitmapFormat::BGRA8888:
case Gfx::BitmapFormat::BGRx8888:
return kBGRA_8888_SkColorType;
case Gfx::BitmapFormat::RGBA8888:
return kRGBA_8888_SkColorType;
case Gfx::BitmapFormat::RGBx8888:
return kRGB_888x_SkColorType;
default:
return kUnknown_SkColorType;
}
}
static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type) static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type)
{ {
switch (alpha_type) { switch (alpha_type) {

View file

@ -6,6 +6,8 @@
#pragma once #pragma once
#include <AK/Assertions.h>
#include <AK/StdLibExtras.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <initializer_list> #include <initializer_list>

View file

@ -13,20 +13,9 @@
#include <LibGfx/PathSkia.h> #include <LibGfx/PathSkia.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <core/SkBitmap.h>
#include <core/SkBlurTypes.h>
#include <core/SkCanvas.h> #include <core/SkCanvas.h>
#include <core/SkColorFilter.h>
#include <core/SkMaskFilter.h>
#include <core/SkPath.h> #include <core/SkPath.h>
#include <core/SkPathBuilder.h>
#include <core/SkRRect.h>
#include <core/SkSurface.h>
#include <effects/SkGradientShader.h> #include <effects/SkGradientShader.h>
#include <effects/SkImageFilters.h>
#include <gpu/GrDirectContext.h>
#include <gpu/ganesh/SkSurfaceGanesh.h>
#include <pathops/SkPathOps.h>
namespace Gfx { namespace Gfx {

Some files were not shown because too many files have changed in this diff Show more