Diff
Modified: trunk/LayoutTests/ChangeLog (235916 => 235917)
--- trunk/LayoutTests/ChangeLog 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/LayoutTests/ChangeLog 2018-09-11 23:00:31 UTC (rev 235917)
@@ -1,3 +1,23 @@
+2018-09-08 Ryosuke Niwa <rn...@webkit.org>
+
+ :first-child, :last-child, :nth-child, and :nth-of-type don't work on shadow root's children
+ https://bugs.webkit.org/show_bug.cgi?id=166748
+ <rdar://problem/29649177>
+
+ Reviewed by Yusuke Suzuki.
+
+ Added ref tests for matching positional pseudo classes on direct child of shadow roots
+ during style resolutions and DOM API matching with C++ selector checker and selector compilers.
+
+ Also added a test for invalidating these selectors.
+
+ * fast/shadow-dom/nth-node-on-shadow-child-expected.html: Added.
+ * fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html: Added.
+ * fast/shadow-dom/nth-node-on-shadow-child-invalidation.html: Added.
+ * fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html: Added.
+ * fast/shadow-dom/nth-node-on-shadow-child-no-jit.html: Added.
+ * fast/shadow-dom/nth-node-on-shadow-child.html: Added.
+
2018-09-11 Ryosuke Niwa <rn...@webkit.org>
Updated the test expectation as this test also causes a crash in release builds.
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <p>Test passes if you see a single 100px by 100px green box below.</p>
+ <div style="width: 100px; height: 100px; background: green;"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <p>Test passes if you see a single 200px by 200px green box below.</p>
+ <div style="width: 200px; height: 200px; background: green;"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Test passes if you see a single 200px by 200px green box below.</p>
+<div id="container" style="width: 200px; height: 200px; background: green;"></div>
+<script>
+
+function style(pseudoClasses) {
+ return `<style>
+ .match, .not-match { display: block; float: left; width: 100px; height: 15px; color: green; font-size: 12px; overflow: hidden; }
+ .match { background: red; }
+ .not-match { background: green; }
+ .match${pseudoClasses} { background: green; }
+ .not-match${pseudoClasses} { background: red; }
+</style>`;
+}
+
+function test(markup, action) {
+ const host = document.createElement('div');
+ const shadowRoot = host.attachShadow({mode: 'closed'});
+ document.getElementById('container').appendChild(host);
+ shadowRoot.innerHTML = markup;
+ return () => action(shadowRoot);
+}
+
+const tests = [
+ test(`<old-first></old-first><new-first class="match">newly first ${style(':first-child')}</new-first>`,
+ shadowRoot => shadowRoot.querySelector('old-first').remove()),
+ test(`<old-first class="not-match">old first ${style(':first-child')}</old-first>`,
+ shadowRoot => shadowRoot.prepend(document.createElement('div'))),
+
+ test(`<new-last class="match">newly last ${style(':last-child')}</new-last><old-last></old-last>`,
+ shadowRoot => shadowRoot.querySelector('old-last').remove()),
+ test(`<old-last class="not-match">old last ${style(':last-child')}</old-last>`,
+ shadowRoot => shadowRoot.append(document.createElement('div'))),
+
+ test(`<div></div><div class="match">only child 1 ${style(':only-child')}</div>`,
+ shadowRoot => shadowRoot.firstChild.remove()),
+ test(`<div class="match">only child 2 ${style(':only-child')}</div><div></div>`,
+ shadowRoot => shadowRoot.lastChild.remove()),
+ test(`<div class="not-match">only child 3 ${style(':only-child')}</div>`,
+ shadowRoot => shadowRoot.prepend(document.createElement('div'))),
+ test(`<div class="not-match">only child 4 ${style(':only-child')}</div>`,
+ shadowRoot => shadowRoot.append(document.createElement('div'))),
+
+ test(`<div></div><div class="match">1st child ${style(':nth-child(1)')}</div>`,
+ shadowRoot => shadowRoot.firstChild.remove()),
+ test(`<div></div><div class="not-match">2nd child ${style(':nth-child(2)')}</div>`,
+ shadowRoot => shadowRoot.prepend(document.createElement('div'))),
+
+ test(`<div class="match">3rd last child ${style(':nth-last-child(3)')}</div><div></div><div></div><div></div>`,
+ shadowRoot => shadowRoot.lastChild.remove()),
+ test(`<div></div><div class="not-match">4th last child ${style(':nth-last-child(4)')}</div><div></div><div></div><div></div>`,
+ shadowRoot => shadowRoot.append(document.createElement('div'))),
+
+ test(`<new-first></new-first><div></div><new-first class="match">first type 1 ${style(':first-of-type')}</new-first>`,
+ shadowRoot => shadowRoot.querySelector('new-first').remove()),
+ test(`<div></div><old-first class="not-match">first type 2 ${style(':first-of-type')}</old-first>`,
+ shadowRoot => shadowRoot.prepend(document.createElement('old-first'))),
+
+ test(`<new-last class="match">last of type 1 ${style(':last-of-type')}</new-last><div></div><new-last></new-last>`,
+ shadowRoot => shadowRoot.lastChild.remove()),
+ test(`<old-last class="not-match">last of type 2 ${style(':last-of-type')}</old-last><div></div>`,
+ shadowRoot => shadowRoot.append(document.createElement('old-last'))),
+
+ test(`<div></div><section></section><div class="match">only of type 1 ${style(':only-of-type')}</div>`,
+ shadowRoot => shadowRoot.firstChild.remove()),
+ test(`<section></section><div class="match">only of type 2 ${style(':only-of-type')}</div><div></div>`,
+ shadowRoot => shadowRoot.lastChild.remove()),
+ test(`<a-b></a-b><div class="not-match">only of type 3 ${style(':only-of-type')}</div><section></section>`,
+ shadowRoot => shadowRoot.insertBefore(document.createElement('div'), shadowRoot.childNodes[2])),
+ test(`<div class="not-match">only of type 4 ${style(':only-of-type')}</div><c-d></c-d>`,
+ shadowRoot => shadowRoot.append(document.createElement('div'))),
+
+ test(`<div></div><section></section><div class="match">1st of type ${style(':nth-of-type(1)')}</div>`,
+ shadowRoot => shadowRoot.firstChild.remove()),
+ test(`<div></div><section></section><div class="not-match">2nd of type ${style(':nth-of-type(2)')}</div>`,
+ shadowRoot => shadowRoot.prepend(document.createElement('div'))),
+
+ test(`<div class="match">3rd last of type ${style(':nth-last-of-type(3)')}</div><div></div><div></div><section></section><div></div>`,
+ shadowRoot => shadowRoot.querySelector('section').previousSibling.remove()),
+ test(`<div></div><div class="not-match">4th last of type ${style(':nth-last-of-type(4)')}</div><div></div><div></div><section></section><div></div>`,
+ shadowRoot => shadowRoot.append(document.createElement('div'))),
+
+ test(`<div></div><div class="match">1st, 3rd last of type ${style(':first-child:nth-last-of-type(3)')}</div><div></div><div></div><section></section><div></div>`,
+ shadowRoot => { shadowRoot.querySelector('section').previousSibling.remove(); shadowRoot.firstChild.remove(); }),
+
+ test(`<div></div><div class="match">2nd, 2rd last of type ${style(':nth-child(3):nth-of-type(2)')}</div>`,
+ shadowRoot => { shadowRoot.prepend(document.createElement('a-b')); shadowRoot.append(document.createElement('div')); }),
+];
+
+if (window.internals)
+ internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
+else
+ window.rect = document.body.getBoundingClientRect();
+
+for (const test of tests)
+ test();
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+ <p>Test passes if you see a single 100px by 100px green box below.</p>
+ <div style="width: 100px; height: 100px; background: green;"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<script>
+const host = document.createElement('div');
+
+const shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = `
+<some-element class="item">
+ some-element:first-child
+ <style>
+ .item { display: block; width: 100px; height: 10px; background: red; color: green; font-size: 9px; overflow: hidden; }
+ :matches(noop, some-element:first-child) { background: green; }
+ :matches(noop, section:nth-of-type(1)) { background: green; }
+ :matches(noop, section:nth-child(3)) { background: green; }
+ :matches(noop, .item:nth-last-child(6)) { background: green; }
+ :matches(noop, a-element:first-of-type) { background: green; }
+ :matches(noop, b-element:only-of-type) { background: green; }
+ :matches(noop, a-element:last-of-type) { background: green; }
+ :matches(noop, c-element:nth-last-of-type(1)) { background: green; }
+ :matches(noop, other-element:last-child) { background: green; }
+ </style>
+</some-element>
+<section class="item">section:nth-of-type(1)</section>
+<section class="item">section:nth-child(3)</section>
+<section class="item">.item:nth-last-child(6)</section>
+<a-element class="item">a-element:first-of-type</a-element>
+<b-element class="item">b-element:only-of-type</b-element>
+<a-element class="item">a-element:last-of-type</a-element>
+<c-element class="item">c-element:nth-last-of-type(1)</c-element>
+<other-element class="item">other-element:last-child</other-element>`;
+document.body.appendChild(host);
+
+const loneHost = document.createElement('div');
+const loneShadowRoot = loneHost.attachShadow({mode: 'closed'});
+loneShadowRoot.innerHTML = `<div class="item">.item:only-child
+<style>
+.item { display: block; width: 100px; height: 10px; background: red; color: green; font-size: 9px; overflow: hidden; }
+:matches(noop, .item:only-child) { background: green; }
+</style>
+</div>`;
+document.body.appendChild(loneHost);
+
+for (const element of [...shadowRoot.querySelectorAll('.item'), loneShadowRoot.firstChild]) {
+ const selector = element.innerText;
+ if (!element.matches(`:matches(noop, ${selector}`))
+ logError(`${selector} did not match an element via element.matches`);
+ else {
+ const queryResult = element.getRootNode().querySelectorAll(`:matches(noop, ${selector}`);
+ if (queryResult.length != 1) {
+ console.log(queryResult);
+ logError(`${selector} matches ${queryResult.length} elements`);
+ }
+ }
+}
+
+function logError(error) {
+ const container = document.createElement('p');
+ container.textContent = error;
+ document.body.append(container);
+}
+
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html (0 => 235917)
--- trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html 2018-09-11 23:00:31 UTC (rev 235917)
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<script>
+const host = document.createElement('div');
+const shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = `
+<some-element class="item">
+ some-element:first-child
+ <style>
+ .item { display: block; width: 100px; height: 10px; background: red; color: green; font-size: 9px; overflow: hidden; }
+ some-element:first-child { background: green; }
+ section:nth-of-type(1) { background: green; }
+ section:nth-child(3) { background: green; }
+ .item:nth-last-child(6) { background: green; }
+ a-element:first-of-type { background: green; }
+ b-element:only-of-type { background: green; }
+ a-element:last-of-type { background: green; }
+ c-element:nth-last-of-type(1) { background: green; }
+ other-element:last-child { background: green; }
+ </style>
+</some-element>
+<section class="item">section:nth-of-type(1)</section>
+<section class="item">section:nth-child(3)</section>
+<section class="item">.item:nth-last-child(6)</section>
+<a-element class="item">a-element:first-of-type</a-element>
+<b-element class="item">b-element:only-of-type</b-element>
+<a-element class="item">a-element:last-of-type</a-element>
+<c-element class="item">c-element:nth-last-of-type(1)</c-element>
+<other-element class="item">other-element:last-child</other-element>`;
+document.body.appendChild(host);
+
+const loneHost = document.createElement('div');
+const loneShadowRoot = loneHost.attachShadow({mode: 'closed'});
+loneShadowRoot.innerHTML = `<div class="item">.item:only-child
+<style>
+.item { display: block; width: 100px; height: 10px; background: red; color: green; font-size: 9px; overflow: hidden; }
+.item:only-child { background: green; }
+</style>
+</div>`;
+document.body.appendChild(loneHost);
+
+for (const element of [...shadowRoot.querySelectorAll('.item'), loneShadowRoot.firstChild]) {
+ const selector = element.innerText;
+ if (!element.matches(selector))
+ logError(`${selector} did not match an element via element.matches`);
+ else {
+ const queryResult = element.getRootNode().querySelectorAll(selector);
+ if (queryResult.length != 1) {
+ console.log(queryResult);
+ logError(`${selector} matches ${queryResult.length} elements`);
+ }
+ }
+}
+
+function logError(error) {
+ const container = document.createElement('p');
+ container.textContent = error;
+ document.body.append(container);
+}
+
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (235916 => 235917)
--- trunk/Source/WebCore/ChangeLog 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/ChangeLog 2018-09-11 23:00:31 UTC (rev 235917)
@@ -1,3 +1,38 @@
+2018-09-08 Ryosuke Niwa <rn...@webkit.org>
+
+ :first-child, :last-child, :nth-child, and :nth-of-type don't work on shadow root's children
+ https://bugs.webkit.org/show_bug.cgi?id=166748
+ <rdar://problem/29649177>
+
+ Reviewed by Yusuke Suzuki.
+
+ Added the support for matching positional pseudo classes. For now, we invalidate whenever a child node
+ of a non-UA ShadowRoot is mutated instead of a fine-grained style invalidation as done for regular elements.
+
+ Tests: fast/shadow-dom/nth-node-on-shadow-child-invalidation.html
+ fast/shadow-dom/nth-node-on-shadow-child-no-jit.html
+ fast/shadow-dom/nth-node-on-shadow-child.html
+
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::checkOne const):
+ * cssjit/SelectorCompiler.cpp:
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateWalkToParentElementOrShadowRoot):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsFirstChild):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsLastChild):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsOnlyChild):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateNthChildParentCheckAndRelationUpdate):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthChild):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthChildOf):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateNthLastChildParentCheckAndRelationUpdate):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthLastChild):
+ (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthLastChildOf):
+ * dom/ShadowRoot.cpp:
+ (WebCore::ShadowRoot::childrenChanged): Invalidate the subtree whenever a child node is mutated.
+ * dom/ShadowRoot.h:
+ * domjit/DOMJITHelpers.h:
+ (WebCore::DOMJIT::branchTestIsShadowRootFlagOnNode): Added.
+ (WebCore::DOMJIT::branchTestIsElementOrShadowRootFlagOnNode): Added.
+
2018-09-11 Per Arne Vollan <pvol...@apple.com>
Addressing post-review feedback on r235619.
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (235916 => 235917)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2018-09-11 23:00:31 UTC (rev 235917)
@@ -736,69 +736,92 @@
return result;
}
- case CSSSelector::PseudoClassFirstChild:
+ case CSSSelector::PseudoClassFirstChild: {
// first-child matches the first child that is an element
- if (const Element* parentElement = element.parentElement()) {
- bool isFirstChild = isFirstChildElement(element);
- if (isFirstChild)
- addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
- addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
- return isFirstChild;
- }
- break;
- case CSSSelector::PseudoClassFirstOfType:
+ bool isFirstChild = isFirstChildElement(element);
+ auto* parent = element.parentNode();
+ if (is<Element>(parent))
+ addStyleRelation(checkingContext, downcast<Element>(*parent), Style::Relation::ChildrenAffectedByFirstChildRules);
+ else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ if (!isFirstChild)
+ break;
+ addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
+ return true;
+ }
+ case CSSSelector::PseudoClassFirstOfType: {
// first-of-type matches the first element of its type
- if (auto* parentElement = element.parentElement()) {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
- return isFirstOfType(element, element.tagQName());
- }
- break;
- case CSSSelector::PseudoClassLastChild:
+ addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ return isFirstOfType(element, element.tagQName());
+ }
+ case CSSSelector::PseudoClassLastChild: {
// last-child matches the last child that is an element
- if (const Element* parentElement = element.parentElement()) {
- bool isLastChild = parentElement->isFinishedParsingChildren() && isLastChildElement(element);
- if (isLastChild)
- addStyleRelation(checkingContext, element, Style::Relation::LastChild);
- addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
- return isLastChild;
- }
- break;
- case CSSSelector::PseudoClassLastOfType:
+ auto* parent = element.parentNode();
+ bool isLastChild = isLastChildElement(element);
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
+ if (!parentElement.isFinishedParsingChildren())
+ isLastChild = false;
+ addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ if (!isLastChild)
+ break;
+ addStyleRelation(checkingContext, element, Style::Relation::LastChild);
+ return true;
+ }
+ case CSSSelector::PseudoClassLastOfType: {
// last-of-type matches the last element of its type
- if (Element* parentElement = element.parentElement()) {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
- if (!parentElement->isFinishedParsingChildren())
+ addStyleRelation(checkingContext, parentElement, relation);
+ if (!parentElement.isFinishedParsingChildren())
return false;
- return isLastOfType(element, element.tagQName());
- }
- break;
- case CSSSelector::PseudoClassOnlyChild:
- if (Element* parentElement = element.parentElement()) {
- bool firstChild = isFirstChildElement(element);
- bool _onlyChild_ = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element);
- addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
- addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
- if (firstChild)
- addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
- if (onlyChild)
- addStyleRelation(checkingContext, element, Style::Relation::LastChild);
- return onlyChild;
- }
- break;
- case CSSSelector::PseudoClassOnlyOfType:
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ return isLastOfType(element, element.tagQName());
+ }
+ case CSSSelector::PseudoClassOnlyChild: {
+ auto* parent = element.parentNode();
+ bool firstChild = isFirstChildElement(element);
+ bool _onlyChild_ = firstChild && isLastChildElement(element);
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
+ addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
+ addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
+ if (!parentElement.isFinishedParsingChildren())
+ _onlyChild_ = false;
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ if (firstChild)
+ addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
+ if (onlyChild)
+ addStyleRelation(checkingContext, element, Style::Relation::LastChild);
+ return onlyChild;
+ }
+ case CSSSelector::PseudoClassOnlyOfType: {
// FIXME: This selector is very slow.
- if (Element* parentElement = element.parentElement()) {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
auto forwardRelation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, forwardRelation);
+ addStyleRelation(checkingContext, parentElement, forwardRelation);
auto backwardRelation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, backwardRelation);
- if (!parentElement->isFinishedParsingChildren())
+ addStyleRelation(checkingContext, parentElement, backwardRelation);
+
+ if (!parentElement.isFinishedParsingChildren())
return false;
- return isFirstOfType(element, element.tagQName()) && isLastOfType(element, element.tagQName());
- }
- break;
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ return isFirstOfType(element, element.tagQName()) && isLastOfType(element, element.tagQName());
+ }
case CSSSelector::PseudoClassMatches:
{
bool hasMatchedAnything = false;
@@ -836,46 +859,54 @@
return downcast<HTMLTextFormControlElement>(element).isPlaceholderVisible();
}
return false;
- case CSSSelector::PseudoClassNthChild:
- if (auto* parentElement = element.parentElement()) {
+ case CSSSelector::PseudoClassNthChild: {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
+ addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
- if (const CSSSelectorList* selectorList = selector.selectorList()) {
- unsigned selectorListSpecificity;
- if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
- return false;
- specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
- }
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ unsigned selectorListSpecificity;
+ if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
+ return false;
+ specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
+ }
- int count = 1;
- if (const CSSSelectorList* selectorList = selector.selectorList()) {
- for (Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
- unsigned ignoredSpecificity;
- if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
- ++count;
- }
- } else {
- count += countElementsBefore(element);
- addStyleRelation(checkingContext, element, Style::Relation::NthChildIndex, count);
+ int count = 1;
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ for (Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
+ unsigned ignoredSpecificity;
+ if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
+ ++count;
}
+ } else {
+ count += countElementsBefore(element);
+ addStyleRelation(checkingContext, element, Style::Relation::NthChildIndex, count);
+ }
- if (selector.matchNth(count))
- return true;
- }
+ if (selector.matchNth(count))
+ return true;
break;
- case CSSSelector::PseudoClassNthOfType:
- if (auto* parentElement = element.parentElement()) {
+ }
+ case CSSSelector::PseudoClassNthOfType: {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByForwardPositionalRules : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
+ addStyleRelation(checkingContext, downcast<Element>(*parent), relation);
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
- int count = 1 + countElementsOfTypeBefore(element, element.tagQName());
- if (selector.matchNth(count))
- return true;
- }
+ int count = 1 + countElementsOfTypeBefore(element, element.tagQName());
+ if (selector.matchNth(count))
+ return true;
break;
- case CSSSelector::PseudoClassNthLastChild:
- if (Element* parentElement = element.parentElement()) {
+ }
+ case CSSSelector::PseudoClassNthLastChild: {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
if (const CSSSelectorList* selectorList = selector.selectorList()) {
unsigned selectorListSpecificity;
if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
@@ -882,42 +913,42 @@
return false;
specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
- addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
+ addStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
} else {
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
+ addStyleRelation(checkingContext, parentElement, relation);
}
-
- if (!parentElement->isFinishedParsingChildren())
+ if (!parentElement.isFinishedParsingChildren())
return false;
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
- int count = 1;
- if (const CSSSelectorList* selectorList = selector.selectorList()) {
- for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
- unsigned ignoredSpecificity;
- if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
- ++count;
- }
- } else
- count += countElementsAfter(element);
+ int count = 1;
+ if (const CSSSelectorList* selectorList = selector.selectorList()) {
+ for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
+ unsigned ignoredSpecificity;
+ if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
+ ++count;
+ }
+ } else
+ count += countElementsAfter(element);
- if (selector.matchNth(count))
- return true;
- }
- break;
- case CSSSelector::PseudoClassNthLastOfType:
- if (Element* parentElement = element.parentElement()) {
+ return selector.matchNth(count);
+ }
+ case CSSSelector::PseudoClassNthLastOfType: {
+ auto* parent = element.parentNode();
+ if (is<Element>(parent)) {
+ auto& parentElement = downcast<Element>(*parent);
auto relation = context.isSubjectOrAdjacentElement ? Style::Relation::ChildrenAffectedByBackwardPositionalRules : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
- addStyleRelation(checkingContext, *parentElement, relation);
+ addStyleRelation(checkingContext, parentElement, relation);
- if (!parentElement->isFinishedParsingChildren())
+ if (!parentElement.isFinishedParsingChildren())
return false;
-
- int count = 1 + countElementsOfTypeAfter(element, element.tagQName());
- if (selector.matchNth(count))
- return true;
- }
- break;
+ } else if (!is<ShadowRoot>(parent))
+ break; // FIXME: Add the support for specifying relations on ShadowRoot.
+ int count = 1 + countElementsOfTypeAfter(element, element.tagQName());
+ return selector.matchNth(count);
+ }
case CSSSelector::PseudoClassTarget:
if (&element == element.document().cssTarget())
return true;
Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (235916 => 235917)
--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2018-09-11 23:00:31 UTC (rev 235917)
@@ -274,6 +274,7 @@
void generateRightmostTreeWalker(Assembler::JumpList& failureCases, const SelectorFragment&);
void generateWalkToParentNode(Assembler::RegisterID targetRegister);
void generateWalkToParentElement(Assembler::JumpList& failureCases, Assembler::RegisterID targetRegister);
+ void generateWalkToParentElementOrShadowRoot(Assembler::JumpList& failureCases, Assembler::RegisterID targetRegister);
void generateParentElementTreeWalker(Assembler::JumpList& failureCases, const SelectorFragment&);
void generateAncestorTreeWalker(Assembler::JumpList& failureCases, const SelectorFragment&);
@@ -345,6 +346,9 @@
Assembler::Jump modulo(JSC::MacroAssembler::ResultCondition, Assembler::RegisterID inputDividend, int divisor);
void moduloIsZero(Assembler::JumpList& failureCases, Assembler::RegisterID inputDividend, int divisor);
+ void generateNthChildParentCheckAndRelationUpdate(Assembler::JumpList& failureCases, const SelectorFragment&);
+ void generateNthLastChildParentCheckAndRelationUpdate(Assembler::JumpList& failureCases, const SelectorFragment&);
+
void pushMacroAssemblerRegisters();
void popMacroAssemblerRegisters(StackAllocator&);
bool generatePrologue();
@@ -2056,6 +2060,16 @@
failureCases.append(DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, targetRegister));
}
+void SelectorCodeGenerator::generateWalkToParentElementOrShadowRoot(Assembler::JumpList& failureCases, Assembler::RegisterID targetRegister)
+{
+ // ContainerNode* parent = parentNode()
+ // if (!parent || !(parent->isElementNode() || parent->isShadowRoot()))
+ // failure
+ generateWalkToParentNode(targetRegister);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, targetRegister));
+ failureCases.append(DOMJIT::branchTestIsElementOrShadowRootFlagOnNode(m_assembler, Assembler::Zero, targetRegister));
+}
+
void SelectorCodeGenerator::generateParentElementTreeWalker(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
{
Assembler::JumpList traversalFailureCases;
@@ -3174,13 +3188,10 @@
failureCases.append(m_assembler.jump());
successCase.link(&m_assembler);
LocalRegister parent(m_registerAllocator);
- generateWalkToParentElement(failureCases, parent);
+ generateWalkToParentElementOrShadowRoot(failureCases, parent);
return;
}
- Assembler::RegisterID parentElement = m_registerAllocator.allocateRegister();
- generateWalkToParentElement(failureCases, parentElement);
-
// Zero in isFirstChildRegister is the success case. The register is set to non-zero if a sibling if found.
LocalRegister isFirstChildRegister(m_registerAllocator);
m_assembler.move(Assembler::TrustedImm32(0), isFirstChildRegister);
@@ -3194,20 +3205,30 @@
successCase.link(&m_assembler);
}
+ LocalRegister parentNode(m_registerAllocator);
+ generateWalkToParentNode(parentNode);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, parentNode));
+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode);
+
LocalRegister checkingContext(m_registerAllocator);
Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
- generateAddStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
- m_registerAllocator.deallocateRegister(parentElement);
-
+ generateAddStyleRelation(checkingContext, parentNode, Style::Relation::ChildrenAffectedByFirstChildRules);
// The parent marking is unconditional. If the matching is not a success, we can now fail.
// Otherwise we need to apply setFirstChildState() on the RenderStyle.
+ Assembler::Label checkWithRelation(m_assembler.label());
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isFirstChildRegister));
-
generateAddStyleRelation(checkingContext, elementAddressRegister, Style::Relation::FirstChild);
+ Assembler::Jump successCase = m_assembler.jump();
+ notElement.link(&m_assembler);
+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode));
+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler);
+
notResolvingStyle.link(&m_assembler);
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isFirstChildRegister));
+
+ successCase.link(&m_assembler);
}
static bool elementIsHovered(const Element* element)
@@ -3265,7 +3286,7 @@
successCase.link(&m_assembler);
LocalRegister parent(m_registerAllocator);
- generateWalkToParentElement(failureCases, parent);
+ generateWalkToParentElementOrShadowRoot(failureCases, parent);
failureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parent, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
@@ -3272,8 +3293,9 @@
return;
}
- Assembler::RegisterID parentElement = m_registerAllocator.allocateRegister();
- generateWalkToParentElement(failureCases, parentElement);
+ LocalRegister parentNode(m_registerAllocator);
+ generateWalkToParentNode(parentNode);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, parentNode));
// Zero in isLastChildRegister is the success case. The register is set to non-zero if a sibling if found.
LocalRegister isLastChildRegister(m_registerAllocator);
@@ -3280,7 +3302,7 @@
m_assembler.move(Assembler::TrustedImm32(0), isLastChildRegister);
{
- Assembler::Jump notFinishedParsingChildren = m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentElement, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished()));
+ Assembler::Jump notFinishedParsingChildren = m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentNode, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished()));
Assembler::JumpList successCase = jumpIfNoNextAdjacentElement();
@@ -3290,20 +3312,27 @@
successCase.link(&m_assembler);
}
+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode);
+
LocalRegister checkingContext(m_registerAllocator);
Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
- generateAddStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
- m_registerAllocator.deallocateRegister(parentElement);
-
+ generateAddStyleRelation(checkingContext, parentNode, Style::Relation::ChildrenAffectedByLastChildRules);
// The parent marking is unconditional. If the matching is not a success, we can now fail.
// Otherwise we need to apply setLastChildState() on the RenderStyle.
+ Assembler::Label checkWithRelation(m_assembler.label());
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isLastChildRegister));
-
generateAddStyleRelation(checkingContext, elementAddressRegister, Style::Relation::LastChild);
+ Assembler::Jump successCase = m_assembler.jump();
+ notElement.link(&m_assembler);
+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode));
+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler);
+
notResolvingStyle.link(&m_assembler);
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isLastChildRegister));
+
+ successCase.link(&m_assembler);
}
void SelectorCodeGenerator::generateElementIsOnlyChild(Assembler::JumpList& failureCases)
@@ -3319,7 +3348,7 @@
nextSuccessCase.link(&m_assembler);
LocalRegister parent(m_registerAllocator);
- generateWalkToParentElement(failureCases, parent);
+ generateWalkToParentElementOrShadowRoot(failureCases, parent);
failureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parent, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
@@ -3326,8 +3355,9 @@
return;
}
- Assembler::RegisterID parentElement = m_registerAllocator.allocateRegister();
- generateWalkToParentElement(failureCases, parentElement);
+ LocalRegister parentNode(m_registerAllocator);
+ generateWalkToParentNode(parentNode);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, parentNode));
// Zero in isOnlyChildRegister is the success case. The register is set to non-zero if a sibling if found.
LocalRegister isOnlyChildRegister(m_registerAllocator);
@@ -3340,7 +3370,7 @@
localFailureCases.append(m_assembler.jump());
successCase.link(&m_assembler);
}
- localFailureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentElement, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
+ localFailureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentNode, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
Assembler::JumpList successCase = jumpIfNoNextAdjacentElement();
localFailureCases.link(&m_assembler);
@@ -3349,23 +3379,29 @@
successCase.link(&m_assembler);
}
+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode);
+
LocalRegister checkingContext(m_registerAllocator);
Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
- generateAddStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
- generateAddStyleRelation(checkingContext, parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
-
- m_registerAllocator.deallocateRegister(parentElement);
-
+ generateAddStyleRelation(checkingContext, parentNode, Style::Relation::ChildrenAffectedByFirstChildRules);
+ generateAddStyleRelation(checkingContext, parentNode, Style::Relation::ChildrenAffectedByLastChildRules);
// The parent marking is unconditional. If the matching is not a success, we can now fail.
// Otherwise we need to apply setLastChildState() on the RenderStyle.
+ Assembler::Label checkWithRelation(m_assembler.label());
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isOnlyChildRegister));
-
generateAddStyleRelation(checkingContext, elementAddressRegister, Style::Relation::FirstChild);
generateAddStyleRelation(checkingContext, elementAddressRegister, Style::Relation::LastChild);
+ Assembler::Jump successCase = m_assembler.jump();
+ notElement.link(&m_assembler);
+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode));
+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler);
+
notResolvingStyle.link(&m_assembler);
failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isOnlyChildRegister));
+
+ successCase.link(&m_assembler);
}
static bool makeContextStyleUniqueIfNecessaryAndTestIsPlaceholderShown(const Element* element, SelectorChecker::CheckingContext* checkingContext)
@@ -3508,16 +3544,29 @@
return false;
}
+void SelectorCodeGenerator::generateNthChildParentCheckAndRelationUpdate(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+ LocalRegister parentNode(m_registerAllocator);
+ generateWalkToParentNode(parentNode);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, parentNode));
+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode);
+
+ generateWalkToParentElement(failureCases, parentNode);
+ auto relation = fragmentMatchesRightmostOrAdjacentElement(fragment)
+ ? Style::Relation::ChildrenAffectedByForwardPositionalRules
+ : Style::Relation::DescendantsAffectedByForwardPositionalRules;
+ generateAddStyleRelationIfResolvingStyle(parentNode, relation);
+ Assembler::Jump parentNodeCheckEnd = m_assembler.jump();
+
+ notElement.link(&m_assembler);
+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode));
+
+ parentNodeCheckEnd.link(&m_assembler);
+}
+
void SelectorCodeGenerator::generateElementIsNthChild(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
{
- {
- LocalRegister parentElement(m_registerAllocator);
- generateWalkToParentElement(failureCases, parentElement);
- auto relation = fragmentMatchesRightmostOrAdjacentElement(fragment)
- ? Style::Relation::ChildrenAffectedByForwardPositionalRules
- : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- generateAddStyleRelationIfResolvingStyle(parentElement, relation);
- }
+ generateNthChildParentCheckAndRelationUpdate(failureCases, fragment);
Vector<std::pair<int, int>, 32> validSubsetFilters;
validSubsetFilters.reserveInitialCapacity(fragment.nthChildFilters.size());
@@ -3573,14 +3622,7 @@
void SelectorCodeGenerator::generateElementIsNthChildOf(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
{
- {
- LocalRegister parentElement(m_registerAllocator);
- generateWalkToParentElement(failureCases, parentElement);
- auto relation = fragmentMatchesRightmostOrAdjacentElement(fragment)
- ? Style::Relation::ChildrenAffectedByForwardPositionalRules
- : Style::Relation::DescendantsAffectedByForwardPositionalRules;
- generateAddStyleRelationIfResolvingStyle(parentElement, relation);
- }
+ generateNthChildParentCheckAndRelationUpdate(failureCases, fragment);
// The initial element must match the selector list.
for (const NthChildOfSelectorInfo& nthChildOfSelectorInfo : fragment.nthChildOfFilters)
@@ -3624,29 +3666,41 @@
}
}
+void SelectorCodeGenerator::generateNthLastChildParentCheckAndRelationUpdate(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+ LocalRegister parentNode(m_registerAllocator);
+ generateWalkToParentNode(parentNode);
+ failureCases.append(m_assembler.branchTestPtr(Assembler::Zero, parentNode));
+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode);
+
+ generateWalkToParentElement(failureCases, parentNode);
+ auto relation = fragmentMatchesRightmostOrAdjacentElement(fragment)
+ ? Style::Relation::ChildrenAffectedByBackwardPositionalRules
+ : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
+ generateAddStyleRelationIfResolvingStyle(parentNode, relation);
+ failureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentNode, Node::nodeFlagsMemoryOffset()),
+ Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
+ Assembler::Jump parentNodeCheckEnd = m_assembler.jump();
+
+ notElement.link(&m_assembler);
+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode));
+
+ parentNodeCheckEnd.link(&m_assembler);
+}
+
void SelectorCodeGenerator::generateElementIsNthLastChild(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
{
+ generateNthLastChildParentCheckAndRelationUpdate(failureCases, fragment);
+
Vector<std::pair<int, int>, 32> validSubsetFilters;
validSubsetFilters.reserveInitialCapacity(fragment.nthLastChildFilters.size());
- { // :nth-last-child() must have a parent to match. If there is a parent, do the invalidation marking.
- LocalRegister parentElement(m_registerAllocator);
- generateWalkToParentElement(failureCases, parentElement);
-
- auto relation = fragmentMatchesRightmostOrAdjacentElement(fragment)
- ? Style::Relation::ChildrenAffectedByBackwardPositionalRules
- : Style::Relation::DescendantsAffectedByBackwardPositionalRules;
- generateAddStyleRelationIfResolvingStyle(parentElement, relation);
-
- failureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentElement, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
-
- for (const auto& slot : fragment.nthLastChildFilters) {
- if (nthFilterIsAlwaysSatisified(slot.first, slot.second))
- continue;
- validSubsetFilters.uncheckedAppend(slot);
- }
- if (validSubsetFilters.isEmpty())
- return;
+ for (const auto& slot : fragment.nthLastChildFilters) {
+ if (nthFilterIsAlwaysSatisified(slot.first, slot.second))
+ continue;
+ validSubsetFilters.uncheckedAppend(slot);
}
+ if (validSubsetFilters.isEmpty())
+ return;
LocalRegister elementCounter(m_registerAllocator);
{ // Loop over the following sibling elements and increment the counter.
@@ -3672,28 +3726,22 @@
void SelectorCodeGenerator::generateElementIsNthLastChildOf(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
{
+ generateNthLastChildParentCheckAndRelationUpdate(failureCases, fragment);
+
Vector<const NthChildOfSelectorInfo*> validSubsetFilters;
validSubsetFilters.reserveInitialCapacity(fragment.nthLastChildOfFilters.size());
- {
- LocalRegister parentElement(m_registerAllocator);
- generateWalkToParentElement(failureCases, parentElement);
- generateAddStyleRelationIfResolvingStyle(parentElement, Style::Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
-
- failureCases.append(m_assembler.branchTest32(Assembler::Zero, Assembler::Address(parentElement, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsParsingChildrenFinished())));
-
- // The initial element must match the selector list.
- for (const NthChildOfSelectorInfo& nthLastChildOfSelectorInfo : fragment.nthLastChildOfFilters)
- generateElementMatchesSelectorList(failureCases, elementAddressRegister, nthLastChildOfSelectorInfo.selectorList);
-
- for (const NthChildOfSelectorInfo& nthLastChildOfSelectorInfo : fragment.nthLastChildOfFilters) {
- if (nthFilterIsAlwaysSatisified(nthLastChildOfSelectorInfo.a, nthLastChildOfSelectorInfo.b))
- continue;
- validSubsetFilters.uncheckedAppend(&nthLastChildOfSelectorInfo);
- }
- if (validSubsetFilters.isEmpty())
- return;
+ // The initial element must match the selector list.
+ for (const NthChildOfSelectorInfo& nthLastChildOfSelectorInfo : fragment.nthLastChildOfFilters)
+ generateElementMatchesSelectorList(failureCases, elementAddressRegister, nthLastChildOfSelectorInfo.selectorList);
+
+ for (const NthChildOfSelectorInfo& nthLastChildOfSelectorInfo : fragment.nthLastChildOfFilters) {
+ if (nthFilterIsAlwaysSatisified(nthLastChildOfSelectorInfo.a, nthLastChildOfSelectorInfo.b))
+ continue;
+ validSubsetFilters.uncheckedAppend(&nthLastChildOfSelectorInfo);
}
+ if (validSubsetFilters.isEmpty())
+ return;
for (const NthChildOfSelectorInfo* nthLastChildOfSelectorInfo : validSubsetFilters) {
// Setup the counter at 1.
Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (235916 => 235917)
--- trunk/Source/WebCore/dom/ShadowRoot.cpp 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp 2018-09-11 23:00:31 UTC (rev 235917)
@@ -106,6 +106,30 @@
document().didRemoveInDocumentShadowRoot(*this);
}
+void ShadowRoot::childrenChanged(const ChildChange& childChange)
+{
+ DocumentFragment::childrenChanged(childChange);
+
+ if (!m_host || m_type == ShadowRootMode::UserAgent)
+ return; // Don't support first-child, nth-of-type, etc... in UA shadow roots as an optimization.
+
+ // FIXME: Avoid always invalidating style just for first-child, etc... as done in Element::childrenChanged.
+ switch (childChange.type) {
+ case ElementInserted:
+ case ElementRemoved:
+ m_host->invalidateStyleForSubtreeInternal();
+ break;
+ case TextInserted:
+ case TextRemoved:
+ case TextChanged:
+ case AllChildrenRemoved:
+ case NonContentsChildRemoved:
+ case NonContentsChildInserted:
+ case AllChildrenReplaced:
+ break;
+ }
+}
+
void ShadowRoot::moveShadowRootToNewParentScope(TreeScope& newScope, Document& newDocument)
{
setParentTreeScope(newScope);
Modified: trunk/Source/WebCore/dom/ShadowRoot.h (235916 => 235917)
--- trunk/Source/WebCore/dom/ShadowRoot.h 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/dom/ShadowRoot.h 2018-09-11 23:00:31 UTC (rev 235917)
@@ -103,6 +103,8 @@
Node::InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) override;
void removedFromAncestor(RemovalType, ContainerNode& insertionPoint) override;
+ void childrenChanged(const ChildChange&) override;
+
bool m_resetStyleInheritance { false };
bool m_hasBegunDeletingDetachedChildren { false };
ShadowRootMode m_type { ShadowRootMode::UserAgent };
Modified: trunk/Source/WebCore/domjit/DOMJITHelpers.h (235916 => 235917)
--- trunk/Source/WebCore/domjit/DOMJITHelpers.h 2018-09-11 22:38:27 UTC (rev 235916)
+++ trunk/Source/WebCore/domjit/DOMJITHelpers.h 2018-09-11 23:00:31 UTC (rev 235917)
@@ -179,6 +179,17 @@
return jit.branchTest32(condition, CCallHelpers::Address(nodeAddress, Node::nodeFlagsMemoryOffset()), CCallHelpers::TrustedImm32(Node::flagIsElement()));
}
+inline CCallHelpers::Jump branchTestIsShadowRootFlagOnNode(MacroAssembler& jit, CCallHelpers::ResultCondition condition, GPRReg nodeAddress)
+{
+ return jit.branchTest32(condition, CCallHelpers::Address(nodeAddress, Node::nodeFlagsMemoryOffset()), CCallHelpers::TrustedImm32(Node::flagIsShadowRoot()));
+}
+
+inline CCallHelpers::Jump branchTestIsElementOrShadowRootFlagOnNode(MacroAssembler& jit, CCallHelpers::ResultCondition condition, GPRReg nodeAddress)
+{
+ return jit.branchTest32(condition, CCallHelpers::Address(nodeAddress, Node::nodeFlagsMemoryOffset()),
+ CCallHelpers::TrustedImm32(Node::flagIsShadowRoot() | Node::flagIsElement()));
+}
+
inline CCallHelpers::Jump branchTestIsHTMLFlagOnNode(MacroAssembler& jit, CCallHelpers::ResultCondition condition, GPRReg nodeAddress)
{
return jit.branchTest32(condition, CCallHelpers::Address(nodeAddress, Node::nodeFlagsMemoryOffset()), CCallHelpers::TrustedImm32(Node::flagIsHTML()));