In the first iteration of this, we kept an ArrayList of all rules with
visibility properties. Why bother evaluating if a rule's selector matches an
element if that rule doesn't have any meanignful (i.e. visibility) properties?
This commit enhances that approach by bucketing the rules. Given the following
selectors:
.hidden {....}
.footer > .small {...}
We can store the rules based on their right-most selector. So, given an element
we can do:
if (getId(el)) |id| {
const rules = id_lookup.get(id) orelse continue;
// check rules
}
if (getClasses(el)) |classes| {
for (classes) |c| {
const rules = class_lookup(c) orelse continue;
// chck rules
}
}
...
On an amazon product page, the total list of visibility-related rules was ~230.
Now, scanning 230 rules for a match isn't _aweful_, but remember that this has
to be done up the ancestor tree AND, for Amazon, this is called over 20K times.
This change requires that the StyleManager becomes more matching/parsing-aware
but a typical visibility check on that same Amazon product page only has to
check 2 rules (down from 230) and often has to check 0 rules.
Also, we now filter out a few more interactive-related pseudo-elements, e.g.
:hover. These aren't supported by the browser as a whole (i.e. they can _never_
match), so they can be filtered out early, when building the rules lookup.
We now pay attention to the type of event that causes the unhandled exception.
This allows us to trigger the window.rejectionhandled event when that is the
correct type. It also lets us no-op for other event types which should not
trigger rejectionhandled or unhandledrejection.
Fixes stackoverflow in github integration.
A Page now has a StyleManager. The StyleManager currently answers two questions:
1 - Is an element hidden
2 - Does an element have pointer-events == none
This is used in calls such as element.checkVisibility which, on some pages, can
be called tens of thousands of times (often through other methods, like
element.getBoundingClientRect). This _can_ be a bottleneck.
The StyleManager keeps a list of rules. The rules include the selector,
specificity, and properties that we care about. Rules in a stylesheet that
contain no properties of interest are ignored. This is the first and likely
most significant optimization. Presumably, most CSS rules don't have a
display/visibility/opacity or pointer-events property.
The list is rules is cached until stylesheets are modified or delete. When this
happens, the StyleManager is flagged as "dirty" and rebuilt on-demand in the
next query. This is our second major optimization.
For now, to check if an element is visible, we still need to scan all rules.
But having a pre-build subset of all the rules is a first step.
The next step might be to optimize the matching, or possibly optimizing common
cases (e.g. id and/or simple class selector)