Commit graph

2607 commits

Author SHA1 Message Date
Andreas Kling
c746bd5049 LibWeb: Avoid needless :has() descendant fanout
When an element was affected by :has() in subject position,
pending :has() mutation invalidation also applied the pseudo-class
invalidation plan for that element. If the same element had ever been
observed in a non-subject :has() selector, that plan could invalidate
the element's whole descendant subtree even when the mutation could
only require the subject element itself to recompute style.

Keep subject-position :has() invalidation to the element itself, and
run the descendant fanout only for non-subject involvement after the
existing mutation feature filter says that fanout may be affected.
Track the strongest invalidation applied to each anchor, so a later
batched mutation can still upgrade earlier anchor-only work to a
descendant fanout.

Extend the filter to treat known state pseudo-classes as concrete
mutation features, so a :hover mutation does not conservatively match
unrelated selectors such as :has(dialog:modal).

Scope boundary selectors are kept conservative where needed: when an
@scope boundary contains :has(), the scope root's match can activate or
deactivate style rules for descendants, so it still records descendant
involvement.

Add coverage for unrelated hover and batched mutation cases on elements
whose ancestors have both subject and non-subject :has() involvement,
ensuring descendant no-op recomputation stays bounded without dropping
required descendant fanout.
2026-06-08 17:16:18 +02:00
Andreas Kling
bae4434780 LibWeb: Avoid sibling walks for last-child invalidation
Track last-child and backward positional selector dependencies
separately on parent nodes. A last-child or only-child selector can only
change the element at the trailing edge, so insertions and removals can
invalidate that element directly instead of walking every previous
sibling.

Keep the previous-sibling walk for selectors such as nth-last-child and
last-of-type, where every previous element's from-end position may
change.
2026-06-08 17:16:18 +02:00
Andreas Kling
62a5ee7ff9 LibWeb: Guard pseudo-class invalidation by selector features
Include tag and attribute selectors in guarded pseudo-class invalidation
plans. This keeps selectors like a:hover .target from applying work to
unrelated descendants when :hover changes on other elements.

Store both original and lowercase attribute names for invalidation keys.
HTML attribute mutations still hit lowercase selector buckets, while SVG
and MathML selectors such as [viewBox] keep their case-sensitive guards.
Use the same names for :has() metadata and mutation feature filtering so
attribute changes do not get filtered out after metadata lookup.

Match tag invalidation properties against lowercased local names, like
invalidation data and rule cache buckets do. The regression tests cover
SVG hover fanout and case-sensitive :has() attribute mutation.
2026-06-08 17:16:18 +02:00
Andreas Kling
dc98a11f4e LibWeb: Handle var() in grid placement shorthands
Preserve unresolved values while serializing grid-row and grid-column
shorthands. The serializer can only test for the auto placement keyword
after confirming that the longhand value is a grid track placement.

Add a reduced crash test covering CSSOM serialization after assigning
var() to grid-row-end and grid-column-end.
2026-06-08 01:04:08 +02:00
Andreas Kling
9340d2d1a3 LibWeb: Make layout nodes refcounted
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.

Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.

Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.

Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.

Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
2026-06-07 20:52:49 +02:00
Andreas Kling
0b94af1109 LibWeb: Stop tracing CSS style values
Remove the visit_edges hook from CSS::StyleValue and stop asking CSS
properties, descriptors, computed values, and layout nodes to trace
through their style values.

Style values are refcounted data objects, so they should not be part of
the GC graph. Keeping this cleanup separate makes the later layout tree
ownership change smaller and easier to review.
2026-06-07 20:52:49 +02:00
Andreas Kling
7c06c3fd14 LibWeb: Make computed style data refcounted
Move ComputedProperties and CascadedProperties out of the GC. They no
longer contain strong references to GC-managed data.

Keep computed styles alive from DOM elements and animation updates with
RefPtr. Pass style into layout constructors by reference, since layout
only copies the values it needs while building nodes.

Use GC::Weak for cascade source links, so entries no longer keep the
style declaration or shadow root alive.
2026-06-06 23:29:48 +02:00
Andreas Kling
222c1f7044 LibWeb: Move CSS image loading state to Document
Move the SharedResourceRequest, animation timer, and current frame
state out of ImageStyleValue and into a Document-owned table keyed
by resolved image URL. ImageStyleValue now keeps only URL metadata
and its client list, so image style values no longer need to trace
GC edges themselves.

Thread the Document through AbstractImageStyleValue APIs that need
decoded image data. CSS image fetches snapshot the stylesheet base URL,
referrer behavior, and origin-clean state instead of retaining the
stylesheet.

Remember each client's registered resolved URL when unregistering. This
keeps a later document base change from leaving an animated image
resource alive.

Add text coverage for inline relative image base URLs, stylesheet
referrers, imported stylesheet origin-clean behavior, inline @import
initiator type, and unregistering an animated background image after a
base element change.
2026-06-06 23:29:48 +02:00
Timothy Flynn
87cdeda93c LibSyntax+LibJS+LibWeb+LibWebView: Remove unused syntax highlighter code
A future patch will port LibSyntax away from UTF-32. The less code that
needs to be ported, the easier that will be.
2026-06-06 18:42:18 +02:00
Aliaksandr Kalenik
852c7a10f3 LibWeb: Let image paint callers own clipping
DecodedImageData::paint() used to take both a destination and a
clip rectangle even though most callers passed the same value. SVG
image painting used that API to wrap every nested SVG display list in
save/add-clip/restore, which put an unbounded command in front of
the bounded nested-list command and made offscreen SVG image content
harder to cull.

Move clipping to ImagePaintable, where the object-fit destination can
be compared with the replaced element box. CSS image and marker
painting continue to draw into their destination rect, while repeated
background images keep their explicit tile clip. The scaled decoded
image display-list command now stores only its destination rect and
uses that as its bounds; playback still clips decoded images to that
rect so bitmap rendering stays unchanged.
2026-06-06 13:23:21 +01:00
Callum Law
3e6cfb1ba6 LibWeb: Remove libweb-* colors
After `Native.css` was removed in the last commit only
`libweb-buttonfacedisabled` and `libweb-buttonfacehover` remain in use,
so the rest can be removed.

Resolving these colors was also the only use of `document` in
`ColorResolutionContex` so we can remove that too.
2026-06-05 12:59:00 +01:00
Sam Atkins
c7fa23b0c3 LibWeb/CSS: Update to latest @font-face serialization spec
The `font-family` descriptor may not be present, so handle that
gracefully. We already did so, but now it's an official part of the
spec.

Corresponds to:
75299e7be1
cfb59f593a
2026-06-05 22:44:55 +12:00
Sam Atkins
9cc6270bbc LibWeb/CSS: Clip image inputs
Corresponds to
7486cd721e
2026-06-05 22:44:55 +12:00
Andreas Kling
789d7cba48 LibWeb: Use direct references to main event loops
Post off-thread font, script, and DNS completion work back to direct
Core::EventLoop references. These callbacks target the process main
loop, which is intentionally kept alive for the lifetime of the
process.
2026-06-05 09:18:39 +02:00
Sam Atkins
879fc69f89 LibWeb/CSS: Link DevTools rules to UA stylesheets
UA and user stylesheets do not have owner nodes or owner rules, so their
matched rules cannot be mapped through the generic stylesheet identifier
helper. Share the built-in UA stylesheet enumeration and map UA rules by
the matched CSSStyleSheet object, so that they stay in sync with any
future changes.
2026-06-04 20:54:33 +01:00
Sam Atkins
64e0aa8c26 LibWeb+LibDevTools: Link style rules to CSS sources
Resolve applied-rule stylesheet identities through StyleSheetsActor and
include Firefox parentStyleSheet, line, and column fields on rule forms.
Firefox can then show matched rules as stylesheet rules and open the
corresponding CSS source location from the Rules panel.
2026-06-04 20:54:33 +01:00
Sam Atkins
b2b164ebd7 LibWeb+WebContent: Report CSS rule source data
Include parser rule locations and stylesheet identities in the applied
style rule data sent to DevTools. This gives the protocol layer enough
information to map matched rules to existing stylesheet resources
without guessing from displayed rule text.
2026-06-04 20:54:33 +01:00
Sam Atkins
52e1d30404 LibWeb+LibDevTools: Report applied style rules to Firefox
Collect the style rules that apply to an inspected element and expose
them through the existing DOM node inspection path. This gives Firefox's
Rules panel real rule forms instead of the previous empty getApplied
response.
2026-06-04 20:54:33 +01:00
Sam Atkins
39106f9326 LibWeb/CSS: Parse authored declarations for DevTools
Add a parser entry point that preserves authored CSS declarations for
DevTools. The Rules panel needs the original property names, shorthand
values, invalid declarations, custom properties, and !important flags
rather than only the expanded computed representation.
2026-06-04 20:54:33 +01:00
Sam Atkins
ee24561ab4 LibWeb/CSS: Store source positions on CSS rules
DevTools needs rule-level source positions to link applied style
rules back to their source sheets. The parser already tracks token
line and column information, so carry that through qualified rules
and nested declarations when creating CSSRule objects.
2026-06-04 20:54:33 +01:00
Sam Atkins
df41e7a1cf LibWeb/CSS: Move Token::Position into SourcePosition
There's nothing about this that's specific to Tokens, and moving it
makes it easier to use for other types. We'll need this for the
following commits.
2026-06-04 20:54:33 +01:00
Sam Atkins
18ac54a402 LibWeb: Invalidate styles for scoped imports
Include scoped import start and end selectors in stylesheet selector
insights, and treat scoped `@import` additions or removals as broad
stylesheet invalidation triggers. Scoped import boundaries can change
which descendants match imported rules, so add/remove invalidation
cannot rely only on the imported style rule selectors.

Add local coverage for changing an import-scope root, changing an end
boundary, replacing or removing a scoped import rule, selector-insight
tracking, and stylesheet removal invalidation.
2026-06-04 20:52:28 +01:00
Sam Atkins
85e14f5f1f LibWeb: Apply scopes from imported stylesheets
Treat a scoped `@import` rule as a scope descriptor while traversing
style-producing rules from imported stylesheets. Imported rules now
inherit an outer scope for unscoped imports and replace it when the
import itself carries scope(...).

Scope resolution was generalized to handle both CSSScopeRule and
CSSImportRule descriptors. Import-scope boundaries are matched using
the stylesheet that parsed the `@import` rule, while normal imported
selectors keep using the imported stylesheet context. This lets
implicit scopes, nested `@scope` rules, :scope, and top-level `&` behave
as the Cascade 6 model requires.
2026-06-04 20:52:28 +01:00
Sam Atkins
538dca4448 LibWeb: Parse scoped CSS import rules
Parse and store the optional `scope` clause in `@import` preludes.

WPT doesn't directly test the serialization behaviour for this, so add a
custom test for it.
2026-06-04 20:52:28 +01:00
Andreas Kling
164ed80244 Meta: Enable exit-time destructor warnings for libraries
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.

Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
2026-06-04 19:20:49 +02:00
Sam Atkins
4ad7f06099 Revert "LibWeb: Delay load event during CSS @import fetch"
This reverts commit 1e0f6d1f7c.

Now that style sheets can block scripts, we don't need this additional
load delayer.
2026-06-04 16:39:54 +01:00
Sam Atkins
5cc0e1408a LibWeb/CSS: Treat non-matching @import supports() as loaded
Mark `@import` rules whose supports() condition is false as loaded
before returning from fetch(). These rules do not fetch a stylesheet,
but they still participate as critical subresources of the parent
sheet.

Leaving them Unloaded kept parser-created `<style>` blocks waiting for
critical subresources that can never finish, which blocked following
parser scripts forever.
2026-06-04 16:39:54 +01:00
Callum Law
4730493667 LibWeb: Apply body attribute link colors as presentational hints
Previously we overrode the resolved values of the `activetext`,
`linktext`, and `visitedtext` system colors based on the values of the
body's `alink`, `link`, and `vlink` attributes respectively.

This was incorrect and the spec instead expects us to apply these to the
relevant link elements as presentational hints while leaving those
colors untouched for other users.
2026-06-04 16:14:05 +02:00
Callum Law
843bd808cf LibWeb: Move `:visited: matching into helper method
And add spec text to explain why it never does.

We will be checking whether the `:visited` pseudo class matches in more
places in a future commit so this avoids duplicating FIXMEs.
2026-06-04 16:14:05 +02:00
Callum Law
b1e3306a24 LibWeb: Remove "attempted pseudo-class match" machinery
This is no longer used anywhere after fa57975
2026-06-04 16:14:05 +02:00
Callum Law
65134fc5da LibWeb: Remove dead code around resolving accent-color <system-color>s
While the spec says that `accentcolor` and `accentcolortext` resolve
relative to the `accent-color` property this isn't implemented by any
other browsers.

This effectively reverts d04b745 but doesn't change behavior since that
was already done in 92897a1.
2026-06-04 16:14:05 +02:00
Andreas Kling
ff13ac2b79 LibWeb: Avoid copying ASF argument component values
Parse arbitrary substitution function arguments as spans into the
existing component value list. This avoids copying each argument into a
new vector for var(), attr(), env(), if(), and inherit() parsing.

Expose a span-returning declaration-value parser that shares the same
walker as the existing vector-returning API, so the argument parser does
not duplicate declaration-value grammar logic. Substitution output still
uses owned vectors, and the unresolved Typed OM reifier now consumes
spans too.
2026-06-03 22:28:54 +02:00
Andreas Kling
70e82d39ed LibWeb: Store unresolved style values as text
Store the source text for unresolved CSS values instead of retaining
the full parsed component value tree. Values that need the component
tree now parse it on demand from the stored text.

This preserves equality, tokenization, variable substitution, and Typed
OM reification. Custom properties keep their original source text.
Unresolved values synthesized from component values store serialized
text.
2026-06-03 22:28:54 +02:00
Andreas Kling
6eb1afbf4b LibWeb: Avoid starting transitions on inactive timelines
Skip starting CSS transitions when the document timeline has no current
time yet. Focus can force a style update from a requestAnimationFrame
callback before the rendering step updates animation timelines, leaving
the DocumentTimeline current time unresolved.

Add crash coverage for a focus-triggered transition from a frame
callback.
2026-06-03 13:50:45 +02:00
Aliaksandr Kalenik
5595efd46b LibWeb: Avoid display list rebuilds for VV changes
Visual viewport scroll and pinch zoom used to invalidate the full
accumulated visual context tree and display list, even though those
changes only modify the root visual viewport transform. That forced a
full display list rerecord before sending updated compositor state.

Patch the reserved visual viewport AVC node in place instead.
VisualViewport marks the tree for compositor update without
invalidating the display list, and Navigable sends the replacement tree
through the new IPC path before updating scroll state.
2026-06-03 02:12:39 +01:00
Aliaksandr Kalenik
0e6528f9da Meta+LibWeb: Generate accumulated visual context property metadata
Style invalidation kept the list of properties that require rebuilding
the accumulated visual context tree in StyleInvalidation.cpp. That made
the classification separate from the existing property metadata used for
layout and stacking-context invalidation.

Move that classification into Properties.json and teach the PropertyID
generator to emit property_affects_accumulated_visual_contexts(). Style
invalidation now uses the generated predicate, preserving the existing
property set and behavior while making future classification changes
data-driven.
2026-06-02 18:55:25 +01:00
sideshowbarker
1e0f6d1f7c LibWeb: Delay load event during CSS @import fetch
Problem: Ref/input/css/images-load-relative-to-imported-style-sheet.html
intermittently fails on the Linux Sanitizer with a deterministic 398px
screenshot mismatch — the entire 20x20 background- image div is
unpainted in the test pass, while the reference paints the checkerboard.

Cause: CSSImportRule::fetch() added to the document’s render-blocking
set (m_pending_css_import_rules) — but didn’t delay the load event. The
load event could fire before the imported stylesheet had finished
fetching — so the background image referenced from inside that sheet
hadn’t even been requested yet when test-web’s on_load_finish callback
ran wait_for_reftest_completion and proceeded to take the screenshot. By
contrast, <link rel="stylesheet"> already delays the load event for its
own duration via m_document_load_event_delayer in HTMLLinkElement —
which is why the sibling test using <link> doesn’t flake.

Fix: Hold a DocumentLoadEventDelayer in CSSImportRule for the duration
of the import fetch, paralleling HTMLLinkElement. By the time the import
rule’s delayer clears in the ScopeGuard, the imported sheet’s image
fetches have already been kicked off via SharedResourceRequest, each of
which takes over load-event delaying through its own delayer — so
there’s no race between the two delayers releasing.
2026-06-02 09:57:02 +01:00
Sam Atkins
8c60d20dfc LibWeb: Keep grid template used values outside DevTools data
Resolved grid-template-columns and grid-template-rows values are needed
for getComputedStyle(), even when DevTools is not connected. Store them
separately from GridLayoutData again so the later lazy DevTools data
work can skip inspector-only geometry without changing CSSOM behavior.

Partly reverts 540b53ac1a.
2026-06-01 17:39:32 +01:00
Sam Atkins
4f8c682c21 LibWeb: Invalidate scoped styles when boundaries change
Scoped rules depend on their `@scope` start and end selectors, not just
the selectors for the declarations inside the rule. Register those
boundary selectors in the style invalidation data and selector insights
so mutations that create or remove scope roots or limits invalidate
descendant styles that may now match differently.

Scope boundary matching also needs to record selector involvement
against the element being styled. That keeps :has(), sibling, and
structural selector invalidation from treating the boundary candidate
as the only affected subject.

Run the stored :has() invalidation plans for affected anchors as well
as marking subject anchors dirty. This lets scope-boundary :has()
changes invalidate scoped descendants instead of leaving stale styles.
2026-06-01 08:28:11 +01:00
Sam Atkins
0c633ebf53 LibWeb/CSS: Cache size containers on Length::ResolutionContext
Avoid repeatedly searching the ancestor chain for a size container
element. Instead, cache the horizontal and vertical containers on the
ResolutionContext, so that anyone using the same ResolutionContext
avoids repeated lookups. For example, StyleComputer uses the same one
to compute all the properties of the same element.
2026-06-01 08:27:17 +01:00
Sam Atkins
a08b05a8e8 LibWeb: Recompute container-unit dependent styles
When a query container changes size or type, descendants that use
container-relative units need the same post-layout style refresh as
elements matched by `@container` rules.

Drop the style-scope gate from the invalidation and getComputedStyle()
paths so any element flagged as depending on a size query container gets
restyled after layout.
2026-06-01 08:27:17 +01:00
Sam Atkins
693517daa0 LibWeb: Resolve container-relative length units
Add cqw/cqh/cqi/cqb/cqmin/cqmax to the unit tables and generated
helpers, then thread them through the shared length resolution path.

Length::ResolutionContext now carries the subject element and whether
its inline axis is horizontal. Container units need that extra context:
the nearest eligible query container is selected from the subject
element's flat-tree ancestors, and cqi/cqb/cqmin/cqmax map logical axes
through the subject's writing mode before resolving to a physical width
or height.

Teach Length to resolve each axis against the selected container's
content box, fall back to viewport lengths when no eligible container
exists, and mark size-container dependencies so post-layout
recomputation can happen when layout is not up to date.

Also expose the new units through Typed OM, reject them for
computationally independent `@property` initial values, and add focused
font-size coverage.
2026-06-01 08:27:17 +01:00
aplefull
a0a3bfbee7 LibWeb: Use single-step division for aspect-ratio in default sizing
When background-size specifies only a width, the missing height was
computed as `(CSSPixels(1) / ratio) * width`. The intermediate
`CSSPixels(1) / ratio` step truncates the inverse ratio and loses
enough precision to produce a visible error in the final height for some
ratios.

Now we do the division in one step, so the full precision is preserved.
2026-06-01 00:04:21 +02:00
Tim Ledbetter
7e1a9ebb99 LibWeb: Avoid NaN values when reversing zero-duration transitions
A transition with zero duration and a positive delay can still start,
but its start and end times are equal. If it is reversed while running,
sampling the old transition's timing function performed a division by
zero and propagated NaN into the new transition timing. This caused a
crash under UBSan. We now treat zero duration transitions as if they
are finished for this calculation, avoiding the division by zero.
2026-05-31 14:10:28 +02:00
Andreas Kling
47cb66ef27 LibWeb: Account for shadow contexts in the cascade
Apply author declarations in encapsulation-context order before layer,
specificity, scope proximity, and source-order tie breaking. Normal
author rules now cascade from inner shadow contexts outward. This lets
outer contexts override component defaults. Important author rules
cascade the other way so inner contexts can enforce requirements.

Keep the ordering context-bucketed. The common document-only path still
collects and sorts one author context. Custom properties follow the same
context order. var() substitution now observes the same winners as
longhand properties.

Make revert-layer remove declarations only from the matching cascade
origin, context, and layer. This keeps presentational hints and values
from other shadow contexts available when one context rolls back its own
layer.

Add coverage for document and shadow-host competition, slotted rules,
nested slot contexts, inline styles, important declarations, custom
properties, and revert-layer across shadow contexts.
2026-05-31 12:51:58 +02:00
Andreas Kling
114c8a7939 LibWeb: Paint CSS mask image layers with sizing
Store CSS mask image layers in computed values so painting can honor the
coordinated mask-position, mask-size, mask-repeat, mask-origin, and
mask-clip longhands. Paint the CSS mask display list through the same
layer resolution path used by backgrounds, but keep the nested mask
commands local to the mask rect so existing mask-image placement remains
unchanged.

Add ref coverage for a no-repeat mask image sized and positioned at the
bottom of an element, which previously painted as a full-element mask.
2026-05-30 21:36:47 +02:00
Andreas Kling
6a05929c1c LibWeb: Reuse scoped rule matching scratch storage
Keep the temporary scoped matching rule list on StyleComputer.
This lets repeated style matching reuse its allocation instead of
creating a large stack object in collect_matching_rules().

Release builds zero that stack object before the constructor runs due
to automatic variable initialization. This was causing a 16 KiB memset
on every call to this function(!)

Verify that the scratch vector is empty on entry so accidental reentry
is caught before it can corrupt the outer rule collection.
2026-05-30 13:43:42 +02:00
Tim Ledbetter
709c10f8ec LibWeb: Let range inputs stretch in grid containers
Previously, the UA stylesheet gave range inputs a definite
`width: 20ch`. This prevented range inputs from filling their grid
track. We now source the default width and height from a new
`RangeInputBox` type. This approach matches the implementation of other
input types.
2026-05-30 12:17:58 +01:00
Shannon Booth
97abc707c7 LibWeb/Bindings: Generate buffer typedefs as variants
Represent BufferSource and ArrayBufferView as ordinary IDL typedefs over
their underlying union types, instead of special casing in the IDL
generator. This allows the union conversion/return machinery handle
these types consistently with other typedefs, which removes buffer
specific paths from the IDL generator.

This necessitates changing the WebIDL::BufferSource and
WebIDL::ArrayBufferView classes as views over these variants. This
replaces the old GC backed BufferableObject wrapper structure and
provide convenience helpers to determine things such as the byte length,
byte offset, backing buffer, and typed-array APIs.
2026-05-30 11:22:08 +02:00
Andreas Kling
162412c81a LibWeb: Narrow pseudo-class self invalidation
Only mark an element itself dirty for interaction pseudo-class changes
when matching rules can plausibly match that element. Check every style
scope observing the element, and keep the normal invalidation plan for
descendants and siblings.

Treat pseudo-elements as neutral during the plausibility check so rules
like a:hover::before still invalidate the originating element. Add text
coverage for non-matching hover rules and pseudo-element hover changes.
2026-05-30 02:13:23 +02:00