// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "util_string.hpp" namespace Sass { // ########################################################################## // To compare/debug against libsass you can use debugger.hpp: // c++: std::cerr << "result " << debug_vec(compound) << "\n"; // dart: stderr.writeln("result " + compound.toString()); // ########################################################################## // ########################################################################## // Returns whether [list1] is a superselector of [list2]. // That is, whether [list1] matches every element that // [list2] matches, as well as possibly additional elements. // ########################################################################## bool listIsSuperslector( const std::vector& list1, const std::vector& list2); // ########################################################################## // Returns whether [complex1] is a superselector of [complex2]. // That is, whether [complex1] matches every element that // [complex2] matches, as well as possibly additional elements. // ########################################################################## bool complexIsSuperselector( const std::vector& complex1, const std::vector& complex2); // ########################################################################## // Returns all pseudo selectors in [compound] that have // a selector argument, and that have the given [name]. // ########################################################################## std::vector selectorPseudoNamed( CompoundSelectorObj compound, std::string name) { std::vector rv; for (SimpleSelectorObj sel : compound->elements()) { if (Pseudo_Selector_Obj pseudo = Cast(sel)) { if (pseudo->isClass() && pseudo->selector()) { if (sel->name() == name) { rv.push_back(sel); } } } } return rv; } // EO selectorPseudoNamed // ########################################################################## // Returns whether [simple1] is a superselector of [simple2]. // That is, whether [simple1] matches every element that // [simple2] matches, as well as possibly additional elements. // ########################################################################## bool simpleIsSuperselector( const SimpleSelectorObj& simple1, const SimpleSelectorObj& simple2) { // If they are equal they are superselectors if (ObjEqualityFn(simple1, simple2)) { return true; } // Some selector pseudoclasses can match normal selectors. if (const Pseudo_Selector* pseudo = Cast(simple2)) { if (pseudo->selector() && isSubselectorPseudo(pseudo->normalized())) { for (auto complex : pseudo->selector()->elements()) { // Make sure we have exacly one items if (complex->length() != 1) { return false; } // That items must be a compound selector if (auto compound = Cast(complex->at(0))) { // It must contain the lhs simple selector if (!compound->contains(simple1)) { return false; } } } return true; } } return false; } // EO simpleIsSuperselector // ########################################################################## // Returns whether [simple] is a superselector of [compound]. // That is, whether [simple] matches every element that // [compound] matches, as well as possibly additional elements. // ########################################################################## bool simpleIsSuperselectorOfCompound( const SimpleSelectorObj& simple, const CompoundSelectorObj& compound) { for (SimpleSelectorObj simple2 : compound->elements()) { if (simpleIsSuperselector(simple, simple2)) { return true; } } return false; } // EO simpleIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool typeIsSuperselectorOfCompound( const Type_Selector_Obj& type, const CompoundSelectorObj& compound) { for (const SimpleSelectorObj& simple : compound->elements()) { if (const Type_Selector_Obj& rhs = Cast(simple)) { if (*type != *rhs) return true; } } return false; } // EO typeIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool idIsSuperselectorOfCompound( const Id_Selector_Obj& id, const CompoundSelectorObj& compound) { for (const SimpleSelectorObj& simple : compound->elements()) { if (const Id_Selector_Obj& rhs = Cast(simple)) { if (*id != *rhs) return true; } } return false; } // EO idIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool pseudoIsSuperselectorOfPseudo( const Pseudo_Selector_Obj& pseudo1, const Pseudo_Selector_Obj& pseudo2, const ComplexSelectorObj& parent ) { if (!pseudo2->selector()) return false; if (pseudo1->name() == pseudo2->name()) { SelectorListObj list = pseudo2->selector(); return listIsSuperslector(list->elements(), { parent }); } return false; } // EO pseudoIsSuperselectorOfPseudo // ########################################################################## // ########################################################################## bool pseudoNotIsSuperselectorOfCompound( const Pseudo_Selector_Obj& pseudo1, const CompoundSelectorObj& compound2, const ComplexSelectorObj& parent) { for (const SimpleSelectorObj& simple2 : compound2->elements()) { if (const Type_Selector_Obj& type2 = Cast(simple2)) { if (const CompoundSelectorObj& compound1 = Cast(parent->last())) { if (typeIsSuperselectorOfCompound(type2, compound1)) return true; } } else if (const Id_Selector_Obj& id2 = Cast(simple2)) { if (const CompoundSelectorObj& compound1 = Cast(parent->last())) { if (idIsSuperselectorOfCompound(id2, compound1)) return true; } } else if (const Pseudo_Selector_Obj& pseudo2 = Cast(simple2)) { if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true; } } return false; } // pseudoNotIsSuperselectorOfCompound // ########################################################################## // Returns whether [pseudo1] is a superselector of [compound2]. // That is, whether [pseudo1] matches every element that [compound2] // matches, as well as possibly additional elements. This assumes that // [pseudo1]'s `selector` argument is not `null`. If [parents] is passed, // it represents the parents of [compound2]. This is relevant for pseudo // selectors with selector arguments, where we may need to know if the // parent selectors in the selector argument match [parents]. // ########################################################################## bool selectorPseudoIsSuperselector( const Pseudo_Selector_Obj& pseudo1, const CompoundSelectorObj& compound2, // ToDo: is this really the most convenient way to do this? std::vector::const_iterator parents_from, std::vector::const_iterator parents_to) { // ToDo: move normalization function std::string name(Util::unvendor(pseudo1->name())); if (name == "matches" || name == "any") { std::vector pseudos = selectorPseudoNamed(compound2, pseudo1->name()); SelectorListObj selector1 = pseudo1->selector(); for (Pseudo_Selector_Obj pseudo2 : pseudos) { SelectorListObj selector = pseudo2->selector(); if (selector1->isSuperselectorOf(selector)) { return true; } } for (ComplexSelectorObj complex1 : selector1->elements()) { std::vector parents; for (auto cur = parents_from; cur != parents_to; cur++) { parents.push_back(*cur); } parents.push_back(compound2); if (complexIsSuperselector(complex1->elements(), parents)) { return true; } } } else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") { std::vector pseudos = selectorPseudoNamed(compound2, pseudo1->name()); SelectorListObj selector1 = pseudo1->selector(); for (Pseudo_Selector_Obj pseudo2 : pseudos) { SelectorListObj selector = pseudo2->selector(); if (selector1->isSuperselectorOf(selector)) { return true; } } } else if (name == "not") { for (ComplexSelectorObj complex : pseudo1->selector()->elements()) { if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, complex)) return false; } return true; } else if (name == "current") { std::vector pseudos = selectorPseudoNamed(compound2, "current"); for (Pseudo_Selector_Obj pseudo2 : pseudos) { if (ObjEqualityFn(pseudo1, pseudo2)) return true; } } else if (name == "nth-child" || name == "nth-last-child") { for (auto simple2 : compound2->elements()) { if (Pseudo_Selector_Obj pseudo2 = simple2->getPseudoSelector()) { if (pseudo1->name() != pseudo2->name()) continue; if (!ObjEqualityFn(pseudo1->argument(), pseudo2->argument())) continue; if (pseudo1->selector()->isSuperselectorOf(pseudo2->selector())) return true; } } return false; } return false; } // EO selectorPseudoIsSuperselector // ########################################################################## // Returns whether [compound1] is a superselector of [compound2]. // That is, whether [compound1] matches every element that [compound2] // matches, as well as possibly additional elements. If [parents] is // passed, it represents the parents of [compound2]. This is relevant // for pseudo selectors with selector arguments, where we may need to // know if the parent selectors in the selector argument match [parents]. // ########################################################################## bool compoundIsSuperselector( const CompoundSelectorObj& compound1, const CompoundSelectorObj& compound2, // ToDo: is this really the most convenient way to do this? const std::vector::const_iterator parents_from, const std::vector::const_iterator parents_to) { // Every selector in [compound1.components] must have // a matching selector in [compound2.components]. for (SimpleSelectorObj simple1 : compound1->elements()) { Pseudo_Selector_Obj pseudo1 = Cast(simple1); if (pseudo1 && pseudo1->selector()) { if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) { return false; } } else if (!simpleIsSuperselectorOfCompound(simple1, compound2)) { return false; } } // [compound1] can't be a superselector of a selector // with pseudo-elements that [compound2] doesn't share. for (SimpleSelectorObj simple2 : compound2->elements()) { Pseudo_Selector_Obj pseudo2 = Cast(simple2); if (pseudo2 && pseudo2->isElement()) { if (!simpleIsSuperselectorOfCompound(pseudo2, compound1)) { return false; } } } return true; } // EO compoundIsSuperselector // ########################################################################## // Returns whether [compound1] is a superselector of [compound2]. // That is, whether [compound1] matches every element that [compound2] // matches, as well as possibly additional elements. If [parents] is // passed, it represents the parents of [compound2]. This is relevant // for pseudo selectors with selector arguments, where we may need to // know if the parent selectors in the selector argument match [parents]. // ########################################################################## bool compoundIsSuperselector( const CompoundSelectorObj& compound1, const CompoundSelectorObj& compound2, const std::vector& parents) { return compoundIsSuperselector( compound1, compound2, parents.begin(), parents.end() ); } // EO compoundIsSuperselector // ########################################################################## // Returns whether [complex1] is a superselector of [complex2]. // That is, whether [complex1] matches every element that // [complex2] matches, as well as possibly additional elements. // ########################################################################## bool complexIsSuperselector( const std::vector& complex1, const std::vector& complex2) { // Selectors with trailing operators are neither superselectors nor subselectors. if (!complex1.empty() && Cast(complex1.back())) return false; if (!complex2.empty() && Cast(complex2.back())) return false; size_t i1 = 0, i2 = 0; while (true) { size_t remaining1 = complex1.size() - i1; size_t remaining2 = complex2.size() - i2; if (remaining1 == 0 || remaining2 == 0) { return false; } // More complex selectors are never // superselectors of less complex ones. if (remaining1 > remaining2) { return false; } // Selectors with leading operators are // neither superselectors nor subselectors. if (Cast(complex1[i1])) { return false; } if (Cast(complex2[i2])) { return false; } CompoundSelectorObj compound1 = Cast(complex1[i1]); CompoundSelectorObj compound2 = Cast(complex2.back()); if (remaining1 == 1) { std::vector::const_iterator parents_to = complex2.end(); std::vector::const_iterator parents_from = complex2.begin(); std::advance(parents_from, i2 + 1); // equivalent to dart `.skip(i2 + 1)` bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to); std::vector pp; std::vector::const_iterator end = parents_to; std::vector::const_iterator beg = parents_from; while (beg != end) { pp.push_back(*beg); beg++; } return rv; } // Find the first index where `complex2.sublist(i2, afterSuperselector)` // is a subselector of [compound1]. We stop before the superselector // would encompass all of [complex2] because we know [complex1] has // more than one element, and consuming all of [complex2] wouldn't // leave anything for the rest of [complex1] to match. size_t afterSuperselector = i2 + 1; for (; afterSuperselector < complex2.size(); afterSuperselector++) { SelectorComponentObj component2 = complex2[afterSuperselector - 1]; if (CompoundSelectorObj compound2 = Cast(component2)) { std::vector::const_iterator parents_to = complex2.begin(); std::vector::const_iterator parents_from = complex2.begin(); // complex2.take(afterSuperselector - 1).skip(i2 + 1) std::advance(parents_from, i2 + 1); // equivalent to dart `.skip` std::advance(parents_to, afterSuperselector); // equivalent to dart `.take` if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) { break; } } } if (afterSuperselector == complex2.size()) { return false; } SelectorComponentObj component1 = complex1[i1 + 1], component2 = complex2[afterSuperselector]; SelectorCombinatorObj combinator1 = Cast(component1); SelectorCombinatorObj combinator2 = Cast(component2); if (!combinator1.isNull()) { if (combinator2.isNull()) { return false; } // `.a ~ .b` is a superselector of `.a + .b`, // but otherwise the combinators must match. if (combinator1->isGeneralCombinator()) { if (combinator2->isChildCombinator()) { return false; } } else if (*combinator1 != *combinator2) { return false; } // `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or // `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of // `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`. if (remaining1 == 3 && remaining2 > 3) { return false; } i1 += 2; i2 = afterSuperselector + 1; } else if (!combinator2.isNull()) { if (!combinator2->isChildCombinator()) { return false; } i1 += 1; i2 = afterSuperselector + 1; } else { i1 += 1; i2 = afterSuperselector; } } return false; } // EO complexIsSuperselector // ########################################################################## // Like [complexIsSuperselector], but compares [complex1] // and [complex2] as though they shared an implicit base // [SimpleSelector]. For example, `B` is not normally a // superselector of `B A`, since it doesn't match elements // that match `A`. However, it *is* a parent superselector, // since `B X` is a superselector of `B A X`. // ########################################################################## bool complexIsParentSuperselector( const std::vector& complex1, const std::vector& complex2) { // Try some simple heuristics to see if we can avoid allocations. if (complex1.empty() && complex2.empty()) return false; if (Cast(complex1.front())) return false; if (Cast(complex2.front())) return false; if (complex1.size() > complex2.size()) return false; // TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations... std::vector cplx1(complex1); std::vector cplx2(complex2); CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]"); cplx1.push_back(base); cplx2.push_back(base); return complexIsSuperselector(cplx1, cplx2); } // EO complexIsParentSuperselector // ########################################################################## // Returns whether [list] has a superselector for [complex]. // That is, whether an item in [list] matches every element that // [complex] matches, as well as possibly additional elements. // ########################################################################## bool listHasSuperslectorForComplex( std::vector list, ComplexSelectorObj complex) { // Return true if every [complex] selector on [list2] // is a super selector of the full selector [list1]. for (ComplexSelectorObj lhs : list) { if (complexIsSuperselector(lhs->elements(), complex->elements())) { return true; } } return false; } // listIsSuperslectorOfComplex // ########################################################################## // Returns whether [list1] is a superselector of [list2]. // That is, whether [list1] matches every element that // [list2] matches, as well as possibly additional elements. // ########################################################################## bool listIsSuperslector( const std::vector& list1, const std::vector& list2) { // Return true if every [complex] selector on [list2] // is a super selector of the full selector [list1]. for (ComplexSelectorObj complex : list2) { if (!listHasSuperslectorForComplex(list1, complex)) { return false; } } return true; } // EO listIsSuperslector // ########################################################################## // Implement selector methods (dispatch to functions) // ########################################################################## bool SelectorList::isSuperselectorOf(const SelectorList* sub) const { return listIsSuperslector(elements(), sub->elements()); } bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const { return complexIsSuperselector(elements(), sub->elements()); } // ########################################################################## // ########################################################################## }