This stops `::before` and `::after` nodes showing up for every single
element in the inspector tree. Unfortunately there's no way for us to
detect that one of these doesn't exist in layout but has *some* style
specified for it, but that seems like a rare use case.
Corresponds to dad91f49ef
The spec text doesn't actually require any changes from us, but I
noticed we were incorrectly calling `is_shadow_including_ancestor_of()`
instead of `is_shadow_including_inclusive_ancestor_of()`, so that's
fixed.
There are multiple things happening here which are interconnected:
- We now use AbstractElement to refer to the source of a counter, which
means we also need to pass that around to compute `content`.
- Give AbstractElement new helper methods that are needed by
CountersSet, so it doesn't have to care whether it's dealing with a
true Element or PseudoElement.
- The CountersSet algorithms now walk the layout tree instead of DOM
tree, so TreeBuilder needs to wait until the layout node exists
before it resolves counters for it.
- Resolve counters when creating a pseudo-element's layout node. We
awkwardly compute the `content` value up to twice: Once to figure out
what kind of node we need to make, and then if it's a string, we do
so again after counters are resolved so we can get the true value of
any `counter()` functions. This will need adjusting in the future but
it works for now.
This is one of those cases where the spec says "element" and
means "element or pseudo-element". The easiest way to handle both is to
make these be free functions that take an AbstractElement, and then
give AbstractElement some helper methods so that the caller doesn't
have to care which it's dealing with.
There are some FIXMEs here because PseudoElement doesn't have a
CountersSet yet, and because the CountersSet currently uses a
UniqueNodeID to identify counter sources, which doesn't support
pseudo-elements.
All other viewport-related dimensions are referenced to by 'viewport',
so let's rename the member that stores the viewport size to prevent
further confusion.
`Element::ordinal_value` is called for every `li` element in
a list (ul, ol, menu).
Before:
`ordinal_value` iterates through all of the children of the list
owner. It is called once for each element: complexity $O(n^2)$.
After:
- Save the result of the first calculation in `m_ordinal_value`
then return it in subsequent calls.
- Tree modifications are intercepted and trigger invalidation
of the first node's `m_ordinal_value`:
- insert_before
- append
- remove
Results in noticeable performance improvement rendering' large
lists: from 20s to 4s for 20K elements.
Before:
`is<HTMLOLListElement>` and other similar calls in this commit
are resolved to `dynamic_cast`, which incurs runtime overhead
for resolving the type. The Performance hit becomes apparent
when rendering large lists. Callgrind analysis points to a
significant performance hit from calls to `is<...>` in
`Element::list_owner`.
Reference: Michael Gibbs and Bjarne Stroustrup (2006) Fast dynamic
casting. Softw. Pract. Exper. 2006; 36:139–156
After:
Implement inline `fast_is` virtual method that immediately
resolves the type. Results in noticeable performance improvement
2x-ish for lists with 20K entries.
Bonus: Convert starting value for LI elements to signed integer
The spec requires the start attribute and starting value to be
"integer". Firefox and Chrome support a negative start attribute.
FIXME: At the time of this PR, about 134 other objects resolve
`is<...>` to `dynamic_cast`. It may be a good idea to coordinate
similar changes to at least [some of] the ones that my have impact
on performance (maybe open a new issue?).
Previously we would just throw it away and construct a new (empty) one
when required. This doesn't work as any existing references to the old
instance will contain out of date information. Now we retain and update
the existing instance instead.
Which has an optmization if both size of the string being passed
through are FlyStrings, which actually ends up being the case
in some places during selector matching comparing attribute names.
Instead of maintaining more overloads of
Infra::is_ascii_case_insensitive_match, switch
everything over to equals_ignoring_ascii_case instead.
The play_or_cancel_animations_after_display_property_change() helper
was being called by Node::inserted() and Node::removed_from() and then
recursing into the shadow-including subtree.
This had quadratic complexity since inserted() and removed_from() are
themselves already invoked recursively for everything in the
shadow-including subtree.
Only one caller of this API actually needed the recursive behavior,
so this patch moves that responsibility to the caller and puts the logic
in style recomputation instead.
1.02x speedup on Speedometer's TodoMVC-jQuery.
After f7a3f785a8, sibling nodes' styles
were no longer invalidated after a node was removed. This reuses the
flag for `:first-child` and `:last-child` to indicate that a node's
style might be affected by any structural change in its siblings.
Fixes#4631.
Resolves the `:only-child` ACID3 failure as documented in #1231.
For attributes like Element.ariaControlsElements, which are a reflection
of FrozenArray<Element>, we must return the same JS::Array object every
time the attribute is invoked - until its contents have changed. This
patch implements caching of the reflected array in accordance with the
spec.
This was recently added to both the HTML and DOM specifications,
introducing the new moveBefore DOM API, as well as the new internal
'removing steps'.
See:
* 432e8fb
* eaf2ac7
To allow for adding the concept of a WorkerAgent to be reused
between shared and dedicated workers. An event loop is the
commonality between the different agent types, though, there
are some differences between those event loops which we customize
on the construction of the HTML::EventLoop.
We are meant to store a weak reference to the element indicated by this
attribute, rather than a GC-protected strong reference. This also hoists
the "get the attr-associated element" AO into its own function, rather
than being hidden in IDL, to match "get the attr-associated elements".
There are ARIA attributes, e.g. ariaControlsElements, which refer to a
list of elements by their ID. For example:
<div aria-controls="item1 item2">
The div.ariaControlsElements attribute would be a list of elements whose
ID matches the values in the aria-controls attribute.
For unscrolled elements (already at 0, 0) that aren't eligible to be the
Document.scrollingElement, we can short-circuit here and avoid doing a
synchronous relayout of the page.
This avoids a bunch of synchronous layouts on Speedometer3's NewsSite
subtests.
Before this change, we were going through the chain of base classes for
each IDL interface object and having them set the prototype to their
prototype.
Instead of doing that, reorder things so that we set the right prototype
immediately in Foo::initialize(), and then don't bother in all the base
class overrides.
This knocks off a ~1% profile item on Speedometer 3.
Many elements never end up needing this string, so instead of eagerly
generating it in the Element constructor, let's defer it until someone
actually requests it.
Knocks off a ~1% profile item on Speedometer3's jQuery test.
We achieve this by keeping track of all checked pseudo class selectors
in the SelectorEngine code. We also give StyleComputer per-pseudo-class
rule caches.
These callbacks are evaluated synchronously via JS::Call. We do not need
to construct an expensive RootVector container just to immediately
invoke the callbacks.
Stylistically, this also helps indicate where the actual arguments start
at the call sites, by wrapping the arguments in braces.
Before this change, we'd skip storing the new ComputedProperties in
Element::recompute_style() if there was no invalidation needed.
This caused us to lose the information about which properties are
inherited and/or important (which is also carried by ComputedProperties,
but doesn't affect invalidation).
Consequently, we'd then fail to recompute inherited styles, since that
mechanism depends on this data.
The fix is simply to always store the new ComputedProperties.