Commit graph

77960 commits

Author SHA1 Message Date
Callum Law
099c3fa590 Meta: Handle juxtaposition in CSS value parsing code generation 2026-05-13 11:26:20 +01:00
Callum Law
30e58a48c6 Meta: Handle optionals in CSS value parsing code generation 2026-05-13 11:26:20 +01:00
Callum Law
fe121d403d LibWeb: Add EmptyOptionalStyleValue
This will be used in generated code to represent missing optionals i.e.
`<foo> <bar>?` with only a `<foo>` value will be represented by a
`StyleValueList` containing the relevant `<foo>` style value and an
`EmptyOptionalStyleValue`
2026-05-13 11:26:20 +01:00
Callum Law
6361dcaae5 Meta: Handle groups in CSS value parsing code generation
We don't yet support required groups so for the moment these just fall
back to generating the parsing code of their child
2026-05-13 11:26:20 +01:00
Sam Atkins
cb14d574de LibWeb: Invalidate descendants for container-* property changes
Whenever an ancestor's container-name or container-type changes, it
affects the matching of any `@container` queries, and so can affect the
style of any descendant.
2026-05-13 11:05:31 +01:00
Sam Atkins
67046049d9 LibWeb: Evaluate container queries when collecting matching style rules
MatchingRules now have a container_rule member which stores the nearest
ancestor CSSContainerRule, if any. When populating the rule cache, we
maintain a stack of CSSContainerRules that we are within, and use
record the last one on the MatchingRule, so that it's O(1) instead of
having to walk up the rule's ancestors each time. This does mean we
have reimplement some "for each rule" code.

When collecting rules to apply to an element, we see if the MatchingRule
has a container_rule, and if so, we evaluate that rule's query to see
if the element has a matching container. We then also match any ancestor
container rules, using the cached parent container rule.

Add a custom test to cover the case of nested name-only `@container`s,
which WPT lacks currently as far as I can tell.
2026-05-13 11:05:31 +01:00
Sam Atkins
1f66b1cc13 LibWeb: Implement ContainerQuery evaluation
ContainerQuery now stores its feature requirements. When evaluated, it
uses these and the passed-in container name to identify a container
element to match against, then evaluates its condition against that
element.

None of this is yet in use anywhere, but will be soon.
2026-05-13 11:05:31 +01:00
Sam Atkins
b6e8239203 LibWeb: Collect container query feature requirements
A given element may be a container in different ways, depending on its
`container-type` property. For a container query to match an element,
that element must have the required container type for each feature
that the query checks. This commit implement a step to collect those
required types, so that we can quickly eliminate potential container
elements that lack a required containment type.
2026-05-13 11:05:31 +01:00
Sam Atkins
2a89c56143 LibWeb/CSS: Cache CSSContainerRule's nearest ancestor CSSContainerRule
Once we start evaluating `@container` matches, we will need to check if
any ancestor `@container`s also match. Caching the nearest one, if any,
will let us skip walking the entire ancestor chain.
2026-05-13 11:05:31 +01:00
Sam Atkins
88aea2e11d LibWeb: Give BooleanExpression an evaluation context
Different users of BooleanExpression have different requirements for
evaluation:
- `@media` needs a Document
- `@supports` doesn't need anything
- `@container` needs a container Element

To support these without expanding the API, replace the Document*
parameter with a BooleanExpressionEvaluationContext type which contains
these different values.

No behaviour changes.
2026-05-13 11:05:31 +01:00
Sam Atkins
442319c676 LibWeb/CSS: Update grammar for @container
Corresponds to
efc0c6f847
2026-05-13 11:05:31 +01:00
Sam Atkins
5000b4fbb7 Tests: Import a test for name-only container queries
Currently fails, as expected.
2026-05-13 11:05:31 +01:00
Sam Atkins
f489a8e1d0 UI/Qt: Copy window size and maximized state for new windows
Create new windows with the size of the previously active window, and
maximized if it was maximized. This matches the behaviour of other
browsers, and solves an annoying issue where new windows would always
be tiny.

Application::new_window() now takes a WindowConfiguration struct that
lets you specify the position, size, and maximized-ness of the window.
All previous callers of new_window() now use this instead of modifying
the window state afterwards.

WindowConfiguration is slightly awkwardly broken up into individual x/y
and width/height fields instead of a position and a size, for the sake
of compatibility with HTML::WebViewHints.
2026-05-13 11:04:20 +01:00
Sam Atkins
f0cb7d9a6f UI/Qt: Add functionality for reopening recently-closed tabs
Record when we close a tab, and map Ctrl+Shift+T to an action that opens
the most recently closed one.
2026-05-13 11:03:41 +01:00
Sam Atkins
9b53baa652 LibWebView: Track recently closed tabs and windows
Maintain a stack of URLs and when they were closed, and allow popping
the most recent one.

These are stored directly on HistoryStore instead of its Storage object,
because they should never persist regardless of which backend we're
using. Even so, we still clear them along with other history data.
2026-05-13 11:03:41 +01:00
Sam Atkins
9d846181f5 Tests: Actually check disabled HistoryStore doesn't record anything
This test only checked that the end state, after clearing the storage,
didn't contain the entry. This is the same behaviour we'd expect if it
wasn't disabled, so to test for that, run the same "is this empty?"
check after each step.
2026-05-13 11:03:41 +01:00
Sam Atkins
d4b63c6c99 LibWebView: Combine HistoryStore storage members into one pointer
HistoryStore always had a TransientStorage even though that went unused
if it also had a PersistentStorage. We also had repeated branching to
make sure we accessed the correct storage, and this is a bit of a
footgun. Instead, just hold a single Storage via OwnPtr and use that
for all storage actions.
2026-05-13 11:03:41 +01:00
Sam Atkins
35b43f7d3a LibWeb/CSS: Insert a combinator before all pseudo-element selectors
Previously, and according to the spec, `a::part(foo)::before` would be a
single CompoundSelector, even though it matches against 3 different
targets. This meant some awkward swapping of targets in the middle of
matching, and in particular it made `::part()` and `::slotted()` quite
hacky, requiring them to track extra data on the MatchContext to then
use later. This was scattered around and difficult to follow.

Partly inspired by Gecko, this commit instead introduces an invisible
PseudoElement combinator. After parsing a selector, we find any
CompoundSelectors that contain a pseudo-element and split them up, so
that each CompoundSelector only has a single target in the end. Where
the pseudo-element was at the start of a CompoundSelector, we insert an
invisible universal selector before it to represent its originating
element.

So now, a CompoundSelector deals with one target, and switching targets
is done at the combinator.

The one inconsistency is that we match the target of ::slotted()
and ::part() in pseudo_element_transition_target(), instead of before
then when processing the SimpleSelector. This is to avoid repeating the
same computations twice.

No outward-facing behaviour changes, though the invalidation metrics
have changed.
2026-05-13 11:03:02 +01:00
Sam Atkins
7c8d1806c9 Tests: Ensure error-stack-must-contain-full-url prints output in order
Store the error traces on a map until they're all done, then sort them
and print them out in alphabetical order by type. This test was
sometimes flaking and printing the module stack before the classic
stack, which this prevents.
2026-05-13 11:02:40 +01:00
jeetsh4h
44476de9c5 UI+LibWebView: Implement "Open Link in New Window" 2026-05-13 05:00:50 -05:00
Callum Law
b39bcaf1e4 Meta: Optimize keyword alternatives parsing 2026-05-13 11:00:07 +01:00
Callum Law
f578d95be0 LibWeb: Support multiple keywords in parse_specific_keyword_value
This will allow us to combine multiple calls into one and avoid
repeatedly parsing and then discarding `KeywordStyleValue`s
2026-05-13 11:00:07 +01:00
Callum Law
a9d2b1dde6 Meta: Read dimensions from Units.json in CSS grammar parser
Avoids us having to maintain a separate hardcoded list.

This does mean we don't support parsing of `decibel` but it's not used
anywhere yet and will be supported automatically when added to
Units.json
2026-05-13 11:00:07 +01:00
Callum Law
c96e8f15e5 Meta: Make CSS dimension loading reusable
Aside from the existing users this will also be used within the
CSSGrammar parser
2026-05-13 11:00:07 +01:00
Yuval Carmon
eaf1078c98 LibWeb: Use stop_fetch() for overlapping HTMLLinkElement fetches
When a link element's href changes, the spec expects the callback
to check if "el contributes a script-blocking style sheet"
(step 6). This check examines current fetch state, which fails
when old fetch callbacks run after a new fetch has started.

Use FetchController::stop_fetch() instead of abort() to prevent
old fetch callbacks from executing entirely. This ensures only the
current fetch's callback runs, allowing it to correctly check
current state per spec without race conditions.
2026-05-13 04:17:24 -05:00
Andreas Kling
9f35827aa0 LibWeb: Use double precision for wheel scroll deltas
Wheel deltas were truncated to int at the platform input boundary,
which dropped the sub-pixel tail of trackpad momentum scrolls. Each
NSEvent's scrollingDeltaY arrived as a CGFloat, got cast to int, and
flowed through IPC, EventHandler, and PaintableBox::scroll_by as int,
losing fractional information that never came back.

Widen Web::MouseEvent::wheel_delta_{x,y} to double and propagate
through Page, EventHandler, Paintable, PaintableBox, and the AppKit,
Qt, and GTK input paths.
2026-05-13 11:00:59 +02:00
Andreas Kling
d4c645b7a4 LibWeb: Preserve async scroll offset across scroll updates
Keep compositor-side scroll snapshots aligned with async viewport
scrolling when main-thread scroll state arrives while an async viewport
offset is pending. A stale scroll-state-only update could otherwise
replace the snapshot used for display list replay and wheel hit-testing
with an older viewport offset, even though the compositor had already
presented newer async scroll positions.

Teach AsyncScrollTree to set a node scroll offset directly and use it
when reconciling both display-list and scroll-state updates. Recompute
the main-thread viewport rect after display list recording as pending
async scroll adoption can move the viewport before presentation.
2026-05-13 11:00:59 +02:00
Shannon Booth
f58be8345a LibWeb: Paint inline-block floats during inline-level painting
Avoid recursing into atomic inline-level descendants during the ancestor
float sweep.

CSS 2.2 paints inline-block and inline-table boxes in the inline-level
painting step, as if they created their own stacking contexts. Paint
their internal floats during
BackgroundAndBordersForInlineLevelAndReplaced instead.
2026-05-13 10:08:54 +02:00
Zaggy1024
3b7c1810e6 LibMedia+Tests: Refer to raw audio frames/samples consistently
Previously, we weren't too consistent about the definition of frame and
sample when it relates to raw audio data. This brings all the usages in
the context of raw data in line (hopefully), with samples referring to
a single PCM value, and frames referring to the multiple samples that
make up an instant's audio across all channels.
2026-05-13 02:05:35 -05:00
Zaggy1024
5103c4746d LibMedia: Make AudioPlaybackSink inherit from AudioSink
With this last piece done, we can now connect a DecodedAudioProducer
directly to an AudioPlaybackSink instead of being forced to go through
the mixer. This should open up options for other pipeline setups if
needed in the future.
2026-05-13 02:05:35 -05:00
Zaggy1024
d67bf635e2 LibMedia: Fit the media pipeline nodes into their intended interfaces
Now, all nodes are connected through Sink::connect_input() and
disconnect_input().

AudioMixer now derives from a base AudioProcessor class that inherits
from both AudioSink and AudioProducer. It is the only current node that
can accept multiple inputs, tracking each one by its pointer identity.
2026-05-13 02:05:35 -05:00
Zaggy1024
a4c2c6c0cb LibMedia+LibWeb: Speculatively find keyframes near fast seek targets
This is the new way of handling fast seeks. Instead of delegating the
logic all the way down the pipeline to the decoder thread's seek
handler, we can just determine the timestamp we want to seek to ahead
of time.
2026-05-13 02:05:35 -05:00
Zaggy1024
a286f30663 LibMedia: Qualify AK::SeekMode to differentiate from Media::SeekMode 2026-05-13 02:05:35 -05:00
Zaggy1024
a06f87d728 LibMedia: Transmit seeks and resolve them through the pipeline
Seeking is now unified under one single method signature implemented by
all producers and transmitted through the pipeline by all sinks. By
doing it this way, we can simply instantaneously notify each node of
the pipeline that it needs to stop what it's doing and seek. For nodes
that are threaded (particularly the source providers), this causes them
to stop pushing data to their queue immediately, so that no stale data
makes it through to the output. Then, when new data does come through,
that is a clear indication that the seek has completed.

Note that track enablement is now through the pipeline as well, which
means that SuspendedStateHandler no longer has a way to suspend newly-
enabled tracks. Decoder suspension will need to be reworked to fit into
this new pipeline, sleeping/disposing and restarting entirely based on
the pull() timing in the producers.
2026-05-13 02:05:35 -05:00
Zaggy1024
2abfa79281 LibMedia: Make the AudioPlaybackSink's processor thread wait for data
Using a callback shared by all producers in the pipeline, notify the
AudioPlaybackSink when it needs to wake up and start processing data
again. Prior to this commit, it was simply burning CPU spinning until
data was produced.
2026-05-13 02:05:35 -05:00
Zaggy1024
7be0ae89e4 LibMedia: Transition producers and sinks to the new pipeline model
This is an intermediate step towards unifying the pipeline around new
Producer/Sink interfaces. Producers now have a pull() method that gets
the next piece of data from them. The pull() method returns a status
that can indicate whether it has current data, and if not, why it's
unavailable. This signal will be passed down the pipeline to the final
sink, which can expose the signal to its user, which in the normal
playback pipeline is PlaybackManager. The signal can be used to
transition between playback states. Currently, this is only hooked up
to the buffering state, but should be used later for ending playback
as well as decoding error propagation.

Buffering is now determined solely based on whether the pipeline is
blocked on incomplete data, so the ready state for video now progresses
past HAVE_METADATA immediately after playback manager initializes. This
will change when files have buffered ranges.
2026-05-13 02:05:35 -05:00
Zaggy1024
a60db31902 LibMedia: Use Vector<float> for AudioBlock storage
This will allow reuse of the allocations, instead of reallocating a
FixedArray for every block that changes size. Generally, it will be
possible to reuse AudioBlock memory throughout most of the pipeline
at least while in a single process.
2026-05-13 02:05:35 -05:00
Zaggy1024
ee05b7dbd1 LibMedia: Check the queue size before waiting in decoder threads
This could result in a lost wakeup otherwise.
2026-05-13 02:05:35 -05:00
Zaggy1024
8f7cf75e24 LibMedia: Introduce base classes for producers (e.g. the data decoders) 2026-05-13 02:05:35 -05:00
Zaggy1024
eeff03c980 LibMedia: Rename some folders/identifiers for the upcoming refactor
- Provider -> producer
- (Audio|Video)DataProvider -> Decoded(Audio|Video)Producer
- MediaTimeProvider remains suffixed Provider, moves out of the
  Providers folder to the root of LibMedia

This brings the naming more in line with the intended split
functionality split between different nodes in the pipeline.
2026-05-13 02:05:35 -05:00
Zaggy1024
03989a5819 LibMedia: Split mixing and audio output into separate classes
This doesn't actually change things too much from the prior commit, but
acts as a step towards making mixing into a sink/provider combo in the
new pipeline model.
2026-05-13 02:05:35 -05:00
Zaggy1024
1d93837443 LibMedia: Rename AudioMixingSink -> AudioPlaybackSink
This is in anticipation of changing it from taking care of mixing and
output to only the latter.
2026-05-13 02:05:35 -05:00
Zaggy1024
5d761397da LibMedia: Remove WrapperTimeProvider in favor of diamond inheritance 2026-05-13 02:05:35 -05:00
Zaggy1024
dd626013b9 AK: Allow AtomicRefCounted to destroy classes using virtual inheritance
This warning is suppressed in RefCounted.h as well.
2026-05-13 02:05:35 -05:00
Zaggy1024
0d29a3bb3b LibMedia: Shift mixing off the PlaybackStream data callback thread
While mixing doesn't cost much time at the moment, moving audio
processing off the callback thread means that the callback itself can
block for a minimal amount of time regardless of the amount of time
needed to actually process the audio data.

This matters a lot for AudioUnit, where the entire daemon can start to
glitch if delays occur.

The goal of this change is to split mixing off into a separate class,
and process audio on both ends of the mixer synchronously. This should
allow a more declarative approach to processing audio, where inserting
a time stretching processor, for example, becomes much simpler.

In this commit, the thread spin-waits for new upstream data. Making it
wait on a condition variable here would overcomplicate this commit, so
it's deferred until a later commit establishes that forward wakeup.
2026-05-13 02:05:35 -05:00
Zaggy1024
a3177c5f71 LibMedia: Give PlaybackStream a way to wake up after underrun
This gives us a cheap way to wake up all PlaybackStream implementations
when they are sleeping due to an underrun. The new function is meant to
be very cheap so that it can be unconditionally called when new data is
ready to be written.

This will allow the audio sink to rely on streams to track the time in
written audio frames instead of writing silence when data isn't ready.

The WASAPI implementation was actually polling for new data every 10ms
after hitting an underrun, so now it drains the buffers and stops when
entering the underrun-paused state.
2026-05-13 02:05:35 -05:00
Zaggy1024
6eeb30ac49 LibMedia: Remove PlaybackStream change checks in AudioMixingSink
The playback stream is never recreated currently, so this wasn't
actually doing anything. This is now made explicit by never resetting
the playback stream creation flag when it succeeds.
2026-05-13 02:05:35 -05:00
Zaggy1024
50864c5728 LibMedia: Set temporary time in AudioMixingSink while initializing
Instead of skipping setting this if the playback stream isn't ready, we
need to store the time so that the stream creation resolution callback
can begin the seek later.
2026-05-13 02:05:35 -05:00
Zaggy1024
7bcea607b6 LibMedia: Recalculate AudioMixingSink's media time upon resume
Recalculating after suspend isn't really useful if the PlaybackStream
implementations ensure that the time doesn't advance past the data that
has been written so far.
2026-05-13 02:05:35 -05:00
Zaggy1024
f4abb2400b LibMedia: Prevent AudioUnit streams' time advancing while paused
Previously, writing less data than the stream requested would result in
the stream advancing its playback time past the enqueued frames. This
breaks the PlaybackStream contract, and was simply hidden by a similar
clamping at the AudioMixingSink. That is going away in the next commit.
2026-05-13 02:05:35 -05:00