A flex item with a preferred aspect ratio but no natural width or
height and no definite cross size is stretched to fill its container's
main size as a fallback. This was applied unconditionally, but when
the container itself is being measured for an intrinsic size, the
container's main size is indefinite and the size evaluated to zero.
This clobbered the max-content size the flex algorithm had already
computed as the intrinsic contribution.
Apparently FFmpeg probes WAV files for 32 packets before returning the
stream info. With the default of 19200 bytes per packet, this could end
up waiting for up to 5 seconds of data to be downloaded.
Setting the max_size option only affects WAV and W64 in our build of
libavformat. No other formats we care about will be affected.
Unfortunately, this necessarily involves parsing codec frames to get
their durations. Ogg's granule positions indicate the last sample of
the last complete frame of the page, so the navigator has to determine
the durations of every packet in the page to offset it back to its
start.
Vorbis is an especially involved codec to determine that for, since it
has initial data defining a mapping of indices to big or small block
sizes that has to be held onto, and that mapping is preceded by a bunch
of conditionally parsed bits. Also, a frame's block size calculation
involves the block size of the previous frame for an overlap add. To
avoid bringing that complexity directly into LibMedia, we delegate the
parsing/calculation to FFmpeg's av_vorbis_parse functions.
A new MP3Navigator class is added, which determines timestamps for byte
positions by resyncing to a frame and then interpolating between known
points on either side. The known points start out as the first frame's
position in the file at timestamp 0, and EOF at the timestamp for
FFmpeg's file duration estimate. New buffered ranges are interpolated
between those two points, but also between the end of a prior range and
the start of the next.
Since MP3 can have variable bitrate without declaring it in the file
header, we have to allow buffered ranges to shift forward as new data
arrives to make room for underestimated durations. This is done for all
ranges following the first that has been appended to, keeping the start
of the current range consistent, so that subsequent seeks within that
range remain consistent.
Seeking is also implemented within the navigator to ensure that the
byte<->timestamp mapping is consistent and the buffered ranges begin
exactly where the seek landed.
We could end up seeked within AVIOContext's internal buffer without it
checking whether new data could be buffered without hitting EOF again,
so we could get an unexpected read error.
Add a new FLACNavigator class that can scan for frames both forward and
backward from a byte offset to get the exact buffered ranges. The reads
are done in chunks, which keeps each call pretty cheap.
The ranges themselves are resolved by its ScanningContainerNavigator
base class, which reconciles cached ranges against the incoming ones to
avoid repeating work unnecessarily. With those combined optimizations,
the calls normally take under 1 microsecond.
The default mode is to grab the index and use it to determine the start
and end of the buffered ranges. This works well for MP4.
For WAV, the index is incomplete, so instead use a much simpler method,
just determining the ranges based on the constant bitrate of the file.
This is implemented through a new virtual ContainerNavigator class.
Also, update the ready state when the progress event is fired.
Otherwise, we won't autoplay if 5 seconds of data aren't available in
on_metadata_parsed().
This was unintentionally clobbering the prior modification to the
request position. Requesting data slightly earlier in the file is
intended to ensure that we have data if the demuxer decides to seek
backwards.
Returning early if an added chunk didn't overlap the previous one could
result in overlapping chunks or 0-byte gaps if a chunk was after but
within the new one. Instead, fall through to allow the merging to
continue as normal.
Also, make the joining condition check if the new chunk spans multiple
existing chunks to merge/remove those.
This is necessary to allow the main thread to read out frames to
determine the buffered ranges, without affecting the request position
of an IncrementallyPopulatedStream or causing a deadlock waiting for
data to come in.
We would ignore whether the callback/event loop had been set up for the
stream. Tests appending enough data for it to start a new request after
the new data would cause a crash.
If a page contains multiple <link rel="icon"> elements, we would send
each of them to the UI process. We would then just use whichever was
sent last as the favicon in the UI.
We now only send the favicon that was chosen for the document. This
will either be the largest icon decoded from a link element, or the
singular fallback icon.
We currently pick the first favicon in reverse tree order. But we are
encouraged by the spec to pick the most appropraite icon. We now
consider the size of the decoded icon, and choose the largest.
These both require the same helper processes, and maintaining the same
list in multiple places is error-prone. For example, I spent too long
debugging why test-web was crashing, when the issue was that Compositor
hadn't been rebuilt as test-web didn't depend on it. That problem can
no longer happen if we define the same dependency list for both.
Problem: Select All draws the selection highlight across
user-select:none content — breaking compat with Chrome and Firefox,
which leave such content unhighlighted.
Cause: ViewportPaintable::recompute_selection_states walks the Range and
assigns SelectionState::Start|::Full|::End to each layout node within.
The walk filters out is_inert() nodes — but not user-select: none nodes.
Fix: Extend the existing inert-only guards into a helper that also
rejects nodes whose used value of user-select is ‘none’. Such nodes stay
at SelectionState::None from the initial reset — so the selection
highlight skips them.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9695
Problem: Select All (Ctrl+A or the context menu) followed by Ctrl+C
unexpectedly copies text from elements with user-select:none — breaking
compat with Chrome and Firefox, which both exclude user-select:none
content from the clipboard.
Cause: Navigable::selected_text() walks the selection range via
visible_text_in_range() and concatenates each text node’s data. The walk
filters out nodes without a layout, but not nodes whose used value of
user-select is ‘none’.
Fix: Add a user-select check at each visible_text_in_range() walk point.
The Selection range itself is left unchanged. Selection.toString() still
returns the full text per spec — but the clipboard-extraction path now
excludes user-select: none subtrees, per spec
Previously, `text_path() used the glyph outline bounding box width to
compute text alignment offsets and maxWidth scaling. The bounding box
only covers visible glyph outlines, excluding the advance of spaces and
glyph side bearings. This caused text with `textAlign` "end", "right",
or "center" to be mispositioned. We now use the sum of glyph run
advance widths instead.
Previously, when no previous sibling has a line box fragment, the
static position for inline-level elements defaulted to (0, 0), ignoring
float intrusion into the line box. We now use
`leftmost_inline_offset_at()` so the hypothetical box is placed on the
float-shortened line.
Previously, we only checked the immediate previous sibling for line box
fragments. We now walk backwards through all previous siblings instead,
so that intermediate siblings without fragments do not cause the search
to end prematurely.
Also use `m_containing_block_used_values` for the fragment search
instead of the sibling containing block, which for absolutely
positioned elements returns the nearest positioned ancestor rather than
the IFC parent that owns the line boxes.
Previously, starting a view transition on an element inside a
`display:none` subtree would crash because querying the view
transition name unconditionally accessed `computed_properties()`, which
is not calculated for these elements unless explicitly requested. We
now skip these non-rendered elements early, before querying the view
transition name, to avoid the crash.