WebKit Bugzilla
Attachment 349252 Details for
Bug 166748
: :first-child, :last-child, :nth-child, and :nth-of-type don't work on shadow root's children
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Fixes the bug
bug-166748-20180908010857.patch (text/plain), 58.90 KB, created by
Ryosuke Niwa
on 2018-09-08 01:08:57 PDT
(
hide
)
Description:
Fixes the bug
Filename:
MIME Type:
Creator:
Ryosuke Niwa
Created:
2018-09-08 01:08:57 PDT
Size:
58.90 KB
patch
obsolete
>Index: Source/WebCore/ChangeLog >=================================================================== >--- Source/WebCore/ChangeLog (revision 235827) >+++ Source/WebCore/ChangeLog (working copy) >@@ -1,3 +1,38 @@ >+2018-09-08 Ryosuke Niwa <rniwa@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 NOBODY (OOPS!). >+ >+ 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-07 Fujii Hironori <Hironori.Fujii@sony.com> > > [Win][Clang] exceptionShouldTerminateProgram of StructuredExceptionHandlerSuppressor.cpp should take DWORD >Index: Source/WebCore/css/SelectorChecker.cpp >=================================================================== >--- Source/WebCore/css/SelectorChecker.cpp (revision 235780) >+++ Source/WebCore/css/SelectorChecker.cpp (working copy) >@@ -736,69 +736,92 @@ bool SelectorChecker::checkOne(CheckingC > > 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,88 +859,96 @@ bool SelectorChecker::checkOne(CheckingC > 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); >- >- if (const CSSSelectorList* selectorList = selector.selectorList()) { >- unsigned selectorListSpecificity; >- if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity)) >- return false; >- specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity); >- } >+ 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); >+ } > >- 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; > } >- >- if (selector.matchNth(count)) >- return true; >+ } else { >+ count += countElementsBefore(element); >+ addStyleRelation(checkingContext, element, Style::Relation::NthChildIndex, count); > } >+ >+ 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)) > 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; >Index: Source/WebCore/cssjit/SelectorCompiler.cpp >=================================================================== >--- Source/WebCore/cssjit/SelectorCompiler.cpp (revision 235780) >+++ Source/WebCore/cssjit/SelectorCompiler.cpp (working copy) >@@ -274,6 +274,7 @@ private: > 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 @@ private: > 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 @@ void SelectorCodeGenerator::generateWalk > 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; >@@ -3166,13 +3180,10 @@ void SelectorCodeGenerator::generateElem > 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); >@@ -3186,20 +3197,36 @@ void SelectorCodeGenerator::generateElem > successCase.link(&m_assembler); > } > >+ Assembler::RegisterID parentNode = m_registerAllocator.allocateRegister(); >+ 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); >+ Assembler::JumpList successCases; > >- 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); >+ successCases.append(m_assembler.jump()); > >+ Assembler::Label checkWithoutRelation(m_assembler.label()); > notResolvingStyle.link(&m_assembler); > failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isFirstChildRegister)); >+ successCases.append(m_assembler.jump()); >+ >+ notElement.link(&m_assembler); >+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode)); >+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler); >+ m_assembler.jump().linkTo(checkWithoutRelation, &m_assembler); >+ >+ m_registerAllocator.deallocateRegister(parentNode); >+ >+ successCases.link(&m_assembler); > } > > static bool elementIsHovered(const Element* element) >@@ -3257,22 +3284,23 @@ void SelectorCodeGenerator::generateElem > > 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()))); > > return; > } > >- Assembler::RegisterID parentElement = m_registerAllocator.allocateRegister(); >- generateWalkToParentElement(failureCases, parentElement); >+ Assembler::RegisterID parentNode = m_registerAllocator.allocateRegister(); >+ 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); > 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(); > >@@ -3282,20 +3310,33 @@ void SelectorCodeGenerator::generateElem > successCase.link(&m_assembler); > } > >+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode); >+ > LocalRegister checkingContext(m_registerAllocator); > Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext); >+ Assembler::JumpList successCases; > >- 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); >+ successCases.append(m_assembler.jump()); > >+ Assembler::Label checkWithoutRelation(m_assembler.label()); > notResolvingStyle.link(&m_assembler); > failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isLastChildRegister)); >+ successCases.append(m_assembler.jump()); >+ >+ notElement.link(&m_assembler); >+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode)); >+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler); >+ m_assembler.jump().linkTo(checkWithoutRelation, &m_assembler); >+ >+ m_registerAllocator.deallocateRegister(parentNode); >+ >+ successCases.link(&m_assembler); > } > > void SelectorCodeGenerator::generateElementIsOnlyChild(Assembler::JumpList& failureCases) >@@ -3311,15 +3352,16 @@ void SelectorCodeGenerator::generateElem > 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()))); > > return; > } > >- Assembler::RegisterID parentElement = m_registerAllocator.allocateRegister(); >- generateWalkToParentElement(failureCases, parentElement); >+ Assembler::RegisterID parentNode = m_registerAllocator.allocateRegister(); >+ 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); >@@ -3332,7 +3374,7 @@ void SelectorCodeGenerator::generateElem > 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); >@@ -3341,23 +3383,35 @@ void SelectorCodeGenerator::generateElem > successCase.link(&m_assembler); > } > >+ Assembler::Jump notElement = DOMJIT::branchTestIsElementFlagOnNode(m_assembler, Assembler::Zero, parentNode); >+ > LocalRegister checkingContext(m_registerAllocator); > Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext); >+ Assembler::JumpList successCases; > >- 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); >+ successCases.append(m_assembler.jump()); > >+ Assembler::Label checkWithoutRelation(m_assembler.label()); > notResolvingStyle.link(&m_assembler); > failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isOnlyChildRegister)); >+ successCases.append(m_assembler.jump()); >+ >+ notElement.link(&m_assembler); >+ failureCases.append(DOMJIT::branchTestIsShadowRootFlagOnNode(m_assembler, Assembler::Zero, parentNode)); >+ jumpIfNotResolvingStyle(checkingContext).linkTo(checkWithRelation, &m_assembler); >+ m_assembler.jump().linkTo(checkWithoutRelation, &m_assembler); >+ >+ m_registerAllocator.deallocateRegister(parentNode); >+ >+ successCases.link(&m_assembler); > } > > static bool makeContextStyleUniqueIfNecessaryAndTestIsPlaceholderShown(const Element* element, SelectorChecker::CheckingContext* checkingContext) >@@ -3500,16 +3554,29 @@ static bool nthFilterIsAlwaysSatisified( > 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()); >@@ -3565,14 +3632,7 @@ void SelectorCodeGenerator::generateElem > > 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) >@@ -3616,29 +3676,41 @@ void SelectorCodeGenerator::generateElem > } > } > >+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. >@@ -3664,28 +3736,22 @@ void SelectorCodeGenerator::generateElem > > 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. >Index: Source/WebCore/dom/ShadowRoot.cpp >=================================================================== >--- Source/WebCore/dom/ShadowRoot.cpp (revision 235780) >+++ Source/WebCore/dom/ShadowRoot.cpp (working copy) >@@ -106,6 +106,30 @@ void ShadowRoot::removedFromAncestor(Rem > 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); >Index: Source/WebCore/dom/ShadowRoot.h >=================================================================== >--- Source/WebCore/dom/ShadowRoot.h (revision 235780) >+++ Source/WebCore/dom/ShadowRoot.h (working copy) >@@ -106,6 +106,8 @@ private: > 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 }; >Index: Source/WebCore/domjit/DOMJITHelpers.h >=================================================================== >--- Source/WebCore/domjit/DOMJITHelpers.h (revision 235780) >+++ Source/WebCore/domjit/DOMJITHelpers.h (working copy) >@@ -179,6 +179,17 @@ inline CCallHelpers::Jump branchTestIsEl > 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())); >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 235780) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,23 @@ >+2018-09-08 Ryosuke Niwa <rniwa@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 NOBODY (OOPS!). >+ >+ 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-06 Zalan Bujtas <zalan@apple.com> > > [LFC] Add support for min/max-height percentage values. >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-expected.html (working copy) >@@ -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> >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation-expected.html (working copy) >@@ -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> >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-invalidation.html (working copy) >@@ -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> >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit-expected.html (working copy) >@@ -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> >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child-no-jit.html (working copy) >@@ -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> >Index: LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html >=================================================================== >--- LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html (nonexistent) >+++ LayoutTests/fast/shadow-dom/nth-node-on-shadow-child.html (working copy) >@@ -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>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 166748
:
298151
|
349116
|
349124
|
349125
|
349127
|
349128
|
349252
|
349359