User:Jdm/SlowCSSSelectors
https://developer.mozilla.org/en/Writing_Efficient_CSS
http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSRuleProcessor.cpp
http://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSRuleProcessor.h
http://mxr.mozilla.org/mozilla-central/source/layout/style/StyleRule.h
http://mxr.mozilla.org/mozilla-central/source/layout/style/StyleRule.cpp
http://mxr.mozilla.org/mozilla-central/source/layout/style/nsNthIndexCache.cpp
http://mxr.mozilla.org/mozilla-central/source/layout/style/nsNthIndexCache.h
Functions like SelectorMatches show up in C++ profiles when CSS selector matching is being a problem. However, that is too low level - individual calls of SelectorMatches are fast, but the sheer volume of calls causes the problem. Instead, RuleHash is used to avoid even looking at most selectors.
11:56 <bz> so in Gecko there are at least 3 ways in which a selector can be expensive
11:57 <bz> 1) The selector might need to be matched against a bunch of nodes and be slow to match
11:57 <bz> This is what you can detect from EnumerateAllRules
11:58 <bz> basically, measure the time needed for the ContentEnumFunc call, and accumulate it in the selector
11:58 <bz> this won't add any new code to SelectorMatches, which is good
11:58 <bz> and if you do it only when profiling is active somehow, it shouldn't be a perf hit in general
11:59 <bz> 2) The selector might be very broad and catch a lot of dynamic changes, triggering a lot of style recomputation on dynamic changes
11:59 <bz> 3) The selector might be very broad and catch a lot of sibling stuff, triggering a lot of style recomputation on DOM insertion/removal
12:00 <bz> not sure how best to account for 2 and 3
12:03 <bz> #2 is mostly nsCSSRuleProcessor::HasAttributeDependentStyle and nsCSSRuleProcessor::HasStateDependentStyle
12:04 <bz> #3 is the various places where the rule processor sets the slow selector flags on the node and the places in presshell and frame constructor that then examine those flags....
12:05 <bz> Specifically, NODE_HAS_EDGE_CHILD_SELECTOR, NODE_HAS_SLOW_SELECTOR, NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS, NODE_HAS_EMPTY_SELECTOR
12:05 <bz> The problem is not the call
12:05 <bz> the problem is that if it matches, we restyle either the node or the node and all its descendants or the node and all its following siblings and all descendants of all those
12:05 <bz> depending on the exact selectors that matches
For #1, we can add a timer accumulator to the nsCSSSelector class, and surround all calls to SelectorMatches with a timer that adds its duration to this accumulator. To expose this information, we could potentitally add a method to iniDOMUtils.idl that takes a DOM CSS rule and returns profiling data for all selectors within it? This requires further discussion.
With regards to #2, it would be best served by timing restyles that occur and linking them to triggering CSS selectors, but this is complicated by restyle coalescing in the engine. This can be skipped for this project.
Likewise, #3 would be best served in a similar manner as #2, but we can at least expose a flag on nsCSSSelector to indicate that the selector was involved in a LATER_SIBILINGS restyle. Those are some of the worst offenders, so this knowledge can be useful. This could be part of the information exposed in #1.