If we end up in a situation where the navigable no longer has an active
window, we can't perform navigation or many other navigable operations.
These are all ad-hoc, since the navigables spec is basically all written
as if there's always an active window. Unfortunately, the active window
comes from the active document's browsing context, which is a nullable
concept even in the spec, so we do need to deal with null here.
This removes all the locally reproducible crashes when running WPT over
the legacy Japanese encoding directory on my computer.
Yes, this is a bit of a monkey patch, but it should be harmless since
we're (as I understand it) dealing with navigables that are still
hanging around with related tasks queued on them. Once all these tasks
have been completed, the navigables will go away anyway.
While Origin is defined in the HTML spec - this leaves us with quite an
awkward relationship as the URL spec makes use of AO's from what is
defined in the HTML spec.
To simplify this factoring, relocate Origin into LibURL.
I originally believed that this could never receive a null URL and the
spec was inaccurate, but it seems like it can indeed.
I don't have a distilled test, but this makes logging in with GitHub
work on https://v0.dev/
You can now build with STYLE_INVALIDATION_DEBUG and get a debug stream
of reasons why style invalidations are happening and where.
I've rewritten this code many times, so instead of throwing it away once
again, I figured we should at least have it behind a flag.
Calls to `Document::set_needs_display()` and
`Paintable::set_needs_display()` now invalidate the display list by
default. This behavior can be changed by passing
`InvalidateDisplayList::No` to the function where invalidating the
display list is not necessary.
The spec requires that "multipart/form-data" Content-Type headers also
include a boundary directive. This allows the content server to validate
the submitted form data.
Google Lens, for example, rejects forms missing this directive.
Copy a display list item and apply scroll offset instead of mutating
display list directly.
It's a preparation for upcoming changes where a display list will be
cached across repaints and used multiple times with different scroll
offsets.
This allows us to use HeapFunction all of the way down, allowing us
to remove the Handle usage in after_session_callback for
create_new_child_navigable.
...For the completion steps. This is quite nice, as we can simply
capture this in the heap function where it is used instead of
needing to establish a new root.
Note that with these changes, to represent 'an empty algorithm', we now
use a null HeapFunction and do not invoke the steps.
This change causes the viewport to be treated as a "scroll frame,"
similar to how it already works for boxes with "overflow: scroll."
This means that, instead of encoding the viewport translation into a
display list, the items will be assigned the scroll frame id of the
viewport and then shifted by the scroll offset before execution. In the
future it will allow us to reuse a display list for repainting if only
scroll offset has changed.
As a side effect, it also removes the need for special handling of
"position: fixed" because compensating for the viewport offset while
painting or hit-testing is no longer necessary. Instead, anything
contained within a "position: fixed" element is simply not assigned
a scroll frame id, which means it is not shifted by the scroll offset.
While introducing clip and scroll frame trees, I made a mistake by
assuming that the paintable tree includes boxes from nested navigables.
Therefore, this comment in the code was incorrect, and clip/scroll
frames were simply not assigned for iframes:
// NOTE: We only need to refresh the scroll state for traversables
// because they are responsible for tracking the state of all
// nested navigables.
As a result, anything with "overflow: scroll" is currently not
scrollable inside an iframe
This change fixes that by ensuring clip and scroll frames are assigned
and refreshed for each navigable. To achieve this, I had to modify the
display list building process to record a separate display list for each
navigable. This is necessary because scroll frame ids are local to a
navigable, making it impossible to call
`DisplayList::apply_scroll_offsets()` on a display list that contains
ids from multiple navigables.
`m_needs_repaint = true` is not enough because it doesn't schedule
repaint of a parent navigable.
Fixes the bug when an iframe is not repainted after scrolling.
Navigables are re-used for navigations within the same tab. Its current
ownership of the cursor position is a bit ad-hoc, so nothing in the spec
indicates when to reset the cursor, nor do we manually do so. So when a
cursor update happens on one page, that cursor is retained on the next
page.
Instead, let's have the document own the cursor. Each navigation results
in a new document, thus we don't need to worry about resetting cursors.
This also makes many of the callsites feel nicer. We were previously
often going from the node, to the document, to the navigable, to the
cursor. This patch removes the navigable hop.
Contrary to LibGfx, where corner clipping was implemented by sampling
and blitting pixels under corners into a temporary bitmap, Skia allows
us to simply apply a mask. As a result, we no longer need the
BlitCornerClipping command, which has become a no-op.
- SampleUnderCorners is renamed to AddRoundedRectClip
- The optimization that skipped unnecessary blit and sample commands has
been removed. However, this should not result in a performance
regression because Skia seems to perform mask rasterization lazily.
The main intention of this change is to have a consistent look and
behavior across all scrollbars, including elements with
`overflow: scroll` and `overflow: auto`, iframes, and a page.
Before:
- Page's scrollbar is painted by Browser (Qt/AppKit) using the
corresponding UI framework style,
- Both WebContent and Browser know the scroll position offset.
- WebContent uses did_request_scroll_to() IPC call to send updates.
- Browser uses set_viewport_rect() to send updates.
After:
- Page's scrollbar is painted on WebContent side using the same style as
currently used for elements with `overflow: scroll` and
`overflow: auto`. A nice side effects: scrollbars are now painted for
iframes, and page's scrollbar respects scrollbar-width CSS property.
- Only WebContent knows scroll position offset.
- did_request_scroll_to() is no longer used.
- set_viewport_rect() is changed to set_viewport_size().
...instead of scheduling repaint timer in PageClient.
This change fixes flickering on Discord that happened because:
- Event loop schedules repainting by activating repaint timer
- `Document::tear_down_layout_tree()` destroys paintable tree
- Repaint timer invokes callback and renders an empty frame because
paintable tree was destroyed
Before this change we were recording and executing sample/blit commands
for each painting phase, even if there are no painting commands
in-between sample and blit that produce result visible on a canvas.
This change adds an optimization pass that goes through recorded
painting commands list and marks sample and blit commands that could
be skipped.
Reduces sample and blit corners executing from 17% to 8% on Discord.
The only subclass was already GC-allocated, so let's hoist the JS::Cell
inheritance up one level. This ends up simplifying a bit of rather
dubious looking code where we were previously slicing ESOs.
Changes the signature of queue_global_task() from AK:Function to
JS::HeapFunction to be more clear to the user of the function that this
is what it uses internally.
Before this change JS console was initialise from
activate_history_entry() which is too late for about:blank documents
that are ready to run scripts immediately after creation.
...because existance of this method conflicts with the purpose of
having a queue as it allows to start executing next task in the middle
of ongoing task.
For example:
1. SHTQ timer starts executing a task.
2. Task does SHTQ::process().
Another example:
1. SHTQ::process() start executing a task.
2. task does SHTQ::process().
Workaround spec bug by explicitly carrying information whether
navigation is sync (History api, fragment change) or not.
See for more details https://github.com/whatwg/html/issues/10232
If navigation early returns before reaching "finalize a cross document
navigation" then we have to make sure delaying load events is disabled.
See spec issue https://github.com/whatwg/html/issues/10252
No need to force an allocation. This makes a future patch a bit simpler,
where we will have the encoding as a String. With this patch, we won't
have to convert it to a ByteString.
These changes do not solve hanging `location.reload()` and
`location.go()` but only align implementation with the latest edits in
the specification.
`WindowProxy-Get-after-detaching-from-browsing-context` test output is
affected because `iframe.remove();` no longer synchronously does
destruction of a document, but queues a task on event loop.
Co-Authored-By: Andrew Kaster <akaster@serenityos.org>