Browse Source

LibWeb: Remove CSS::StyleInvalidator in favor of dirtying + lazy update

Style updates are lazy since late last year, so the StyleInvalidator is
actually hurting us more than it's helping by running the entire CSS
selector machine on the whole DOM for every attribute change.

Instead, simply mark the entire DOM dirty and let the lazy style update
mechanism run *once* on next event loop iteration.
Andreas Kling 3 years ago
parent
commit
04bec7a4f5

+ 29 - 0
Base/res/html/misc/attr-invalidate-style.html

@@ -0,0 +1,29 @@
+<html>
+<head>
+<style>
+    div[foo] { background-color: green; }
+    div[bar] > div { background-color: red; }
+    div[baz] ~ div { background-color: blue; }
+</style>
+</head>
+<body>
+    <div id=bar><div>RED</div></div>
+    <div id=foo>GREEN</div>
+    <div id=baz></div>
+    <div>BLUE</div>
+    <script>
+        setTimeout(function() {
+            for (let id of ["foo", "bar", "baz"]) {
+                let e = document.getElementById(id);
+                e.setAttribute(id, "");
+            }
+            setTimeout(function() {
+                for (let id of ["foo", "bar", "baz"]) {
+                    let e = document.getElementById(id);
+                    e.removeAttribute(id);
+                }
+            }, 1000);
+        }, 1000);
+    </script>
+</body>
+</html>

+ 1 - 0
Base/res/html/misc/welcome.html

@@ -76,6 +76,7 @@
         <h2>CSS</h2>
         <ul>
             <li><h3>CSSOM</h3></li>
+            <li><a href="attr-invalidate-style.html">Style invalidation on attribute changes</a></li>
             <li><a href="computed-style.html">Computed style</a></li>
             <li><a href="supports.html">CSS.supports() and @supports</a></li>
             <li><a href="attributes.html">Attributes</a></li>

+ 0 - 1
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -46,7 +46,6 @@ set(SOURCES
     CSS/Selector.cpp
     CSS/SelectorEngine.cpp
     CSS/StyleComputer.cpp
-    CSS/StyleInvalidator.cpp
     CSS/StyleProperties.cpp
     CSS/StyleSheet.cpp
     CSS/StyleSheetList.cpp

+ 0 - 55
Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp

@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <LibWeb/CSS/CSSStyleRule.h>
-#include <LibWeb/CSS/StyleInvalidator.h>
-#include <LibWeb/DOM/Document.h>
-#include <LibWeb/DOM/Element.h>
-
-namespace Web::CSS {
-
-StyleInvalidator::StyleInvalidator(DOM::Document& document)
-    : m_document(document)
-{
-    if (!m_document.should_invalidate_styles_on_attribute_changes())
-        return;
-    auto& style_computer = m_document.style_computer();
-    m_document.for_each_in_inclusive_subtree_of_type<DOM::Element>([&](auto& element) {
-        m_elements_and_matching_rules_before.set(&element, style_computer.collect_matching_rules(element));
-        return IterationDecision::Continue;
-    });
-}
-
-StyleInvalidator::~StyleInvalidator()
-{
-    if (!m_document.should_invalidate_styles_on_attribute_changes())
-        return;
-    auto& style_computer = m_document.style_computer();
-    m_document.for_each_in_inclusive_subtree_of_type<DOM::Element>([&](auto& element) {
-        auto matching_rules_before_iter = m_elements_and_matching_rules_before.find(&element);
-        if (matching_rules_before_iter == m_elements_and_matching_rules_before.end()) {
-            element.set_needs_style_update(true);
-            return IterationDecision::Continue;
-        }
-        auto& matching_rules_before = matching_rules_before_iter->value;
-        auto matching_rules_after = style_computer.collect_matching_rules(element);
-        if (matching_rules_before.size() != matching_rules_after.size()) {
-            element.set_needs_style_update(true);
-            return IterationDecision::Continue;
-        }
-        style_computer.sort_matching_rules(matching_rules_before);
-        style_computer.sort_matching_rules(matching_rules_after);
-        for (size_t i = 0; i < matching_rules_before.size(); ++i) {
-            if (matching_rules_before[i].rule != matching_rules_after[i].rule) {
-                element.set_needs_style_update(true);
-                break;
-            }
-        }
-        return IterationDecision::Continue;
-    });
-}
-
-}

+ 0 - 26
Userland/Libraries/LibWeb/CSS/StyleInvalidator.h

@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <AK/HashMap.h>
-#include <LibWeb/CSS/StyleComputer.h>
-#include <LibWeb/DOM/Document.h>
-#include <LibWeb/DOM/Element.h>
-
-namespace Web::CSS {
-
-class StyleInvalidator {
-public:
-    explicit StyleInvalidator(DOM::Document&);
-    ~StyleInvalidator();
-
-private:
-    DOM::Document& m_document;
-    HashMap<DOM::Element*, Vector<MatchingRule>> m_elements_and_matching_rules_before;
-};
-
-}

+ 7 - 4
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -10,7 +10,6 @@
 #include <LibWeb/CSS/PropertyID.h>
 #include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
 #include <LibWeb/CSS/SelectorEngine.h>
-#include <LibWeb/CSS/StyleInvalidator.h>
 #include <LibWeb/DOM/DOMException.h>
 #include <LibWeb/DOM/DOMTokenList.h>
 #include <LibWeb/DOM/Document.h>
@@ -70,8 +69,6 @@ ExceptionOr<void> Element::set_attribute(const FlyString& name, const String& va
     if (name.is_empty())
         return InvalidCharacterError::create("Attribute name must not be empty");
 
-    CSS::StyleInvalidator style_invalidator(document());
-
     // 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
     // FIXME: Handle the second condition, assume it is an HTML document for now.
     bool insert_as_lowercase = namespace_uri() == Namespace::HTML;
@@ -93,14 +90,20 @@ ExceptionOr<void> Element::set_attribute(const FlyString& name, const String& va
     }
 
     parse_attribute(attribute->local_name(), value);
+
+    // FIXME: Invalidate less.
+    document().invalidate_style();
+
     return {};
 }
 
 // https://dom.spec.whatwg.org/#dom-element-removeattribute
 void Element::remove_attribute(const FlyString& name)
 {
-    CSS::StyleInvalidator style_invalidator(document());
     m_attributes->remove_attribute(name);
+
+    // FIXME: Invalidate less.
+    document().invalidate_style();
 }
 
 // https://dom.spec.whatwg.org/#dom-element-hasattribute