Blob Blame History Raw
#
#  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;
   }