Diff
Modified: trunk/LayoutTests/ChangeLog (196628 => 196629)
--- trunk/LayoutTests/ChangeLog 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/LayoutTests/ChangeLog 2016-02-16 08:20:58 UTC (rev 196629)
@@ -1,3 +1,13 @@
+2016-02-15 Antti Koivisto <[email protected]>
+
+ Optimize style invalidations for attribute selectors
+ https://bugs.webkit.org/show_bug.cgi?id=154242
+
+ Reviewed by Andreas Kling.
+
+ * fast/css/style-invalidation-attribute-change-descendants-expected.txt: Added.
+ * fast/css/style-invalidation-attribute-change-descendants.html: Added.
+
2016-02-16 Chris Dumez <[email protected]>
Do security checks early in JSDOMWindow::put*()
Added: trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt (0 => 196629)
--- trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants-expected.txt 2016-02-16 08:20:58 UTC (rev 196629)
@@ -0,0 +1,149 @@
+Test that we invalidate the element subtree minimally on class attribute change
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS hasExpectedStyle is true
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value ''
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value2'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Removing attribute 'myattr'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value3'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'dummy value3'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value4-foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value4-foobar'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'dummy value4-foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value5foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value5foobar'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foovalue5'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'foovalue6'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myAttr' value 'foobarvalue6'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'MYATTR' value 'value6foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value7'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value7foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myATTR' value 'foovalue7foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE7foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'Value8'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'value8foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myATTR' value 'FOOVALue8foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE8foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr' value 'VALUE 8foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "InlineStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr2' value ''
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+Setting attribute 'myattr2' value 'foo'
+PASS testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange") is true
+PASS testStyleChangeType("target", "NoStyleChange") is true
+PASS testStyleChangeType("inert", "NoStyleChange") is true
+PASS hasExpectedStyle is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html (0 => 196629)
--- trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html (rev 0)
+++ trunk/LayoutTests/fast/css/style-invalidation-attribute-change-descendants.html 2016-02-16 08:20:58 UTC (rev 196629)
@@ -0,0 +1,242 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+* {
+ color: black;
+}
+[myattr] target {
+ color: rgb(1, 0, 0);
+}
+
+[myattr=value2] > inert target {
+ color: rgb(2, 0, 0);
+}
+
+[myattr~=value3] inert + target {
+ color: rgb(3, 0, 0);
+}
+
+[MYATTR|=value4] inert ~ target {
+ color: rgb(4, 0, 0);
+}
+
+[myAttr^=value5] target {
+ color: rgb(5, 0, 0);
+}
+
+[myattr$=value6] target {
+ color: rgb(6, 0, 0);
+}
+
+[myATTR*=value7] target {
+ color: rgb(7, 0, 0);
+}
+
+[myattr*=vaLUE8 i] target {
+ color: rgb(8, 0, 0);
+}
+
+[myattr2] > target {
+ color: rgb(9, 0, 0);
+}
+
+</style>
+</head>
+<body>
+ <root>
+ <!-- With renderer -->
+ <inert>
+ <inert>
+ <inert></inert>
+ <target>
+ <inert></inert>
+ <target></target>
+ </target>
+ </inert>
+ <target></target>
+ <inert></inert>
+ </inert>
+ </root>
+ <root style="display:none;">
+ <!-- Without renderer -->
+ <inert>
+ <inert>
+ <inert></inert>
+ <target>
+ <inert></inert>
+ <target></target>
+ </target>
+ </inert>
+ <target></target>
+ <inert></inert>
+ </inert>
+ </root>
+</body>
+<script>
+
+description('Test that we invalidate the element subtree minimally on class attribute change');
+
+function testStyleChangeType(tag, type)
+{
+ var elements = document.querySelectorAll(tag);
+ for (var i = 0; i < elements.length; ++i) {
+ if (window.internals.styleChangeType(elements[i]) != type)
+ return false;
+ }
+ return true;
+}
+
+function testStyleInvalidation(expectedDescendantStyleChange) {
+ // Ideally we would't invalidate the root at all.
+ shouldBeTrue('testStyleChangeType("root", "NoStyleChange") || testStyleChangeType("root", "InlineStyleChange")');
+
+ shouldBeTrue('testStyleChangeType("target", "' + expectedDescendantStyleChange +'")');
+
+ shouldBeTrue('testStyleChangeType("inert", "NoStyleChange")');
+}
+
+function setAttribute(name, value) {
+ debug("Setting attribute '" + name + "' value '" + value + "'");
+ var allRoots = document.querySelectorAll("root");
+ allRoots[0].setAttribute(name, value);
+ allRoots[1].setAttribute(name, value);
+}
+
+function removeAttribute(name) {
+ debug("Removing attribute '" + name + "'");
+ var allRoots = document.querySelectorAll("root");
+ allRoots[0].removeAttribute(name);
+ allRoots[1].removeAttribute(name);
+}
+
+function checkStyle(n) {
+ document.documentElement.offsetTop;
+
+ hasExpectedStyle = true;
+ expectedColor = 'rgb('+n+', 0, 0)';
+ var targets = document.querySelectorAll("target");
+ for (var i = 0; i < targets.length; ++i) {
+ hasExpectedStyle = getComputedStyle(targets[i]).color == expectedColor;
+ if (!hasExpectedStyle)
+ break;
+ }
+ shouldBeTrue("hasExpectedStyle");
+}
+
+checkStyle(0);
+testStyleInvalidation("NoStyleChange");
+checkStyle(0);
+
+setAttribute('myattr', '');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'value2');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(2);
+
+setAttribute('myattr', 'foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+removeAttribute('myattr');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(0);
+
+setAttribute('myattr', 'value3');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(3);
+
+setAttribute('myattr', 'dummy value3');
+testStyleInvalidation("NoStyleChange");
+checkStyle(3);
+
+setAttribute('myattr', 'value4-foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(4);
+
+setAttribute('myattr', 'value4-foobar');
+testStyleInvalidation("NoStyleChange");
+checkStyle(4);
+
+setAttribute('myattr', 'dummy value4-foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'value5foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(5);
+
+setAttribute('myattr', 'value5foobar');
+testStyleInvalidation("NoStyleChange");
+checkStyle(5);
+
+setAttribute('myattr', 'foovalue5');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'foovalue6');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(6);
+
+setAttribute('myAttr', 'foobarvalue6');
+testStyleInvalidation("NoStyleChange");
+checkStyle(6);
+
+setAttribute('MYATTR', 'value6foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'value7');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(7);
+
+setAttribute('myattr', 'value7foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(7);
+
+setAttribute('myATTR', 'foovalue7foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(7);
+
+setAttribute('myattr', 'VALUE7foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr', 'Value8');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(8);
+
+setAttribute('myattr', 'value8foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(8);
+
+setAttribute('myATTR', 'FOOVALue8foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(8);
+
+setAttribute('myattr', 'VALUE8foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(8);
+
+setAttribute('myattr', 'VALUE 8foo');
+testStyleInvalidation("InlineStyleChange");
+checkStyle(1);
+
+setAttribute('myattr2', '');
+testStyleInvalidation("NoStyleChange");
+checkStyle(1);
+
+setAttribute('myattr2', 'foo');
+testStyleInvalidation("NoStyleChange");
+checkStyle(1);
+
+</script>
+<script src=""
+</html>
Modified: trunk/Source/WebCore/CMakeLists.txt (196628 => 196629)
--- trunk/Source/WebCore/CMakeLists.txt 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/CMakeLists.txt 2016-02-16 08:20:58 UTC (rev 196629)
@@ -2630,6 +2630,7 @@
storage/StorageMap.cpp
storage/StorageNamespaceProvider.cpp
+ style/AttributeChangeInvalidation.cpp
style/ClassChangeInvalidation.cpp
style/InlineTextBoxStyle.cpp
style/RenderTreePosition.cpp
Modified: trunk/Source/WebCore/ChangeLog (196628 => 196629)
--- trunk/Source/WebCore/ChangeLog 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/ChangeLog 2016-02-16 08:20:58 UTC (rev 196629)
@@ -1,3 +1,90 @@
+2016-02-15 Antti Koivisto <[email protected]>
+
+ Optimize style invalidations for attribute selectors
+ https://bugs.webkit.org/show_bug.cgi?id=154242
+
+ Reviewed by Andreas Kling.
+
+ Currently we invalidate the whole element subtree if there are any attribute selectors for the changed attribute.
+ This is slow as generally few if any elements are really affected. Using attribute selectors for dynamic styling
+ should be performant.
+
+ This patch implements optimization strategy for attributes similar to what we already have for classes:
+
+ - Collect a map of all rules that contains descendant-affecting attribute selectors for a given attribute.
+ - When an attribute value changes check if there are any such rules for it.
+ - Check if the value change affects the results of any of the attribute selectors.
+ - Only if it does invalidate the exact descendant elements affected by the rules.
+
+ Test: fast/css/style-invalidation-attribute-change-descendants.html
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * css/DocumentRuleSets.cpp:
+ (WebCore::DocumentRuleSets::ancestorClassRules):
+ (WebCore::DocumentRuleSets::ancestorAttributeRulesForHTML):
+
+ Create optimization RuleSets when needed.
+
+ * css/DocumentRuleSets.h:
+ (WebCore::DocumentRuleSets::uncommonAttribute):
+ (WebCore::DocumentRuleSets::features):
+ * css/RuleFeature.cpp:
+ (WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
+ (WebCore::makeAttributeSelectorKey):
+ (WebCore::RuleFeatureSet::collectFeatures):
+
+ Collect rules with descendant affecting attribute selectors.
+
+ (WebCore::RuleFeatureSet::add):
+ (WebCore::RuleFeatureSet::clear):
+ (WebCore::RuleFeatureSet::shrinkToFit):
+ * css/RuleFeature.h:
+ * css/SelectorChecker.cpp:
+ (WebCore::anyAttributeMatches):
+ (WebCore::SelectorChecker::attributeSelectorMatches):
+
+ Expose function for matching single attribute selectors.
+
+ (WebCore::canMatchHoverOrActiveInQuirksMode):
+ * css/SelectorChecker.h:
+ * dom/Attr.cpp:
+ (WebCore::Attr::setValue):
+ (WebCore::Attr::childrenChanged):
+ * dom/Element.cpp:
+ (WebCore::Element::setAttributeInternal):
+ (WebCore::makeIdForStyleResolution):
+ (WebCore::Element::attributeChanged):
+ (WebCore::Element::removeAttributeInternal):
+ (WebCore::Element::addAttributeInternal):
+ (WebCore::Element::removeAttribute):
+
+ Add AttributeChangeInvalidation where needed.
+
+ (WebCore::Element::needsStyleInvalidation):
+
+ Move to Element from ClassChangeInvalidation.
+
+ (WebCore::Element::willModifyAttribute):
+
+ No more full style invalidation on attribute change.
+
+ * style/AttributeChangeInvalidation.cpp: Added.
+ (WebCore::Style::AttributeChangeInvalidation::invalidateStyle):
+
+ Invalidate local style.
+ Check if we need to invalidate descendants by looking into ancestorAttributeRules.
+
+ (WebCore::Style::AttributeChangeInvalidation::invalidateDescendants):
+
+ Use StyleInvalidationAnalysis to invalidate the subtree for the relevant rules.
+
+ * style/AttributeChangeInvalidation.h: Added.
+ (WebCore::Style::AttributeChangeInvalidation::needsInvalidation):
+ (WebCore::Style::AttributeChangeInvalidation::AttributeChangeInvalidation):
+ (WebCore::Style::AttributeChangeInvalidation::~AttributeChangeInvalidation):
+
+ If needed, invalidate descendants before and after attribute change to catch rules that start and stop applying.
+
2016-02-16 Chris Dumez <[email protected]>
Do security checks early in JSDOMWindow::put*()
Modified: trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj (196628 => 196629)
--- trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj 2016-02-16 08:20:58 UTC (rev 196629)
@@ -19295,6 +19295,7 @@
<ClCompile Include="..\storage\StorageEventDispatcher.cpp" />
<ClCompile Include="..\storage\StorageMap.cpp" />
<ClCompile Include="..\storage\StorageNamespaceProvider.cpp" />
+ <ClCompile Include="..\style\AttributeChangeInvalidation.cpp" />
<ClCompile Include="..\style\ClassChangeInvalidation.cpp" />
<ClCompile Include="..\style\InlineTextBoxStyle.cpp" />
<ClCompile Include="..\style\RenderTreePosition.cpp" />
@@ -22865,6 +22866,7 @@
<ClInclude Include="..\storage\StorageMap.h" />
<ClInclude Include="..\storage\StorageNamespace.h" />
<ClInclude Include="..\storage\StorageNamespaceProvider.h" />
+ <ClInclude Include="..\style\AttributeChangeInvalidation.h" />
<ClInclude Include="..\style\ClassChangeInvalidation.h" />
<ClInclude Include="..\style\InlineTextBoxStyle.h" />
<ClInclude Include="..\style\RenderTreePosition.h" />
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (196628 => 196629)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-02-16 08:20:58 UTC (rev 196629)
@@ -6637,6 +6637,8 @@
E4A007851B820ED3002C5A6E /* DataURLDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A007841B820ED3002C5A6E /* DataURLDecoder.cpp */; };
E4A814D41C6DEC4000BF85AC /* ClassChangeInvalidation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */; };
E4A814D61C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */; };
+ E4A814D81C70E10500BF85AC /* AttributeChangeInvalidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */; };
+ E4A814DA1C70E10D00BF85AC /* AttributeChangeInvalidation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */; };
E4AE7C1617D1BB950009FB31 /* ElementIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AE7C1517D1BB950009FB31 /* ElementIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
E4AE7C1A17D232350009FB31 /* ElementAncestorIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AE7C1917D232350009FB31 /* ElementAncestorIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
E4AFCFA50DAF29A300F5F55C /* UnitBezier.h in Headers */ = {isa = PBXBuildFile; fileRef = E4AFCFA40DAF29A300F5F55C /* UnitBezier.h */; };
@@ -14643,6 +14645,8 @@
E4A007841B820ED3002C5A6E /* DataURLDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataURLDecoder.cpp; sourceTree = "<group>"; };
E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClassChangeInvalidation.h; sourceTree = "<group>"; };
E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassChangeInvalidation.cpp; sourceTree = "<group>"; };
+ E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AttributeChangeInvalidation.cpp; sourceTree = "<group>"; };
+ E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttributeChangeInvalidation.h; sourceTree = "<group>"; };
E4AE7C1517D1BB950009FB31 /* ElementIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementIterator.h; sourceTree = "<group>"; };
E4AE7C1917D232350009FB31 /* ElementAncestorIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementAncestorIterator.h; sourceTree = "<group>"; };
E4AFCFA40DAF29A300F5F55C /* UnitBezier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnitBezier.h; sourceTree = "<group>"; };
@@ -23433,6 +23437,8 @@
E4763D4A17B2704900D35206 /* style */ = {
isa = PBXGroup;
children = (
+ E4A814D71C70E10500BF85AC /* AttributeChangeInvalidation.cpp */,
+ E4A814D91C70E10D00BF85AC /* AttributeChangeInvalidation.h */,
E4A814D51C6DEE8D00BF85AC /* ClassChangeInvalidation.cpp */,
E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */,
1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */,
@@ -25909,6 +25915,7 @@
31313F661443B35F006E2A90 /* FilterEffectRenderer.h in Headers */,
49ECEB6E1499790D00CDD3A4 /* FilterOperation.h in Headers */,
49ECEB701499790D00CDD3A4 /* FilterOperations.h in Headers */,
+ E4A814DA1C70E10D00BF85AC /* AttributeChangeInvalidation.h in Headers */,
372C00D9129619F8005C9575 /* FindOptions.h in Headers */,
A8CFF04F0A154F09000A4234 /* FixedTableLayout.h in Headers */,
BC073BAA0C399B1F000F5979 /* FloatConversion.h in Headers */,
@@ -30440,6 +30447,7 @@
B59DD6AA11902A71007E9684 /* JSSQLStatementErrorCallback.cpp in Sources */,
9BD4E9161C462872005065BC /* JSCustomElementInterface.cpp in Sources */,
514C76380CE9225E007EF3CD /* JSSQLTransaction.cpp in Sources */,
+ E4A814D81C70E10500BF85AC /* AttributeChangeInvalidation.cpp in Sources */,
B59DD69E11902A42007E9684 /* JSSQLTransactionCallback.cpp in Sources */,
1AD2316E0CD269E700C1F194 /* JSSQLTransactionCustom.cpp in Sources */,
B59DD6A211902A52007E9684 /* JSSQLTransactionErrorCallback.cpp in Sources */,
Modified: trunk/Source/WebCore/css/DocumentRuleSets.cpp (196628 => 196629)
--- trunk/Source/WebCore/css/DocumentRuleSets.cpp 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/DocumentRuleSets.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -118,7 +118,7 @@
RuleSet* DocumentRuleSets::ancestorClassRules(AtomicStringImpl* className) const
{
- auto addResult = m_ancestorClassRuleSet.add(className, nullptr);
+ auto addResult = m_ancestorClassRuleSets.add(className, nullptr);
if (addResult.isNewEntry) {
if (auto* rules = m_features.ancestorClassRules.get(className))
addResult.iterator->value = makeRuleSet(*rules);
@@ -126,4 +126,20 @@
return addResult.iterator->value.get();
}
+const DocumentRuleSets::AttributeRules* DocumentRuleSets::ancestorAttributeRulesForHTML(AtomicStringImpl* attributeName) const
+{
+ auto addResult = m_ancestorAttributeRuleSetsForHTML.add(attributeName, nullptr);
+ auto& value = addResult.iterator->value;
+ if (addResult.isNewEntry) {
+ if (auto* rules = m_features.ancestorAttributeRulesForHTML.get(attributeName)) {
+ value = std::make_unique<AttributeRules>();
+ value->attributeSelectors.reserveCapacity(rules->selectors.size());
+ for (auto* selector : rules->selectors.values())
+ value->attributeSelectors.uncheckedAppend(selector);
+ value->ruleSet = makeRuleSet(rules->features);
+ }
+ }
+ return value.get();
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/css/DocumentRuleSets.h (196628 => 196629)
--- trunk/Source/WebCore/css/DocumentRuleSets.h 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/DocumentRuleSets.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -52,6 +52,12 @@
RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); }
RuleSet* ancestorClassRules(AtomicStringImpl* className) const;
+ struct AttributeRules {
+ Vector<const CSSSelector*> attributeSelectors;
+ std::unique_ptr<RuleSet> ruleSet;
+ };
+ const AttributeRules* ancestorAttributeRulesForHTML(AtomicStringImpl*) const;
+
void initUserStyle(ExtensionStyleSheets&, const MediaQueryEvaluator&, StyleResolver&);
void resetAuthorStyle();
void appendAuthorStyleSheets(const Vector<RefPtr<CSSStyleSheet>>&, MediaQueryEvaluator*, InspectorCSSOMWrappers&, StyleResolver*);
@@ -67,7 +73,8 @@
mutable unsigned m_defaultStyleVersionOnFeatureCollection { 0 };
mutable std::unique_ptr<RuleSet> m_siblingRuleSet;
mutable std::unique_ptr<RuleSet> m_uncommonAttributeRuleSet;
- mutable HashMap<AtomicStringImpl*, std::unique_ptr<RuleSet>> m_ancestorClassRuleSet;
+ mutable HashMap<AtomicStringImpl*, std::unique_ptr<RuleSet>> m_ancestorClassRuleSets;
+ mutable HashMap<AtomicStringImpl*, std::unique_ptr<AttributeRules>> m_ancestorAttributeRuleSetsForHTML;
};
inline const RuleFeatureSet& DocumentRuleSets::features() const
Modified: trunk/Source/WebCore/css/RuleFeature.cpp (196628 => 196629)
--- trunk/Source/WebCore/css/RuleFeature.cpp 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/RuleFeature.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -46,8 +46,12 @@
if (matchesAncestor)
selectorFeatures.classesMatchingAncestors.append(selector->value().impl());
} else if (selector->isAttributeSelector()) {
- attributeCanonicalLocalNamesInRules.add(selector->attributeCanonicalLocalName().impl());
- attributeLocalNamesInRules.add(selector->attribute().localName().impl());
+ auto* canonicalLocalName = selector->attributeCanonicalLocalName().impl();
+ auto* localName = selector->attribute().localName().impl();
+ attributeCanonicalLocalNamesInRules.add(canonicalLocalName);
+ attributeLocalNamesInRules.add(localName);
+ if (matchesAncestor)
+ selectorFeatures.attributeSelectorsMatchingAncestors.append(selector);
} else if (selector->match() == CSSSelector::PseudoElement) {
switch (selector->pseudoElementType()) {
case CSSSelector::PseudoElementFirstLine:
@@ -78,6 +82,13 @@
} while (selector);
}
+static std::pair<AtomicStringImpl*, unsigned> makeAttributeSelectorKey(const CSSSelector& selector)
+{
+ bool caseInsensitive = selector.attributeValueMatchingIsCaseInsensitive();
+ unsigned matchAndCase = static_cast<unsigned>(selector.match()) << 1 | caseInsensitive;
+ return std::make_pair(selector.attributeCanonicalLocalName().impl(), matchAndCase);
+}
+
void RuleFeatureSet::collectFeatures(const RuleData& ruleData)
{
SelectorFeatures selectorFeatures;
@@ -92,6 +103,16 @@
addResult.iterator->value = std::make_unique<Vector<RuleFeature>>();
addResult.iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
}
+ for (auto* selector : selectorFeatures.attributeSelectorsMatchingAncestors) {
+ // Hashing by attributeCanonicalLocalName makes this HTML specific.
+ auto addResult = ancestorAttributeRulesForHTML.add(selector->attributeCanonicalLocalName().impl(), nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = std::make_unique<AttributeRules>();
+ auto& rules = *addResult.iterator->value;
+ rules.features.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
+ // Deduplicate selectors.
+ rules.selectors.add(makeAttributeSelectorKey(*selector), selector);
+ }
}
void RuleFeatureSet::add(const RuleFeatureSet& other)
@@ -109,6 +130,15 @@
else
addResult.iterator->value->appendVector(*keyValuePair.value);
}
+ for (auto& keyValuePair : other.ancestorAttributeRulesForHTML) {
+ auto addResult = ancestorAttributeRulesForHTML.add(keyValuePair.key, nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = std::make_unique<AttributeRules>();
+ auto& rules = *addResult.iterator->value;
+ rules.features.appendVector(keyValuePair.value->features);
+ for (auto& selectorPair : keyValuePair.value->selectors)
+ rules.selectors.add(selectorPair.key, selectorPair.value);
+ }
usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
usesFirstLetterRules = usesFirstLetterRules || other.usesFirstLetterRules;
}
@@ -122,6 +152,7 @@
siblingRules.clear();
uncommonAttributeRules.clear();
ancestorClassRules.clear();
+ ancestorAttributeRulesForHTML.clear();
usesFirstLineRules = false;
usesFirstLetterRules = false;
}
@@ -132,6 +163,8 @@
uncommonAttributeRules.shrinkToFit();
for (auto& rules : ancestorClassRules.values())
rules->shrinkToFit();
+ for (auto& rules : ancestorAttributeRulesForHTML.values())
+ rules->features.shrinkToFit();
}
} // namespace WebCore
Modified: trunk/Source/WebCore/css/RuleFeature.h (196628 => 196629)
--- trunk/Source/WebCore/css/RuleFeature.h 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/RuleFeature.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -22,6 +22,7 @@
#ifndef RuleFeature_h
#define RuleFeature_h
+#include "CSSSelector.h"
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
@@ -29,7 +30,6 @@
namespace WebCore {
-class CSSSelector;
class RuleData;
class StyleRule;
@@ -58,6 +58,12 @@
Vector<RuleFeature> siblingRules;
Vector<RuleFeature> uncommonAttributeRules;
HashMap<AtomicStringImpl*, std::unique_ptr<Vector<RuleFeature>>> ancestorClassRules;
+
+ struct AttributeRules {
+ HashMap<std::pair<AtomicStringImpl*, unsigned>, const CSSSelector*> selectors;
+ Vector<RuleFeature> features;
+ };
+ HashMap<AtomicStringImpl*, std::unique_ptr<AttributeRules>> ancestorAttributeRulesForHTML;
bool usesFirstLineRules { false };
bool usesFirstLetterRules { false };
@@ -65,6 +71,7 @@
struct SelectorFeatures {
bool hasSiblingSelector { false };
Vector<AtomicStringImpl*> classesMatchingAncestors;
+ Vector<const CSSSelector*> attributeSelectorsMatchingAncestors;
};
void recursivelyCollectFeaturesFromSelector(SelectorFeatures&, const CSSSelector&, bool matchesAncestor = false);
};
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (196628 => 196629)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -518,6 +518,21 @@
return false;
}
+bool SelectorChecker::attributeSelectorMatches(const Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue, const CSSSelector& selector)
+{
+ ASSERT(selector.isAttributeSelector());
+ auto& selectorAttribute = selector.attribute();
+ auto& selectorName = element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttribute.localName();
+ if (!Attribute::nameMatchesFilter(attributeName, selectorAttribute.prefix(), selectorName, selectorAttribute.namespaceURI()))
+ return false;
+ bool caseSensitive = true;
+ if (selector.attributeValueMatchingIsCaseInsensitive())
+ caseSensitive = false;
+ else if (element.document().isHTMLDocument() && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(selector.attribute()))
+ caseSensitive = false;
+ return attributeValueMatches(Attribute(attributeName, attributeValue), selector.match(), selector.value(), caseSensitive);
+}
+
static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::LocalContext& context)
{
// For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
Modified: trunk/Source/WebCore/css/SelectorChecker.h (196628 => 196629)
--- trunk/Source/WebCore/css/SelectorChecker.h 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/css/SelectorChecker.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -119,6 +119,7 @@
static bool isCommonPseudoClassSelector(const CSSSelector*);
static bool matchesFocusPseudoClass(const Element&);
+ static bool attributeSelectorMatches(const Element&, const QualifiedName&, const AtomicString& attributeValue, const CSSSelector&);
enum LinkMatchMask { MatchDefault = 0, MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
static unsigned determineLinkMatchType(const CSSSelector*);
Modified: trunk/Source/WebCore/dom/Attr.cpp (196628 => 196629)
--- trunk/Source/WebCore/dom/Attr.cpp 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Attr.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -23,6 +23,7 @@
#include "config.h"
#include "Attr.h"
+#include "AttributeChangeInvalidation.h"
#include "Event.h"
#include "ExceptionCode.h"
#include "ScopedEventQueue.h"
@@ -108,9 +109,10 @@
EventQueueScope scope;
m_ignoreChildrenChanged++;
removeChildren();
- if (m_element)
+ if (m_element) {
+ Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), elementAttribute().value(), value);
elementAttribute().setValue(value);
- else
+ } else
m_standaloneValue = value;
createTextChild();
m_ignoreChildrenChanged--;
@@ -163,9 +165,10 @@
if (m_element)
m_element->willModifyAttribute(qualifiedName(), oldValue, newValue);
- if (m_element)
+ if (m_element) {
+ Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), oldValue, newValue);
elementAttribute().setValue(newValue);
- else
+ } else
m_standaloneValue = newValue;
if (m_element)
Modified: trunk/Source/WebCore/dom/Element.cpp (196628 => 196629)
--- trunk/Source/WebCore/dom/Element.cpp 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Element.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -28,6 +28,7 @@
#include "AXObjectCache.h"
#include "Attr.h"
+#include "AttributeChangeInvalidation.h"
#include "CSSParser.h"
#include "Chrome.h"
#include "ChromeClient.h"
@@ -1182,26 +1183,30 @@
return;
}
+ if (inSynchronizationOfLazyAttribute) {
+ ensureUniqueElementData().attributeAt(index).setValue(newValue);
+ return;
+ }
+
const Attribute& attribute = attributeAt(index);
+ QualifiedName attributeName = attribute.name();
AtomicString oldValue = attribute.value();
- bool valueChanged = newValue != oldValue;
- QualifiedName attributeName = (!inSynchronizationOfLazyAttribute || valueChanged) ? attribute.name() : name;
- if (!inSynchronizationOfLazyAttribute)
- willModifyAttribute(attributeName, oldValue, newValue);
+ willModifyAttribute(attributeName, oldValue, newValue);
- if (valueChanged) {
+ if (newValue != oldValue) {
// If there is an Attr node hooked to this attribute, the Attr::setValue() call below
// will write into the ElementData.
// FIXME: Refactor this so it makes some sense.
- if (RefPtr<Attr> attrNode = inSynchronizationOfLazyAttribute ? nullptr : attrIfExists(attributeName))
+ if (RefPtr<Attr> attrNode = attrIfExists(attributeName))
attrNode->setValue(newValue);
- else
+ else {
+ Style::AttributeChangeInvalidation styleInvalidation(*this, name, oldValue, newValue);
ensureUniqueElementData().attributeAt(index).setValue(newValue);
+ }
}
- if (!inSynchronizationOfLazyAttribute)
- didModifyAttribute(attributeName, oldValue, newValue);
+ didModifyAttribute(attributeName, oldValue, newValue);
}
static inline AtomicString makeIdForStyleResolution(const AtomicString& value, bool inQuirksMode)
@@ -1268,9 +1273,6 @@
invalidateNodeListAndCollectionCachesInAncestors(&name, this);
- // If there is currently no StyleResolver, we can't be sure that this attribute change won't affect style.
- shouldInvalidateStyle |= !styleResolver;
-
if (shouldInvalidateStyle)
setNeedsStyleRecalc();
@@ -2050,27 +2052,38 @@
QualifiedName name = elementData.attributeAt(index).name();
AtomicString valueBeingRemoved = elementData.attributeAt(index).value();
- if (!inSynchronizationOfLazyAttribute) {
- if (!valueBeingRemoved.isNull())
- willModifyAttribute(name, valueBeingRemoved, nullAtom);
- }
-
if (RefPtr<Attr> attrNode = attrIfExists(name))
detachAttrNodeFromElementWithValue(attrNode.get(), elementData.attributeAt(index).value());
- elementData.removeAttribute(index);
+ if (inSynchronizationOfLazyAttribute) {
+ elementData.removeAttribute(index);
+ return;
+ }
- if (!inSynchronizationOfLazyAttribute)
- didRemoveAttribute(name, valueBeingRemoved);
+ if (!valueBeingRemoved.isNull())
+ willModifyAttribute(name, valueBeingRemoved, nullAtom);
+
+ {
+ Style::AttributeChangeInvalidation styleInvalidation(*this, name, valueBeingRemoved, nullAtom);
+ elementData.removeAttribute(index);
+ }
+
+ didRemoveAttribute(name, valueBeingRemoved);
}
void Element::addAttributeInternal(const QualifiedName& name, const AtomicString& value, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
{
- if (!inSynchronizationOfLazyAttribute)
- willModifyAttribute(name, nullAtom, value);
- ensureUniqueElementData().addAttribute(name, value);
- if (!inSynchronizationOfLazyAttribute)
- didAddAttribute(name, value);
+ if (inSynchronizationOfLazyAttribute) {
+ ensureUniqueElementData().addAttribute(name, value);
+ return;
+ }
+
+ willModifyAttribute(name, nullAtom, value);
+ {
+ Style::AttributeChangeInvalidation styleInvalidation(*this, name, nullAtom, value);
+ ensureUniqueElementData().addAttribute(name, value);
+ }
+ didAddAttribute(name, value);
}
bool Element::removeAttribute(const AtomicString& name)
@@ -2485,6 +2498,18 @@
return style;
}
+bool Element::needsStyleInvalidation() const
+{
+ if (!inRenderedDocument())
+ return false;
+ if (styleChangeType() >= FullStyleChange)
+ return false;
+ if (!document().styleResolverIfExists())
+ return false;
+
+ return true;
+}
+
void Element::setStyleAffectedByEmpty()
{
ensureElementRareData().setStyleAffectedByEmpty(true);
@@ -3080,12 +3105,6 @@
updateLabel(treeScope(), oldValue, newValue);
}
- if (oldValue != newValue) {
- auto styleResolver = document().styleResolverIfExists();
- if (styleResolver && styleResolver->hasSelectorForAttribute(*this, name.localName()))
- setNeedsStyleRecalc();
- }
-
if (std::unique_ptr<MutationObserverInterestGroup> recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name))
recipients->enqueueMutationRecord(MutationRecord::createAttributes(*this, name, oldValue));
Modified: trunk/Source/WebCore/dom/Element.h (196628 => 196629)
--- trunk/Source/WebCore/dom/Element.h 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/dom/Element.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -279,6 +279,8 @@
virtual RenderStyle* computedStyle(PseudoId = NOPSEUDO) override;
+ bool needsStyleInvalidation() const;
+
// Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.)
bool styleAffectedByEmpty() const { return hasRareData() && rareDataStyleAffectedByEmpty(); }
bool childrenAffectedByHover() const { return getFlag(ChildrenAffectedByHoverRulesFlag); }
Added: trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp (0 => 196629)
--- trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp (rev 0)
+++ trunk/Source/WebCore/style/AttributeChangeInvalidation.cpp 2016-02-16 08:20:58 UTC (rev 196629)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AttributeChangeInvalidation.h"
+
+#include "DocumentRuleSets.h"
+#include "ElementIterator.h"
+#include "StyleInvalidationAnalysis.h"
+#include "StyleResolver.h"
+
+namespace WebCore {
+namespace Style {
+
+void AttributeChangeInvalidation::invalidateStyle(const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
+{
+ if (newValue == oldValue)
+ return;
+
+ auto& ruleSets = m_element.styleResolver().ruleSets();
+ bool isHTML = m_element.isHTMLElement();
+
+ auto& nameSet = isHTML ? ruleSets.features().attributeCanonicalLocalNamesInRules : ruleSets.features().attributeLocalNamesInRules;
+ bool shouldInvalidate = nameSet.contains(attributeName.localName().impl());
+ if (!shouldInvalidate)
+ return;
+
+ if (!isHTML) {
+ m_element.setNeedsStyleRecalc(FullStyleChange);
+ return;
+ }
+
+ if (m_element.shadowRoot() && ruleSets.authorStyle()->hasShadowPseudoElementRules()) {
+ m_element.setNeedsStyleRecalc(FullStyleChange);
+ return;
+ }
+
+ m_element.setNeedsStyleRecalc(InlineStyleChange);
+
+ if (!childrenOfType<Element>(m_element).first())
+ return;
+
+ auto* attributeRules = ruleSets.ancestorAttributeRulesForHTML(attributeName.localName().impl());
+ if (!attributeRules)
+ return;
+
+ // Check if descendants may be affected by this attribute change.
+ for (auto* selector : attributeRules->attributeSelectors) {
+ bool oldMatches = oldValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, oldValue, *selector);
+ bool newMatches = newValue.isNull() ? false : SelectorChecker::attributeSelectorMatches(m_element, attributeName, newValue, *selector);
+
+ if (oldMatches != newMatches) {
+ m_descendantInvalidationRuleSet = attributeRules->ruleSet.get();
+ return;
+ }
+ }
+}
+
+void AttributeChangeInvalidation::invalidateDescendants()
+{
+ if (!m_descendantInvalidationRuleSet)
+ return;
+ StyleInvalidationAnalysis invalidationAnalysis(*m_descendantInvalidationRuleSet);
+ invalidationAnalysis.invalidateStyle(m_element);
+}
+
+}
+}
Added: trunk/Source/WebCore/style/AttributeChangeInvalidation.h (0 => 196629)
--- trunk/Source/WebCore/style/AttributeChangeInvalidation.h (rev 0)
+++ trunk/Source/WebCore/style/AttributeChangeInvalidation.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AttributeChangeInvalidation_h
+#define AttributeChangeInvalidation_h
+
+#include "Element.h"
+
+namespace WebCore {
+
+class RuleSet;
+
+namespace Style {
+
+class AttributeChangeInvalidation {
+public:
+ AttributeChangeInvalidation(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+ ~AttributeChangeInvalidation();
+
+private:
+ void invalidateStyle(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue);
+ void invalidateDescendants();
+
+ const bool m_isEnabled;
+ Element& m_element;
+
+ RuleSet* m_descendantInvalidationRuleSet { nullptr };
+};
+
+inline AttributeChangeInvalidation::AttributeChangeInvalidation(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
+ : m_isEnabled(element.needsStyleInvalidation())
+ , m_element(element)
+{
+ if (!m_isEnabled)
+ return;
+ invalidateStyle(attributeName, oldValue, newValue);
+ invalidateDescendants();
+}
+
+inline AttributeChangeInvalidation::~AttributeChangeInvalidation()
+{
+ if (!m_isEnabled)
+ return;
+ invalidateDescendants();
+}
+
+}
+}
+
+#endif
+
Modified: trunk/Source/WebCore/style/ClassChangeInvalidation.h (196628 => 196629)
--- trunk/Source/WebCore/style/ClassChangeInvalidation.h 2016-02-16 08:11:47 UTC (rev 196628)
+++ trunk/Source/WebCore/style/ClassChangeInvalidation.h 2016-02-16 08:20:58 UTC (rev 196629)
@@ -46,7 +46,6 @@
private:
using ClassChangeVector = Vector<AtomicStringImpl*, 4>;
- static bool needsInvalidation(const Element&);
void computeClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses);
void invalidateStyle(const ClassChangeVector&);
@@ -59,19 +58,8 @@
ClassChangeVector m_removedClasses;
};
-inline bool ClassChangeInvalidation::needsInvalidation(const Element& element)
-{
- if (!element.inRenderedDocument())
- return false;
- if (element.styleChangeType() >= FullStyleChange)
- return false;
- if (!element.document().styleResolverIfExists())
- return false;
- return true;
-}
-
inline ClassChangeInvalidation::ClassChangeInvalidation(Element& element, const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses)
- : m_isEnabled(needsInvalidation(element))
+ : m_isEnabled(element.needsStyleInvalidation())
, m_element(element)
{