Implements idea described in
https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8
Invalidation sets are used to reduce the number of elements marked for
style recalculation by collecting metadata from style rules about the
dependencies between properties that could affect an element’s style.
Currently, this optimization is only applied to style invalidation
triggered by class list mutations on an element.
These common cases now cause us to invalidate the layout tree starting
at the relevant parent node instead of invalidating the entire tree.
- DOM node insertion
- DOM node removal
- innerHTML setter
- textContent setter
This makes a lot of dynamic content much faster. For example, demos
on shadertoy.com go from ~18 fps to ~28 fps on my machine.
DOM nodes now have two additional flags:
- Needs layout tree update
- Child needs layout tree update
These work similarly to the needs-style-update flags, but instead signal
the need to rebuild the corresponding part of the layout tree.
When a specific DOM node needs a layout tree update, we try to create
a new subtree starting at that node, and then replace the subtree in the
old layout tree with the newly created subtree.
This required some refactoring in TreeBuilder so that we can skip over
entire subtrees during a tree update.
Note that no partial updates happen yet (as of this commit) since we
always invalidate the full layout tree still. That will change in the
next commit.
Per https://w3c.github.io/aria/#document-handling_author-errors_roles,
determining whether to ignore certain specified landmark roles requires
first determining whether the element for which the role is specified
has an accessible name.
But if we then try to retrieve a role for such elements, we end up
calling right back into the accessible-name computation code — which
would cause the calls to loop infinitely.
So to avoid that — and to have handling for any other future cases the
spec may introduce of such recursive calls that will loop indefinitely —
this change introduces a parameter that callers can pass to cause
role-attribute lookup to be skipped during accessible-name computation.
When setting the textContent of an element with no children to null or
the empty string, nothing happens. Even so, we were still invalidating
style, layout and collections, causing pointless churn.
Skipping invalidation in this case also revealed that we were missing
invalidation when changing the selected state of HTMLOptionElement.
This was all caught by existing tests already in-tree. :^)
This change ensures that when an accessible name is computed from
multiple labels, the parts computed from each label are separated by
spaces. Otherwise, without this change, the parts are run together in
the accessible name, with no space in between.
We can now mark an element as needing an "inherited style update" rather
than a full "style update". This effectively means that the next style
update will visit the element and pull all of its inherited properties
from the relevant ancestor element.
This is now used for descendants of elements with animated style.
The DOM spec defines what it means for an element to be an "editing
host", and the Editing spec does the same for the "editable" concept.
Replace our `Node::is_editable()` implementation with these
spec-compliant algorithms.
An editing host is an element that has the properties to make its
contents effectively editable. Editable elements are descendants of an
editing host. Concepts like the inheritable contenteditable attribute
are propagated through the editable algorithm.
This change implements spec-conformant computation of default ARIA roles
for elements whose expected default role depends on the element’s
context — specifically, either on the element’s ancestry, or on whether
the element has an accessible name, or both. This affects the “aside”,
“footer”, “header”, and “section” elements.
Otherwise, without this change, “aside”, “footer”, “header”, and
“section” elements may unexpectedly end up with the wrong default roles.
This is a minor change to the Node::name_or_description code to switch
some instances of element.has_attribute("value"_string) over to instead
using element.has_attribute(HTML::AttributeNames::value).
This change makes Ladybird give the value of the aria-label attribute
the correct precedence for accessible-name computation required by the
“Accessible Name and Description Computation” and HTML-AAM specs and by
the corresponding WPT tests.
Otherwise, without this change, Ladybird fails some of the WPT subtests
of the test at https://wpt.fyi/results/accname/name/comp_label.html.
This change implements full support for the “A. Hidden Not Referenced”
step at https://w3c.github.io/accname/#step2A in the “Accessible Name
and Description Computation” spec — including handling all hidden nodes
that must be ignored, as well as handling hidden nodes that, for the
purposes of accessible-name computation, must not be ignored (due to
having aria-labelledby/aria-describedby references from other nodes).
Otherwise, without this change, not all cases of hidden nodes get
ignored as expected, while cases of nodes that are hidden but that have
aria-labelledby/aria-describedby references from other nodes get
unexpectedly ignored.
This change adds handling for the “Determine Child Nodes” substep at
https://w3c.github.io/accname/#comp_name_from_content_find_child in the
“Accessible Name and Description Computation” spec. Specifically, it
adds handling for the “If the current node has an attached shadow root”
and “if the current node is a slot with assigned nodes” conditions.
Otherwise, without this change, AT users don’t hear the expected
accessible names in cases where the content for which an accessible name
being computed is in a shadow root or slot element.
This change makes Ladybird correctly handle all “encapsulation” tests in
the https://wpt.fyi/results/accname/name/comp_host_language_label.html
set of tests in WPT.
Those all test the requirement that when computing the accessible name
for a <label>-ed form control, then any content (text content or
attribute values) from the control itself that would otherwise be
included in the accessible-name computation for it ancestor <label> must
instead be skipped and not included.
The HTML-AAM spec seems to try to achieve that result by expressing
specific steps for each particular type of form control. But what all
that reduces/optimizes/simplifies down to is just, “skip over self”.
Otherwise, without this change, Ladybird includes that “self” content
from those “encapsulated” elements when doing accessible-name
computation for the elements — which results in AT users hearing
unexpected extra content in the accessible names for those elements.
This change makes Ladybird conform to the requirements in the HTML-AAM
spec at https://w3c.github.io/html-aam/#accname-computation for the
cases of HTML input@type=button, input@type=submit, and input@type=reset
elements. Otherwise, without this change, Ladybird fails to expose the
expected accessible names for those cases.
This change makes Ladybird conform to the requirements in the HTML-AAM
spec at https://w3c.github.io/html-aam/#accname-computation for the
cases of HTML table, fieldset, and input@type=image elements. Otherwise,
without this change, Ladybird fails to expose the expected accessible
names for those cases.
This isn't directly in the spec, but since replaceChild is implemented
in terms of remove + insert, the removal step may cause arbitrary code
to execute, and so we have to verify that the replaceChild inputs still
make sense afterwards, before doing the insertion.
This roughly matches what WebKit does, and makes a bunch of HTML parsing
tests in WPT stop asserting.
This change adds support for computing accessible names for SVG
elements, per the https://w3c.github.io/svg-aam/#mapping_additional_nd
spec requirements. Otherwise, without this change, accessible names for
SVG elements don’t get exposed as expected.
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.
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:
* JS::NonnullGCPtr -> GC::Ref
* JS::GCPtr -> GC::Ptr
* JS::HeapFunction -> GC::Function
* JS::CellImpl -> GC::Cell
* JS::Handle -> GC::Root
The main motivation behind this is to remove JS specifics of the Realm
from the implementation of the Heap.
As a side effect of this change, this is a bit nicer to read than the
previous approach, and in my opinion, also makes it a little more clear
that this method is specific to a JavaScript Realm.
This change removes the append_without_space, append_with_space,
prepend_without_space, and prepend_with_space functions from DOM::Node.
All those methods were added with the initial “Implement Accessible Name
and Description Calculation” commit in da5c918 and were only used in the
code related to accessible-name computation. But subsequent changes to
that code have removed all the calls to those functions — so now they’re
all completely unused.
This change ensures that when the aria-labelledby attribute is used, the
expected text from the element referenced in the aria-labelledby value
appears in the computed accessible name. Otherwise, without this change,
the expected text doesn’t appear in the computed accessible name.
This change fixes handling for substep ii of the “F. Name From Content”
step at https://w3c.github.io/accname/#step2F in the “Accessible Name
and Description Computation” spec — to correctly include any ::before
and ::after pseudo-element content in the computation of accessible
names. Otherwise, without this change, accessible names unexpectedly
don’t include that pseudo-element content.
This change implements the https://w3c.github.io/accname/#comp_append
step in the “Accessible Name and Description Computation” spec — so that
when an accessible name is computed from multiple sources in a document
subtree, the parts of the computed text are joined together with spaces.
Otherwise without this change, in accessible names computed from
multiple sources in a document subtree, the parts of the computed text
are unexpectedly run together, with no spaces between the parts.
These changes are arbitrarily divided into multiple commits to make it
easier to find potentially introduced bugs with git bisect.Everything:
The modifications in this commit were automatically made using the
following command:
find . -name '*.cpp' -exec sed -i -E 's/dbg\(\) << ("[^"{]*");/dbgln\(\1\);/' {} \;
The StyleResolver can find the specified CSS values for the parent
element via the DOM. Forcing everyone to locate specified values for
their parent was completely unnecessary.
This patch adds a second style dirty bit that tracks whether a DOM node
has one or more children with dirty style. This allows the style update
to skip over entire subtrees where all nodes are clean.