#
# Do separate handling of '+' (adjacent sibling combinator)
# in the context of the pending restyling.
#
# If we set 'eRestyle_LaterSiblings' for '+' in the same way as for '~' (general sibling),
# this results in an unneeded iteration for all the siblings in RestyleTracker::DoProcessRestyles().
# This can cause certain sites to slow down significantly.
#
# A good example for the issue is hg.mozilla.org with a huge enough file (about 10000 lines),
# say https://hg.mozilla.org/mozilla-central/file/tip/js/src/frontend/BytecodeEmitter.cpp .
# Just move mouse a bit over the row number column somewhere in the top of the page.
# The correspond style code uses '+' here, but instead of checking the adjacent element only,
# all about 10000 later siblings (rows) are checked. This results in a kind of 'o(n^2)',
# which freezes cpu up to a few seconds.
#
diff -Nrup mozilla-OLD/layout/base/GeckoRestyleManager.cpp mozilla/layout/base/GeckoRestyleManager.cpp
--- mozilla-OLD/layout/base/GeckoRestyleManager.cpp 2022-04-11 23:40:35.197413086 +0300
+++ mozilla/layout/base/GeckoRestyleManager.cpp 2022-04-11 23:35:45.751600219 +0300
@@ -742,6 +742,23 @@ GeckoRestyleManager::PostRestyleEvent(El
return;
}
+ if (aRestyleHint & eRestyle_NextSibling) {
+ aRestyleHint &= ~eRestyle_NextSibling;
+
+ if (!(aRestyleHint & eRestyle_LaterSiblings)) {
+ // Just the one next sibling is not enough on practice,
+ // at least 2 seem to be needed in some cases.
+ int maxNextSiblings = 10;
+
+ for (Element* nextSibling = aElement->GetNextElementSibling();
+ nextSibling && maxNextSiblings--;
+ nextSibling = nextSibling->GetNextElementSibling()) {
+ mPendingRestyles.AddPendingRestyle(nextSibling, eRestyle_Subtree, nsChangeHint(0),
+ aRestyleHintData);
+ }
+ }
+ }
+
mPendingRestyles.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint,
aRestyleHintData);
diff -Nrup mozilla-OLD/layout/base/nsChangeHint.h mozilla/layout/base/nsChangeHint.h
--- mozilla-OLD/layout/base/nsChangeHint.h 2021-08-08 16:02:00.000000000 +0300
+++ mozilla/layout/base/nsChangeHint.h 2022-02-01 20:05:06.767443223 +0300
@@ -600,6 +600,10 @@ enum nsRestyleHint : uint32_t {
// descendants rather than just continuing the restyling process.
eRestyle_ForceDescendants = 1 << 9,
+ // Rerun selector matching on the next sibling of the element and
+ // all of its descendants.
+ eRestyle_NextSibling = 1 << 10,
+
// Useful unions:
eRestyle_AllHintsWithAnimations = eRestyle_CSSTransitions |
eRestyle_CSSAnimations |
diff -Nrup mozilla-OLD/layout/style/nsCSSRuleProcessor.cpp mozilla/layout/style/nsCSSRuleProcessor.cpp
--- mozilla-OLD/layout/style/nsCSSRuleProcessor.cpp 2020-02-18 02:37:58.000000000 +0300
+++ mozilla/layout/style/nsCSSRuleProcessor.cpp 2022-02-02 09:46:52.736328160 +0300
@@ -2705,7 +2705,11 @@ nsCSSRuleProcessor::RulesMatching(XULTre
static inline nsRestyleHint RestyleHintForOp(char16_t oper)
{
- if (oper == char16_t('+') || oper == char16_t('~')) {
+ if (oper == char16_t('+')) {
+ return eRestyle_NextSibling;
+ }
+
+ if (oper == char16_t('~')) {
return eRestyle_LaterSiblings;
}
@@ -2852,7 +2856,11 @@ RestyleHintForSelectorWithAttributeChang
char16_t oper = aSelector->mOperator;
- if (oper == char16_t('+') || oper == char16_t('~')) {
+ if (oper == char16_t('+')) {
+ return eRestyle_NextSibling;
+ }
+
+ if (oper == char16_t('~')) {
return eRestyle_LaterSiblings;
}